diff --git a/.github/actions/add-card-to-project/Dockerfile b/.github/actions/add-card-to-project/Dockerfile deleted file mode 100644 index 95c2fe1628..0000000000 --- a/.github/actions/add-card-to-project/Dockerfile +++ /dev/null @@ -1,11 +0,0 @@ -# Container image that runs your code -FROM alpine:3.10 - -RUN apk add --no-cache --no-progress curl jq - -# Copies your code file from your action repository to the filesystem path `/` of the container -COPY entrypoint.sh /entrypoint.sh -RUN chmod +x /entrypoint.sh - -# Code file to execute when the docker container starts up (`entrypoint.sh`) -ENTRYPOINT ["/entrypoint.sh"] diff --git a/.github/actions/add-card-to-project/action.yml b/.github/actions/add-card-to-project/action.yml deleted file mode 100644 index e7fcba44c6..0000000000 --- a/.github/actions/add-card-to-project/action.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: 'add_card_to_project' -description: 'A GitHub Action to add a card to a project and set the card position' -author: 'Steve Richards' -branding: - icon: 'command' - color: 'blue' -inputs: - project: - description: 'The url of the project to be assigned to.' - required: true - column_name: - description: 'The column name of the project, defaults to "To do" for issues and "In progress" for pull requests.' - required: false - card_position: - description: 'The card position of the card in the column, defaults to "bottom". Valid values are "top", "bottom" and "after:".' - required: false -runs: - using: 'docker' - image: 'Dockerfile' - args: - - ${{ inputs.project }} - - ${{ inputs.column_name }} - - ${{ inputs.card_position }} diff --git a/.github/actions/add-card-to-project/entrypoint.sh b/.github/actions/add-card-to-project/entrypoint.sh deleted file mode 100644 index a67bfb1d96..0000000000 --- a/.github/actions/add-card-to-project/entrypoint.sh +++ /dev/null @@ -1,157 +0,0 @@ -#!/bin/sh -l - -PROJECT_URL="$INPUT_PROJECT" -if [ -z "$PROJECT_URL" ]; then - echo "PROJECT_URL is not defined." >&2 - exit 1 -fi - -get_project_type() { - _PROJECT_URL="$1" - - case "$_PROJECT_URL" in - https://github.com/orgs/*) - echo "org" - ;; - https://github.com/users/*) - echo "user" - ;; - https://github.com/*/projects/*) - echo "repo" - ;; - *) - echo "Invalid PROJECT_URL: $_PROJECT_URL" >&2 - exit 1 - ;; - esac - - unset _PROJECT_URL -} - -find_project_id() { - _PROJECT_TYPE="$1" - _PROJECT_URL="$2" - - case "$_PROJECT_TYPE" in - org) - _ORG_NAME=$(echo "$_PROJECT_URL" | sed -e 's@https://github.com/orgs/\([^/]\+\)/projects/[0-9]\+@\1@') - _ENDPOINT="https://api.github.com/orgs/$_ORG_NAME/projects" - ;; - user) - _USER_NAME=$(echo "$_PROJECT_URL" | sed -e 's@https://github.com/users/\([^/]\+\)/projects/[0-9]\+@\1@') - _ENDPOINT="https://api.github.com/users/$_USER_NAME/projects" - ;; - repo) - _ENDPOINT="https://api.github.com/repos/$GITHUB_REPOSITORY/projects" - ;; - esac - - _PROJECTS=$(curl -s -X GET -u "$GITHUB_ACTOR:$TOKEN" --retry 3 \ - -H 'Accept: application/vnd.github.inertia-preview+json' \ - "$_ENDPOINT") - - _PROJECTID=$(echo "$_PROJECTS" | jq -r ".[] | select(.html_url == \"$_PROJECT_URL\").id") - - if [ "$_PROJECTID" != "" ]; then - echo "$_PROJECTID" - else - echo "No project was found." >&2 - exit 1 - fi - - unset _PROJECT_TYPE _PROJECT_URL _ORG_NAME _USER_NAME _ENDPOINT _PROJECTS _PROJECTID -} - -find_column_id() { - _PROJECT_ID="$1" - _INITIAL_COLUMN_NAME="$2" - - _COLUMNS=$(curl -s -X GET -u "$GITHUB_ACTOR:$TOKEN" --retry 3 \ - -H 'Accept: application/vnd.github.inertia-preview+json' \ - "https://api.github.com/projects/$_PROJECT_ID/columns") - - - echo "$_COLUMNS" | jq -r ".[] | select(.name == \"$_INITIAL_COLUMN_NAME\").id" - unset _PROJECT_ID _INITIAL_COLUMN_NAME _COLUMNS -} - -PROJECT_TYPE=$(get_project_type "${PROJECT_URL:? required this environment variable}") - -if [ "$PROJECT_TYPE" = org ] || [ "$PROJECT_TYPE" = user ]; then - if [ -z "$MY_GITHUB_TOKEN" ]; then - echo "MY_GITHUB_TOKEN not defined" >&2 - exit 1 - fi - - TOKEN="$MY_GITHUB_TOKEN" # It's User's personal access token. It should be secret. -else - if [ -z "$GITHUB_TOKEN" ]; then - echo "GITHUB_TOKEN not defined" >&2 - exit 1 - fi - - TOKEN="$GITHUB_TOKEN" # GitHub sets. The scope in only the repository containing the workflow file. -fi - -INITIAL_COLUMN_NAME="$INPUT_COLUMN_NAME" -if [ -z "$INITIAL_COLUMN_NAME" ]; then - # assing the column name by default - INITIAL_COLUMN_NAME='To do' - if [ "$GITHUB_EVENT_NAME" == "pull_request" ] || [ "$GITHUB_EVENT_NAME" == "pull_request_target" ]; then - echo "changing col name for PR event" - INITIAL_COLUMN_NAME='In progress' - fi -fi - - -PROJECT_ID=$(find_project_id "$PROJECT_TYPE" "$PROJECT_URL") -INITIAL_COLUMN_ID=$(find_column_id "$PROJECT_ID" "${INITIAL_COLUMN_NAME:? required this environment variable}") - -if [ -z "$INITIAL_COLUMN_ID" ]; then - echo "INITIAL_COLUMN_ID is not found." >&2 - exit 1 -fi - -case "$GITHUB_EVENT_NAME" in - issues) - ISSUE_ID=$(jq -r '.issue.id' < "$GITHUB_EVENT_PATH") - - # Add this issue to the project column - _CARDS=$(curl -s -X POST -u "$GITHUB_ACTOR:$TOKEN" --retry 3 \ - -H 'Accept: application/vnd.github.inertia-preview+json' \ - -d "{\"content_type\": \"Issue\", \"content_id\": $ISSUE_ID}" \ - "https://api.github.com/projects/columns/$INITIAL_COLUMN_ID/cards") - ;; - pull_request|pull_request_target) - PULL_REQUEST_ID=$(jq -r '.pull_request.id' < "$GITHUB_EVENT_PATH") - - # Add this pull_request to the project column - _CARDS=$(curl -s -X POST -u "$GITHUB_ACTOR:$TOKEN" --retry 3 \ - -H 'Accept: application/vnd.github.inertia-preview+json' \ - -d "{\"content_type\": \"PullRequest\", \"content_id\": $PULL_REQUEST_ID}" \ - "https://api.github.com/projects/columns/$INITIAL_COLUMN_ID/cards") - ;; - *) - echo "Nothing to be done on this action: $GITHUB_EVENT_NAME" >&2 - exit 1 - ;; -esac - -CARDS_ID=$(echo "$_CARDS" | jq -r .id) -unset _CARDS - -if [ -z "$CARDS_ID" ]; then - echo "CARDS_ID is not found." >&2 - exit 1 -fi - -INITIAL_CARD_POSITION="$INPUT_CARD_POSITION" -if [ -z "$INITIAL_CARD_POSITION" ]; then - # assign the card position by default - INITIAL_CARD_POSITION='bottom' -fi - -_CARDS=$(curl -s -X POST -u "$GITHUB_ACTOR:$TOKEN" --retry 3 \ - -H 'Accept: application/vnd.github.inertia-preview+json' \ - -d "{\"position\": \"$INITIAL_CARD_POSITION\"}" \ - "https://api.github.com/projects/columns/cards/$CARDS_ID/moves") diff --git a/.github/workflows/add-to-project-board.yaml b/.github/workflows/add-to-project-board.yaml deleted file mode 100644 index 5ebb3de16e..0000000000 --- a/.github/workflows/add-to-project-board.yaml +++ /dev/null @@ -1,33 +0,0 @@ -name: Add Card to Project(s) -on: - issues: - types: [opened] - pull_request_target: - types: [opened] -env: - MY_GITHUB_TOKEN: ${{ secrets.PROJECT_GITHUB_TOKEN }} - EVENT_TYPE: $GITHUB_EVENT_NAME - -jobs: - add_card_to_project: - runs-on: ubuntu-latest - name: Add Card to Project(s) - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Get Event Type - run: echo $GITHUB_EVENT_NAME - - name: Assign NEW issues to project 1 - uses: ./.github/actions/add-card-to-project - if: github.event_name == 'issues' && github.event.action == 'opened' - with: - project: "https://github.com/orgs/lensapp/projects/1" - column_name: "Backlog" - card_position: "bottom" - - name: Assign NEW pull requests to project 1 - uses: ./.github/actions/add-card-to-project - if: github.event_name == 'pull_request_target' && github.event.action == 'opened' - with: - project: "https://github.com/orgs/lensapp/projects/1" - column_name: "PRs" - card_position: "bottom" diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9eee6fbda9..4c63c78d36 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -57,7 +57,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install git+https://${{ secrets.GH_TOKEN }}@github.com/lensapp/mkdocs-material-insiders.git + pip install mkdocs-material pip install mike - name: Checkout Release from lens diff --git a/.github/workflows/mkdocs-delete-version.yml b/.github/workflows/mkdocs-delete-version.yml index a5ff72b8cb..aa0d0d0ccf 100644 --- a/.github/workflows/mkdocs-delete-version.yml +++ b/.github/workflows/mkdocs-delete-version.yml @@ -18,7 +18,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install git+https://${{ secrets.GH_TOKEN }}@github.com/lensapp/mkdocs-material-insiders.git + pip install mkdocs-material pip install mike - name: Checkout Release from lens diff --git a/.github/workflows/mkdocs-manual.yml b/.github/workflows/mkdocs-manual.yml index adb0094d62..5656cdce77 100644 --- a/.github/workflows/mkdocs-manual.yml +++ b/.github/workflows/mkdocs-manual.yml @@ -21,7 +21,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install git+https://${{ secrets.GH_TOKEN }}@github.com/lensapp/mkdocs-material-insiders.git + pip install mkdocs-material pip install mike - name: Checkout Version from lens diff --git a/.github/workflows/mkdocs-set-default-version.yml b/.github/workflows/mkdocs-set-default-version.yml index a4bcf5fbb2..509ddc785b 100644 --- a/.github/workflows/mkdocs-set-default-version.yml +++ b/.github/workflows/mkdocs-set-default-version.yml @@ -18,7 +18,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install git+https://${{ secrets.GH_TOKEN }}@github.com/lensapp/mkdocs-material-insiders.git + pip install mkdocs-material pip install mike - name: Checkout Release from lens diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c733570f07..fb972b31d6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -121,5 +121,7 @@ jobs: retry_on: error command: npm ci - - run: npm run test:unit + - run: | + npm run build -- --ignore open-lens + npm run test:unit name: Run tests diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index e4063525ae..15223a3492 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -2,5 +2,6 @@ \ No newline at end of file diff --git a/.idea/jsLinters/eslint.xml b/.idea/jsLinters/eslint.xml new file mode 100644 index 0000000000..541945bb08 --- /dev/null +++ b/.idea/jsLinters/eslint.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/lens.iml b/.idea/lens.iml index 3bef0f9888..df2c450ec8 100644 --- a/.idea/lens.iml +++ b/.idea/lens.iml @@ -6,11 +6,12 @@ - - - + + + + diff --git a/.nxignore b/.nxignore new file mode 100644 index 0000000000..885f2fc229 --- /dev/null +++ b/.nxignore @@ -0,0 +1,4 @@ +packages/**/dist +packages/**/static/build +packages/**/build/webpack +packages/**/binaries diff --git a/mkdocs.yml b/mkdocs.yml index e0f1568b87..401bd7b12f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -53,6 +53,7 @@ theme: - toc.autohide - search.suggest - search.highlight + - content.code.copy extra_css: - stylesheets/extra.css diff --git a/package-lock.json b/package-lock.json index 946b5f8168..9d52332f11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,8 +11,8 @@ "devDependencies": { "adr": "^1.4.3", "cross-env": "^7.0.3", - "lerna": "^6.5.1", - "rimraf": "^4.4.0" + "lerna": "^6.6.1", + "rimraf": "^4.4.1" } }, "node_modules/@adobe/css-tools": { @@ -3313,6 +3313,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", + "peer": true, "dependencies": { "@jest/types": "^28.1.3", "@types/node": "*", @@ -3329,6 +3330,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "peer": true, "dependencies": { "@sinclair/typebox": "^0.24.1" }, @@ -3340,6 +3342,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "peer": true, "dependencies": { "@jest/schemas": "^28.1.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -3355,12 +3358,14 @@ "node_modules/@jest/console/node_modules/@sinclair/typebox": { "version": "0.24.51", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", + "peer": true }, "node_modules/@jest/console/node_modules/jest-util": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "peer": true, "dependencies": { "@jest/types": "^28.1.3", "@types/node": "*", @@ -3377,6 +3382,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/core/-/core-28.1.3.tgz", "integrity": "sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA==", + "peer": true, "dependencies": { "@jest/console": "^28.1.3", "@jest/reporters": "^28.1.3", @@ -3424,6 +3430,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-28.1.3.tgz", "integrity": "sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==", + "peer": true, "dependencies": { "@jest/environment": "^28.1.3", "@jest/expect": "^28.1.3", @@ -3437,6 +3444,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "peer": true, "dependencies": { "@sinclair/typebox": "^0.24.1" }, @@ -3448,6 +3456,7 @@ "version": "28.1.2", "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-28.1.2.tgz", "integrity": "sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==", + "peer": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.13", "callsites": "^3.0.0", @@ -3461,6 +3470,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-28.1.3.tgz", "integrity": "sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==", + "peer": true, "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^28.1.3", @@ -3486,6 +3496,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "peer": true, "dependencies": { "@jest/schemas": "^28.1.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -3501,12 +3512,14 @@ "node_modules/@jest/core/node_modules/@sinclair/typebox": { "version": "0.24.51", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", + "peer": true }, "node_modules/@jest/core/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==", + "peer": true, "engines": { "node": ">=10" }, @@ -3517,12 +3530,14 @@ "node_modules/@jest/core/node_modules/convert-source-map": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "peer": true }, "node_modules/@jest/core/node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "peer": true, "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -3545,6 +3560,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "peer": true, "engines": { "node": ">=10" }, @@ -3556,6 +3572,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "peer": true, "engines": { "node": ">=8" }, @@ -3567,6 +3584,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-28.1.3.tgz", "integrity": "sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==", + "peer": true, "dependencies": { "@jest/types": "^28.1.3", "@types/graceful-fs": "^4.1.3", @@ -3591,6 +3609,7 @@ "version": "28.0.2", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "peer": true, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } @@ -3599,6 +3618,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-28.1.3.tgz", "integrity": "sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==", + "peer": true, "dependencies": { "@jest/environment": "^28.1.3", "@jest/fake-timers": "^28.1.3", @@ -3631,6 +3651,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "peer": true, "dependencies": { "@jest/types": "^28.1.3", "@types/node": "*", @@ -3647,6 +3668,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "peer": true, "dependencies": { "path-key": "^3.0.0" }, @@ -3658,6 +3680,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "peer": true, "dependencies": { "@jest/schemas": "^28.1.3", "ansi-regex": "^5.0.1", @@ -3671,12 +3694,14 @@ "node_modules/@jest/core/node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "peer": true }, "node_modules/@jest/core/node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "peer": true, "dependencies": { "glob": "^7.1.3" }, @@ -3771,6 +3796,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-28.1.3.tgz", "integrity": "sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw==", + "peer": true, "dependencies": { "expect": "^28.1.3", "jest-snapshot": "^28.1.3" @@ -4094,6 +4120,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-28.1.3.tgz", "integrity": "sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg==", + "peer": true, "dependencies": { "@bcoe/v8-coverage": "^0.2.3", "@jest/console": "^28.1.3", @@ -4137,6 +4164,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "peer": true, "dependencies": { "@sinclair/typebox": "^0.24.1" }, @@ -4148,6 +4176,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-28.1.3.tgz", "integrity": "sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==", + "peer": true, "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^28.1.3", @@ -4173,6 +4202,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "peer": true, "dependencies": { "@jest/schemas": "^28.1.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -4188,17 +4218,20 @@ "node_modules/@jest/reporters/node_modules/@sinclair/typebox": { "version": "0.24.51", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", + "peer": true }, "node_modules/@jest/reporters/node_modules/convert-source-map": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "peer": true }, "node_modules/@jest/reporters/node_modules/jest-haste-map": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-28.1.3.tgz", "integrity": "sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==", + "peer": true, "dependencies": { "@jest/types": "^28.1.3", "@types/graceful-fs": "^4.1.3", @@ -4223,6 +4256,7 @@ "version": "28.0.2", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "peer": true, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } @@ -4231,6 +4265,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "peer": true, "dependencies": { "@jest/types": "^28.1.3", "@types/node": "*", @@ -4271,6 +4306,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", + "peer": true, "dependencies": { "@jest/console": "^28.1.3", "@jest/types": "^28.1.3", @@ -4285,6 +4321,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "peer": true, "dependencies": { "@sinclair/typebox": "^0.24.1" }, @@ -4296,6 +4333,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "peer": true, "dependencies": { "@jest/schemas": "^28.1.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -4311,12 +4349,14 @@ "node_modules/@jest/test-result/node_modules/@sinclair/typebox": { "version": "0.24.51", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", + "peer": true }, "node_modules/@jest/test-sequencer": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz", "integrity": "sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw==", + "peer": true, "dependencies": { "@jest/test-result": "^28.1.3", "graceful-fs": "^4.2.9", @@ -4331,6 +4371,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "peer": true, "dependencies": { "@sinclair/typebox": "^0.24.1" }, @@ -4342,6 +4383,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "peer": true, "dependencies": { "@jest/schemas": "^28.1.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -4357,12 +4399,14 @@ "node_modules/@jest/test-sequencer/node_modules/@sinclair/typebox": { "version": "0.24.51", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", + "peer": true }, "node_modules/@jest/test-sequencer/node_modules/jest-haste-map": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-28.1.3.tgz", "integrity": "sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==", + "peer": true, "dependencies": { "@jest/types": "^28.1.3", "@types/graceful-fs": "^4.1.3", @@ -4387,6 +4431,7 @@ "version": "28.0.2", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "peer": true, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } @@ -4395,6 +4440,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "peer": true, "dependencies": { "@jest/types": "^28.1.3", "@types/node": "*", @@ -4560,6 +4606,10 @@ "resolved": "packages/infrastructure/jest", "link": true }, + "node_modules/@k8slens/keyboard-shortcuts": { + "resolved": "packages/business-features/keyboard-shortcuts", + "link": true + }, "node_modules/@k8slens/legacy-extension-example": { "resolved": "packages/legacy-extension-example", "link": true @@ -4588,6 +4638,14 @@ "resolved": "packages/node-fetch", "link": true }, + "node_modules/@k8slens/react-application": { + "resolved": "packages/technical-features/react-application", + "link": true + }, + "node_modules/@k8slens/react-testing-library-discovery": { + "resolved": "packages/utility-features/react-testing-library-discovery", + "link": true + }, "node_modules/@k8slens/release-tool": { "resolved": "packages/release-tool", "link": true @@ -4658,9 +4716,9 @@ "dev": true }, "node_modules/@lerna/child-process": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@lerna/child-process/-/child-process-6.5.1.tgz", - "integrity": "sha512-QfyleXSD9slh4qM54wDaqKVPvtUH1NJMgsFc9BabqSHO1Ttpandv1EAvTCN9Lu73RbCX3LJpn+BfJmnjHbjCyw==", + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/@lerna/child-process/-/child-process-6.6.1.tgz", + "integrity": "sha512-yUCDCcRNNbI9UUsUB6FYEmDHpo5Tn/f0q5D7vhDP4i6Or8kBj82y7+e31hwfLvK2ykOYlDVs2MxAluH/+QUBOQ==", "dev": true, "dependencies": { "chalk": "^4.1.0", @@ -4731,12 +4789,12 @@ } }, "node_modules/@lerna/create": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@lerna/create/-/create-6.5.1.tgz", - "integrity": "sha512-ejERJnfA36jEuKrfM+94feLiyf2/hF2NoG923N0rE4rsmvRFPr1XLVPvAKleXW+Gdi/t1p410lJ7NKaLRMYCYw==", + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/@lerna/create/-/create-6.6.1.tgz", + "integrity": "sha512-GDmHFhQ0mr0RcXWXrsLyfMV6ch/dZV/Ped1e6sFVQhsLL9P+FFXX1ZWxa/dQQ90VWF2qWcmK0+S/L3kUz2xvTA==", "dev": true, "dependencies": { - "@lerna/child-process": "6.5.1", + "@lerna/child-process": "6.6.1", "dedent": "^0.7.0", "fs-extra": "^9.1.0", "init-package-json": "^3.0.2", @@ -4766,6 +4824,716 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@lerna/legacy-package-management": { + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/@lerna/legacy-package-management/-/legacy-package-management-6.6.1.tgz", + "integrity": "sha512-0EYxSFr34VgeudA5rvjGJSY7s4seITMVB7AJ9LRFv9QDUk6jpvapV13ZAaKnhDTxX5vNCfnJuWHXXWq0KyPF/Q==", + "dev": true, + "dependencies": { + "@npmcli/arborist": "6.2.3", + "@npmcli/run-script": "4.1.7", + "@nrwl/devkit": ">=15.5.2 < 16", + "@octokit/rest": "19.0.3", + "byte-size": "7.0.0", + "chalk": "4.1.0", + "clone-deep": "4.0.1", + "cmd-shim": "5.0.0", + "columnify": "1.6.0", + "config-chain": "1.1.12", + "conventional-changelog-core": "4.2.4", + "conventional-recommended-bump": "6.1.0", + "cosmiconfig": "7.0.0", + "dedent": "0.7.0", + "dot-prop": "6.0.1", + "execa": "5.0.0", + "file-url": "3.0.0", + "find-up": "5.0.0", + "fs-extra": "9.1.0", + "get-port": "5.1.1", + "get-stream": "6.0.0", + "git-url-parse": "13.1.0", + "glob-parent": "5.1.2", + "globby": "11.1.0", + "graceful-fs": "4.2.10", + "has-unicode": "2.0.1", + "inquirer": "8.2.4", + "is-ci": "2.0.0", + "is-stream": "2.0.0", + "libnpmpublish": "6.0.4", + "load-json-file": "6.2.0", + "make-dir": "3.1.0", + "minimatch": "3.0.5", + "multimatch": "5.0.0", + "node-fetch": "2.6.7", + "npm-package-arg": "8.1.1", + "npm-packlist": "5.1.1", + "npm-registry-fetch": "14.0.3", + "npmlog": "6.0.2", + "p-map": "4.0.0", + "p-map-series": "2.1.0", + "p-queue": "6.6.2", + "p-waterfall": "2.1.1", + "pacote": "13.6.2", + "pify": "5.0.0", + "pretty-format": "29.4.3", + "read-cmd-shim": "3.0.0", + "read-package-json": "5.0.1", + "resolve-from": "5.0.0", + "semver": "7.3.8", + "signal-exit": "3.0.7", + "slash": "3.0.0", + "ssri": "9.0.1", + "strong-log-transformer": "2.1.0", + "tar": "6.1.11", + "temp-dir": "1.0.0", + "tempy": "1.0.0", + "upath": "2.0.1", + "uuid": "8.3.2", + "write-file-atomic": "4.0.1", + "write-pkg": "4.0.0", + "yargs": "16.2.0" + }, + "engines": { + "node": "^14.15.0 || >=16.0.0" + } + }, + "node_modules/@lerna/legacy-package-management/node_modules/@npmcli/fs": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.0.tgz", + "integrity": "sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w==", + "dev": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@lerna/legacy-package-management/node_modules/cacache": { + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-17.0.5.tgz", + "integrity": "sha512-Y/PRQevNSsjAPWykl9aeGz8Pr+OI6BYM9fYDNMvOkuUiG9IhG4LEmaYrZZZvioMUEQ+cBCxT0v8wrnCURccyKA==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^9.3.1", + "lru-cache": "^7.7.1", + "minipass": "^4.0.0", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@lerna/legacy-package-management/node_modules/cacache/node_modules/fs-minipass": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.1.tgz", + "integrity": "sha512-MhaJDcFRTuLidHrIttu0RDGyyXs/IYHVmlcxfLAEFIWjc1vdLAkdwT7Ace2u7DbitWC0toKMl5eJZRYNVreIMw==", + "dev": true, + "dependencies": { + "minipass": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@lerna/legacy-package-management/node_modules/cacache/node_modules/ssri": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.1.tgz", + "integrity": "sha512-WVy6di9DlPOeBWEjMScpNipeSX2jIZBGEn5Uuo8Q7aIuFEuDX0pw8RxcOjlD1TWP4obi24ki7m/13+nFpcbXrw==", + "dev": true, + "dependencies": { + "minipass": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@lerna/legacy-package-management/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "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/@lerna/legacy-package-management/node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "node_modules/@lerna/legacy-package-management/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/@lerna/legacy-package-management/node_modules/dot-prop": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", + "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", + "dev": true, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@lerna/legacy-package-management/node_modules/execa": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.0.0.tgz", + "integrity": "sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ==", + "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/@lerna/legacy-package-management/node_modules/get-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.0.tgz", + "integrity": "sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@lerna/legacy-package-management/node_modules/glob": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.4.tgz", + "integrity": "sha512-qaSc49hojMOv1EPM4EuyITjDSgSKI0rthoHnvE81tcOi1SCVndHko7auqxdQ14eiQG2NDBJBE86+2xIrbIvrbA==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@lerna/legacy-package-management/node_modules/glob/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/@lerna/legacy-package-management/node_modules/glob/node_modules/minimatch": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.2.tgz", + "integrity": "sha512-ikHGF67ODxj7vS5NKU2wvTsFLbExee+KXVCnBWh8Cg2hVJfBMQIrlo50qru/09E0EifjnU8dZhJ/iHhyXJM6Mw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@lerna/legacy-package-management/node_modules/hosted-git-info": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz", + "integrity": "sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==", + "dev": true, + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@lerna/legacy-package-management/node_modules/inquirer": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", + "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.1", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "ora": "^5.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@lerna/legacy-package-management/node_modules/inquirer/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@lerna/legacy-package-management/node_modules/is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "dependencies": { + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/@lerna/legacy-package-management/node_modules/is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@lerna/legacy-package-management/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@lerna/legacy-package-management/node_modules/make-fetch-happen": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.0.3.tgz", + "integrity": "sha512-oPLh5m10lRNNZDjJ2kP8UpboUx2uFXVaVweVe/lWut4iHWcQEmfqSVJt2ihZsFI8HbpwyyocaXbCAWf0g1ukIA==", + "dev": true, + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^17.0.0", + "http-cache-semantics": "^4.1.1", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^4.0.0", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@lerna/legacy-package-management/node_modules/make-fetch-happen/node_modules/ssri": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.1.tgz", + "integrity": "sha512-WVy6di9DlPOeBWEjMScpNipeSX2jIZBGEn5Uuo8Q7aIuFEuDX0pw8RxcOjlD1TWP4obi24ki7m/13+nFpcbXrw==", + "dev": true, + "dependencies": { + "minipass": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@lerna/legacy-package-management/node_modules/minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@lerna/legacy-package-management/node_modules/minipass-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.1.tgz", + "integrity": "sha512-t9/wowtf7DYkwz8cfMSt0rMwiyNIBXf5CKZ3S5ZMqRqMYT0oLTp0x1WorMI9WTwvaPg21r1JbFxJMum8JrLGfw==", + "dev": true, + "dependencies": { + "minipass": "^4.0.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/@lerna/legacy-package-management/node_modules/node-fetch": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", + "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/@lerna/legacy-package-management/node_modules/npm-registry-fetch": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-14.0.3.tgz", + "integrity": "sha512-YaeRbVNpnWvsGOjX2wk5s85XJ7l1qQBGAp724h8e2CZFFhMSuw9enom7K1mWVUtvXO1uUSFIAPofQK0pPN0ZcA==", + "dev": true, + "dependencies": { + "make-fetch-happen": "^11.0.0", + "minipass": "^4.0.0", + "minipass-fetch": "^3.0.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^10.0.0", + "proc-log": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@lerna/legacy-package-management/node_modules/npm-registry-fetch/node_modules/npm-package-arg": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-10.1.0.tgz", + "integrity": "sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^6.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@lerna/legacy-package-management/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/@lerna/legacy-package-management/node_modules/pify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-5.0.0.tgz", + "integrity": "sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@lerna/legacy-package-management/node_modules/pretty-format": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.4.3.tgz", + "integrity": "sha512-cvpcHTc42lcsvOOAzd3XuNWTcvk1Jmnzqeu+WsOuiPmxUJTnkbAcFNsRKvEpBEUFVUgy/GTZLulZDcDEi+CIlA==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.4.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@lerna/legacy-package-management/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/@lerna/legacy-package-management/node_modules/proc-log": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", + "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@lerna/legacy-package-management/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/@lerna/legacy-package-management/node_modules/socks-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", + "dev": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@lerna/legacy-package-management/node_modules/tar": { + "version": "6.1.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", + "integrity": "sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==", + "dev": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@lerna/legacy-package-management/node_modules/tar/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@lerna/legacy-package-management/node_modules/tempy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-1.0.0.tgz", + "integrity": "sha512-eLXG5B1G0mRPHmgH2WydPl5v4jH35qEn3y/rA/aahKhIa91Pn119SsU7n7v/433gtT9ONzC8ISvNHIh2JSTm0w==", + "dev": true, + "dependencies": { + "del": "^6.0.0", + "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/@lerna/legacy-package-management/node_modules/tempy/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/@lerna/legacy-package-management/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "node_modules/@lerna/legacy-package-management/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/@lerna/legacy-package-management/node_modules/unique-filename": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", + "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", + "dev": true, + "dependencies": { + "unique-slug": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@lerna/legacy-package-management/node_modules/unique-slug": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", + "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@lerna/legacy-package-management/node_modules/validate-npm-package-name": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz", + "integrity": "sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ==", + "dev": true, + "dependencies": { + "builtins": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@lerna/legacy-package-management/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "node_modules/@lerna/legacy-package-management/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/@lerna/legacy-package-management/node_modules/write-file-atomic": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.1.tgz", + "integrity": "sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/@lerna/legacy-package-management/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/@lerna/legacy-package-management/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/@malept/cross-spawn-promise": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", @@ -5050,63 +5818,304 @@ } }, "node_modules/@npmcli/arborist": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-5.3.0.tgz", - "integrity": "sha512-+rZ9zgL1lnbl8Xbb1NQdMjveOMwj4lIYfcDtyJHHi5x4X8jtR6m8SXooJMZy5vmFVZ8w7A2Bnd/oX9eTuU8w5A==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-6.2.3.tgz", + "integrity": "sha512-lpGOC2ilSJXcc2zfW9QtukcCTcMbl3fVI0z4wvFB2AFIl0C+Q6Wv7ccrpdrQa8rvJ1ZVuc6qkX7HVTyKlzGqKA==", "dev": true, "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", - "@npmcli/installed-package-contents": "^1.0.7", - "@npmcli/map-workspaces": "^2.0.3", - "@npmcli/metavuln-calculator": "^3.0.1", - "@npmcli/move-file": "^2.0.0", - "@npmcli/name-from-folder": "^1.0.1", - "@npmcli/node-gyp": "^2.0.0", - "@npmcli/package-json": "^2.0.0", - "@npmcli/run-script": "^4.1.3", - "bin-links": "^3.0.0", - "cacache": "^16.0.6", + "@npmcli/fs": "^3.1.0", + "@npmcli/installed-package-contents": "^2.0.0", + "@npmcli/map-workspaces": "^3.0.2", + "@npmcli/metavuln-calculator": "^5.0.0", + "@npmcli/name-from-folder": "^2.0.0", + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/package-json": "^3.0.0", + "@npmcli/query": "^3.0.0", + "@npmcli/run-script": "^6.0.0", + "bin-links": "^4.0.1", + "cacache": "^17.0.4", "common-ancestor-path": "^1.0.1", - "json-parse-even-better-errors": "^2.3.1", + "hosted-git-info": "^6.1.1", + "json-parse-even-better-errors": "^3.0.0", "json-stringify-nice": "^1.1.4", - "mkdirp": "^1.0.4", - "mkdirp-infer-owner": "^2.0.0", - "nopt": "^5.0.0", - "npm-install-checks": "^5.0.0", - "npm-package-arg": "^9.0.0", - "npm-pick-manifest": "^7.0.0", - "npm-registry-fetch": "^13.0.0", - "npmlog": "^6.0.2", - "pacote": "^13.6.1", - "parse-conflict-json": "^2.0.1", - "proc-log": "^2.0.0", + "minimatch": "^6.1.6", + "nopt": "^7.0.0", + "npm-install-checks": "^6.0.0", + "npm-package-arg": "^10.1.0", + "npm-pick-manifest": "^8.0.1", + "npm-registry-fetch": "^14.0.3", + "npmlog": "^7.0.1", + "pacote": "^15.0.8", + "parse-conflict-json": "^3.0.0", + "proc-log": "^3.0.0", "promise-all-reject-late": "^1.0.0", "promise-call-limit": "^1.0.1", - "read-package-json-fast": "^2.0.2", - "readdir-scoped-modules": "^1.1.0", - "rimraf": "^3.0.2", + "read-package-json-fast": "^3.0.2", "semver": "^7.3.7", - "ssri": "^9.0.0", - "treeverse": "^2.0.0", + "ssri": "^10.0.1", + "treeverse": "^3.0.0", "walk-up-path": "^1.0.0" }, "bin": { "arborist": "bin/index.js" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/@npmcli/fs": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.0.tgz", + "integrity": "sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w==", + "dev": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/@npmcli/git": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-4.0.4.tgz", + "integrity": "sha512-5yZghx+u5M47LghaybLCkdSyFzV/w4OuH12d96HO389Ik9CDsLaDZJVynSGGVJOLn6gy/k7Dz5XYcplM3uxXRg==", + "dev": true, + "dependencies": { + "@npmcli/promise-spawn": "^6.0.0", + "lru-cache": "^7.4.4", + "npm-pick-manifest": "^8.0.0", + "proc-log": "^3.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/@npmcli/git/node_modules/which": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-3.0.0.tgz", + "integrity": "sha512-nla//68K9NU6yRiwDY/Q8aU6siKlSs64aEC7+IV56QoAuyQT2ovsJcgGYGyqMOmI/CGN1BOR6mM5EN0FBO+zyQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/@npmcli/promise-spawn": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-6.0.2.tgz", + "integrity": "sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg==", + "dev": true, + "dependencies": { + "which": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/@npmcli/promise-spawn/node_modules/which": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-3.0.0.tgz", + "integrity": "sha512-nla//68K9NU6yRiwDY/Q8aU6siKlSs64aEC7+IV56QoAuyQT2ovsJcgGYGyqMOmI/CGN1BOR6mM5EN0FBO+zyQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/@npmcli/run-script": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-6.0.0.tgz", + "integrity": "sha512-ql+AbRur1TeOdl1FY+RAwGW9fcr4ZwiVKabdvm93mujGREVuVLbdkXRJDrkTXSdCjaxYydr1wlA2v67jxWG5BQ==", + "dev": true, + "dependencies": { + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/promise-spawn": "^6.0.0", + "node-gyp": "^9.0.0", + "read-package-json-fast": "^3.0.0", + "which": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/@npmcli/run-script/node_modules/which": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-3.0.0.tgz", + "integrity": "sha512-nla//68K9NU6yRiwDY/Q8aU6siKlSs64aEC7+IV56QoAuyQT2ovsJcgGYGyqMOmI/CGN1BOR6mM5EN0FBO+zyQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/arborist/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/@npmcli/arborist/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/@npmcli/arborist/node_modules/cacache": { + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-17.0.5.tgz", + "integrity": "sha512-Y/PRQevNSsjAPWykl9aeGz8Pr+OI6BYM9fYDNMvOkuUiG9IhG4LEmaYrZZZvioMUEQ+cBCxT0v8wrnCURccyKA==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^9.3.1", + "lru-cache": "^7.7.1", + "minipass": "^4.0.0", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/cacache/node_modules/glob": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.4.tgz", + "integrity": "sha512-qaSc49hojMOv1EPM4EuyITjDSgSKI0rthoHnvE81tcOi1SCVndHko7auqxdQ14eiQG2NDBJBE86+2xIrbIvrbA==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/arborist/node_modules/cacache/node_modules/minimatch": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.2.tgz", + "integrity": "sha512-ikHGF67ODxj7vS5NKU2wvTsFLbExee+KXVCnBWh8Cg2hVJfBMQIrlo50qru/09E0EifjnU8dZhJ/iHhyXJM6Mw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/arborist/node_modules/fs-minipass": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.1.tgz", + "integrity": "sha512-MhaJDcFRTuLidHrIttu0RDGyyXs/IYHVmlcxfLAEFIWjc1vdLAkdwT7Ace2u7DbitWC0toKMl5eJZRYNVreIMw==", + "dev": true, + "dependencies": { + "minipass": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/@npmcli/arborist/node_modules/hosted-git-info": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.2.1.tgz", - "integrity": "sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz", + "integrity": "sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==", "dev": true, "dependencies": { "lru-cache": "^7.5.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/ignore-walk": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.2.tgz", + "integrity": "sha512-ezmQ1Dg2b3jVZh2Dh+ar6Eu2MqNSTkyb32HU2MAQQQX9tKM3q/UQ/9lf03lQ5hW+fOeoMnwxwkleZ0xcNp0/qg==", + "dev": true, + "dependencies": { + "minimatch": "^7.4.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/ignore-walk/node_modules/minimatch": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.4.tgz", + "integrity": "sha512-T+8B3kNrLP7jDb5eaC4rUIp6DKoeTSb6f9SwF2phcY2gxJUA0GEf1i29/FHxBMEfx0ppWlr434/D0P+6jb8bOQ==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/arborist/node_modules/json-parse-even-better-errors": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz", + "integrity": "sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/@npmcli/arborist/node_modules/lru-cache": { @@ -5118,21 +6127,542 @@ "node": ">=12" } }, - "node_modules/@npmcli/arborist/node_modules/npm-package-arg": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-9.1.2.tgz", - "integrity": "sha512-pzd9rLEx4TfNJkovvlBSLGhq31gGu2QDexFPWT19yCDh0JgnRhlBLNo5759N0AJmBk+kQ9Y/hXoLnlgFD+ukmg==", + "node_modules/@npmcli/arborist/node_modules/make-fetch-happen": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", + "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", "dev": true, "dependencies": { - "hosted-git-info": "^5.0.0", - "proc-log": "^2.0.1", - "semver": "^7.3.5", - "validate-npm-package-name": "^4.0.0" + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^2.0.3", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" }, "engines": { "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/@npmcli/arborist/node_modules/make-fetch-happen/node_modules/@npmcli/fs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", + "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", + "dev": true, + "dependencies": { + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/make-fetch-happen/node_modules/cacache": { + "version": "16.1.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz", + "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/make-fetch-happen/node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/arborist/node_modules/make-fetch-happen/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/arborist/node_modules/make-fetch-happen/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/arborist/node_modules/make-fetch-happen/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@npmcli/arborist/node_modules/make-fetch-happen/node_modules/ssri": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", + "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", + "dev": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/make-fetch-happen/node_modules/unique-filename": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", + "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==", + "dev": true, + "dependencies": { + "unique-slug": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/make-fetch-happen/node_modules/unique-slug": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz", + "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/minimatch": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-6.2.0.tgz", + "integrity": "sha512-sauLxniAmvnhhRjFwPNnJKaPFYyddAgbYdeUpHULtCT/GhzdCx/MDNy+Y40lBxTQUrMzDE8e0S43Z5uqfO0REg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/arborist/node_modules/minipass-fetch": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", + "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", + "dev": true, + "dependencies": { + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/@npmcli/arborist/node_modules/minipass-fetch/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@npmcli/arborist/node_modules/node-gyp": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.3.1.tgz", + "integrity": "sha512-4Q16ZCqq3g8awk6UplT7AuxQ35XN4R/yf/+wSAwcBUAjg7l58RTactWaP8fIDTi0FzI7YcVLujwExakZlfWkXg==", + "dev": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^10.0.3", + "nopt": "^6.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^12.13 || ^14.13 || >=16" + } + }, + "node_modules/@npmcli/arborist/node_modules/node-gyp/node_modules/nopt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", + "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", + "dev": true, + "dependencies": { + "abbrev": "^1.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/node-gyp/node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "dev": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/nopt": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.1.0.tgz", + "integrity": "sha512-ZFPLe9Iu0tnx7oWhFxAo4s7QTn8+NNDDxYNaKLjE7Dp0tbakQ3M1QhQzsnzXHQBTUO3K9BmwaxnyO8Ayn2I95Q==", + "dev": true, + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/nopt/node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/normalize-package-data": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-5.0.0.tgz", + "integrity": "sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==", + "dev": true, + "dependencies": { + "hosted-git-info": "^6.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/npm-package-arg": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-10.1.0.tgz", + "integrity": "sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^6.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/npm-packlist": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-7.0.4.tgz", + "integrity": "sha512-d6RGEuRrNS5/N84iglPivjaJPxhDbZmlbTwTDX2IbcRHG5bZCdtysYMhwiPvcF4GisXHGn7xsxv+GQ7T/02M5Q==", + "dev": true, + "dependencies": { + "ignore-walk": "^6.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/npm-registry-fetch": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-14.0.3.tgz", + "integrity": "sha512-YaeRbVNpnWvsGOjX2wk5s85XJ7l1qQBGAp724h8e2CZFFhMSuw9enom7K1mWVUtvXO1uUSFIAPofQK0pPN0ZcA==", + "dev": true, + "dependencies": { + "make-fetch-happen": "^11.0.0", + "minipass": "^4.0.0", + "minipass-fetch": "^3.0.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^10.0.0", + "proc-log": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/npm-registry-fetch/node_modules/make-fetch-happen": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.0.3.tgz", + "integrity": "sha512-oPLh5m10lRNNZDjJ2kP8UpboUx2uFXVaVweVe/lWut4iHWcQEmfqSVJt2ihZsFI8HbpwyyocaXbCAWf0g1ukIA==", + "dev": true, + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^17.0.0", + "http-cache-semantics": "^4.1.1", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^4.0.0", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/npm-registry-fetch/node_modules/minipass-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.1.tgz", + "integrity": "sha512-t9/wowtf7DYkwz8cfMSt0rMwiyNIBXf5CKZ3S5ZMqRqMYT0oLTp0x1WorMI9WTwvaPg21r1JbFxJMum8JrLGfw==", + "dev": true, + "dependencies": { + "minipass": "^4.0.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/@npmcli/arborist/node_modules/npmlog": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-7.0.1.tgz", + "integrity": "sha512-uJ0YFk/mCQpLBt+bxN88AKd+gyqZvZDbtiNxk6Waqcj2aPRyfVx8ITawkyQynxUagInjdYT1+qj4NfA5KJJUxg==", + "dev": true, + "dependencies": { + "are-we-there-yet": "^4.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^5.0.0", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/npmlog/node_modules/are-we-there-yet": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-4.0.0.tgz", + "integrity": "sha512-nSXlV+u3vtVjRgihdTzbfWYzxPWGo424zPgQbHD0ZqIla3jqYAewDcvee0Ua2hjS5IfTAmjGlx1Jf0PKwjZDEw==", + "dev": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^4.1.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/npmlog/node_modules/gauge": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-5.0.0.tgz", + "integrity": "sha512-0s5T5eciEG7Q3ugkxAkFtaDhrrhXsCRivA5y8C9WMHWuI8UlMOJg7+Iwf7Mccii+Dfs3H5jHepU0joPVyQU0Lw==", + "dev": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/pacote": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-15.1.1.tgz", + "integrity": "sha512-eeqEe77QrA6auZxNHIp+1TzHQ0HBKf5V6c8zcaYZ134EJe1lCi+fjXATkNiEEfbG+e50nu02GLvUtmZcGOYabQ==", + "dev": true, + "dependencies": { + "@npmcli/git": "^4.0.0", + "@npmcli/installed-package-contents": "^2.0.1", + "@npmcli/promise-spawn": "^6.0.1", + "@npmcli/run-script": "^6.0.0", + "cacache": "^17.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^4.0.0", + "npm-package-arg": "^10.0.0", + "npm-packlist": "^7.0.0", + "npm-pick-manifest": "^8.0.0", + "npm-registry-fetch": "^14.0.0", + "proc-log": "^3.0.0", + "promise-retry": "^2.0.1", + "read-package-json": "^6.0.0", + "read-package-json-fast": "^3.0.0", + "sigstore": "^1.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "lib/bin.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/proc-log": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", + "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/read-package-json": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-6.0.1.tgz", + "integrity": "sha512-AaHqXxfAVa+fNL07x8iAghfKOds/XXsu7zoouIVsbm7PEbQ3nMWXlvjcbrNLjElnUHWQtAo4QEa0RXuvD4XlpA==", + "dev": true, + "dependencies": { + "glob": "^9.3.0", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^5.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/read-package-json/node_modules/glob": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.4.tgz", + "integrity": "sha512-qaSc49hojMOv1EPM4EuyITjDSgSKI0rthoHnvE81tcOi1SCVndHko7auqxdQ14eiQG2NDBJBE86+2xIrbIvrbA==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/arborist/node_modules/read-package-json/node_modules/minimatch": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.2.tgz", + "integrity": "sha512-ikHGF67ODxj7vS5NKU2wvTsFLbExee+KXVCnBWh8Cg2hVJfBMQIrlo50qru/09E0EifjnU8dZhJ/iHhyXJM6Mw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/arborist/node_modules/readable-stream": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.3.0.tgz", + "integrity": "sha512-MuEnA0lbSi7JS8XM+WNJlWZkHAAdm7gETHdFK//Q/mChGyj2akEFtdLZh32jSdkWGbRwCW9pn6g3LWDdDeZnBQ==", + "dev": true, + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/@npmcli/arborist/node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -5148,6 +6678,74 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@npmcli/arborist/node_modules/socks-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", + "dev": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@npmcli/arborist/node_modules/ssri": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.1.tgz", + "integrity": "sha512-WVy6di9DlPOeBWEjMScpNipeSX2jIZBGEn5Uuo8Q7aIuFEuDX0pw8RxcOjlD1TWP4obi24ki7m/13+nFpcbXrw==", + "dev": true, + "dependencies": { + "minipass": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/unique-filename": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", + "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", + "dev": true, + "dependencies": { + "unique-slug": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/unique-slug": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", + "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/validate-npm-package-name": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz", + "integrity": "sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ==", + "dev": true, + "dependencies": { + "builtins": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/arborist/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/@npmcli/fs": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", @@ -5181,6 +6779,18 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/@npmcli/git/node_modules/hosted-git-info": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.2.1.tgz", + "integrity": "sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==", + "dev": true, + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/@npmcli/git/node_modules/lru-cache": { "version": "7.18.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", @@ -5190,37 +6800,88 @@ "node": ">=12" } }, - "node_modules/@npmcli/installed-package-contents": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz", - "integrity": "sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw==", + "node_modules/@npmcli/git/node_modules/npm-install-checks": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-5.0.0.tgz", + "integrity": "sha512-65lUsMI8ztHCxFz5ckCEC44DRvEGdZX5usQFriauxHEwt7upv1FKaQEmAtU0YnOAdwuNWCmk64xYiQABNrEyLA==", "dev": true, "dependencies": { - "npm-bundled": "^1.1.1", - "npm-normalize-package-bin": "^1.0.1" - }, - "bin": { - "installed-package-contents": "index.js" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/@npmcli/map-workspaces": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@npmcli/map-workspaces/-/map-workspaces-2.0.4.tgz", - "integrity": "sha512-bMo0aAfwhVwqoVM5UzX1DJnlvVvzDCHae821jv48L1EsrYwfOZChlqWYXEtto/+BkBXetPbEWgau++/brh4oVg==", - "dev": true, - "dependencies": { - "@npmcli/name-from-folder": "^1.0.1", - "glob": "^8.0.1", - "minimatch": "^5.0.1", - "read-package-json-fast": "^2.0.3" + "semver": "^7.1.1" }, "engines": { "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/@npmcli/git/node_modules/npm-normalize-package-bin": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", + "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/git/node_modules/npm-package-arg": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-9.1.2.tgz", + "integrity": "sha512-pzd9rLEx4TfNJkovvlBSLGhq31gGu2QDexFPWT19yCDh0JgnRhlBLNo5759N0AJmBk+kQ9Y/hXoLnlgFD+ukmg==", + "dev": true, + "dependencies": { + "hosted-git-info": "^5.0.0", + "proc-log": "^2.0.1", + "semver": "^7.3.5", + "validate-npm-package-name": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/git/node_modules/npm-pick-manifest": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-7.0.2.tgz", + "integrity": "sha512-gk37SyRmlIjvTfcYl6RzDbSmS9Y4TOBXfsPnoYqTHARNgWbyDiCSMLUpmALDj4jjcTZpURiEfsSHJj9k7EV4Rw==", + "dev": true, + "dependencies": { + "npm-install-checks": "^5.0.0", + "npm-normalize-package-bin": "^2.0.0", + "npm-package-arg": "^9.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/installed-package-contents": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-2.0.2.tgz", + "integrity": "sha512-xACzLPhnfD51GKvTOOuNX2/V4G4mz9/1I2MfDoye9kBM3RYe5g2YbscsaGoTlaWqkxeiapBWyseULVKpSVHtKQ==", + "dev": true, + "dependencies": { + "npm-bundled": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "bin": { + "installed-package-contents": "lib/index.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/map-workspaces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/map-workspaces/-/map-workspaces-3.0.3.tgz", + "integrity": "sha512-HlCvFuTzw4UNoKyZdqiNrln+qMF71QJkxy2dsusV8QQdoa89e2TF4dATCzBxbl4zzRzdDoWWyP5ADVrNAH9cRQ==", + "dev": true, + "dependencies": { + "@npmcli/name-from-folder": "^2.0.0", + "glob": "^9.3.1", + "minimatch": "^7.4.2", + "read-package-json-fast": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/@npmcli/map-workspaces/node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -5231,6 +6892,329 @@ } }, "node_modules/@npmcli/map-workspaces/node_modules/glob": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.4.tgz", + "integrity": "sha512-qaSc49hojMOv1EPM4EuyITjDSgSKI0rthoHnvE81tcOi1SCVndHko7auqxdQ14eiQG2NDBJBE86+2xIrbIvrbA==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/map-workspaces/node_modules/glob/node_modules/minimatch": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.2.tgz", + "integrity": "sha512-ikHGF67ODxj7vS5NKU2wvTsFLbExee+KXVCnBWh8Cg2hVJfBMQIrlo50qru/09E0EifjnU8dZhJ/iHhyXJM6Mw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/map-workspaces/node_modules/minimatch": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.4.tgz", + "integrity": "sha512-T+8B3kNrLP7jDb5eaC4rUIp6DKoeTSb6f9SwF2phcY2gxJUA0GEf1i29/FHxBMEfx0ppWlr434/D0P+6jb8bOQ==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/metavuln-calculator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/metavuln-calculator/-/metavuln-calculator-5.0.0.tgz", + "integrity": "sha512-BBFQx4M12wiEuVwCgtX/Depx0B/+NHMwDWOlXT41/Pdy5W/1Fenk+hibUlMSrFWwASbX+fY90UbILAEIYH02/A==", + "dev": true, + "dependencies": { + "cacache": "^17.0.0", + "json-parse-even-better-errors": "^3.0.0", + "pacote": "^15.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/@npmcli/fs": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.0.tgz", + "integrity": "sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w==", + "dev": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/@npmcli/git": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-4.0.4.tgz", + "integrity": "sha512-5yZghx+u5M47LghaybLCkdSyFzV/w4OuH12d96HO389Ik9CDsLaDZJVynSGGVJOLn6gy/k7Dz5XYcplM3uxXRg==", + "dev": true, + "dependencies": { + "@npmcli/promise-spawn": "^6.0.0", + "lru-cache": "^7.4.4", + "npm-pick-manifest": "^8.0.0", + "proc-log": "^3.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/@npmcli/promise-spawn": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-6.0.2.tgz", + "integrity": "sha512-gGq0NJkIGSwdbUt4yhdF8ZrmkGKVz9vAdVzpOfnom+V8PLSmSOVhZwbNvZZS1EYcJN5hzzKBxmmVVAInM6HQLg==", + "dev": true, + "dependencies": { + "which": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/@npmcli/run-script": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-6.0.0.tgz", + "integrity": "sha512-ql+AbRur1TeOdl1FY+RAwGW9fcr4ZwiVKabdvm93mujGREVuVLbdkXRJDrkTXSdCjaxYydr1wlA2v67jxWG5BQ==", + "dev": true, + "dependencies": { + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/promise-spawn": "^6.0.0", + "node-gyp": "^9.0.0", + "read-package-json-fast": "^3.0.0", + "which": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/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/@npmcli/metavuln-calculator/node_modules/cacache": { + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-17.0.5.tgz", + "integrity": "sha512-Y/PRQevNSsjAPWykl9aeGz8Pr+OI6BYM9fYDNMvOkuUiG9IhG4LEmaYrZZZvioMUEQ+cBCxT0v8wrnCURccyKA==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^9.3.1", + "lru-cache": "^7.7.1", + "minipass": "^4.0.0", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/fs-minipass": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.1.tgz", + "integrity": "sha512-MhaJDcFRTuLidHrIttu0RDGyyXs/IYHVmlcxfLAEFIWjc1vdLAkdwT7Ace2u7DbitWC0toKMl5eJZRYNVreIMw==", + "dev": true, + "dependencies": { + "minipass": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/glob": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.4.tgz", + "integrity": "sha512-qaSc49hojMOv1EPM4EuyITjDSgSKI0rthoHnvE81tcOi1SCVndHko7auqxdQ14eiQG2NDBJBE86+2xIrbIvrbA==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/hosted-git-info": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz", + "integrity": "sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==", + "dev": true, + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/ignore-walk": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-6.0.2.tgz", + "integrity": "sha512-ezmQ1Dg2b3jVZh2Dh+ar6Eu2MqNSTkyb32HU2MAQQQX9tKM3q/UQ/9lf03lQ5hW+fOeoMnwxwkleZ0xcNp0/qg==", + "dev": true, + "dependencies": { + "minimatch": "^7.4.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/ignore-walk/node_modules/minimatch": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.4.tgz", + "integrity": "sha512-T+8B3kNrLP7jDb5eaC4rUIp6DKoeTSb6f9SwF2phcY2gxJUA0GEf1i29/FHxBMEfx0ppWlr434/D0P+6jb8bOQ==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/json-parse-even-better-errors": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz", + "integrity": "sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/make-fetch-happen": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", + "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", + "dev": true, + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^2.0.3", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/make-fetch-happen/node_modules/@npmcli/fs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", + "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", + "dev": true, + "dependencies": { + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/make-fetch-happen/node_modules/cacache": { + "version": "16.1.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz", + "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/make-fetch-happen/node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/make-fetch-happen/node_modules/glob": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", @@ -5249,7 +7233,7 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@npmcli/map-workspaces/node_modules/minimatch": { + "node_modules/@npmcli/metavuln-calculator/node_modules/make-fetch-happen/node_modules/minimatch": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", @@ -5261,21 +7245,493 @@ "node": ">=10" } }, - "node_modules/@npmcli/metavuln-calculator": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/metavuln-calculator/-/metavuln-calculator-3.1.1.tgz", - "integrity": "sha512-n69ygIaqAedecLeVH3KnO39M6ZHiJ2dEv5A7DGvcqCB8q17BGUgW8QaanIkbWUo2aYGZqJaOORTLAlIvKjNDKA==", + "node_modules/@npmcli/metavuln-calculator/node_modules/make-fetch-happen/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dev": true, "dependencies": { - "cacache": "^16.0.0", - "json-parse-even-better-errors": "^2.3.1", - "pacote": "^13.0.3", - "semver": "^7.3.5" + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/make-fetch-happen/node_modules/ssri": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", + "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", + "dev": true, + "dependencies": { + "minipass": "^3.1.1" }, "engines": { "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/@npmcli/metavuln-calculator/node_modules/make-fetch-happen/node_modules/unique-filename": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", + "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==", + "dev": true, + "dependencies": { + "unique-slug": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/make-fetch-happen/node_modules/unique-slug": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz", + "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/minimatch": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.2.tgz", + "integrity": "sha512-ikHGF67ODxj7vS5NKU2wvTsFLbExee+KXVCnBWh8Cg2hVJfBMQIrlo50qru/09E0EifjnU8dZhJ/iHhyXJM6Mw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/minipass-fetch": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", + "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", + "dev": true, + "dependencies": { + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/minipass-fetch/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/node-gyp": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.3.1.tgz", + "integrity": "sha512-4Q16ZCqq3g8awk6UplT7AuxQ35XN4R/yf/+wSAwcBUAjg7l58RTactWaP8fIDTi0FzI7YcVLujwExakZlfWkXg==", + "dev": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^10.0.3", + "nopt": "^6.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^12.13 || ^14.13 || >=16" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/node-gyp/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/node-gyp/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/node-gyp/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/node-gyp/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/nopt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", + "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", + "dev": true, + "dependencies": { + "abbrev": "^1.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/normalize-package-data": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-5.0.0.tgz", + "integrity": "sha512-h9iPVIfrVZ9wVYQnxFgtw1ugSvGEMOlyPWWtm8BMJhnwyEL/FLbYbTY3V3PpjI/BUK67n9PEWDu6eHzu1fB15Q==", + "dev": true, + "dependencies": { + "hosted-git-info": "^6.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/npm-package-arg": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-10.1.0.tgz", + "integrity": "sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^6.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/npm-packlist": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-7.0.4.tgz", + "integrity": "sha512-d6RGEuRrNS5/N84iglPivjaJPxhDbZmlbTwTDX2IbcRHG5bZCdtysYMhwiPvcF4GisXHGn7xsxv+GQ7T/02M5Q==", + "dev": true, + "dependencies": { + "ignore-walk": "^6.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/npm-registry-fetch": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-14.0.3.tgz", + "integrity": "sha512-YaeRbVNpnWvsGOjX2wk5s85XJ7l1qQBGAp724h8e2CZFFhMSuw9enom7K1mWVUtvXO1uUSFIAPofQK0pPN0ZcA==", + "dev": true, + "dependencies": { + "make-fetch-happen": "^11.0.0", + "minipass": "^4.0.0", + "minipass-fetch": "^3.0.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^10.0.0", + "proc-log": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/npm-registry-fetch/node_modules/make-fetch-happen": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.0.3.tgz", + "integrity": "sha512-oPLh5m10lRNNZDjJ2kP8UpboUx2uFXVaVweVe/lWut4iHWcQEmfqSVJt2ihZsFI8HbpwyyocaXbCAWf0g1ukIA==", + "dev": true, + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^17.0.0", + "http-cache-semantics": "^4.1.1", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^4.0.0", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/npm-registry-fetch/node_modules/minipass-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.1.tgz", + "integrity": "sha512-t9/wowtf7DYkwz8cfMSt0rMwiyNIBXf5CKZ3S5ZMqRqMYT0oLTp0x1WorMI9WTwvaPg21r1JbFxJMum8JrLGfw==", + "dev": true, + "dependencies": { + "minipass": "^4.0.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/pacote": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-15.1.1.tgz", + "integrity": "sha512-eeqEe77QrA6auZxNHIp+1TzHQ0HBKf5V6c8zcaYZ134EJe1lCi+fjXATkNiEEfbG+e50nu02GLvUtmZcGOYabQ==", + "dev": true, + "dependencies": { + "@npmcli/git": "^4.0.0", + "@npmcli/installed-package-contents": "^2.0.1", + "@npmcli/promise-spawn": "^6.0.1", + "@npmcli/run-script": "^6.0.0", + "cacache": "^17.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^4.0.0", + "npm-package-arg": "^10.0.0", + "npm-packlist": "^7.0.0", + "npm-pick-manifest": "^8.0.0", + "npm-registry-fetch": "^14.0.0", + "proc-log": "^3.0.0", + "promise-retry": "^2.0.1", + "read-package-json": "^6.0.0", + "read-package-json-fast": "^3.0.0", + "sigstore": "^1.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "lib/bin.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/proc-log": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", + "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/read-package-json": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-6.0.1.tgz", + "integrity": "sha512-AaHqXxfAVa+fNL07x8iAghfKOds/XXsu7zoouIVsbm7PEbQ3nMWXlvjcbrNLjElnUHWQtAo4QEa0RXuvD4XlpA==", + "dev": true, + "dependencies": { + "glob": "^9.3.0", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^5.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/socks-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", + "dev": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/ssri": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.1.tgz", + "integrity": "sha512-WVy6di9DlPOeBWEjMScpNipeSX2jIZBGEn5Uuo8Q7aIuFEuDX0pw8RxcOjlD1TWP4obi24ki7m/13+nFpcbXrw==", + "dev": true, + "dependencies": { + "minipass": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/unique-filename": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", + "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", + "dev": true, + "dependencies": { + "unique-slug": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/unique-slug": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", + "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/validate-npm-package-name": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz", + "integrity": "sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ==", + "dev": true, + "dependencies": { + "builtins": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/which": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-3.0.0.tgz", + "integrity": "sha512-nla//68K9NU6yRiwDY/Q8aU6siKlSs64aEC7+IV56QoAuyQT2ovsJcgGYGyqMOmI/CGN1BOR6mM5EN0FBO+zyQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/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/@npmcli/move-file": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", @@ -5306,30 +7762,42 @@ } }, "node_modules/@npmcli/name-from-folder": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@npmcli/name-from-folder/-/name-from-folder-1.0.1.tgz", - "integrity": "sha512-qq3oEfcLFwNfEYOQ8HLimRGKlD8WSeGEdtUa7hmzpR8Sa7haL1KVQrvgO6wqMjhWFFVjgtrh1gIxDz+P8sjUaA==", - "dev": true - }, - "node_modules/@npmcli/node-gyp": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-2.0.0.tgz", - "integrity": "sha512-doNI35wIe3bBaEgrlPfdJPaCpUR89pJWep4Hq3aRdh6gKazIVWfs0jHttvSSoq47ZXgC7h73kDsUl8AoIQUB+A==", + "resolved": "https://registry.npmjs.org/@npmcli/name-from-folder/-/name-from-folder-2.0.0.tgz", + "integrity": "sha512-pwK+BfEBZJbKdNYpHHRTNBwBoqrN/iIMO0AiGvYsp3Hoaq0WbgGSWQR6SCldZovoDpY3yje5lkFUe6gsDgJ2vg==", "dev": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/node-gyp": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz", + "integrity": "sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/@npmcli/package-json": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-2.0.0.tgz", - "integrity": "sha512-42jnZ6yl16GzjWSH7vtrmWyJDGVa/LXPdpN2rcUWolFjc9ON2N3uz0qdBbQACfmhuJZ2lbKYtmK5qx68ZPLHMA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-3.0.0.tgz", + "integrity": "sha512-NnuPuM97xfiCpbTEJYtEuKz6CFbpUHtaT0+5via5pQeI25omvQDFbp1GcGJ/c4zvL/WX0qbde6YiLgfZbWFgvg==", "dev": true, "dependencies": { - "json-parse-even-better-errors": "^2.3.1" + "json-parse-even-better-errors": "^3.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@npmcli/package-json/node_modules/json-parse-even-better-errors": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz", + "integrity": "sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/@npmcli/promise-spawn": { @@ -5344,6 +7812,18 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/@npmcli/query": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/query/-/query-3.0.0.tgz", + "integrity": "sha512-MFNDSJNgsLZIEBVZ0Q9w9K7o07j5N4o4yjtdz2uEpuCZlXGMuPENiRaFYk0vRqAA64qVuUQwC05g27fRtfUgnA==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/@npmcli/run-script": { "version": "4.1.7", "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-4.1.7.tgz", @@ -5360,6 +7840,15 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/@npmcli/run-script/node_modules/@npmcli/node-gyp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-2.0.0.tgz", + "integrity": "sha512-doNI35wIe3bBaEgrlPfdJPaCpUR89pJWep4Hq3aRdh6gKazIVWfs0jHttvSSoq47ZXgC7h73kDsUl8AoIQUB+A==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/@npmcli/run-script/node_modules/lru-cache": { "version": "7.18.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", @@ -5464,6 +7953,25 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/@npmcli/run-script/node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "dev": true + }, + "node_modules/@npmcli/run-script/node_modules/read-package-json-fast": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz", + "integrity": "sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==", + "dev": true, + "dependencies": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@npmcli/run-script/node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -6411,6 +8919,15 @@ "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" }, + "node_modules/@sigstore/protobuf-specs": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.1.0.tgz", + "integrity": "sha512-a31EnjuIDSX8IXBUib3cYLDRlPMU36AWX4xS8ysLaNu4ZzUesDiPt83pgrW2X1YLMe5L2HbDyaKK5BrL4cNKaQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/@sinclair/typebox": { "version": "0.25.24", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", @@ -6488,6 +9005,7 @@ "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.40.tgz", "integrity": "sha512-ZQJ+NID24PQkPIHnbO2B68YNQ6aMEyDz6dcsZucpRK4r7+aPqQ2yVLaqFcQU9VcGMyo4JJydmokzyTr1roWPIQ==", "hasInstallScript": true, + "peer": true, "engines": { "node": ">=10" }, @@ -6519,6 +9037,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">=10" } @@ -6534,6 +9053,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">=10" } @@ -6549,6 +9069,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=10" } @@ -6564,6 +9085,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=10" } @@ -6579,6 +9101,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=10" } @@ -6594,6 +9117,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=10" } @@ -6609,6 +9133,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=10" } @@ -6624,6 +9149,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=10" } @@ -6639,6 +9165,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=10" } @@ -6654,6 +9181,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=10" } @@ -6870,6 +9398,42 @@ "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "devOptional": true }, + "node_modules/@tufjs/models": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-1.0.1.tgz", + "integrity": "sha512-AY0VoG/AXdlSOocuREfPoEW4SNhOPp/7fw6mpAxfVIny1uZ+0fEtMoCi7NhELSlqQIRLMu7RgfKhkxT+AJ+EXg==", + "dev": true, + "dependencies": { + "minimatch": "^7.4.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/models/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/@tufjs/models/node_modules/minimatch": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.4.tgz", + "integrity": "sha512-T+8B3kNrLP7jDb5eaC4rUIp6DKoeTSb6f9SwF2phcY2gxJUA0GEf1i29/FHxBMEfx0ppWlr434/D0P+6jb8bOQ==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@types/aria-query": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz", @@ -7500,15 +10064,6 @@ "csstype": "^3.0.2" } }, - "node_modules/@types/react-beautiful-dnd": { - "version": "13.1.3", - "resolved": "https://registry.npmjs.org/@types/react-beautiful-dnd/-/react-beautiful-dnd-13.1.3.tgz", - "integrity": "sha512-BNdmvONKtsrZq3AGrujECQrIn8cDT+fZsxBLXuX3YWY/nHfZinUFx4W88eS0rkcXzuLbXpKOsu/1WCMPMLEpPg==", - "dev": true, - "dependencies": { - "@types/react": "*" - } - }, "node_modules/@types/react-dom": { "version": "17.0.19", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.19.tgz", @@ -7768,6 +10323,8 @@ "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-5.28.0.tgz", "integrity": "sha512-8cP0CzcxUiFuA9xGJkfeVpqmWTk9nx6CWwamRGCj95ph1SmlRRk9KlCZ6avhCbZd4L68LvYT6l1kpdEnQXrF8w==", "dev": true, + "optional": true, + "peer": true, "dependencies": { "@types/node": "*", "tapable": "^2.2.0", @@ -8895,12 +11452,6 @@ "node": ">=0.10.0" } }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true - }, "node_modules/asar": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/asar/-/asar-3.2.0.tgz", @@ -9135,6 +11686,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-28.1.3.tgz", "integrity": "sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q==", + "peer": true, "dependencies": { "@jest/transform": "^28.1.3", "@types/babel__core": "^7.1.14", @@ -9155,6 +11707,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "peer": true, "dependencies": { "@sinclair/typebox": "^0.24.1" }, @@ -9166,6 +11719,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-28.1.3.tgz", "integrity": "sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==", + "peer": true, "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^28.1.3", @@ -9191,6 +11745,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "peer": true, "dependencies": { "@jest/schemas": "^28.1.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -9206,17 +11761,20 @@ "node_modules/babel-jest/node_modules/@sinclair/typebox": { "version": "0.24.51", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", + "peer": true }, "node_modules/babel-jest/node_modules/convert-source-map": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "peer": true }, "node_modules/babel-jest/node_modules/jest-haste-map": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-28.1.3.tgz", "integrity": "sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==", + "peer": true, "dependencies": { "@jest/types": "^28.1.3", "@types/graceful-fs": "^4.1.3", @@ -9241,6 +11799,7 @@ "version": "28.0.2", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "peer": true, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } @@ -9249,6 +11808,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "peer": true, "dependencies": { "@jest/types": "^28.1.3", "@types/node": "*", @@ -9280,6 +11840,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz", "integrity": "sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q==", + "peer": true, "dependencies": { "@babel/template": "^7.3.3", "@babel/types": "^7.3.3", @@ -9384,6 +11945,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz", "integrity": "sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A==", + "peer": true, "dependencies": { "babel-plugin-jest-hoist": "^28.1.3", "babel-preset-current-node-syntax": "^1.0.0" @@ -9486,44 +12048,49 @@ } }, "node_modules/bin-links": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/bin-links/-/bin-links-3.0.3.tgz", - "integrity": "sha512-zKdnMPWEdh4F5INR07/eBrodC7QrF5JKvqskjz/ZZRXg5YSAZIbn8zGhbhUrElzHBZ2fvEQdOU59RHcTG3GiwA==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bin-links/-/bin-links-4.0.1.tgz", + "integrity": "sha512-bmFEM39CyX336ZGGRsGPlc6jZHriIoHacOQcTt72MktIjpPhZoP4te2jOyUXF3BLILmJ8aNLncoPVeIIFlrDeA==", "dev": true, "dependencies": { - "cmd-shim": "^5.0.0", - "mkdirp-infer-owner": "^2.0.0", - "npm-normalize-package-bin": "^2.0.0", - "read-cmd-shim": "^3.0.0", - "rimraf": "^3.0.0", - "write-file-atomic": "^4.0.0" + "cmd-shim": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "read-cmd-shim": "^4.0.0", + "write-file-atomic": "^5.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/bin-links/node_modules/npm-normalize-package-bin": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", - "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", + "node_modules/bin-links/node_modules/cmd-shim": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-6.0.1.tgz", + "integrity": "sha512-S9iI9y0nKR4hwEQsVWpyxld/6kRfGepGfzff83FcaiEBpmvlbA2nnGe7Cylgrx2f/p1P5S5wpRm9oL8z1PbS3Q==", "dev": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/bin-links/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "node_modules/bin-links/node_modules/read-cmd-shim": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-4.0.0.tgz", + "integrity": "sha512-yILWifhaSEEytfXI76kB9xEEiG1AiozaCJZ83A87ytjRiN+jVibXjedjCRNjoZviinhG+4UkalO3mWTd8u5O0Q==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/bin-links/node_modules/write-file-atomic": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.0.tgz", + "integrity": "sha512-R7NYMnHSlV42K54lwY9lvW6MnSm1HSJqZL3xiSgi9E7//FYaI74r2G0rd+/X6VAMkHEdzxQaU5HUOXWUz5kA/w==", "dev": true, "dependencies": { - "glob": "^7.1.3" + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/bin-version": { @@ -11008,29 +13575,6 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/conf": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/conf/-/conf-7.1.2.tgz", - "integrity": "sha512-r8/HEoWPFn4CztjhMJaWNAe5n+gPUCSaJ0oufbqDLFKsA1V8JjAG7G+p0pgoDFAws9Bpk2VtVLLXqOBA7WxLeg==", - "dependencies": { - "ajv": "^6.12.2", - "atomically": "^1.3.1", - "debounce-fn": "^4.0.0", - "dot-prop": "^5.2.0", - "env-paths": "^2.2.0", - "json-schema-typed": "^7.0.3", - "make-dir": "^3.1.0", - "onetime": "^5.1.0", - "pkg-up": "^3.1.0", - "semver": "^7.3.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/config-chain": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", @@ -11782,15 +14326,6 @@ } } }, - "node_modules/debuglog": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", - "integrity": "sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", @@ -12171,16 +14706,6 @@ "node": ">=0.8.0" } }, - "node_modules/dezalgo": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", - "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", - "dev": true, - "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, "node_modules/diacritics-map": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/diacritics-map/-/diacritics-map-0.1.0.tgz", @@ -12487,6 +15012,7 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, "dependencies": { "is-obj": "^2.0.0" }, @@ -12876,6 +15402,7 @@ "version": "0.10.2", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", + "peer": true, "engines": { "node": ">=12" }, @@ -15117,6 +17644,15 @@ "url": "https://github.com/sindresorhus/file-type?sponsor=1" } }, + "node_modules/file-url": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/file-url/-/file-url-3.0.0.tgz", + "integrity": "sha512-g872QGsHexznxkIAdK8UiZRe7SkE6kvylShU4Nsj8NvfvZag7S0QuQ4IgvPDkk75HxgjIVDwycFTDAgIiO4nDA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/filehound": { "version": "1.17.6", "resolved": "https://registry.npmjs.org/filehound/-/filehound-1.17.6.tgz", @@ -17735,6 +20271,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest/-/jest-28.1.3.tgz", "integrity": "sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==", + "peer": true, "dependencies": { "@jest/core": "^28.1.3", "@jest/types": "^28.1.3", @@ -17770,6 +20307,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-28.1.3.tgz", "integrity": "sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA==", + "peer": true, "dependencies": { "execa": "^5.0.0", "p-limit": "^3.1.0" @@ -17782,6 +20320,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "peer": true, "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -17804,6 +20343,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "peer": true, "engines": { "node": ">=10" }, @@ -17815,6 +20355,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "peer": true, "engines": { "node": ">=8" }, @@ -17826,6 +20367,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "peer": true, "dependencies": { "path-key": "^3.0.0" }, @@ -17837,6 +20379,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-28.1.3.tgz", "integrity": "sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow==", + "peer": true, "dependencies": { "@jest/environment": "^28.1.3", "@jest/expect": "^28.1.3", @@ -17866,6 +20409,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-28.1.3.tgz", "integrity": "sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==", + "peer": true, "dependencies": { "@jest/environment": "^28.1.3", "@jest/expect": "^28.1.3", @@ -17879,6 +20423,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "peer": true, "dependencies": { "@sinclair/typebox": "^0.24.1" }, @@ -17890,6 +20435,7 @@ "version": "28.1.2", "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-28.1.2.tgz", "integrity": "sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==", + "peer": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.13", "callsites": "^3.0.0", @@ -17903,6 +20449,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-28.1.3.tgz", "integrity": "sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==", + "peer": true, "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^28.1.3", @@ -17928,6 +20475,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "peer": true, "dependencies": { "@jest/schemas": "^28.1.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -17943,12 +20491,14 @@ "node_modules/jest-circus/node_modules/@sinclair/typebox": { "version": "0.24.51", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", + "peer": true }, "node_modules/jest-circus/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==", + "peer": true, "engines": { "node": ">=10" }, @@ -17959,12 +20509,14 @@ "node_modules/jest-circus/node_modules/convert-source-map": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "peer": true }, "node_modules/jest-circus/node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "peer": true, "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -17987,6 +20539,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "peer": true, "engines": { "node": ">=10" }, @@ -17998,6 +20551,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "peer": true, "engines": { "node": ">=8" }, @@ -18009,6 +20563,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-28.1.3.tgz", "integrity": "sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==", + "peer": true, "dependencies": { "@jest/types": "^28.1.3", "@types/graceful-fs": "^4.1.3", @@ -18033,6 +20588,7 @@ "version": "28.0.2", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "peer": true, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } @@ -18041,6 +20597,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-28.1.3.tgz", "integrity": "sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==", + "peer": true, "dependencies": { "@jest/environment": "^28.1.3", "@jest/fake-timers": "^28.1.3", @@ -18073,6 +20630,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "peer": true, "dependencies": { "@jest/types": "^28.1.3", "@types/node": "*", @@ -18089,6 +20647,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "peer": true, "dependencies": { "path-key": "^3.0.0" }, @@ -18100,6 +20659,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "peer": true, "dependencies": { "@jest/schemas": "^28.1.3", "ansi-regex": "^5.0.1", @@ -18113,12 +20673,14 @@ "node_modules/jest-circus/node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "peer": true }, "node_modules/jest-cli": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-28.1.3.tgz", "integrity": "sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==", + "peer": true, "dependencies": { "@jest/core": "^28.1.3", "@jest/test-result": "^28.1.3", @@ -18152,6 +20714,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "peer": true, "dependencies": { "@sinclair/typebox": "^0.24.1" }, @@ -18163,6 +20726,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "peer": true, "dependencies": { "@jest/schemas": "^28.1.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -18178,12 +20742,14 @@ "node_modules/jest-cli/node_modules/@sinclair/typebox": { "version": "0.24.51", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", + "peer": true }, "node_modules/jest-cli/node_modules/jest-util": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "peer": true, "dependencies": { "@jest/types": "^28.1.3", "@types/node": "*", @@ -18200,6 +20766,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-28.1.3.tgz", "integrity": "sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==", + "peer": true, "dependencies": { "@babel/core": "^7.11.6", "@jest/test-sequencer": "^28.1.3", @@ -18244,6 +20811,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "peer": true, "dependencies": { "@sinclair/typebox": "^0.24.1" }, @@ -18255,6 +20823,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "peer": true, "dependencies": { "@jest/schemas": "^28.1.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -18270,12 +20839,14 @@ "node_modules/jest-config/node_modules/@sinclair/typebox": { "version": "0.24.51", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", + "peer": true }, "node_modules/jest-config/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==", + "peer": true, "engines": { "node": ">=10" }, @@ -18287,6 +20858,7 @@ "version": "28.0.2", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "peer": true, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } @@ -18295,6 +20867,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "peer": true, "dependencies": { "@jest/types": "^28.1.3", "@types/node": "*", @@ -18311,6 +20884,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "peer": true, "dependencies": { "@jest/schemas": "^28.1.3", "ansi-regex": "^5.0.1", @@ -18324,7 +20898,8 @@ "node_modules/jest-config/node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "peer": true }, "node_modules/jest-diff": { "version": "28.1.3", @@ -18390,6 +20965,7 @@ "version": "28.1.1", "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-28.1.1.tgz", "integrity": "sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA==", + "peer": true, "dependencies": { "detect-newline": "^3.0.0" }, @@ -18401,6 +20977,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-28.1.3.tgz", "integrity": "sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g==", + "peer": true, "dependencies": { "@jest/types": "^28.1.3", "chalk": "^4.0.0", @@ -18416,6 +20993,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "peer": true, "dependencies": { "@sinclair/typebox": "^0.24.1" }, @@ -18427,6 +21005,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "peer": true, "dependencies": { "@jest/schemas": "^28.1.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -18442,12 +21021,14 @@ "node_modules/jest-each/node_modules/@sinclair/typebox": { "version": "0.24.51", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", + "peer": true }, "node_modules/jest-each/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==", + "peer": true, "engines": { "node": ">=10" }, @@ -18459,6 +21040,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "peer": true, "dependencies": { "@jest/types": "^28.1.3", "@types/node": "*", @@ -18475,6 +21057,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "peer": true, "dependencies": { "@jest/schemas": "^28.1.3", "ansi-regex": "^5.0.1", @@ -18488,7 +21071,8 @@ "node_modules/jest-each/node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "peer": true }, "node_modules/jest-environment-jsdom": { "version": "28.1.3", @@ -18767,6 +21351,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-28.1.3.tgz", "integrity": "sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A==", + "peer": true, "dependencies": { "@jest/environment": "^28.1.3", "@jest/fake-timers": "^28.1.3", @@ -18783,6 +21368,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "peer": true, "dependencies": { "@sinclair/typebox": "^0.24.1" }, @@ -18794,6 +21380,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "peer": true, "dependencies": { "@jest/schemas": "^28.1.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -18809,12 +21396,14 @@ "node_modules/jest-environment-node/node_modules/@sinclair/typebox": { "version": "0.24.51", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", + "peer": true }, "node_modules/jest-environment-node/node_modules/jest-util": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "peer": true, "dependencies": { "@jest/types": "^28.1.3", "@types/node": "*", @@ -18891,6 +21480,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz", "integrity": "sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==", + "peer": true, "dependencies": { "jest-get-type": "^28.0.2", "pretty-format": "^28.1.3" @@ -18903,6 +21493,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "peer": true, "dependencies": { "@sinclair/typebox": "^0.24.1" }, @@ -18913,12 +21504,14 @@ "node_modules/jest-leak-detector/node_modules/@sinclair/typebox": { "version": "0.24.51", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", + "peer": true }, "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==", + "peer": true, "engines": { "node": ">=10" }, @@ -18930,6 +21523,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "peer": true, "dependencies": { "@jest/schemas": "^28.1.3", "ansi-regex": "^5.0.1", @@ -18943,7 +21537,8 @@ "node_modules/jest-leak-detector/node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "peer": true }, "node_modules/jest-matcher-utils": { "version": "28.1.3", @@ -19171,6 +21766,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-28.1.3.tgz", "integrity": "sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ==", + "peer": true, "dependencies": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", @@ -19190,6 +21786,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz", "integrity": "sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA==", + "peer": true, "dependencies": { "jest-regex-util": "^28.0.2", "jest-snapshot": "^28.1.3" @@ -19202,6 +21799,7 @@ "version": "28.0.2", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "peer": true, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } @@ -19210,6 +21808,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "peer": true, "dependencies": { "@sinclair/typebox": "^0.24.1" }, @@ -19221,6 +21820,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "peer": true, "dependencies": { "@jest/schemas": "^28.1.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -19236,12 +21836,14 @@ "node_modules/jest-resolve/node_modules/@sinclair/typebox": { "version": "0.24.51", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", + "peer": true }, "node_modules/jest-resolve/node_modules/jest-haste-map": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-28.1.3.tgz", "integrity": "sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==", + "peer": true, "dependencies": { "@jest/types": "^28.1.3", "@types/graceful-fs": "^4.1.3", @@ -19266,6 +21868,7 @@ "version": "28.0.2", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "peer": true, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } @@ -19274,6 +21877,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "peer": true, "dependencies": { "@jest/types": "^28.1.3", "@types/node": "*", @@ -19290,6 +21894,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-28.1.3.tgz", "integrity": "sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA==", + "peer": true, "dependencies": { "@jest/console": "^28.1.3", "@jest/environment": "^28.1.3", @@ -19321,6 +21926,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-28.1.3.tgz", "integrity": "sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==", + "peer": true, "dependencies": { "@jest/environment": "^28.1.3", "@jest/expect": "^28.1.3", @@ -19334,6 +21940,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "peer": true, "dependencies": { "@sinclair/typebox": "^0.24.1" }, @@ -19345,6 +21952,7 @@ "version": "28.1.2", "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-28.1.2.tgz", "integrity": "sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==", + "peer": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.13", "callsites": "^3.0.0", @@ -19358,6 +21966,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-28.1.3.tgz", "integrity": "sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==", + "peer": true, "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^28.1.3", @@ -19383,6 +21992,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "peer": true, "dependencies": { "@jest/schemas": "^28.1.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -19398,17 +22008,20 @@ "node_modules/jest-runner/node_modules/@sinclair/typebox": { "version": "0.24.51", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", + "peer": true }, "node_modules/jest-runner/node_modules/convert-source-map": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "peer": true }, "node_modules/jest-runner/node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "peer": true, "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -19431,6 +22044,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "peer": true, "engines": { "node": ">=10" }, @@ -19442,6 +22056,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "peer": true, "engines": { "node": ">=8" }, @@ -19453,6 +22068,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-28.1.3.tgz", "integrity": "sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==", + "peer": true, "dependencies": { "@jest/types": "^28.1.3", "@types/graceful-fs": "^4.1.3", @@ -19477,6 +22093,7 @@ "version": "28.0.2", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "peer": true, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } @@ -19485,6 +22102,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-28.1.3.tgz", "integrity": "sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==", + "peer": true, "dependencies": { "@jest/environment": "^28.1.3", "@jest/fake-timers": "^28.1.3", @@ -19517,6 +22135,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "peer": true, "dependencies": { "@jest/types": "^28.1.3", "@types/node": "*", @@ -19533,6 +22152,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "peer": true, "dependencies": { "path-key": "^3.0.0" }, @@ -19544,6 +22164,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -19552,6 +22173,7 @@ "version": "0.5.13", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "peer": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -19887,6 +22509,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-28.1.3.tgz", "integrity": "sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==", + "peer": true, "dependencies": { "@babel/core": "^7.11.6", "@babel/generator": "^7.7.2", @@ -19920,6 +22543,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "peer": true, "dependencies": { "@sinclair/typebox": "^0.24.1" }, @@ -19931,6 +22555,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-28.1.3.tgz", "integrity": "sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==", + "peer": true, "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^28.1.3", @@ -19956,6 +22581,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "peer": true, "dependencies": { "@jest/schemas": "^28.1.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -19971,12 +22597,14 @@ "node_modules/jest-snapshot/node_modules/@sinclair/typebox": { "version": "0.24.51", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", + "peer": true }, "node_modules/jest-snapshot/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==", + "peer": true, "engines": { "node": ">=10" }, @@ -19987,12 +22615,14 @@ "node_modules/jest-snapshot/node_modules/convert-source-map": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "peer": true }, "node_modules/jest-snapshot/node_modules/jest-haste-map": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-28.1.3.tgz", "integrity": "sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==", + "peer": true, "dependencies": { "@jest/types": "^28.1.3", "@types/graceful-fs": "^4.1.3", @@ -20017,6 +22647,7 @@ "version": "28.0.2", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "peer": true, "engines": { "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } @@ -20025,6 +22656,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "peer": true, "dependencies": { "@jest/types": "^28.1.3", "@types/node": "*", @@ -20041,6 +22673,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "peer": true, "dependencies": { "@jest/schemas": "^28.1.3", "ansi-regex": "^5.0.1", @@ -20054,7 +22687,8 @@ "node_modules/jest-snapshot/node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "peer": true }, "node_modules/jest-util": { "version": "29.5.0", @@ -20076,6 +22710,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-28.1.3.tgz", "integrity": "sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA==", + "peer": true, "dependencies": { "@jest/types": "^28.1.3", "camelcase": "^6.2.0", @@ -20092,6 +22727,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "peer": true, "dependencies": { "@sinclair/typebox": "^0.24.1" }, @@ -20103,6 +22739,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "peer": true, "dependencies": { "@jest/schemas": "^28.1.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -20118,12 +22755,14 @@ "node_modules/jest-validate/node_modules/@sinclair/typebox": { "version": "0.24.51", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", + "peer": true }, "node_modules/jest-validate/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==", + "peer": true, "engines": { "node": ">=10" }, @@ -20135,6 +22774,7 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "peer": true, "engines": { "node": ">=10" }, @@ -20146,6 +22786,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "peer": true, "dependencies": { "@jest/schemas": "^28.1.3", "ansi-regex": "^5.0.1", @@ -20159,7 +22800,8 @@ "node_modules/jest-validate/node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "peer": true }, "node_modules/jest-watch-select-projects": { "version": "2.0.0", @@ -20526,6 +23168,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", + "peer": true, "dependencies": { "@jest/test-result": "^28.1.3", "@jest/types": "^28.1.3", @@ -20544,6 +23187,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "peer": true, "dependencies": { "@sinclair/typebox": "^0.24.1" }, @@ -20555,6 +23199,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "peer": true, "dependencies": { "@jest/schemas": "^28.1.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -20570,12 +23215,14 @@ "node_modules/jest-watcher/node_modules/@sinclair/typebox": { "version": "0.24.51", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", + "peer": true }, "node_modules/jest-watcher/node_modules/jest-util": { "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "peer": true, "dependencies": { "@jest/types": "^28.1.3", "@types/node": "*", @@ -20592,6 +23239,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", + "peer": true, "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -20605,6 +23253,7 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -20619,6 +23268,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "peer": true, "dependencies": { "@sinclair/typebox": "^0.24.1" }, @@ -20630,6 +23280,7 @@ "version": "28.1.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "peer": true, "dependencies": { "@jest/schemas": "^28.1.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -20645,7 +23296,8 @@ "node_modules/jest/node_modules/@sinclair/typebox": { "version": "0.24.51", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==" + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", + "peer": true }, "node_modules/joi": { "version": "17.8.4", @@ -21068,9 +23720,9 @@ } }, "node_modules/just-diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/just-diff/-/just-diff-5.2.0.tgz", - "integrity": "sha512-6ufhP9SHjb7jibNFrNxyFZ6od3g+An6Ai9mhGRvcYe8UJlH0prseN64M+6ZBBUoKYHZsitDP42gAJ8+eVWr3lw==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/just-diff/-/just-diff-6.0.2.tgz", + "integrity": "sha512-S59eriX5u3/QhMNq3v/gm8Kd0w8OS6Tz2FS1NG4blv+z0MuQcBRJyFWjdovM0Rad4/P4aUPFtnkNjMjyMlMSYA==", "dev": true }, "node_modules/just-diff-apply": { @@ -21131,6 +23783,16 @@ "language-subtag-registry": "~0.3.2" } }, + "node_modules/launch-editor": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.0.tgz", + "integrity": "sha512-JpDCcQnyAAzZZaZ7vEiSqL690w7dAEyLao+KC96zBplnYbJS7TYNjvM3M7y3dGz+v7aIsJk3hllWuc0kWAjyRQ==", + "dev": true, + "dependencies": { + "picocolors": "^1.0.0", + "shell-quote": "^1.7.3" + } + }, "node_modules/lazy-cache": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz", @@ -21149,14 +23811,15 @@ "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==" }, "node_modules/lerna": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/lerna/-/lerna-6.5.1.tgz", - "integrity": "sha512-Va1bysubwWdoWZ1ncKcoTGBXNAu/10/TwELb550TTivXmEWjCCdek4eX0BNLTEYKxu3tpV2UEeqVisUiWGn4WA==", + "version": "6.6.1", + "resolved": "https://registry.npmjs.org/lerna/-/lerna-6.6.1.tgz", + "integrity": "sha512-WJtrvmbmR+6hMB9b5pvsxJzew0lRL6hARgW/My9BM4vYaxwPIA2I0riv3qQu5Zd7lYse7FEqJkTnl9Kn1bXhLA==", "dev": true, "dependencies": { - "@lerna/child-process": "6.5.1", - "@lerna/create": "6.5.1", - "@npmcli/arborist": "5.3.0", + "@lerna/child-process": "6.6.1", + "@lerna/create": "6.6.1", + "@lerna/legacy-package-management": "6.6.1", + "@npmcli/arborist": "6.2.3", "@npmcli/run-script": "4.1.7", "@nrwl/devkit": ">=15.5.2 < 16", "@octokit/plugin-enterprise-rest": "6.0.1", @@ -21198,7 +23861,7 @@ "node-fetch": "2.6.7", "npm-package-arg": "8.1.1", "npm-packlist": "5.1.1", - "npm-registry-fetch": "13.3.0", + "npm-registry-fetch": "^14.0.3", "npmlog": "^6.0.2", "nx": ">=15.5.2 < 16", "p-map": "4.0.0", @@ -21207,14 +23870,13 @@ "p-queue": "6.6.2", "p-reduce": "2.1.0", "p-waterfall": "2.1.1", - "pacote": "13.6.1", - "path-exists": "4.0.0", + "pacote": "13.6.2", "pify": "5.0.0", "read-cmd-shim": "3.0.0", "read-package-json": "5.0.1", "resolve-from": "5.0.0", - "rimraf": "^3.0.2", - "semver": "7.3.4", + "rimraf": "^4.4.1", + "semver": "^7.3.8", "signal-exit": "3.0.7", "slash": "3.0.0", "ssri": "9.0.1", @@ -21238,6 +23900,75 @@ "node": "^14.15.0 || >=16.0.0" } }, + "node_modules/lerna/node_modules/@npmcli/fs": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.0.tgz", + "integrity": "sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w==", + "dev": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/lerna/node_modules/cacache": { + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-17.0.5.tgz", + "integrity": "sha512-Y/PRQevNSsjAPWykl9aeGz8Pr+OI6BYM9fYDNMvOkuUiG9IhG4LEmaYrZZZvioMUEQ+cBCxT0v8wrnCURccyKA==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^9.3.1", + "lru-cache": "^7.7.1", + "minipass": "^4.0.0", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/lerna/node_modules/cacache/node_modules/fs-minipass": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.1.tgz", + "integrity": "sha512-MhaJDcFRTuLidHrIttu0RDGyyXs/IYHVmlcxfLAEFIWjc1vdLAkdwT7Ace2u7DbitWC0toKMl5eJZRYNVreIMw==", + "dev": true, + "dependencies": { + "minipass": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/lerna/node_modules/cacache/node_modules/minipass": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.5.tgz", + "integrity": "sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/lerna/node_modules/cacache/node_modules/ssri": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.1.tgz", + "integrity": "sha512-WVy6di9DlPOeBWEjMScpNipeSX2jIZBGEn5Uuo8Q7aIuFEuDX0pw8RxcOjlD1TWP4obi24ki7m/13+nFpcbXrw==", + "dev": true, + "dependencies": { + "minipass": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/lerna/node_modules/chalk": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", @@ -21326,6 +24057,69 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lerna/node_modules/glob": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.4.tgz", + "integrity": "sha512-qaSc49hojMOv1EPM4EuyITjDSgSKI0rthoHnvE81tcOi1SCVndHko7auqxdQ14eiQG2NDBJBE86+2xIrbIvrbA==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/lerna/node_modules/glob/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/lerna/node_modules/glob/node_modules/minimatch": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.2.tgz", + "integrity": "sha512-ikHGF67ODxj7vS5NKU2wvTsFLbExee+KXVCnBWh8Cg2hVJfBMQIrlo50qru/09E0EifjnU8dZhJ/iHhyXJM6Mw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/lerna/node_modules/glob/node_modules/minipass": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.5.tgz", + "integrity": "sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/lerna/node_modules/hosted-git-info": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz", + "integrity": "sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==", + "dev": true, + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/lerna/node_modules/inquirer": { "version": "8.2.5", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", @@ -21390,15 +24184,59 @@ } }, "node_modules/lerna/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==", + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/lerna/node_modules/make-fetch-happen": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.0.3.tgz", + "integrity": "sha512-oPLh5m10lRNNZDjJ2kP8UpboUx2uFXVaVweVe/lWut4iHWcQEmfqSVJt2ihZsFI8HbpwyyocaXbCAWf0g1ukIA==", "dev": true, "dependencies": { - "yallist": "^4.0.0" + "agentkeepalive": "^4.2.1", + "cacache": "^17.0.0", + "http-cache-semantics": "^4.1.1", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^4.0.0", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^10.0.0" }, "engines": { - "node": ">=10" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/lerna/node_modules/make-fetch-happen/node_modules/minipass": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.5.tgz", + "integrity": "sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/lerna/node_modules/make-fetch-happen/node_modules/ssri": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.1.tgz", + "integrity": "sha512-WVy6di9DlPOeBWEjMScpNipeSX2jIZBGEn5Uuo8Q7aIuFEuDX0pw8RxcOjlD1TWP4obi24ki7m/13+nFpcbXrw==", + "dev": true, + "dependencies": { + "minipass": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/lerna/node_modules/minimatch": { @@ -21425,6 +24263,32 @@ "node": ">=8" } }, + "node_modules/lerna/node_modules/minipass-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.1.tgz", + "integrity": "sha512-t9/wowtf7DYkwz8cfMSt0rMwiyNIBXf5CKZ3S5ZMqRqMYT0oLTp0x1WorMI9WTwvaPg21r1JbFxJMum8JrLGfw==", + "dev": true, + "dependencies": { + "minipass": "^4.0.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/lerna/node_modules/minipass-fetch/node_modules/minipass": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.5.tgz", + "integrity": "sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/lerna/node_modules/node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -21445,6 +24309,60 @@ } } }, + "node_modules/lerna/node_modules/npm-registry-fetch": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-14.0.3.tgz", + "integrity": "sha512-YaeRbVNpnWvsGOjX2wk5s85XJ7l1qQBGAp724h8e2CZFFhMSuw9enom7K1mWVUtvXO1uUSFIAPofQK0pPN0ZcA==", + "dev": true, + "dependencies": { + "make-fetch-happen": "^11.0.0", + "minipass": "^4.0.0", + "minipass-fetch": "^3.0.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^10.0.0", + "proc-log": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/lerna/node_modules/npm-registry-fetch/node_modules/minipass": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.5.tgz", + "integrity": "sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/lerna/node_modules/npm-registry-fetch/node_modules/npm-package-arg": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-10.1.0.tgz", + "integrity": "sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^6.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/lerna/node_modules/npm-registry-fetch/node_modules/validate-npm-package-name": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz", + "integrity": "sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ==", + "dev": true, + "dependencies": { + "builtins": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/lerna/node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", @@ -21469,34 +24387,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lerna/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "node_modules/lerna/node_modules/proc-log": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", + "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==", "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/lerna/node_modules/semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", + "node_modules/lerna/node_modules/socks-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", "dev": true, "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" }, "engines": { - "node": ">=10" + "node": ">= 10" } }, "node_modules/lerna/node_modules/tar": { @@ -21522,6 +24433,30 @@ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "dev": true }, + "node_modules/lerna/node_modules/unique-filename": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", + "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", + "dev": true, + "dependencies": { + "unique-slug": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/lerna/node_modules/unique-slug": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", + "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/lerna/node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -23916,31 +26851,37 @@ } }, "node_modules/npm-bundled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", - "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-3.0.0.tgz", + "integrity": "sha512-Vq0eyEQy+elFpzsKjMss9kxqb9tG3YHg4dsyWuUENuzvSUWe1TCnW/vV9FkhvBk/brEDoDiVd+M1Btosa6ImdQ==", "dev": true, "dependencies": { - "npm-normalize-package-bin": "^1.0.1" + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm-install-checks": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-5.0.0.tgz", - "integrity": "sha512-65lUsMI8ztHCxFz5ckCEC44DRvEGdZX5usQFriauxHEwt7upv1FKaQEmAtU0YnOAdwuNWCmk64xYiQABNrEyLA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.1.0.tgz", + "integrity": "sha512-udSGENih/5xKh3Ex+L0PtZcOt0Pa+6ppDLnpG5D49/EhMja3LupaY9E/DtJTxyFBwE09ot7Fc+H4DywnZNWTVA==", "dev": true, "dependencies": { "semver": "^7.1.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm-normalize-package-bin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", - "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", - "dev": true + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-3.0.0.tgz", + "integrity": "sha512-g+DPQSkusnk7HYXr75NtzkIP4+N81i3RPsGFidF3DzHd9MT9wWngmqoeg/fnHFz5MNdtG4w03s+QnhewSLTT2Q==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, "node_modules/npm-package-arg": { "version": "8.1.1", @@ -24059,31 +27000,46 @@ "node": ">=10" } }, - "node_modules/npm-pick-manifest": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-7.0.2.tgz", - "integrity": "sha512-gk37SyRmlIjvTfcYl6RzDbSmS9Y4TOBXfsPnoYqTHARNgWbyDiCSMLUpmALDj4jjcTZpURiEfsSHJj9k7EV4Rw==", + "node_modules/npm-packlist/node_modules/npm-bundled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", + "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", "dev": true, "dependencies": { - "npm-install-checks": "^5.0.0", - "npm-normalize-package-bin": "^2.0.0", - "npm-package-arg": "^9.0.0", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "node_modules/npm-packlist/node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "dev": true + }, + "node_modules/npm-pick-manifest": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-8.0.1.tgz", + "integrity": "sha512-mRtvlBjTsJvfCCdmPtiu2bdlx8d/KXtF7yNXNWe7G0Z36qWA9Ny5zXsI2PfBZEv7SXgoxTmNaTzGSbbzDZChoA==", + "dev": true, + "dependencies": { + "npm-install-checks": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "npm-package-arg": "^10.0.0", "semver": "^7.3.5" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm-pick-manifest/node_modules/hosted-git-info": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.2.1.tgz", - "integrity": "sha512-xIcQYMnhcx2Nr4JTjsFmwwnr9vldugPy9uVm0o87bjqqWMv9GaqsTeT+i99wTl0mk1uLxJtHxLb8kymqTENQsw==", + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-6.1.1.tgz", + "integrity": "sha512-r0EI+HBMcXadMrugk0GCQ+6BQV39PiWAZVfq7oIckeGiN7sjRGyQxPdft3nQekFTCQbYxLBH+/axZMeH8UX6+w==", "dev": true, "dependencies": { "lru-cache": "^7.5.1" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm-pick-manifest/node_modules/lru-cache": { @@ -24095,28 +27051,40 @@ "node": ">=12" } }, - "node_modules/npm-pick-manifest/node_modules/npm-normalize-package-bin": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", - "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", - "dev": true, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, "node_modules/npm-pick-manifest/node_modules/npm-package-arg": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-9.1.2.tgz", - "integrity": "sha512-pzd9rLEx4TfNJkovvlBSLGhq31gGu2QDexFPWT19yCDh0JgnRhlBLNo5759N0AJmBk+kQ9Y/hXoLnlgFD+ukmg==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-10.1.0.tgz", + "integrity": "sha512-uFyyCEmgBfZTtrKk/5xDfHp6+MdrqGotX/VoOyEEl3mBwiEE5FlBaePanazJSVMPT7vKepcjYBY2ztg9A3yPIA==", "dev": true, "dependencies": { - "hosted-git-info": "^5.0.0", - "proc-log": "^2.0.1", + "hosted-git-info": "^6.0.0", + "proc-log": "^3.0.0", "semver": "^7.3.5", - "validate-npm-package-name": "^4.0.0" + "validate-npm-package-name": "^5.0.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-pick-manifest/node_modules/proc-log": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", + "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm-pick-manifest/node_modules/validate-npm-package-name": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.0.tgz", + "integrity": "sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ==", + "dev": true, + "dependencies": { + "builtins": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/npm-registry-fetch": { @@ -27267,9 +30235,9 @@ } }, "node_modules/pacote": { - "version": "13.6.1", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-13.6.1.tgz", - "integrity": "sha512-L+2BI1ougAPsFjXRyBhcKmfT016NscRFLv6Pz5EiNf1CCFJFU0pSKKQwsZTyAQB+sTuUL4TyFyp6J1Ork3dOqw==", + "version": "13.6.2", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-13.6.2.tgz", + "integrity": "sha512-Gu8fU3GsvOPkak2CkbojR7vjs3k3P9cA6uazKTHdsdV0gpCEQq2opelnEv30KRQWgVzP5Vd/5umjcedma3MKtg==", "dev": true, "dependencies": { "@npmcli/git": "^3.0.0", @@ -27301,6 +30269,22 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/pacote/node_modules/@npmcli/installed-package-contents": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz", + "integrity": "sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw==", + "dev": true, + "dependencies": { + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + }, + "bin": { + "installed-package-contents": "index.js" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/pacote/node_modules/hosted-git-info": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.2.1.tgz", @@ -27334,6 +30318,33 @@ "node": ">=8" } }, + "node_modules/pacote/node_modules/npm-bundled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", + "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", + "dev": true, + "dependencies": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "node_modules/pacote/node_modules/npm-install-checks": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-5.0.0.tgz", + "integrity": "sha512-65lUsMI8ztHCxFz5ckCEC44DRvEGdZX5usQFriauxHEwt7upv1FKaQEmAtU0YnOAdwuNWCmk64xYiQABNrEyLA==", + "dev": true, + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/pacote/node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "dev": true + }, "node_modules/pacote/node_modules/npm-package-arg": { "version": "9.1.2", "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-9.1.2.tgz", @@ -27349,6 +30360,43 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/pacote/node_modules/npm-pick-manifest": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-7.0.2.tgz", + "integrity": "sha512-gk37SyRmlIjvTfcYl6RzDbSmS9Y4TOBXfsPnoYqTHARNgWbyDiCSMLUpmALDj4jjcTZpURiEfsSHJj9k7EV4Rw==", + "dev": true, + "dependencies": { + "npm-install-checks": "^5.0.0", + "npm-normalize-package-bin": "^2.0.0", + "npm-package-arg": "^9.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/pacote/node_modules/npm-pick-manifest/node_modules/npm-normalize-package-bin": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", + "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/pacote/node_modules/read-package-json-fast": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz", + "integrity": "sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==", + "dev": true, + "dependencies": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/pacote/node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -27397,17 +30445,26 @@ } }, "node_modules/parse-conflict-json": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/parse-conflict-json/-/parse-conflict-json-2.0.2.tgz", - "integrity": "sha512-jDbRGb00TAPFsKWCpZZOT93SxVP9nONOSgES3AevqRq/CHvavEBvKAjxX9p5Y5F0RZLxH9Ufd9+RwtCsa+lFDA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/parse-conflict-json/-/parse-conflict-json-3.0.1.tgz", + "integrity": "sha512-01TvEktc68vwbJOtWZluyWeVGWjP+bZwXtPDMQVbBKzbJ/vZBif0L69KH1+cHv1SZ6e0FKLvjyHe8mqsIqYOmw==", "dev": true, "dependencies": { - "json-parse-even-better-errors": "^2.3.1", - "just-diff": "^5.0.1", + "json-parse-even-better-errors": "^3.0.0", + "just-diff": "^6.0.0", "just-diff-apply": "^5.2.0" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/parse-conflict-json/node_modules/json-parse-even-better-errors": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz", + "integrity": "sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/parse-json": { @@ -27747,34 +30804,6 @@ "node": ">=4" } }, - "node_modules/playwright": { - "version": "1.31.2", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.31.2.tgz", - "integrity": "sha512-jpC47n2PKQNtzB7clmBuWh6ftBRS/Bt5EGLigJ9k2QAKcNeYXZkEaDH5gmvb6+AbcE0DO6GnXdbl9ogG6Eh+og==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "playwright-core": "1.31.2" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/playwright-core": { - "version": "1.31.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.31.2.tgz", - "integrity": "sha512-a1dFgCNQw4vCsG7bnojZjDnPewZcw7tZUNFN0ZkcLYKj+mPmXvg4MpaaKZ5SgqPsOmqIf2YsVRkgqiRDxD+fDQ==", - "dev": true, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=14" - } - }, "node_modules/plist": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/plist/-/plist-3.0.6.tgz", @@ -28458,9 +31487,9 @@ } }, "node_modules/promise-call-limit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-call-limit/-/promise-call-limit-1.0.1.tgz", - "integrity": "sha512-3+hgaa19jzCGLuSCbieeRsu5C2joKfYn8pY6JAuXFRVfF4IO+L7UPpFWNTeWT9pM7uhskvbPPd/oEOktCn317Q==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/promise-call-limit/-/promise-call-limit-1.0.2.tgz", + "integrity": "sha512-1vTUnfI2hzui8AEIixbdAJlFY4LFDXqQswy/2eOlThAscXCY4It8FdVuI0fMJGAB2aWGbdQf/gv0skKYXmdrHA==", "dev": true, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -29183,16 +32212,25 @@ } }, "node_modules/read-package-json-fast": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz", - "integrity": "sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-3.0.2.tgz", + "integrity": "sha512-0J+Msgym3vrLOUB3hzQCuZHII0xkNGCtz/HJH9xZshwv9DbDwkw1KaE3gx/e2J5rpEY5rtOy6cyhKOPrkP7FZw==", "dev": true, "dependencies": { - "json-parse-even-better-errors": "^2.3.0", - "npm-normalize-package-bin": "^1.0.1" + "json-parse-even-better-errors": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" }, "engines": { - "node": ">=10" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/read-package-json-fast/node_modules/json-parse-even-better-errors": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.0.tgz", + "integrity": "sha512-iZbGHafX/59r39gPwVPRBGw0QQKnA7tte5pSMrhWOW7swGsVvVTjmfyAV9pNqk8YGT7tRCdxRu8uzcgZwoDooA==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/read-package-json/node_modules/brace-expansion": { @@ -29271,6 +32309,12 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/read-package-json/node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "dev": true + }, "node_modules/read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -29479,19 +32523,6 @@ "url": "https://github.com/sponsors/Borewit" } }, - "node_modules/readdir-scoped-modules": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz", - "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", - "deprecated": "This functionality has been moved to @npmcli/fs", - "dev": true, - "dependencies": { - "debuglog": "^1.0.1", - "dezalgo": "^1.0.0", - "graceful-fs": "^4.1.2", - "once": "^1.3.0" - } - }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -29856,6 +32887,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", + "peer": true, "engines": { "node": ">=10" } @@ -29912,9 +32944,9 @@ "integrity": "sha512-tYGfLpKIq9X7lrt4o3IkD9w9bpeAtsejfAqWNR98AoxfTsZqCepKa8eDlRiX8QMiCOD9vMx0/YbKLx0G1nPi5w==" }, "node_modules/rimraf": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.4.0.tgz", - "integrity": "sha512-X36S+qpCUR0HjXlkDe4NAOhS//aHH0Z+h8Ckf2auGJk3PTnx5rLmrHkwNdbVQuCSUhOyFrlRvFEllZOYE+yZGQ==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.4.1.tgz", + "integrity": "sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==", "dev": true, "dependencies": { "glob": "^9.2.0" @@ -30601,6 +33633,215 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "node_modules/sigstore": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-1.2.0.tgz", + "integrity": "sha512-Fr9+W1nkBSIZCkJQR7jDn/zI0UXNsVpp+7mDQkCnZOIxG9p6yNXBx9xntHsfUyYHE55XDkkVV3+rYbrkzAeesA==", + "dev": true, + "dependencies": { + "@sigstore/protobuf-specs": "^0.1.0", + "make-fetch-happen": "^11.0.1", + "tuf-js": "^1.0.0" + }, + "bin": { + "sigstore": "bin/sigstore.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/sigstore/node_modules/@npmcli/fs": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.0.tgz", + "integrity": "sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w==", + "dev": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/sigstore/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/sigstore/node_modules/cacache": { + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-17.0.5.tgz", + "integrity": "sha512-Y/PRQevNSsjAPWykl9aeGz8Pr+OI6BYM9fYDNMvOkuUiG9IhG4LEmaYrZZZvioMUEQ+cBCxT0v8wrnCURccyKA==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^9.3.1", + "lru-cache": "^7.7.1", + "minipass": "^4.0.0", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/sigstore/node_modules/fs-minipass": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.1.tgz", + "integrity": "sha512-MhaJDcFRTuLidHrIttu0RDGyyXs/IYHVmlcxfLAEFIWjc1vdLAkdwT7Ace2u7DbitWC0toKMl5eJZRYNVreIMw==", + "dev": true, + "dependencies": { + "minipass": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/sigstore/node_modules/glob": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.4.tgz", + "integrity": "sha512-qaSc49hojMOv1EPM4EuyITjDSgSKI0rthoHnvE81tcOi1SCVndHko7auqxdQ14eiQG2NDBJBE86+2xIrbIvrbA==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sigstore/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/sigstore/node_modules/make-fetch-happen": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.0.3.tgz", + "integrity": "sha512-oPLh5m10lRNNZDjJ2kP8UpboUx2uFXVaVweVe/lWut4iHWcQEmfqSVJt2ihZsFI8HbpwyyocaXbCAWf0g1ukIA==", + "dev": true, + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^17.0.0", + "http-cache-semantics": "^4.1.1", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^4.0.0", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/sigstore/node_modules/minimatch": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.2.tgz", + "integrity": "sha512-ikHGF67ODxj7vS5NKU2wvTsFLbExee+KXVCnBWh8Cg2hVJfBMQIrlo50qru/09E0EifjnU8dZhJ/iHhyXJM6Mw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sigstore/node_modules/minipass-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.1.tgz", + "integrity": "sha512-t9/wowtf7DYkwz8cfMSt0rMwiyNIBXf5CKZ3S5ZMqRqMYT0oLTp0x1WorMI9WTwvaPg21r1JbFxJMum8JrLGfw==", + "dev": true, + "dependencies": { + "minipass": "^4.0.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/sigstore/node_modules/socks-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", + "dev": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/sigstore/node_modules/ssri": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.1.tgz", + "integrity": "sha512-WVy6di9DlPOeBWEjMScpNipeSX2jIZBGEn5Uuo8Q7aIuFEuDX0pw8RxcOjlD1TWP4obi24ki7m/13+nFpcbXrw==", + "dev": true, + "dependencies": { + "minipass": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/sigstore/node_modules/unique-filename": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", + "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", + "dev": true, + "dependencies": { + "unique-slug": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/sigstore/node_modules/unique-slug": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", + "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", @@ -31513,6 +34754,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "peer": true, "dependencies": { "has-flag": "^4.0.0", "supports-color": "^7.0.0" @@ -31966,6 +35208,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "peer": true, "dependencies": { "ansi-escapes": "^4.2.1", "supports-hyperlinks": "^2.0.0" @@ -32366,12 +35609,12 @@ } }, "node_modules/treeverse": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/treeverse/-/treeverse-2.0.0.tgz", - "integrity": "sha512-N5gJCkLu1aXccpOTtqV6ddSEi6ZmGkh3hjmbu1IjcavJK4qyOVQmi0myQKM7z5jVGmD68SJoliaVrMmVObhj6A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/treeverse/-/treeverse-3.0.0.tgz", + "integrity": "sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ==", "dev": true, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/trim-newlines": { @@ -32551,6 +35794,211 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, + "node_modules/tuf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-1.1.2.tgz", + "integrity": "sha512-gBfbnS6khluxjvoFCpRV0fhWT265xNfpiNXOcBX0Ze6HGbPhe93UG5V5DdKcgm/aXsMadnY76l/h6j63GmJS5g==", + "dev": true, + "dependencies": { + "@tufjs/models": "1.0.1", + "make-fetch-happen": "^11.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/tuf-js/node_modules/@npmcli/fs": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.0.tgz", + "integrity": "sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w==", + "dev": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/tuf-js/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/tuf-js/node_modules/cacache": { + "version": "17.0.5", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-17.0.5.tgz", + "integrity": "sha512-Y/PRQevNSsjAPWykl9aeGz8Pr+OI6BYM9fYDNMvOkuUiG9IhG4LEmaYrZZZvioMUEQ+cBCxT0v8wrnCURccyKA==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^9.3.1", + "lru-cache": "^7.7.1", + "minipass": "^4.0.0", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/tuf-js/node_modules/fs-minipass": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.1.tgz", + "integrity": "sha512-MhaJDcFRTuLidHrIttu0RDGyyXs/IYHVmlcxfLAEFIWjc1vdLAkdwT7Ace2u7DbitWC0toKMl5eJZRYNVreIMw==", + "dev": true, + "dependencies": { + "minipass": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/tuf-js/node_modules/glob": { + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.4.tgz", + "integrity": "sha512-qaSc49hojMOv1EPM4EuyITjDSgSKI0rthoHnvE81tcOi1SCVndHko7auqxdQ14eiQG2NDBJBE86+2xIrbIvrbA==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tuf-js/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/tuf-js/node_modules/make-fetch-happen": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-11.0.3.tgz", + "integrity": "sha512-oPLh5m10lRNNZDjJ2kP8UpboUx2uFXVaVweVe/lWut4iHWcQEmfqSVJt2ihZsFI8HbpwyyocaXbCAWf0g1ukIA==", + "dev": true, + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^17.0.0", + "http-cache-semantics": "^4.1.1", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^4.0.0", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/tuf-js/node_modules/minimatch": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.2.tgz", + "integrity": "sha512-ikHGF67ODxj7vS5NKU2wvTsFLbExee+KXVCnBWh8Cg2hVJfBMQIrlo50qru/09E0EifjnU8dZhJ/iHhyXJM6Mw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tuf-js/node_modules/minipass-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.1.tgz", + "integrity": "sha512-t9/wowtf7DYkwz8cfMSt0rMwiyNIBXf5CKZ3S5ZMqRqMYT0oLTp0x1WorMI9WTwvaPg21r1JbFxJMum8JrLGfw==", + "dev": true, + "dependencies": { + "minipass": "^4.0.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/tuf-js/node_modules/socks-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", + "dev": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/tuf-js/node_modules/ssri": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.1.tgz", + "integrity": "sha512-WVy6di9DlPOeBWEjMScpNipeSX2jIZBGEn5Uuo8Q7aIuFEuDX0pw8RxcOjlD1TWP4obi24ki7m/13+nFpcbXrw==", + "dev": true, + "dependencies": { + "minipass": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/tuf-js/node_modules/unique-filename": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", + "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", + "dev": true, + "dependencies": { + "unique-slug": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/tuf-js/node_modules/unique-slug": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", + "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -34134,10 +37582,197 @@ }, "devDependencies": { "@swc/cli": "^0.1.61", - "@swc/core": "^1.3.37", + "@swc/core": "^1.3.44", "@types/node": "^16.18.11", "@types/semver": "^7.3.13", - "rimraf": "^4.1.2" + "rimraf": "^4.4.1", + "run-script-os": "^1.1.6" + } + }, + "packages/bump-version-for-cron/node_modules/@swc/core": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.44.tgz", + "integrity": "sha512-RC25C8nxOCdfGS//F9Q8aHKx4XoCsxvgO+sSUhvt7zDz1Y2ruVUTu2UOH0VeE0WkA8j6oEZH+xH2SUfDUkxXdA==", + "dev": true, + "hasInstallScript": true, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.3.44", + "@swc/core-darwin-x64": "1.3.44", + "@swc/core-linux-arm-gnueabihf": "1.3.44", + "@swc/core-linux-arm64-gnu": "1.3.44", + "@swc/core-linux-arm64-musl": "1.3.44", + "@swc/core-linux-x64-gnu": "1.3.44", + "@swc/core-linux-x64-musl": "1.3.44", + "@swc/core-win32-arm64-msvc": "1.3.44", + "@swc/core-win32-ia32-msvc": "1.3.44", + "@swc/core-win32-x64-msvc": "1.3.44" + } + }, + "packages/bump-version-for-cron/node_modules/@swc/core-darwin-arm64": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.44.tgz", + "integrity": "sha512-Y+oVsCjXUPvr3D9YLuB1gjP84TseM/CRkbPNrf+3JXQhsPEkgxdIdFP1cl/obeqMQrRgPpvSfK+TOvGuOuV22g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "packages/bump-version-for-cron/node_modules/@swc/core-darwin-x64": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.44.tgz", + "integrity": "sha512-bM0IKBjlSD0yHJbd7bE3il5fTu3oUjUO2zjLkzfIx6tiqbmDyvOX8adaSqse9N+d8Ip9p26b5Vo7pMHq0POGkg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "packages/bump-version-for-cron/node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.44.tgz", + "integrity": "sha512-D4lfVwCbkWscDTb6btb89+bN0kgvjGBPfOmcvih7nY5hxaorwvp+PefkYAhFw8vKmL92lrnWUFNiTemVFN4bxg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/bump-version-for-cron/node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.44.tgz", + "integrity": "sha512-muExPTrN26MFmtO+5uffkH5v4lmd8GdmyWvlC2tL95h7o9genTIQyr7kcSepGZrDe4fM9G6++5YfENhUpXHo+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/bump-version-for-cron/node_modules/@swc/core-linux-arm64-musl": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.44.tgz", + "integrity": "sha512-MY1wY3kSIosjJKKCfFGniJFFVkt3oPvJLN4Dlj+bMgAt3O7anm8lGbyLqUpJ1Ep4rTsJj7ObO06DQiSWp4Fhcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/bump-version-for-cron/node_modules/@swc/core-linux-x64-gnu": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.44.tgz", + "integrity": "sha512-xV4pDxJM06g0yUDTA22ZHgonzGqf/poIlgADRmEkx9cWWm5qLRhmWrrkVX1xZVvdlcXj1ERnia/UkvrDux96lg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/bump-version-for-cron/node_modules/@swc/core-linux-x64-musl": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.44.tgz", + "integrity": "sha512-NJnnlE8vCkKHoo/wIaoywNN/01vNsvhKUjBgUx865sUM/XWAIanpbV41yfdEkC+Iwd+/zB3ZZnOYql3b+Nn8aQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/bump-version-for-cron/node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.44.tgz", + "integrity": "sha512-/xOtjZhX25GEOPLN55icjj9E061DDH7G0A9HfUAEilURgBbvm6bIlqK+t8mOKK0tOsDoHftdTBRkYhTAqUtakQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "packages/bump-version-for-cron/node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.44.tgz", + "integrity": "sha512-uKTAWQuMEW1gJnl8F3eiz3kdk8CiaR5dMWWlGbHIq6dRbur2hoKaEnINR4UqkvvAhhY1YB0Xr5DV1H986Xu2EA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "packages/bump-version-for-cron/node_modules/@swc/core-win32-x64-msvc": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.44.tgz", + "integrity": "sha512-D9fBRhr44cK4YIKFikpktyUDLkZgVj0ISaSl8IjiTvjqTrE/9+E+dzTNHULn5tc3lHVLLxyVwMjJRr2G0D4O6w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" } }, "packages/bump-version-for-cron/node_modules/@types/node": { @@ -34146,6 +37781,56 @@ "integrity": "sha512-ZOzvDRWp8dCVBmgnkIqYCArgdFOO9YzocZp8Ra25N/RStKiWvMOXHMz+GjSeVNe5TstaTmTWPucGJkDw0XXJWA==", "dev": true }, + "packages/business-features/dock": { + "name": "@k8slens/dock", + "version": "1.0.0-alpha.0", + "extraneous": true, + "license": "MIT", + "devDependencies": { + "@async-fn/jest": "^1.6.4", + "@k8slens/eslint-config": "6.5.0-alpha.1", + "@k8slens/react-testing-library-discovery": "^1.0.0-alpha.0" + }, + "peerDependencies": { + "@k8slens/feature-core": "^6.5.0-alpha.0", + "@ogre-tools/fp": "^15.1.2", + "@ogre-tools/injectable": "^15.1.2", + "@ogre-tools/injectable-extension-for-auto-registration": "^15.1.2", + "lodash": "^4.17.21" + } + }, + "packages/business-features/dock/agnostic": { + "version": "1.0.0-alpha.0", + "extraneous": true, + "license": "MIT", + "devDependencies": { + "@async-fn/jest": "^1.6.4", + "@k8slens/eslint-config": "6.5.0-alpha.1" + }, + "peerDependencies": { + "@k8slens/feature-core": "^6.5.0-alpha.0", + "@ogre-tools/injectable": "^15.1.2", + "@ogre-tools/injectable-extension-for-auto-registration": "^15.1.2" + } + }, + "packages/business-features/keyboard-shortcuts": { + "name": "@k8slens/keyboard-shortcuts", + "version": "1.0.0-alpha.0", + "license": "MIT", + "devDependencies": { + "@async-fn/jest": "^1.6.4", + "@k8slens/eslint-config": "6.5.0-alpha.1", + "@k8slens/react-testing-library-discovery": "^1.0.0-alpha.0" + }, + "peerDependencies": { + "@k8slens/feature-core": "^6.5.0-alpha.0", + "@k8slens/react-application": "^1.0.0-alpha.0", + "@ogre-tools/fp": "^15.1.2", + "@ogre-tools/injectable": "^15.1.2", + "@ogre-tools/injectable-extension-for-auto-registration": "^15.1.2", + "lodash": "^4.17.21" + } + }, "packages/cluster-settings": { "name": "@k8slens/cluster-settings", "version": "6.5.0-alpha.1", @@ -34153,10 +37838,196 @@ "devDependencies": { "@ogre-tools/injectable": "^15.1.2", "@swc/cli": "^0.1.61", - "@swc/core": "^1.3.37", + "@swc/core": "^1.3.44", "@types/node": "^16.18.11", "@types/semver": "^7.3.13", - "rimraf": "^4.1.2" + "rimraf": "^4.4.1" + } + }, + "packages/cluster-settings/node_modules/@swc/core": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.44.tgz", + "integrity": "sha512-RC25C8nxOCdfGS//F9Q8aHKx4XoCsxvgO+sSUhvt7zDz1Y2ruVUTu2UOH0VeE0WkA8j6oEZH+xH2SUfDUkxXdA==", + "dev": true, + "hasInstallScript": true, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.3.44", + "@swc/core-darwin-x64": "1.3.44", + "@swc/core-linux-arm-gnueabihf": "1.3.44", + "@swc/core-linux-arm64-gnu": "1.3.44", + "@swc/core-linux-arm64-musl": "1.3.44", + "@swc/core-linux-x64-gnu": "1.3.44", + "@swc/core-linux-x64-musl": "1.3.44", + "@swc/core-win32-arm64-msvc": "1.3.44", + "@swc/core-win32-ia32-msvc": "1.3.44", + "@swc/core-win32-x64-msvc": "1.3.44" + } + }, + "packages/cluster-settings/node_modules/@swc/core-darwin-arm64": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.44.tgz", + "integrity": "sha512-Y+oVsCjXUPvr3D9YLuB1gjP84TseM/CRkbPNrf+3JXQhsPEkgxdIdFP1cl/obeqMQrRgPpvSfK+TOvGuOuV22g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "packages/cluster-settings/node_modules/@swc/core-darwin-x64": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.44.tgz", + "integrity": "sha512-bM0IKBjlSD0yHJbd7bE3il5fTu3oUjUO2zjLkzfIx6tiqbmDyvOX8adaSqse9N+d8Ip9p26b5Vo7pMHq0POGkg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "packages/cluster-settings/node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.44.tgz", + "integrity": "sha512-D4lfVwCbkWscDTb6btb89+bN0kgvjGBPfOmcvih7nY5hxaorwvp+PefkYAhFw8vKmL92lrnWUFNiTemVFN4bxg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/cluster-settings/node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.44.tgz", + "integrity": "sha512-muExPTrN26MFmtO+5uffkH5v4lmd8GdmyWvlC2tL95h7o9genTIQyr7kcSepGZrDe4fM9G6++5YfENhUpXHo+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/cluster-settings/node_modules/@swc/core-linux-arm64-musl": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.44.tgz", + "integrity": "sha512-MY1wY3kSIosjJKKCfFGniJFFVkt3oPvJLN4Dlj+bMgAt3O7anm8lGbyLqUpJ1Ep4rTsJj7ObO06DQiSWp4Fhcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/cluster-settings/node_modules/@swc/core-linux-x64-gnu": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.44.tgz", + "integrity": "sha512-xV4pDxJM06g0yUDTA22ZHgonzGqf/poIlgADRmEkx9cWWm5qLRhmWrrkVX1xZVvdlcXj1ERnia/UkvrDux96lg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/cluster-settings/node_modules/@swc/core-linux-x64-musl": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.44.tgz", + "integrity": "sha512-NJnnlE8vCkKHoo/wIaoywNN/01vNsvhKUjBgUx865sUM/XWAIanpbV41yfdEkC+Iwd+/zB3ZZnOYql3b+Nn8aQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/cluster-settings/node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.44.tgz", + "integrity": "sha512-/xOtjZhX25GEOPLN55icjj9E061DDH7G0A9HfUAEilURgBbvm6bIlqK+t8mOKK0tOsDoHftdTBRkYhTAqUtakQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "packages/cluster-settings/node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.44.tgz", + "integrity": "sha512-uKTAWQuMEW1gJnl8F3eiz3kdk8CiaR5dMWWlGbHIq6dRbur2hoKaEnINR4UqkvvAhhY1YB0Xr5DV1H986Xu2EA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "packages/cluster-settings/node_modules/@swc/core-win32-x64-msvc": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.44.tgz", + "integrity": "sha512-D9fBRhr44cK4YIKFikpktyUDLkZgVj0ISaSl8IjiTvjqTrE/9+E+dzTNHULn5tc3lHVLLxyVwMjJRr2G0D4O6w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" } }, "packages/cluster-settings/node_modules/@types/node": { @@ -34173,8 +38044,8 @@ "@astronautlabs/jsonpath": "^1.1.0", "@hapi/call": "^9.0.1", "@hapi/subtext": "^7.1.0", - "@k8slens/cluster-settings": "^6.5.0-alpha.1", "@k8slens/node-fetch": "^6.5.0-alpha.1", + "@k8slens/react-application": "^1.0.0-alpha.0", "@kubernetes/client-node": "^0.18.1", "@material-ui/styles": "^4.11.5", "@ogre-tools/fp": "^15.1.2", @@ -34189,7 +38060,7 @@ "await-lock": "^2.2.2", "byline": "^5.0.0", "chokidar": "^3.5.3", - "conf": "^7.1.2", + "conf": "^10.2.0", "crypto-js": "^4.1.1", "electron-devtools-installer": "^3.2.0", "electron-updater": "^4.6.5", @@ -34197,7 +38068,6 @@ "filehound": "^1.17.6", "fs-extra": "^9.0.1", "glob-to-regexp": "^0.4.1", - "got": "^11.8.6", "grapheme-splitter": "^1.0.4", "handlebars": "^4.7.7", "history": "^4.10.1", @@ -34244,12 +38114,13 @@ "devDependencies": { "@async-fn/jest": "1.6.4", "@k8slens/messaging-fake-bridge": "^1.0.0-alpha.1", + "@k8slens/react-testing-library-discovery": "^1.0.0-alpha.0", "@material-ui/core": "^4.12.3", "@material-ui/icons": "^4.11.2", "@material-ui/lab": "^4.0.0-alpha.60", "@sentry/types": "^6.19.7", "@swc/cli": "^0.1.61", - "@swc/core": "^1.3.37", + "@swc/core": "^1.3.44", "@swc/jest": "^0.2.24", "@testing-library/dom": "^7.31.2", "@testing-library/jest-dom": "^5.16.5", @@ -34268,7 +38139,7 @@ "@types/hapi__call": "^9.0.0", "@types/hapi__subtext": "^7.0.0", "@types/http-proxy": "^1.17.9", - "@types/jest": "^28.1.6", + "@types/jest": "^29.5.0", "@types/js-yaml": "^4.0.5", "@types/lodash": "^4.14.191", "@types/marked": "^4.0.8", @@ -34279,7 +38150,7 @@ "@types/proper-lockfile": "^4.1.2", "@types/randomcolor": "^0.5.7", "@types/react": "^17.0.45", - "@types/react-beautiful-dnd": "^13.1.3", + "@types/react-beautiful-dnd": "^13.1.4", "@types/react-dom": "^17.0.16", "@types/react-router": "^5.1.19", "@types/react-router-dom": "^5.3.3", @@ -34293,7 +38164,7 @@ "@types/triple-beam": "^1.3.2", "@types/url-parse": "^1.4.8", "@types/uuid": "^8.3.4", - "@types/webpack": "^5.28.0", + "@types/webpack": "^5.28.1", "@types/webpack-dev-server": "^4.7.2", "@types/webpack-env": "^1.18.0", "@types/webpack-node-externals": "^2.5.3", @@ -34326,7 +38197,7 @@ "identity-obj-proxy": "^3.0.0", "ignore-loader": "^0.1.2", "include-media": "^1.4.9", - "jest": "^28.1.3", + "jest": "^29.5.0", "jest-canvas-mock": "^2.3.1", "jest-environment-jsdom": "^28.1.3", "jest-mock-extended": "^2.0.9", @@ -34340,7 +38211,7 @@ "node-gyp": "^8.3.0", "node-loader": "^2.0.0", "nodemon": "^2.0.20", - "playwright": "^1.30.0", + "playwright": "^1.32.1", "postcss": "^8.4.21", "postcss-loader": "^6.2.1", "query-string": "^7.1.3", @@ -34353,7 +38224,7 @@ "react-select-event": "^5.5.1", "react-table": "^7.8.0", "react-window": "^1.8.8", - "rimraf": "^4.1.2", + "rimraf": "^4.4.1", "sass": "^1.58.2", "sass-loader": "^12.6.0", "style-loader": "^3.3.1", @@ -34366,9 +38237,9 @@ "typedoc-plugin-markdown": "^3.13.6", "typescript": "^4.9.5", "typescript-plugin-css-modules": "^3.4.0", - "webpack": "^5.75.0", + "webpack": "^5.77.0", "webpack-cli": "^4.9.2", - "webpack-dev-server": "^4.11.1", + "webpack-dev-server": "^4.13.2", "webpack-node-externals": "^3.0.0", "xterm": "^4.19.0", "xterm-addon-fit": "^0.5.0" @@ -34379,6 +38250,7 @@ "peerDependencies": { "@k8slens/application": "^6.5.0-alpha.0", "@k8slens/application-for-electron-main": "^6.5.0-alpha.0", + "@k8slens/cluster-settings": "^6.5.0-alpha.1", "@k8slens/legacy-extensions": "^1.0.0-alpha.0", "@k8slens/messaging": "^1.0.0-alpha.1", "@k8slens/messaging-for-main": "^1.0.0-alpha.1", @@ -34407,12 +38279,1402 @@ "xterm-addon-fit": "^0.5.0" } }, + "packages/core/node_modules/@jest/console": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.5.0.tgz", + "integrity": "sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/core/node_modules/@jest/core": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.5.0.tgz", + "integrity": "sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.5.0", + "@jest/reporters": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.5.0", + "jest-config": "^29.5.0", + "jest-haste-map": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-regex-util": "^29.4.3", + "jest-resolve": "^29.5.0", + "jest-resolve-dependencies": "^29.5.0", + "jest-runner": "^29.5.0", + "jest-runtime": "^29.5.0", + "jest-snapshot": "^29.5.0", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "jest-watcher": "^29.5.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.5.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "packages/core/node_modules/@jest/core/node_modules/jest-config": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.5.0.tgz", + "integrity": "sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.5.0", + "@jest/types": "^29.5.0", + "babel-jest": "^29.5.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.5.0", + "jest-environment-node": "^29.5.0", + "jest-get-type": "^29.4.3", + "jest-regex-util": "^29.4.3", + "jest-resolve": "^29.5.0", + "jest-runner": "^29.5.0", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.5.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "packages/core/node_modules/@jest/environment": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.5.0.tgz", + "integrity": "sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "jest-mock": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/core/node_modules/@jest/expect": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.5.0.tgz", + "integrity": "sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g==", + "dev": true, + "dependencies": { + "expect": "^29.5.0", + "jest-snapshot": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/core/node_modules/@jest/expect-utils": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz", + "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.4.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/core/node_modules/@jest/fake-timers": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.5.0.tgz", + "integrity": "sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.5.0", + "jest-mock": "^29.5.0", + "jest-util": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/core/node_modules/@jest/reporters": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.5.0.tgz", + "integrity": "sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@jridgewell/trace-mapping": "^0.3.15", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "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-message-util": "^29.5.0", + "jest-util": "^29.5.0", + "jest-worker": "^29.5.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "packages/core/node_modules/@jest/test-result": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.5.0.tgz", + "integrity": "sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/core/node_modules/@jest/test-sequencer": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz", + "integrity": "sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.5.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.5.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/core/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "packages/core/node_modules/@sinonjs/fake-timers": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", + "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^2.0.0" + } + }, + "packages/core/node_modules/@swc/core": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.44.tgz", + "integrity": "sha512-RC25C8nxOCdfGS//F9Q8aHKx4XoCsxvgO+sSUhvt7zDz1Y2ruVUTu2UOH0VeE0WkA8j6oEZH+xH2SUfDUkxXdA==", + "dev": true, + "hasInstallScript": true, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.3.44", + "@swc/core-darwin-x64": "1.3.44", + "@swc/core-linux-arm-gnueabihf": "1.3.44", + "@swc/core-linux-arm64-gnu": "1.3.44", + "@swc/core-linux-arm64-musl": "1.3.44", + "@swc/core-linux-x64-gnu": "1.3.44", + "@swc/core-linux-x64-musl": "1.3.44", + "@swc/core-win32-arm64-msvc": "1.3.44", + "@swc/core-win32-ia32-msvc": "1.3.44", + "@swc/core-win32-x64-msvc": "1.3.44" + } + }, + "packages/core/node_modules/@swc/core-darwin-arm64": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.44.tgz", + "integrity": "sha512-Y+oVsCjXUPvr3D9YLuB1gjP84TseM/CRkbPNrf+3JXQhsPEkgxdIdFP1cl/obeqMQrRgPpvSfK+TOvGuOuV22g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "packages/core/node_modules/@swc/core-darwin-x64": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.44.tgz", + "integrity": "sha512-bM0IKBjlSD0yHJbd7bE3il5fTu3oUjUO2zjLkzfIx6tiqbmDyvOX8adaSqse9N+d8Ip9p26b5Vo7pMHq0POGkg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "packages/core/node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.44.tgz", + "integrity": "sha512-D4lfVwCbkWscDTb6btb89+bN0kgvjGBPfOmcvih7nY5hxaorwvp+PefkYAhFw8vKmL92lrnWUFNiTemVFN4bxg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/core/node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.44.tgz", + "integrity": "sha512-muExPTrN26MFmtO+5uffkH5v4lmd8GdmyWvlC2tL95h7o9genTIQyr7kcSepGZrDe4fM9G6++5YfENhUpXHo+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/core/node_modules/@swc/core-linux-arm64-musl": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.44.tgz", + "integrity": "sha512-MY1wY3kSIosjJKKCfFGniJFFVkt3oPvJLN4Dlj+bMgAt3O7anm8lGbyLqUpJ1Ep4rTsJj7ObO06DQiSWp4Fhcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/core/node_modules/@swc/core-linux-x64-gnu": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.44.tgz", + "integrity": "sha512-xV4pDxJM06g0yUDTA22ZHgonzGqf/poIlgADRmEkx9cWWm5qLRhmWrrkVX1xZVvdlcXj1ERnia/UkvrDux96lg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/core/node_modules/@swc/core-linux-x64-musl": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.44.tgz", + "integrity": "sha512-NJnnlE8vCkKHoo/wIaoywNN/01vNsvhKUjBgUx865sUM/XWAIanpbV41yfdEkC+Iwd+/zB3ZZnOYql3b+Nn8aQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/core/node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.44.tgz", + "integrity": "sha512-/xOtjZhX25GEOPLN55icjj9E061DDH7G0A9HfUAEilURgBbvm6bIlqK+t8mOKK0tOsDoHftdTBRkYhTAqUtakQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "packages/core/node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.44.tgz", + "integrity": "sha512-uKTAWQuMEW1gJnl8F3eiz3kdk8CiaR5dMWWlGbHIq6dRbur2hoKaEnINR4UqkvvAhhY1YB0Xr5DV1H986Xu2EA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "packages/core/node_modules/@swc/core-win32-x64-msvc": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.44.tgz", + "integrity": "sha512-D9fBRhr44cK4YIKFikpktyUDLkZgVj0ISaSl8IjiTvjqTrE/9+E+dzTNHULn5tc3lHVLLxyVwMjJRr2G0D4O6w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "packages/core/node_modules/@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true + }, + "packages/core/node_modules/@types/jest": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.0.tgz", + "integrity": "sha512-3Emr5VOl/aoBwnWcH/EFQvlSAmjV+XtV9GGu5mwdYew5vhQh0IUZx/60x0TzHDu09Bi7HMx10t/namdJw5QIcg==", + "dev": true, + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, "packages/core/node_modules/@types/node": { "version": "16.18.16", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.16.tgz", "integrity": "sha512-ZOzvDRWp8dCVBmgnkIqYCArgdFOO9YzocZp8Ra25N/RStKiWvMOXHMz+GjSeVNe5TstaTmTWPucGJkDw0XXJWA==", "dev": true }, + "packages/core/node_modules/@types/react-beautiful-dnd": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/@types/react-beautiful-dnd/-/react-beautiful-dnd-13.1.4.tgz", + "integrity": "sha512-4bIBdzOr0aavN+88q3C7Pgz+xkb7tz3whORYrmSj77wfVEMfiWiooIwVWFR7KM2e+uGTe5BVrXqSfb0aHeflJA==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "packages/core/node_modules/@types/webpack": { + "version": "5.28.1", + "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-5.28.1.tgz", + "integrity": "sha512-qw1MqGZclCoBrpiSe/hokSgQM/su8Ocpl3L/YHE0L6moyaypg4+5F7Uzq7NgaPKPxUxUbQ4fLPLpDWdR27bCZw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "tapable": "^2.2.0", + "webpack": "^5" + } + }, + "packages/core/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "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" + } + }, + "packages/core/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" + } + }, + "packages/core/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" + } + }, + "packages/core/node_modules/babel-jest": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.5.0.tgz", + "integrity": "sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==", + "dev": true, + "dependencies": { + "@jest/transform": "^29.5.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.5.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "packages/core/node_modules/babel-plugin-jest-hoist": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz", + "integrity": "sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/core/node_modules/babel-preset-jest": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz", + "integrity": "sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.5.0", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "packages/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" + } + }, + "packages/core/node_modules/conf": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/conf/-/conf-10.2.0.tgz", + "integrity": "sha512-8fLl9F04EJqjSqH+QjITQfJF8BrOVaYr1jewVgSRAEWePfxT0sku4w2hrGQ60BC/TNLGQ2pgxNlTbWQmMPFvXg==", + "dependencies": { + "ajv": "^8.6.3", + "ajv-formats": "^2.1.1", + "atomically": "^1.7.0", + "debounce-fn": "^4.0.0", + "dot-prop": "^6.0.1", + "env-paths": "^2.2.1", + "json-schema-typed": "^7.0.3", + "onetime": "^5.1.2", + "pkg-up": "^3.1.0", + "semver": "^7.3.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/core/node_modules/diff-sequences": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", + "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/core/node_modules/dot-prop": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", + "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/core/node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "packages/core/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" + } + }, + "packages/core/node_modules/expect": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz", + "integrity": "sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.5.0", + "jest-get-type": "^29.4.3", + "jest-matcher-utils": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/core/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" + } + }, + "packages/core/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" + } + }, + "packages/core/node_modules/jest": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.5.0.tgz", + "integrity": "sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==", + "dev": true, + "dependencies": { + "@jest/core": "^29.5.0", + "@jest/types": "^29.5.0", + "import-local": "^3.0.2", + "jest-cli": "^29.5.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "packages/core/node_modules/jest-changed-files": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.5.0.tgz", + "integrity": "sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/core/node_modules/jest-circus": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.5.0.tgz", + "integrity": "sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.5.0", + "@jest/expect": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.5.0", + "jest-matcher-utils": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-runtime": "^29.5.0", + "jest-snapshot": "^29.5.0", + "jest-util": "^29.5.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.5.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/core/node_modules/jest-cli": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.5.0.tgz", + "integrity": "sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw==", + "dev": true, + "dependencies": { + "@jest/core": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/types": "^29.5.0", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^29.5.0", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "prompts": "^2.0.1", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "packages/core/node_modules/jest-cli/node_modules/jest-config": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.5.0.tgz", + "integrity": "sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.5.0", + "@jest/types": "^29.5.0", + "babel-jest": "^29.5.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.5.0", + "jest-environment-node": "^29.5.0", + "jest-get-type": "^29.4.3", + "jest-regex-util": "^29.4.3", + "jest-resolve": "^29.5.0", + "jest-runner": "^29.5.0", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.5.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "packages/core/node_modules/jest-diff": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz", + "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.4.3", + "jest-get-type": "^29.4.3", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/core/node_modules/jest-docblock": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.3.tgz", + "integrity": "sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/core/node_modules/jest-each": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.5.0.tgz", + "integrity": "sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.4.3", + "jest-util": "^29.5.0", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/core/node_modules/jest-environment-node": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.5.0.tgz", + "integrity": "sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.5.0", + "@jest/fake-timers": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "jest-mock": "^29.5.0", + "jest-util": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/core/node_modules/jest-get-type": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", + "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/core/node_modules/jest-leak-detector": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz", + "integrity": "sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.4.3", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/core/node_modules/jest-matcher-utils": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz", + "integrity": "sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.5.0", + "jest-get-type": "^29.4.3", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/core/node_modules/jest-message-util": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz", + "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.5.0", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.5.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/core/node_modules/jest-mock": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.5.0.tgz", + "integrity": "sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@types/node": "*", + "jest-util": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/core/node_modules/jest-resolve": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.5.0.tgz", + "integrity": "sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.5.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/core/node_modules/jest-resolve-dependencies": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz", + "integrity": "sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg==", + "dev": true, + "dependencies": { + "jest-regex-util": "^29.4.3", + "jest-snapshot": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/core/node_modules/jest-runner": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.5.0.tgz", + "integrity": "sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.5.0", + "@jest/environment": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.4.3", + "jest-environment-node": "^29.5.0", + "jest-haste-map": "^29.5.0", + "jest-leak-detector": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-resolve": "^29.5.0", + "jest-runtime": "^29.5.0", + "jest-util": "^29.5.0", + "jest-watcher": "^29.5.0", + "jest-worker": "^29.5.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/core/node_modules/jest-snapshot": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.5.0.tgz", + "integrity": "sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.5.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.5.0", + "jest-get-type": "^29.4.3", + "jest-matcher-utils": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.5.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/core/node_modules/jest-validate": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.5.0.tgz", + "integrity": "sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.4.3", + "leven": "^3.1.0", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/core/node_modules/jest-watcher": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.5.0.tgz", + "integrity": "sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.5.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/core/node_modules/jest-worker": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.5.0.tgz", + "integrity": "sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.5.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/core/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==" + }, + "packages/core/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" + } + }, + "packages/core/node_modules/playwright": { + "version": "1.32.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.32.1.tgz", + "integrity": "sha512-GnEizysWMvoqHC3I9l8+4/ZxeLwLNdJJG76xdKGxzOcIZDcw5RSk/FKrFb5CuA+zcLpjIM2p9eR9Z4CuUDkWXg==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "playwright-core": "1.32.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=14" + } + }, + "packages/core/node_modules/playwright-core": { + "version": "1.32.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.32.1.tgz", + "integrity": "sha512-KZYUQC10mXD2Am1rGlidaalNGYk3LU1vZqqNk0gT4XPty1jOqgup8KDP8l2CUlqoNKhXM5IfGjWgW37xvGllBA==", + "dev": true, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=14" + } + }, + "packages/core/node_modules/pretty-format": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", + "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.4.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/core/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "packages/core/node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "packages/core/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" + } + }, + "packages/core/node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "packages/core/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" + } + }, + "packages/core/node_modules/webpack": { + "version": "5.77.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.77.0.tgz", + "integrity": "sha512-sbGNjBr5Ya5ss91yzjeJTLKyfiwo5C628AFjEa6WSXcZa4E+F57om3Cc8xLb1Jh0b243AWuSYRf3dn7HVeFQ9Q==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.10.0", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "packages/core/node_modules/webpack-dev-server": { + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.13.2.tgz", + "integrity": "sha512-5i6TrGBRxG4vnfDpB6qSQGfnB6skGBXNL5/542w2uRGLimX6qeE5BQMLrzIC3JYV/xlGOv+s+hTleI9AZKUQNw==", + "dev": true, + "dependencies": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.1", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "launch-editor": "^2.6.0", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.1.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.13.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-cli": { + "optional": true + } + } + }, + "packages/core/node_modules/webpack-dev-server/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/core/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" + } + }, + "packages/core/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" + } + }, "packages/ensure-binaries": { "name": "@k8slens/ensure-binaries", "version": "6.5.0-alpha.1", @@ -34430,13 +39692,200 @@ }, "devDependencies": { "@swc/cli": "^0.1.61", - "@swc/core": "^1.3.37", + "@swc/core": "^1.3.44", "@types/cli-progress": "^3.11.0", "@types/gunzip-maybe": "^1.4.0", "@types/node": "^16.18.11", "@types/semver": "^7.3.13", "@types/tar-stream": "^2.2.2", - "rimraf": "^4.1.2" + "rimraf": "^4.4.1", + "run-script-os": "^1.1.6" + } + }, + "packages/ensure-binaries/node_modules/@swc/core": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.44.tgz", + "integrity": "sha512-RC25C8nxOCdfGS//F9Q8aHKx4XoCsxvgO+sSUhvt7zDz1Y2ruVUTu2UOH0VeE0WkA8j6oEZH+xH2SUfDUkxXdA==", + "dev": true, + "hasInstallScript": true, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.3.44", + "@swc/core-darwin-x64": "1.3.44", + "@swc/core-linux-arm-gnueabihf": "1.3.44", + "@swc/core-linux-arm64-gnu": "1.3.44", + "@swc/core-linux-arm64-musl": "1.3.44", + "@swc/core-linux-x64-gnu": "1.3.44", + "@swc/core-linux-x64-musl": "1.3.44", + "@swc/core-win32-arm64-msvc": "1.3.44", + "@swc/core-win32-ia32-msvc": "1.3.44", + "@swc/core-win32-x64-msvc": "1.3.44" + } + }, + "packages/ensure-binaries/node_modules/@swc/core-darwin-arm64": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.44.tgz", + "integrity": "sha512-Y+oVsCjXUPvr3D9YLuB1gjP84TseM/CRkbPNrf+3JXQhsPEkgxdIdFP1cl/obeqMQrRgPpvSfK+TOvGuOuV22g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "packages/ensure-binaries/node_modules/@swc/core-darwin-x64": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.44.tgz", + "integrity": "sha512-bM0IKBjlSD0yHJbd7bE3il5fTu3oUjUO2zjLkzfIx6tiqbmDyvOX8adaSqse9N+d8Ip9p26b5Vo7pMHq0POGkg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "packages/ensure-binaries/node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.44.tgz", + "integrity": "sha512-D4lfVwCbkWscDTb6btb89+bN0kgvjGBPfOmcvih7nY5hxaorwvp+PefkYAhFw8vKmL92lrnWUFNiTemVFN4bxg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/ensure-binaries/node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.44.tgz", + "integrity": "sha512-muExPTrN26MFmtO+5uffkH5v4lmd8GdmyWvlC2tL95h7o9genTIQyr7kcSepGZrDe4fM9G6++5YfENhUpXHo+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/ensure-binaries/node_modules/@swc/core-linux-arm64-musl": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.44.tgz", + "integrity": "sha512-MY1wY3kSIosjJKKCfFGniJFFVkt3oPvJLN4Dlj+bMgAt3O7anm8lGbyLqUpJ1Ep4rTsJj7ObO06DQiSWp4Fhcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/ensure-binaries/node_modules/@swc/core-linux-x64-gnu": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.44.tgz", + "integrity": "sha512-xV4pDxJM06g0yUDTA22ZHgonzGqf/poIlgADRmEkx9cWWm5qLRhmWrrkVX1xZVvdlcXj1ERnia/UkvrDux96lg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/ensure-binaries/node_modules/@swc/core-linux-x64-musl": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.44.tgz", + "integrity": "sha512-NJnnlE8vCkKHoo/wIaoywNN/01vNsvhKUjBgUx865sUM/XWAIanpbV41yfdEkC+Iwd+/zB3ZZnOYql3b+Nn8aQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/ensure-binaries/node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.44.tgz", + "integrity": "sha512-/xOtjZhX25GEOPLN55icjj9E061DDH7G0A9HfUAEilURgBbvm6bIlqK+t8mOKK0tOsDoHftdTBRkYhTAqUtakQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "packages/ensure-binaries/node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.44.tgz", + "integrity": "sha512-uKTAWQuMEW1gJnl8F3eiz3kdk8CiaR5dMWWlGbHIq6dRbur2hoKaEnINR4UqkvvAhhY1YB0Xr5DV1H986Xu2EA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "packages/ensure-binaries/node_modules/@swc/core-win32-x64-msvc": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.44.tgz", + "integrity": "sha512-D9fBRhr44cK4YIKFikpktyUDLkZgVj0ISaSl8IjiTvjqTrE/9+E+dzTNHULn5tc3lHVLLxyVwMjJRr2G0D4O6w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" } }, "packages/ensure-binaries/node_modules/@types/node": { @@ -34454,12 +39903,12 @@ }, "devDependencies": { "@types/node": "^16.18.6", - "@types/webpack": "^5.28.0", + "@types/webpack": "^5.28.1", "@types/webpack-env": "^1.18.0", "@types/webpack-node-externals": "2.5.3", "css-loader": "^6.7.2", "node-loader": "^2.0.0", - "rimraf": "^4.1.2", + "rimraf": "^4.4.1", "style-loader": "^3.3.1", "ts-loader": "^9.4.2", "ts-node": "^10.9.1", @@ -34468,16 +39917,33 @@ "typedoc-plugin-markdown": "^3.13.6", "typescript": "^4.9.5", "typescript-plugin-css-modules": "^4.1.1", - "webpack": "^5.75.0", + "webpack": "^5.77.0", "webpack-cli": "^5.0.1" } }, + "packages/extension-api/node_modules/@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true + }, "packages/extension-api/node_modules/@types/node": { "version": "16.18.16", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.16.tgz", "integrity": "sha512-ZOzvDRWp8dCVBmgnkIqYCArgdFOO9YzocZp8Ra25N/RStKiWvMOXHMz+GjSeVNe5TstaTmTWPucGJkDw0XXJWA==", "dev": true }, + "packages/extension-api/node_modules/@types/webpack": { + "version": "5.28.1", + "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-5.28.1.tgz", + "integrity": "sha512-qw1MqGZclCoBrpiSe/hokSgQM/su8Ocpl3L/YHE0L6moyaypg4+5F7Uzq7NgaPKPxUxUbQ4fLPLpDWdR27bCZw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "tapable": "^2.2.0", + "webpack": "^5" + } + }, "packages/extension-api/node_modules/@webpack-cli/configtest": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.0.1.tgz", @@ -34642,6 +40108,53 @@ "typescript": ">=3.9.0" } }, + "packages/extension-api/node_modules/webpack": { + "version": "5.77.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.77.0.tgz", + "integrity": "sha512-sbGNjBr5Ya5ss91yzjeJTLKyfiwo5C628AFjEa6WSXcZa4E+F57om3Cc8xLb1Jh0b243AWuSYRf3dn7HVeFQ9Q==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.10.0", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, "packages/extension-api/node_modules/webpack-cli": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.0.1.tgz", @@ -34687,6 +40200,15 @@ } } }, + "packages/extension-api/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" + } + }, "packages/generate-tray-icons": { "name": "@k8slens/generate-tray-icons", "version": "6.5.0-alpha.1", @@ -34694,7 +40216,7 @@ "dependencies": { "arg": "^5.0.2", "jsdom": "^21.1.0", - "rimraf": "^4.1.2", + "rimraf": "^4.4.1", "sharp": "^0.31.3" }, "bin": { @@ -34702,11 +40224,198 @@ }, "devDependencies": { "@swc/cli": "^0.1.61", - "@swc/core": "^1.3.37", + "@swc/core": "^1.3.44", "@types/jsdom": "^20.0.1", "@types/node": "^18.11.18", "@types/sharp": "^0.31.1", - "rimraf": "^4.1.2" + "rimraf": "^4.4.1", + "run-script-os": "^1.1.6" + } + }, + "packages/generate-tray-icons/node_modules/@swc/core": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.44.tgz", + "integrity": "sha512-RC25C8nxOCdfGS//F9Q8aHKx4XoCsxvgO+sSUhvt7zDz1Y2ruVUTu2UOH0VeE0WkA8j6oEZH+xH2SUfDUkxXdA==", + "dev": true, + "hasInstallScript": true, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.3.44", + "@swc/core-darwin-x64": "1.3.44", + "@swc/core-linux-arm-gnueabihf": "1.3.44", + "@swc/core-linux-arm64-gnu": "1.3.44", + "@swc/core-linux-arm64-musl": "1.3.44", + "@swc/core-linux-x64-gnu": "1.3.44", + "@swc/core-linux-x64-musl": "1.3.44", + "@swc/core-win32-arm64-msvc": "1.3.44", + "@swc/core-win32-ia32-msvc": "1.3.44", + "@swc/core-win32-x64-msvc": "1.3.44" + } + }, + "packages/generate-tray-icons/node_modules/@swc/core-darwin-arm64": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.44.tgz", + "integrity": "sha512-Y+oVsCjXUPvr3D9YLuB1gjP84TseM/CRkbPNrf+3JXQhsPEkgxdIdFP1cl/obeqMQrRgPpvSfK+TOvGuOuV22g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "packages/generate-tray-icons/node_modules/@swc/core-darwin-x64": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.44.tgz", + "integrity": "sha512-bM0IKBjlSD0yHJbd7bE3il5fTu3oUjUO2zjLkzfIx6tiqbmDyvOX8adaSqse9N+d8Ip9p26b5Vo7pMHq0POGkg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "packages/generate-tray-icons/node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.44.tgz", + "integrity": "sha512-D4lfVwCbkWscDTb6btb89+bN0kgvjGBPfOmcvih7nY5hxaorwvp+PefkYAhFw8vKmL92lrnWUFNiTemVFN4bxg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/generate-tray-icons/node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.44.tgz", + "integrity": "sha512-muExPTrN26MFmtO+5uffkH5v4lmd8GdmyWvlC2tL95h7o9genTIQyr7kcSepGZrDe4fM9G6++5YfENhUpXHo+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/generate-tray-icons/node_modules/@swc/core-linux-arm64-musl": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.44.tgz", + "integrity": "sha512-MY1wY3kSIosjJKKCfFGniJFFVkt3oPvJLN4Dlj+bMgAt3O7anm8lGbyLqUpJ1Ep4rTsJj7ObO06DQiSWp4Fhcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/generate-tray-icons/node_modules/@swc/core-linux-x64-gnu": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.44.tgz", + "integrity": "sha512-xV4pDxJM06g0yUDTA22ZHgonzGqf/poIlgADRmEkx9cWWm5qLRhmWrrkVX1xZVvdlcXj1ERnia/UkvrDux96lg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/generate-tray-icons/node_modules/@swc/core-linux-x64-musl": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.44.tgz", + "integrity": "sha512-NJnnlE8vCkKHoo/wIaoywNN/01vNsvhKUjBgUx865sUM/XWAIanpbV41yfdEkC+Iwd+/zB3ZZnOYql3b+Nn8aQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/generate-tray-icons/node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.44.tgz", + "integrity": "sha512-/xOtjZhX25GEOPLN55icjj9E061DDH7G0A9HfUAEilURgBbvm6bIlqK+t8mOKK0tOsDoHftdTBRkYhTAqUtakQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "packages/generate-tray-icons/node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.44.tgz", + "integrity": "sha512-uKTAWQuMEW1gJnl8F3eiz3kdk8CiaR5dMWWlGbHIq6dRbur2hoKaEnINR4UqkvvAhhY1YB0Xr5DV1H986Xu2EA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "packages/generate-tray-icons/node_modules/@swc/core-win32-x64-msvc": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.44.tgz", + "integrity": "sha512-D9fBRhr44cK4YIKFikpktyUDLkZgVj0ISaSl8IjiTvjqTrE/9+E+dzTNHULn5tc3lHVLLxyVwMjJRr2G0D4O6w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" } }, "packages/generate-tray-icons/node_modules/color": { @@ -34780,7 +40489,7 @@ "version": "6.5.0-alpha.1", "license": "MIT", "dependencies": { - "@swc/core": "^1.3.38", + "@swc/core": "^1.3.44", "@swc/jest": "^0.2.23", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", @@ -35013,6 +40722,181 @@ "@sinonjs/commons": "^2.0.0" } }, + "packages/infrastructure/jest/node_modules/@swc/core": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.44.tgz", + "integrity": "sha512-RC25C8nxOCdfGS//F9Q8aHKx4XoCsxvgO+sSUhvt7zDz1Y2ruVUTu2UOH0VeE0WkA8j6oEZH+xH2SUfDUkxXdA==", + "hasInstallScript": true, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.3.44", + "@swc/core-darwin-x64": "1.3.44", + "@swc/core-linux-arm-gnueabihf": "1.3.44", + "@swc/core-linux-arm64-gnu": "1.3.44", + "@swc/core-linux-arm64-musl": "1.3.44", + "@swc/core-linux-x64-gnu": "1.3.44", + "@swc/core-linux-x64-musl": "1.3.44", + "@swc/core-win32-arm64-msvc": "1.3.44", + "@swc/core-win32-ia32-msvc": "1.3.44", + "@swc/core-win32-x64-msvc": "1.3.44" + } + }, + "packages/infrastructure/jest/node_modules/@swc/core-darwin-arm64": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.44.tgz", + "integrity": "sha512-Y+oVsCjXUPvr3D9YLuB1gjP84TseM/CRkbPNrf+3JXQhsPEkgxdIdFP1cl/obeqMQrRgPpvSfK+TOvGuOuV22g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "packages/infrastructure/jest/node_modules/@swc/core-darwin-x64": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.44.tgz", + "integrity": "sha512-bM0IKBjlSD0yHJbd7bE3il5fTu3oUjUO2zjLkzfIx6tiqbmDyvOX8adaSqse9N+d8Ip9p26b5Vo7pMHq0POGkg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "packages/infrastructure/jest/node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.44.tgz", + "integrity": "sha512-D4lfVwCbkWscDTb6btb89+bN0kgvjGBPfOmcvih7nY5hxaorwvp+PefkYAhFw8vKmL92lrnWUFNiTemVFN4bxg==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/infrastructure/jest/node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.44.tgz", + "integrity": "sha512-muExPTrN26MFmtO+5uffkH5v4lmd8GdmyWvlC2tL95h7o9genTIQyr7kcSepGZrDe4fM9G6++5YfENhUpXHo+g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/infrastructure/jest/node_modules/@swc/core-linux-arm64-musl": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.44.tgz", + "integrity": "sha512-MY1wY3kSIosjJKKCfFGniJFFVkt3oPvJLN4Dlj+bMgAt3O7anm8lGbyLqUpJ1Ep4rTsJj7ObO06DQiSWp4Fhcw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/infrastructure/jest/node_modules/@swc/core-linux-x64-gnu": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.44.tgz", + "integrity": "sha512-xV4pDxJM06g0yUDTA22ZHgonzGqf/poIlgADRmEkx9cWWm5qLRhmWrrkVX1xZVvdlcXj1ERnia/UkvrDux96lg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/infrastructure/jest/node_modules/@swc/core-linux-x64-musl": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.44.tgz", + "integrity": "sha512-NJnnlE8vCkKHoo/wIaoywNN/01vNsvhKUjBgUx865sUM/XWAIanpbV41yfdEkC+Iwd+/zB3ZZnOYql3b+Nn8aQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/infrastructure/jest/node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.44.tgz", + "integrity": "sha512-/xOtjZhX25GEOPLN55icjj9E061DDH7G0A9HfUAEilURgBbvm6bIlqK+t8mOKK0tOsDoHftdTBRkYhTAqUtakQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "packages/infrastructure/jest/node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.44.tgz", + "integrity": "sha512-uKTAWQuMEW1gJnl8F3eiz3kdk8CiaR5dMWWlGbHIq6dRbur2hoKaEnINR4UqkvvAhhY1YB0Xr5DV1H986Xu2EA==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "packages/infrastructure/jest/node_modules/@swc/core-win32-x64-msvc": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.44.tgz", + "integrity": "sha512-D9fBRhr44cK4YIKFikpktyUDLkZgVj0ISaSl8IjiTvjqTrE/9+E+dzTNHULn5tc3lHVLLxyVwMjJRr2G0D4O6w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, "packages/infrastructure/jest/node_modules/@testing-library/dom": { "version": "8.20.0", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.0.tgz", @@ -36119,11 +42003,16 @@ "sass-loader": "^13.2.0", "style-loader": "^3.3.1", "ts-loader": "^9.4.1", - "webpack": "^5.76.0", + "webpack": "^5.77.0", "webpack-cli": "^4.10.0", "webpack-node-externals": "^3.0.0" } }, + "packages/infrastructure/webpack/node_modules/@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==" + }, "packages/infrastructure/webpack/node_modules/sass-loader": { "version": "13.2.0", "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.2.0.tgz", @@ -36161,6 +42050,60 @@ } } }, + "packages/infrastructure/webpack/node_modules/webpack": { + "version": "5.77.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.77.0.tgz", + "integrity": "sha512-sbGNjBr5Ya5ss91yzjeJTLKyfiwo5C628AFjEa6WSXcZa4E+F57om3Cc8xLb1Jh0b243AWuSYRf3dn7HVeFQ9Q==", + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.10.0", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "packages/infrastructure/webpack/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==", + "engines": { + "node": ">=10.13.0" + } + }, "packages/legacy-extension-example": { "name": "@k8slens/legacy-extension-example", "version": "1.0.0-alpha.1", @@ -36169,13 +42112,19 @@ "@k8slens/extensions": "^6.5.0-alpha.3", "@types/node": "^16.18.16", "typescript": "^4.9.5", - "webpack": "^5.76.1", + "webpack": "^5.77.0", "webpack-cli": "^5.0.1" }, "engines": { "lens": "6.5" } }, + "packages/legacy-extension-example/node_modules/@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true + }, "packages/legacy-extension-example/node_modules/@types/node": { "version": "16.18.16", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.16.tgz", @@ -36256,6 +42205,53 @@ "node": ">= 10.13.0" } }, + "packages/legacy-extension-example/node_modules/webpack": { + "version": "5.77.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.77.0.tgz", + "integrity": "sha512-sbGNjBr5Ya5ss91yzjeJTLKyfiwo5C628AFjEa6WSXcZa4E+F57om3Cc8xLb1Jh0b243AWuSYRf3dn7HVeFQ9Q==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.10.0", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, "packages/legacy-extension-example/node_modules/webpack-cli": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.0.1.tgz", @@ -36301,23 +42297,38 @@ } } }, + "packages/legacy-extension-example/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" + } + }, "packages/node-fetch": { "name": "@k8slens/node-fetch", "version": "6.5.0-alpha.1", "license": "MIT", "dependencies": { "node-fetch": "^3.3.0", - "rimraf": "^4.1.2" + "rimraf": "^4.4.1" }, "devDependencies": { - "rimraf": "^4.1.2", + "rimraf": "^4.4.1", "ts-loader": "^9.4.2", "ts-node": "^10.9.1", "typescript": "^4.9.5", - "webpack": "^5.75.0", + "webpack": "^5.77.0", "webpack-cli": "^5.0.1" } }, + "packages/node-fetch/node_modules/@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true + }, "packages/node-fetch/node_modules/@webpack-cli/configtest": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.0.1.tgz", @@ -36392,6 +42403,53 @@ "node": ">= 10.13.0" } }, + "packages/node-fetch/node_modules/webpack": { + "version": "5.77.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.77.0.tgz", + "integrity": "sha512-sbGNjBr5Ya5ss91yzjeJTLKyfiwo5C628AFjEa6WSXcZa4E+F57om3Cc8xLb1Jh0b243AWuSYRf3dn7HVeFQ9Q==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.10.0", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, "packages/node-fetch/node_modules/webpack-cli": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.0.1.tgz", @@ -36437,6 +42495,15 @@ } } }, + "packages/node-fetch/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" + } + }, "packages/open-lens": { "version": "6.5.0-alpha.3", "hasInstallScript": true, @@ -36447,15 +42514,16 @@ "@k8slens/core": "^6.5.0-alpha.3", "@k8slens/ensure-binaries": "^6.5.0-alpha.1", "@k8slens/feature-core": "^6.5.0-alpha.1", - "@k8slens/generate-tray-icons": "^6.5.0-alpha.1", + "@k8slens/keyboard-shortcuts": "^1.0.0-alpha.0", "@k8slens/legacy-extension-example": "^1.0.0-alpha.1", "@k8slens/legacy-extensions": "^1.0.0-alpha.1", "@k8slens/messaging": "^1.0.0-alpha.1", "@k8slens/messaging-for-main": "^1.0.0-alpha.1", "@k8slens/messaging-for-renderer": "^1.0.0-alpha.1", + "@k8slens/node-fetch": "^6.5.0-alpha.1", + "@k8slens/react-application": "^1.0.0-alpha.0", "@k8slens/run-many": "^1.0.0-alpha.1", "@k8slens/startable-stoppable": "^1.0.0-alpha.1", - "@k8slens/test-utils": "^1.0.0-alpha.1", "@k8slens/utilities": "^1.0.0-alpha.1", "@ogre-tools/fp": "^15.1.2", "@ogre-tools/injectable": "^15.1.2", @@ -36463,14 +42531,15 @@ "@ogre-tools/injectable-extension-for-mobx": "^15.1.2", "@ogre-tools/injectable-react": "^15.1.2", "mobx": "^6.8.0", - "rimraf": "^4.1.2" + "rimraf": "^4.4.1" }, "devDependencies": { "@electron/rebuild": "^3.2.10", - "@k8slens/node-fetch": "^6.5.0-alpha.1", + "@k8slens/generate-tray-icons": "^6.5.0-alpha.1", + "@k8slens/test-utils": "^1.0.0-alpha.1", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.10", "@swc/cli": "^0.1.61", - "@swc/core": "^1.3.37", + "@swc/core": "^1.3.44", "@swc/jest": "^0.2.24", "@types/byline": "^4.2.33", "@types/chart.js": "^2.9.36", @@ -36488,7 +42557,7 @@ "@types/tcp-port-used": "^1.0.1", "@types/url-parse": "^1.4.8", "@types/uuid": "^8.3.4", - "@types/webpack": "^5.28.0", + "@types/webpack": "^5.28.1", "@types/webpack-dev-server": "^4.7.2", "@types/webpack-env": "^1.18.0", "@types/webpack-node-externals": "2.5.3", @@ -36504,7 +42573,7 @@ "esbuild-loader": "^2.20.0", "fork-ts-checker-webpack-plugin": "^7.3.0", "html-webpack-plugin": "^5.5.0", - "jest": "^28.1.3", + "jest": "^29.5.0", "jest-environment-jsdom": "^28.1.3", "jsonfile": "^6.1.0", "mini-css-extract-plugin": "^2.7.1", @@ -36512,11 +42581,11 @@ "monaco-editor-webpack-plugin": "^5.0.0", "node-loader": "^2.0.0", "nodemon": "^2.0.20", - "playwright": "^1.30.0", + "playwright": "^1.32.1", "react-refresh": "^0.14.0", "react-refresh-typescript": "^2.0.7", "react-select": "^5.7.0", - "rimraf": "^4.1.2", + "rimraf": "^4.4.1", "run-script-os": "^1.1.6", "style-loader": "^3.3.1", "tailwindcss": "^3.2.4", @@ -36526,9 +42595,9 @@ "typed-emitter": "^1.4.0", "typescript": "^4.9.5", "typescript-plugin-css-modules": "^4.1.1", - "webpack": "^5.75.0", + "webpack": "^5.77.0", "webpack-cli": "^4.9.2", - "webpack-dev-server": "^4.11.1", + "webpack-dev-server": "^4.13.2", "webpack-node-externals": "^3.0.0", "xterm-addon-fit": "^0.5.0" }, @@ -36536,12 +42605,585 @@ "node": ">=16 <17" } }, + "packages/open-lens/node_modules/@jest/console": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.5.0.tgz", + "integrity": "sha512-NEpkObxPwyw/XxZVLPmAGKE89IQRp4puc6IQRPru6JKd1M3fW9v1xM1AnzIJE65hbCkzQAdnL8P47e9hzhiYLQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/open-lens/node_modules/@jest/core": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.5.0.tgz", + "integrity": "sha512-28UzQc7ulUrOQw1IsN/kv1QES3q2kkbl/wGslyhAclqZ/8cMdB5M68BffkIdSJgKBUt50d3hbwJ92XESlE7LiQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.5.0", + "@jest/reporters": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.5.0", + "jest-config": "^29.5.0", + "jest-haste-map": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-regex-util": "^29.4.3", + "jest-resolve": "^29.5.0", + "jest-resolve-dependencies": "^29.5.0", + "jest-runner": "^29.5.0", + "jest-runtime": "^29.5.0", + "jest-snapshot": "^29.5.0", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "jest-watcher": "^29.5.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.5.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "packages/open-lens/node_modules/@jest/core/node_modules/jest-config": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.5.0.tgz", + "integrity": "sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.5.0", + "@jest/types": "^29.5.0", + "babel-jest": "^29.5.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.5.0", + "jest-environment-node": "^29.5.0", + "jest-get-type": "^29.4.3", + "jest-regex-util": "^29.4.3", + "jest-resolve": "^29.5.0", + "jest-runner": "^29.5.0", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.5.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "packages/open-lens/node_modules/@jest/environment": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.5.0.tgz", + "integrity": "sha512-5FXw2+wD29YU1d4I2htpRX7jYnAyTRjP2CsXQdo9SAM8g3ifxWPSV0HnClSn71xwctr0U3oZIIH+dtbfmnbXVQ==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "jest-mock": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/open-lens/node_modules/@jest/expect": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.5.0.tgz", + "integrity": "sha512-PueDR2HGihN3ciUNGr4uelropW7rqUfTiOn+8u0leg/42UhblPxHkfoh0Ruu3I9Y1962P3u2DY4+h7GVTSVU6g==", + "dev": true, + "dependencies": { + "expect": "^29.5.0", + "jest-snapshot": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/open-lens/node_modules/@jest/expect-utils": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz", + "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.4.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/open-lens/node_modules/@jest/fake-timers": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.5.0.tgz", + "integrity": "sha512-9ARvuAAQcBwDAqOnglWq2zwNIRUDtk/SCkp/ToGEhFv5r86K21l+VEs0qNTaXtyiY0lEePl3kylijSYJQqdbDg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.5.0", + "jest-mock": "^29.5.0", + "jest-util": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/open-lens/node_modules/@jest/reporters": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.5.0.tgz", + "integrity": "sha512-D05STXqj/M8bP9hQNSICtPqz97u7ffGzZu+9XLucXhkOFBqKcXe04JLZOgIekOxdb73MAoBUFnqvf7MCpKk5OA==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@jridgewell/trace-mapping": "^0.3.15", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "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-message-util": "^29.5.0", + "jest-util": "^29.5.0", + "jest-worker": "^29.5.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "packages/open-lens/node_modules/@jest/test-result": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.5.0.tgz", + "integrity": "sha512-fGl4rfitnbfLsrfx1uUpDEESS7zM8JdgZgOCQuxQvL1Sn/I6ijeAVQWGfXI9zb1i9Mzo495cIpVZhA0yr60PkQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/open-lens/node_modules/@jest/test-sequencer": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.5.0.tgz", + "integrity": "sha512-yPafQEcKjkSfDXyvtgiV4pevSeyuA6MQr6ZIdVkWJly9vkqjnFfcfhRQqpD5whjoU8EORki752xQmjaqoFjzMQ==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.5.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.5.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/open-lens/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "packages/open-lens/node_modules/@sinonjs/fake-timers": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.0.2.tgz", + "integrity": "sha512-SwUDyjWnah1AaNl7kxsa7cfLhlTYoiyhDAIgyh+El30YvXs/o7OLXpYH88Zdhyx9JExKrmHDJ+10bwIcY80Jmw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^2.0.0" + } + }, + "packages/open-lens/node_modules/@swc/core": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.44.tgz", + "integrity": "sha512-RC25C8nxOCdfGS//F9Q8aHKx4XoCsxvgO+sSUhvt7zDz1Y2ruVUTu2UOH0VeE0WkA8j6oEZH+xH2SUfDUkxXdA==", + "dev": true, + "hasInstallScript": true, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.3.44", + "@swc/core-darwin-x64": "1.3.44", + "@swc/core-linux-arm-gnueabihf": "1.3.44", + "@swc/core-linux-arm64-gnu": "1.3.44", + "@swc/core-linux-arm64-musl": "1.3.44", + "@swc/core-linux-x64-gnu": "1.3.44", + "@swc/core-linux-x64-musl": "1.3.44", + "@swc/core-win32-arm64-msvc": "1.3.44", + "@swc/core-win32-ia32-msvc": "1.3.44", + "@swc/core-win32-x64-msvc": "1.3.44" + } + }, + "packages/open-lens/node_modules/@swc/core-darwin-arm64": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.44.tgz", + "integrity": "sha512-Y+oVsCjXUPvr3D9YLuB1gjP84TseM/CRkbPNrf+3JXQhsPEkgxdIdFP1cl/obeqMQrRgPpvSfK+TOvGuOuV22g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "packages/open-lens/node_modules/@swc/core-darwin-x64": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.44.tgz", + "integrity": "sha512-bM0IKBjlSD0yHJbd7bE3il5fTu3oUjUO2zjLkzfIx6tiqbmDyvOX8adaSqse9N+d8Ip9p26b5Vo7pMHq0POGkg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "packages/open-lens/node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.44.tgz", + "integrity": "sha512-D4lfVwCbkWscDTb6btb89+bN0kgvjGBPfOmcvih7nY5hxaorwvp+PefkYAhFw8vKmL92lrnWUFNiTemVFN4bxg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/open-lens/node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.44.tgz", + "integrity": "sha512-muExPTrN26MFmtO+5uffkH5v4lmd8GdmyWvlC2tL95h7o9genTIQyr7kcSepGZrDe4fM9G6++5YfENhUpXHo+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/open-lens/node_modules/@swc/core-linux-arm64-musl": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.44.tgz", + "integrity": "sha512-MY1wY3kSIosjJKKCfFGniJFFVkt3oPvJLN4Dlj+bMgAt3O7anm8lGbyLqUpJ1Ep4rTsJj7ObO06DQiSWp4Fhcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/open-lens/node_modules/@swc/core-linux-x64-gnu": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.44.tgz", + "integrity": "sha512-xV4pDxJM06g0yUDTA22ZHgonzGqf/poIlgADRmEkx9cWWm5qLRhmWrrkVX1xZVvdlcXj1ERnia/UkvrDux96lg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/open-lens/node_modules/@swc/core-linux-x64-musl": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.44.tgz", + "integrity": "sha512-NJnnlE8vCkKHoo/wIaoywNN/01vNsvhKUjBgUx865sUM/XWAIanpbV41yfdEkC+Iwd+/zB3ZZnOYql3b+Nn8aQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/open-lens/node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.44.tgz", + "integrity": "sha512-/xOtjZhX25GEOPLN55icjj9E061DDH7G0A9HfUAEilURgBbvm6bIlqK+t8mOKK0tOsDoHftdTBRkYhTAqUtakQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "packages/open-lens/node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.44.tgz", + "integrity": "sha512-uKTAWQuMEW1gJnl8F3eiz3kdk8CiaR5dMWWlGbHIq6dRbur2hoKaEnINR4UqkvvAhhY1YB0Xr5DV1H986Xu2EA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "packages/open-lens/node_modules/@swc/core-win32-x64-msvc": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.44.tgz", + "integrity": "sha512-D9fBRhr44cK4YIKFikpktyUDLkZgVj0ISaSl8IjiTvjqTrE/9+E+dzTNHULn5tc3lHVLLxyVwMjJRr2G0D4O6w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "packages/open-lens/node_modules/@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true + }, "packages/open-lens/node_modules/@types/node": { "version": "16.18.16", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.16.tgz", "integrity": "sha512-ZOzvDRWp8dCVBmgnkIqYCArgdFOO9YzocZp8Ra25N/RStKiWvMOXHMz+GjSeVNe5TstaTmTWPucGJkDw0XXJWA==", "dev": true }, + "packages/open-lens/node_modules/@types/webpack": { + "version": "5.28.1", + "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-5.28.1.tgz", + "integrity": "sha512-qw1MqGZclCoBrpiSe/hokSgQM/su8Ocpl3L/YHE0L6moyaypg4+5F7Uzq7NgaPKPxUxUbQ4fLPLpDWdR27bCZw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "tapable": "^2.2.0", + "webpack": "^5" + } + }, + "packages/open-lens/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "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" + } + }, + "packages/open-lens/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" + } + }, + "packages/open-lens/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" + } + }, + "packages/open-lens/node_modules/babel-jest": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.5.0.tgz", + "integrity": "sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==", + "dev": true, + "dependencies": { + "@jest/transform": "^29.5.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.5.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "packages/open-lens/node_modules/babel-plugin-jest-hoist": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.5.0.tgz", + "integrity": "sha512-zSuuuAlTMT4mzLj2nPnUm6fsE6270vdOfnpbJ+RmruU75UhLFvL0N2NgI7xpeS7NaB6hGqmd5pVpGTDYvi4Q3w==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/open-lens/node_modules/babel-preset-jest": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.5.0.tgz", + "integrity": "sha512-JOMloxOqdiBSxMAzjRaH023/vvcaSaec49zvg+2LmNsktC7ei39LTJGw02J+9uUtTZUq6xbLyJ4dxe9sSmIuAg==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.5.0", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "packages/open-lens/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" + } + }, + "packages/open-lens/node_modules/diff-sequences": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", + "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "packages/open-lens/node_modules/dotenv": { "version": "16.0.3", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", @@ -36551,6 +43193,605 @@ "node": ">=12" } }, + "packages/open-lens/node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "packages/open-lens/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" + } + }, + "packages/open-lens/node_modules/expect": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz", + "integrity": "sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.5.0", + "jest-get-type": "^29.4.3", + "jest-matcher-utils": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/open-lens/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" + } + }, + "packages/open-lens/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" + } + }, + "packages/open-lens/node_modules/jest": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.5.0.tgz", + "integrity": "sha512-juMg3he2uru1QoXX078zTa7pO85QyB9xajZc6bU+d9yEGwrKX6+vGmJQ3UdVZsvTEUARIdObzH68QItim6OSSQ==", + "dev": true, + "dependencies": { + "@jest/core": "^29.5.0", + "@jest/types": "^29.5.0", + "import-local": "^3.0.2", + "jest-cli": "^29.5.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "packages/open-lens/node_modules/jest-changed-files": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.5.0.tgz", + "integrity": "sha512-IFG34IUMUaNBIxjQXF/iu7g6EcdMrGRRxaUSw92I/2g2YC6vCdTltl4nHvt7Ci5nSJwXIkCu8Ka1DKF+X7Z1Ag==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/open-lens/node_modules/jest-circus": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.5.0.tgz", + "integrity": "sha512-gq/ongqeQKAplVxqJmbeUOJJKkW3dDNPY8PjhJ5G0lBRvu0e3EWGxGy5cI4LAGA7gV2UHCtWBI4EMXK8c9nQKA==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.5.0", + "@jest/expect": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.5.0", + "jest-matcher-utils": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-runtime": "^29.5.0", + "jest-snapshot": "^29.5.0", + "jest-util": "^29.5.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.5.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/open-lens/node_modules/jest-cli": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.5.0.tgz", + "integrity": "sha512-L1KcP1l4HtfwdxXNFCL5bmUbLQiKrakMUriBEcc1Vfz6gx31ORKdreuWvmQVBit+1ss9NNR3yxjwfwzZNdQXJw==", + "dev": true, + "dependencies": { + "@jest/core": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/types": "^29.5.0", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^29.5.0", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "prompts": "^2.0.1", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "packages/open-lens/node_modules/jest-cli/node_modules/jest-config": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.5.0.tgz", + "integrity": "sha512-kvDUKBnNJPNBmFFOhDbm59iu1Fii1Q6SxyhXfvylq3UTHbg6o7j/g8k2dZyXWLvfdKB1vAPxNZnMgtKJcmu3kA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.5.0", + "@jest/types": "^29.5.0", + "babel-jest": "^29.5.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.5.0", + "jest-environment-node": "^29.5.0", + "jest-get-type": "^29.4.3", + "jest-regex-util": "^29.4.3", + "jest-resolve": "^29.5.0", + "jest-runner": "^29.5.0", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.5.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "packages/open-lens/node_modules/jest-diff": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz", + "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.4.3", + "jest-get-type": "^29.4.3", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/open-lens/node_modules/jest-docblock": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.4.3.tgz", + "integrity": "sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/open-lens/node_modules/jest-each": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.5.0.tgz", + "integrity": "sha512-HM5kIJ1BTnVt+DQZ2ALp3rzXEl+g726csObrW/jpEGl+CDSSQpOJJX2KE/vEg8cxcMXdyEPu6U4QX5eruQv5hA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.4.3", + "jest-util": "^29.5.0", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/open-lens/node_modules/jest-environment-node": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.5.0.tgz", + "integrity": "sha512-ExxuIK/+yQ+6PRGaHkKewYtg6hto2uGCgvKdb2nfJfKXgZ17DfXjvbZ+jA1Qt9A8EQSfPnt5FKIfnOO3u1h9qw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.5.0", + "@jest/fake-timers": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "jest-mock": "^29.5.0", + "jest-util": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/open-lens/node_modules/jest-get-type": { + "version": "29.4.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", + "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/open-lens/node_modules/jest-leak-detector": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.5.0.tgz", + "integrity": "sha512-u9YdeeVnghBUtpN5mVxjID7KbkKE1QU4f6uUwuxiY0vYRi9BUCLKlPEZfDGR67ofdFmDz9oPAy2G92Ujrntmow==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.4.3", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/open-lens/node_modules/jest-matcher-utils": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz", + "integrity": "sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.5.0", + "jest-get-type": "^29.4.3", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/open-lens/node_modules/jest-message-util": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz", + "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.5.0", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.5.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/open-lens/node_modules/jest-mock": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.5.0.tgz", + "integrity": "sha512-GqOzvdWDE4fAV2bWQLQCkujxYWL7RxjCnj71b5VhDAGOevB3qj3Ovg26A5NI84ZpODxyzaozXLOh2NCgkbvyaw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "@types/node": "*", + "jest-util": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/open-lens/node_modules/jest-resolve": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.5.0.tgz", + "integrity": "sha512-1TzxJ37FQq7J10jPtQjcc+MkCkE3GBpBecsSUWJ0qZNJpmg6m0D9/7II03yJulm3H/fvVjgqLh/k2eYg+ui52w==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.5.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.5.0", + "jest-validate": "^29.5.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/open-lens/node_modules/jest-resolve-dependencies": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.5.0.tgz", + "integrity": "sha512-sjV3GFr0hDJMBpYeUuGduP+YeCRbd7S/ck6IvL3kQ9cpySYKqcqhdLLC2rFwrcL7tz5vYibomBrsFYWkIGGjOg==", + "dev": true, + "dependencies": { + "jest-regex-util": "^29.4.3", + "jest-snapshot": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/open-lens/node_modules/jest-runner": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.5.0.tgz", + "integrity": "sha512-m7b6ypERhFghJsslMLhydaXBiLf7+jXy8FwGRHO3BGV1mcQpPbwiqiKUR2zU2NJuNeMenJmlFZCsIqzJCTeGLQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.5.0", + "@jest/environment": "^29.5.0", + "@jest/test-result": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.4.3", + "jest-environment-node": "^29.5.0", + "jest-haste-map": "^29.5.0", + "jest-leak-detector": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-resolve": "^29.5.0", + "jest-runtime": "^29.5.0", + "jest-util": "^29.5.0", + "jest-watcher": "^29.5.0", + "jest-worker": "^29.5.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/open-lens/node_modules/jest-snapshot": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.5.0.tgz", + "integrity": "sha512-x7Wolra5V0tt3wRs3/ts3S6ciSQVypgGQlJpz2rsdQYoUKxMxPNaoHMGJN6qAuPJqS+2iQ1ZUn5kl7HCyls84g==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.5.0", + "@jest/transform": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.5.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.5.0", + "jest-get-type": "^29.4.3", + "jest-matcher-utils": "^29.5.0", + "jest-message-util": "^29.5.0", + "jest-util": "^29.5.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.5.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/open-lens/node_modules/jest-validate": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.5.0.tgz", + "integrity": "sha512-pC26etNIi+y3HV8A+tUGr/lph9B18GnzSRAkPaaZJIE1eFdiYm6/CewuiJQ8/RlfHd1u/8Ioi8/sJ+CmbA+zAQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.5.0", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.4.3", + "leven": "^3.1.0", + "pretty-format": "^29.5.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/open-lens/node_modules/jest-watcher": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.5.0.tgz", + "integrity": "sha512-KmTojKcapuqYrKDpRwfqcQ3zjMlwu27SYext9pt4GlF5FUgB+7XE1mcCnSm6a4uUpFyQIkb6ZhzZvHl+jiBCiA==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.5.0", + "@jest/types": "^29.5.0", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.5.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/open-lens/node_modules/jest-worker": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.5.0.tgz", + "integrity": "sha512-NcrQnevGoSp4b5kg+akIpthoAFHxPBcb5P6mYPY0fUNT+sSvmtu6jlkEle3anczUKIKEbMxFimk9oTP/tpIPgA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.5.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/open-lens/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 + }, + "packages/open-lens/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" + } + }, + "packages/open-lens/node_modules/playwright": { + "version": "1.32.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.32.1.tgz", + "integrity": "sha512-GnEizysWMvoqHC3I9l8+4/ZxeLwLNdJJG76xdKGxzOcIZDcw5RSk/FKrFb5CuA+zcLpjIM2p9eR9Z4CuUDkWXg==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "playwright-core": "1.32.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=14" + } + }, + "packages/open-lens/node_modules/playwright-core": { + "version": "1.32.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.32.1.tgz", + "integrity": "sha512-KZYUQC10mXD2Am1rGlidaalNGYk3LU1vZqqNk0gT4XPty1jOqgup8KDP8l2CUlqoNKhXM5IfGjWgW37xvGllBA==", + "dev": true, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=14" + } + }, + "packages/open-lens/node_modules/pretty-format": { + "version": "29.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", + "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.4.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "packages/open-lens/node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "packages/open-lens/node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "packages/open-lens/node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "packages/open-lens/node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "packages/open-lens/node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -36582,6 +43823,21 @@ "url": "https://opencollective.com/stylus" } }, + "packages/open-lens/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" + } + }, "packages/open-lens/node_modules/tsconfig-paths": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.1.2.tgz", @@ -36623,6 +43879,155 @@ "typescript": ">=3.9.0" } }, + "packages/open-lens/node_modules/webpack": { + "version": "5.77.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.77.0.tgz", + "integrity": "sha512-sbGNjBr5Ya5ss91yzjeJTLKyfiwo5C628AFjEa6WSXcZa4E+F57om3Cc8xLb1Jh0b243AWuSYRf3dn7HVeFQ9Q==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.10.0", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "packages/open-lens/node_modules/webpack-dev-server": { + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.13.2.tgz", + "integrity": "sha512-5i6TrGBRxG4vnfDpB6qSQGfnB6skGBXNL5/542w2uRGLimX6qeE5BQMLrzIC3JYV/xlGOv+s+hTleI9AZKUQNw==", + "dev": true, + "dependencies": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.1", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "launch-editor": "^2.6.0", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.1.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.13.0" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-cli": { + "optional": true + } + } + }, + "packages/open-lens/node_modules/webpack-dev-server/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "packages/open-lens/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" + } + }, + "packages/open-lens/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" + } + }, "packages/release-tool": { "name": "@k8slens/release-tool", "version": "6.5.0-alpha.2", @@ -36637,11 +44042,198 @@ }, "devDependencies": { "@swc/cli": "^0.1.61", - "@swc/core": "^1.3.37", + "@swc/core": "^1.3.44", "@types/inquirer": "^9.0.3", "@types/node": "^16.18.11", "@types/semver": "^7.3.13", - "rimraf": "^4.1.2" + "rimraf": "^4.4.1", + "run-script-os": "^1.1.6" + } + }, + "packages/release-tool/node_modules/@swc/core": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.44.tgz", + "integrity": "sha512-RC25C8nxOCdfGS//F9Q8aHKx4XoCsxvgO+sSUhvt7zDz1Y2ruVUTu2UOH0VeE0WkA8j6oEZH+xH2SUfDUkxXdA==", + "dev": true, + "hasInstallScript": true, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.3.44", + "@swc/core-darwin-x64": "1.3.44", + "@swc/core-linux-arm-gnueabihf": "1.3.44", + "@swc/core-linux-arm64-gnu": "1.3.44", + "@swc/core-linux-arm64-musl": "1.3.44", + "@swc/core-linux-x64-gnu": "1.3.44", + "@swc/core-linux-x64-musl": "1.3.44", + "@swc/core-win32-arm64-msvc": "1.3.44", + "@swc/core-win32-ia32-msvc": "1.3.44", + "@swc/core-win32-x64-msvc": "1.3.44" + } + }, + "packages/release-tool/node_modules/@swc/core-darwin-arm64": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.44.tgz", + "integrity": "sha512-Y+oVsCjXUPvr3D9YLuB1gjP84TseM/CRkbPNrf+3JXQhsPEkgxdIdFP1cl/obeqMQrRgPpvSfK+TOvGuOuV22g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "packages/release-tool/node_modules/@swc/core-darwin-x64": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.44.tgz", + "integrity": "sha512-bM0IKBjlSD0yHJbd7bE3il5fTu3oUjUO2zjLkzfIx6tiqbmDyvOX8adaSqse9N+d8Ip9p26b5Vo7pMHq0POGkg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "packages/release-tool/node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.44.tgz", + "integrity": "sha512-D4lfVwCbkWscDTb6btb89+bN0kgvjGBPfOmcvih7nY5hxaorwvp+PefkYAhFw8vKmL92lrnWUFNiTemVFN4bxg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/release-tool/node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.44.tgz", + "integrity": "sha512-muExPTrN26MFmtO+5uffkH5v4lmd8GdmyWvlC2tL95h7o9genTIQyr7kcSepGZrDe4fM9G6++5YfENhUpXHo+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/release-tool/node_modules/@swc/core-linux-arm64-musl": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.44.tgz", + "integrity": "sha512-MY1wY3kSIosjJKKCfFGniJFFVkt3oPvJLN4Dlj+bMgAt3O7anm8lGbyLqUpJ1Ep4rTsJj7ObO06DQiSWp4Fhcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/release-tool/node_modules/@swc/core-linux-x64-gnu": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.44.tgz", + "integrity": "sha512-xV4pDxJM06g0yUDTA22ZHgonzGqf/poIlgADRmEkx9cWWm5qLRhmWrrkVX1xZVvdlcXj1ERnia/UkvrDux96lg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/release-tool/node_modules/@swc/core-linux-x64-musl": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.44.tgz", + "integrity": "sha512-NJnnlE8vCkKHoo/wIaoywNN/01vNsvhKUjBgUx865sUM/XWAIanpbV41yfdEkC+Iwd+/zB3ZZnOYql3b+Nn8aQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/release-tool/node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.44.tgz", + "integrity": "sha512-/xOtjZhX25GEOPLN55icjj9E061DDH7G0A9HfUAEilURgBbvm6bIlqK+t8mOKK0tOsDoHftdTBRkYhTAqUtakQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "packages/release-tool/node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.44.tgz", + "integrity": "sha512-uKTAWQuMEW1gJnl8F3eiz3kdk8CiaR5dMWWlGbHIq6dRbur2hoKaEnINR4UqkvvAhhY1YB0Xr5DV1H986Xu2EA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "packages/release-tool/node_modules/@swc/core-win32-x64-msvc": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.44.tgz", + "integrity": "sha512-D9fBRhr44cK4YIKFikpktyUDLkZgVj0ISaSl8IjiTvjqTrE/9+E+dzTNHULn5tc3lHVLLxyVwMjJRr2G0D4O6w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" } }, "packages/release-tool/node_modules/@types/node": { @@ -36949,11 +44541,197 @@ }, "devDependencies": { "@swc/cli": "^0.1.61", - "@swc/core": "^1.3.37", + "@swc/core": "^1.3.44", "@types/command-line-args": "^5.2.0", "@types/node": "^16.18.11", "@types/semver": "^7.3.13", - "rimraf": "^4.1.2" + "rimraf": "^4.4.1" + } + }, + "packages/semver/node_modules/@swc/core": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.3.44.tgz", + "integrity": "sha512-RC25C8nxOCdfGS//F9Q8aHKx4XoCsxvgO+sSUhvt7zDz1Y2ruVUTu2UOH0VeE0WkA8j6oEZH+xH2SUfDUkxXdA==", + "dev": true, + "hasInstallScript": true, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.3.44", + "@swc/core-darwin-x64": "1.3.44", + "@swc/core-linux-arm-gnueabihf": "1.3.44", + "@swc/core-linux-arm64-gnu": "1.3.44", + "@swc/core-linux-arm64-musl": "1.3.44", + "@swc/core-linux-x64-gnu": "1.3.44", + "@swc/core-linux-x64-musl": "1.3.44", + "@swc/core-win32-arm64-msvc": "1.3.44", + "@swc/core-win32-ia32-msvc": "1.3.44", + "@swc/core-win32-x64-msvc": "1.3.44" + } + }, + "packages/semver/node_modules/@swc/core-darwin-arm64": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.44.tgz", + "integrity": "sha512-Y+oVsCjXUPvr3D9YLuB1gjP84TseM/CRkbPNrf+3JXQhsPEkgxdIdFP1cl/obeqMQrRgPpvSfK+TOvGuOuV22g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "packages/semver/node_modules/@swc/core-darwin-x64": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.3.44.tgz", + "integrity": "sha512-bM0IKBjlSD0yHJbd7bE3il5fTu3oUjUO2zjLkzfIx6tiqbmDyvOX8adaSqse9N+d8Ip9p26b5Vo7pMHq0POGkg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "packages/semver/node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.44.tgz", + "integrity": "sha512-D4lfVwCbkWscDTb6btb89+bN0kgvjGBPfOmcvih7nY5hxaorwvp+PefkYAhFw8vKmL92lrnWUFNiTemVFN4bxg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/semver/node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.44.tgz", + "integrity": "sha512-muExPTrN26MFmtO+5uffkH5v4lmd8GdmyWvlC2tL95h7o9genTIQyr7kcSepGZrDe4fM9G6++5YfENhUpXHo+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/semver/node_modules/@swc/core-linux-arm64-musl": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.44.tgz", + "integrity": "sha512-MY1wY3kSIosjJKKCfFGniJFFVkt3oPvJLN4Dlj+bMgAt3O7anm8lGbyLqUpJ1Ep4rTsJj7ObO06DQiSWp4Fhcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/semver/node_modules/@swc/core-linux-x64-gnu": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.44.tgz", + "integrity": "sha512-xV4pDxJM06g0yUDTA22ZHgonzGqf/poIlgADRmEkx9cWWm5qLRhmWrrkVX1xZVvdlcXj1ERnia/UkvrDux96lg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/semver/node_modules/@swc/core-linux-x64-musl": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.44.tgz", + "integrity": "sha512-NJnnlE8vCkKHoo/wIaoywNN/01vNsvhKUjBgUx865sUM/XWAIanpbV41yfdEkC+Iwd+/zB3ZZnOYql3b+Nn8aQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "packages/semver/node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.44.tgz", + "integrity": "sha512-/xOtjZhX25GEOPLN55icjj9E061DDH7G0A9HfUAEilURgBbvm6bIlqK+t8mOKK0tOsDoHftdTBRkYhTAqUtakQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "packages/semver/node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.44.tgz", + "integrity": "sha512-uKTAWQuMEW1gJnl8F3eiz3kdk8CiaR5dMWWlGbHIq6dRbur2hoKaEnINR4UqkvvAhhY1YB0Xr5DV1H986Xu2EA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "packages/semver/node_modules/@swc/core-win32-x64-msvc": { + "version": "1.3.44", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.44.tgz", + "integrity": "sha512-D9fBRhr44cK4YIKFikpktyUDLkZgVj0ISaSl8IjiTvjqTrE/9+E+dzTNHULn5tc3lHVLLxyVwMjJRr2G0D4O6w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" } }, "packages/semver/node_modules/@types/node": { @@ -37109,6 +44887,179 @@ "lodash": "^4.17.21" } }, + "packages/technical-features/react-application": { + "name": "@k8slens/react-application", + "version": "1.0.0-alpha.0", + "license": "MIT", + "devDependencies": { + "@async-fn/jest": "^1.6.4", + "@k8slens/eslint-config": "6.5.0-alpha.1", + "@k8slens/react-testing-library-discovery": "*", + "@testing-library/react": "^12.1.5" + }, + "peerDependencies": { + "@k8slens/application": "^6.5.0-alpha.2", + "@k8slens/feature-core": "^6.5.0-alpha.0", + "@ogre-tools/fp": "^15.1.2", + "@ogre-tools/injectable": "^15.1.2", + "@ogre-tools/injectable-extension-for-auto-registration": "^15.1.2", + "@ogre-tools/injectable-extension-for-mobx": "^15.1.2", + "@ogre-tools/injectable-react": "^15.1.2", + "lodash": "^4.17.15", + "mobx": "^6.8.0", + "react": "^17.0.2", + "react-dom": "^17.0.2" + } + }, + "packages/technical-features/react-application-root": { + "name": "@k8slens/react-application-root", + "version": "1.0.0-alpha.0", + "extraneous": true, + "license": "MIT", + "devDependencies": { + "@async-fn/jest": "^1.6.4", + "@k8slens/eslint-config": "6.5.0-alpha.1", + "@k8slens/react-testing-library-discovery": "*", + "@testing-library/react": "^12.1.5" + }, + "peerDependencies": { + "@k8slens/application": "^6.5.0-alpha.2", + "@k8slens/feature-core": "^6.5.0-alpha.0", + "@ogre-tools/fp": "^15.1.2", + "@ogre-tools/injectable": "^15.1.2", + "@ogre-tools/injectable-extension-for-auto-registration": "^15.1.2", + "@ogre-tools/injectable-extension-for-mobx": "^15.1.2", + "@ogre-tools/injectable-react": "^15.1.2", + "lodash": "^4.17.15", + "mobx": "^6.8.0", + "react": "^17.0.2", + "react-dom": "^17.0.2" + } + }, + "packages/utility-features/react-testing-library-discovery": { + "name": "@k8slens/react-testing-library-discovery", + "version": "1.0.0-alpha.0", + "license": "MIT", + "dependencies": { + "@testing-library/dom": "^8.19.0", + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^13.4.0" + } + }, + "packages/utility-features/react-testing-library-discovery/node_modules/@testing-library/dom": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.0.tgz", + "integrity": "sha512-d9ULIT+a4EXLX3UU8FBjauG9NnsZHkHztXoIcTsOKoOw030fyjheN9svkTULjJxtYag9DZz5Jz5qkWZDPxTFwA==", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "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" + } + }, + "packages/utility-features/react-testing-library-discovery/node_modules/@testing-library/react": { + "version": "13.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-13.4.0.tgz", + "integrity": "sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "@testing-library/dom": "^8.5.0", + "@types/react-dom": "^18.0.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "packages/utility-features/react-testing-library-discovery/node_modules/@types/aria-query": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz", + "integrity": "sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==" + }, + "packages/utility-features/react-testing-library-discovery/node_modules/@types/react-dom": { + "version": "18.0.11", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.11.tgz", + "integrity": "sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw==", + "dependencies": { + "@types/react": "*" + } + }, + "packages/utility-features/react-testing-library-discovery/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==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "packages/utility-features/react-testing-library-discovery/node_modules/aria-query": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "dependencies": { + "deep-equal": "^2.0.5" + } + }, + "packages/utility-features/react-testing-library-discovery/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==", + "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" + } + }, + "packages/utility-features/react-testing-library-discovery/node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "packages/utility-features/react-testing-library-discovery/node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "packages/utility-features/react-testing-library-discovery/node_modules/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + } + }, "packages/utility-features/run-many": { "name": "@k8slens/run-many", "version": "1.0.0-alpha.1", diff --git a/package.json b/package.json index 0a9f693adb..016cb3464c 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,10 @@ "build:docs": "lerna run --stream build:docs", "clean": "lerna run clean --stream", "clean:node_modules": "lerna clean -y && rimraf node_modules", - "dev": "lerna run dev --stream --skip-nx-cache", + "dev": "cross-env NODE_ENV=development lerna run build --stream --skip-nx-cache", + "postdev": "lerna watch -- lerna run build --stream --scope \\$LERNA_PACKAGE_NAME", + "prestart-dev": "cd packages/open-lens && rimraf static/build/ && npm run build:tray-icons && npm run download:binaries", + "start-dev": "lerna run start", "lint": "lerna run lint --stream", "lint:fix": "lerna run lint:fix --stream", "mkdocs:serve-local": "docker build -t mkdocs-serve-local:latest mkdocs/ && docker run --rm -it -p 8000:8000 -v ${PWD}:/docs mkdocs-serve-local:latest", @@ -37,7 +40,7 @@ "devDependencies": { "adr": "^1.4.3", "cross-env": "^7.0.3", - "lerna": "^6.5.1", - "rimraf": "^4.4.0" + "lerna": "^6.6.1", + "rimraf": "^4.4.1" } } diff --git a/packages/bump-version-for-cron/package.json b/packages/bump-version-for-cron/package.json index cba08964d2..c5a0ad681c 100644 --- a/packages/bump-version-for-cron/package.json +++ b/packages/bump-version-for-cron/package.json @@ -5,7 +5,10 @@ "license": "MIT", "scripts": { "clean": "rimraf dist/", - "build": "swc ./src/index.ts -d ./dist" + "build": "swc ./src/index.ts -d ./dist", + "postbuild": "run-script-os", + "postbuild:windows": "", + "postbuild:nix": "chmod u+x ./dist/index.js" }, "type": "module", "bin": "./dist/index.js", @@ -23,9 +26,10 @@ }, "devDependencies": { "@swc/cli": "^0.1.61", - "@swc/core": "^1.3.37", + "@swc/core": "^1.3.44", "@types/node": "^16.18.11", "@types/semver": "^7.3.13", - "rimraf": "^4.1.2" + "rimraf": "^4.4.1", + "run-script-os": "^1.1.6" } } diff --git a/packages/business-features/keyboard-shortcuts/.eslintrc.json b/packages/business-features/keyboard-shortcuts/.eslintrc.json new file mode 100644 index 0000000000..b15115cb69 --- /dev/null +++ b/packages/business-features/keyboard-shortcuts/.eslintrc.json @@ -0,0 +1,6 @@ +{ + "extends": "@k8slens/eslint-config/eslint", + "parserOptions": { + "project": "./tsconfig.json" + } +} diff --git a/packages/business-features/keyboard-shortcuts/.prettierrc b/packages/business-features/keyboard-shortcuts/.prettierrc new file mode 100644 index 0000000000..edd47b479e --- /dev/null +++ b/packages/business-features/keyboard-shortcuts/.prettierrc @@ -0,0 +1 @@ +"@k8slens/eslint-config/prettier" diff --git a/packages/business-features/keyboard-shortcuts/README.md b/packages/business-features/keyboard-shortcuts/README.md new file mode 100644 index 0000000000..25cc2f8cdb --- /dev/null +++ b/packages/business-features/keyboard-shortcuts/README.md @@ -0,0 +1,21 @@ +# @k8slens/keyboard-shortcuts + +This Feature enables keyboard shortcuts in Lens + +# Usage + +```bash +$ npm install @k8slens/keyboard-shortcuts +``` + +```typescript +import { keyboardShortcutsFeature } from "@k8slens/keyboard-shortcuts"; +import { registerFeature } from "@k8slens/feature-core"; +import { createContainer } from "@ogre-tools/injectable"; + +const di = createContainer("some-container"); + +registerFeature(di, keyboardShortcutsFeature); +``` + +## Extendability diff --git a/packages/business-features/keyboard-shortcuts/index.ts b/packages/business-features/keyboard-shortcuts/index.ts new file mode 100644 index 0000000000..7f8ac51c47 --- /dev/null +++ b/packages/business-features/keyboard-shortcuts/index.ts @@ -0,0 +1,7 @@ +export { KeyboardShortcutScope } from "./src/keyboard-shortcut-scope"; +export type { KeyboardShortcutScopeProps } from "./src/keyboard-shortcut-scope"; + +export { keyboardShortcutInjectionToken } from "./src/keyboard-shortcut-injection-token"; +export type { Binding, KeyboardShortcut } from "./src/keyboard-shortcut-injection-token"; + +export { keyboardShortcutsFeature } from "./src/feature"; diff --git a/packages/business-features/keyboard-shortcuts/jest.config.js b/packages/business-features/keyboard-shortcuts/jest.config.js new file mode 100644 index 0000000000..38d54ab7b6 --- /dev/null +++ b/packages/business-features/keyboard-shortcuts/jest.config.js @@ -0,0 +1 @@ +module.exports = require("@k8slens/jest").monorepoPackageConfig(__dirname).configForReact; diff --git a/packages/business-features/keyboard-shortcuts/package.json b/packages/business-features/keyboard-shortcuts/package.json new file mode 100644 index 0000000000..1b60e66133 --- /dev/null +++ b/packages/business-features/keyboard-shortcuts/package.json @@ -0,0 +1,46 @@ +{ + "name": "@k8slens/keyboard-shortcuts", + "private": false, + "version": "1.0.0-alpha.0", + "description": "Keyboard shortcuts for Lens", + "type": "commonjs", + "files": [ + "dist" + ], + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/lensapp/lens.git" + }, + "main": "dist/index.js", + "types": "dist/index.d.ts", + "author": { + "name": "OpenLens Authors", + "email": "info@k8slens.dev" + }, + "license": "MIT", + "homepage": "https://github.com/lensapp/lens", + "scripts": { + "build": "webpack", + "clean": "rimraf dist/", + "test:unit": "jest --coverage --runInBand", + "lint": "lens-lint", + "lint:fix": "lens-lint --fix" + }, + "peerDependencies": { + "@k8slens/feature-core": "^6.5.0-alpha.0", + "@k8slens/react-application": "^1.0.0-alpha.0", + "@ogre-tools/injectable": "^15.1.2", + "@ogre-tools/injectable-extension-for-auto-registration": "^15.1.2", + "@ogre-tools/fp": "^15.1.2", + "lodash": "^4.17.21" + }, + "devDependencies": { + "@async-fn/jest": "^1.6.4", + "@k8slens/eslint-config": "6.5.0-alpha.1", + "@k8slens/react-testing-library-discovery": "^1.0.0-alpha.0" + } +} diff --git a/packages/business-features/keyboard-shortcuts/src/__snapshots__/keyboard-shortcuts.test.tsx.snap b/packages/business-features/keyboard-shortcuts/src/__snapshots__/keyboard-shortcuts.test.tsx.snap new file mode 100644 index 0000000000..b2cba6a543 --- /dev/null +++ b/packages/business-features/keyboard-shortcuts/src/__snapshots__/keyboard-shortcuts.test.tsx.snap @@ -0,0 +1,15 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`keyboard-shortcuts when application is started renders 1`] = ` + +
+
+
+
+
+ +`; diff --git a/packages/business-features/keyboard-shortcuts/src/feature.ts b/packages/business-features/keyboard-shortcuts/src/feature.ts new file mode 100644 index 0000000000..32d5f2bc62 --- /dev/null +++ b/packages/business-features/keyboard-shortcuts/src/feature.ts @@ -0,0 +1,17 @@ +import { getFeature } from "@k8slens/feature-core"; +import { autoRegister } from "@ogre-tools/injectable-extension-for-auto-registration"; +import { reactApplicationFeature } from "@k8slens/react-application"; + +export const keyboardShortcutsFeature = getFeature({ + id: "keyboard-shortcuts", + + register: (di) => { + autoRegister({ + di, + targetModule: module, + getRequireContexts: () => [require.context("./", true, /\.injectable\.(ts|tsx)$/)], + }); + }, + + dependencies: [reactApplicationFeature], +}); diff --git a/packages/business-features/keyboard-shortcuts/src/invoke-shortcut.injectable.ts b/packages/business-features/keyboard-shortcuts/src/invoke-shortcut.injectable.ts new file mode 100644 index 0000000000..5b0c324203 --- /dev/null +++ b/packages/business-features/keyboard-shortcuts/src/invoke-shortcut.injectable.ts @@ -0,0 +1,94 @@ +import { pipeline } from "@ogre-tools/fp"; +import { filter, isString } from "lodash/fp"; +import { getInjectable } from "@ogre-tools/injectable"; +import { + Binding, + KeyboardShortcut, + keyboardShortcutInjectionToken, +} from "./keyboard-shortcut-injection-token"; +import platformInjectable from "./platform.injectable"; + +export type InvokeShortcut = (event: KeyboardEvent) => void; + +const toShortcutsWithMatchingScope = (shortcut: KeyboardShortcut) => { + const activeScopeElement = document.activeElement?.closest("[data-keyboard-shortcut-scope]"); + + if (!activeScopeElement) { + const shortcutIsRootLevel = !shortcut.scope; + + return shortcutIsRootLevel; + } + + const castedActiveScopeElementHtml = activeScopeElement as HTMLDivElement; + + // eslint-disable-next-line xss/no-mixed-html + const activeScope = castedActiveScopeElementHtml.dataset.keyboardShortcutScope; + + return shortcut.scope === activeScope; +}; + +const toBindingWithDefaults = (binding: Binding) => + isString(binding) + ? { + code: binding, + shift: false, + ctrl: false, + altOrOption: false, + meta: false, + ctrlOrCommand: false, + } + : { + ctrl: false, + shift: false, + altOrOption: false, + meta: false, + ctrlOrCommand: false, + ...binding, + }; + +const toShortcutsWithMatchingBinding = + (event: KeyboardEvent, platform: string) => (shortcut: KeyboardShortcut) => { + const binding = toBindingWithDefaults(shortcut.binding); + + const shiftModifierMatches = binding.shift === event.shiftKey; + const altModifierMatches = binding.altOrOption === event.altKey; + + const isMac = platform === "darwin"; + + const ctrlModifierMatches = + binding.ctrl === event.ctrlKey || (!isMac && binding.ctrlOrCommand === event.ctrlKey); + + const metaModifierMatches = + binding.meta === event.metaKey || (isMac && binding.ctrlOrCommand === event.metaKey); + + return ( + event.code === binding.code && + shiftModifierMatches && + ctrlModifierMatches && + altModifierMatches && + metaModifierMatches + ); + }; + +const invokeShortcutInjectable = getInjectable({ + id: "invoke-shortcut", + + instantiate: (di): InvokeShortcut => { + const getShortcuts = () => di.injectMany(keyboardShortcutInjectionToken); + const platform = di.inject(platformInjectable); + + return (event) => { + const shortcutsToInvoke = pipeline( + getShortcuts(), + filter(toShortcutsWithMatchingBinding(event, platform)), + filter(toShortcutsWithMatchingScope), + ); + + if (shortcutsToInvoke.length) { + shortcutsToInvoke.forEach((shortcut) => shortcut.invoke()); + } + }; + }, +}); + +export default invokeShortcutInjectable; diff --git a/packages/business-features/keyboard-shortcuts/src/keyboard-shortcut-injection-token.ts b/packages/business-features/keyboard-shortcuts/src/keyboard-shortcut-injection-token.ts new file mode 100644 index 0000000000..637990b107 --- /dev/null +++ b/packages/business-features/keyboard-shortcuts/src/keyboard-shortcut-injection-token.ts @@ -0,0 +1,22 @@ +import { getInjectionToken } from "@ogre-tools/injectable"; + +export type Binding = + | string + | { + code: string; + shift?: boolean; + ctrl?: boolean; + altOrOption?: boolean; + meta?: boolean; + ctrlOrCommand?: boolean; + }; + +export type KeyboardShortcut = { + binding: Binding; + invoke: () => void; + scope?: string; +}; + +export const keyboardShortcutInjectionToken = getInjectionToken({ + id: "keyboard-shortcut-injection-token", +}); diff --git a/packages/business-features/keyboard-shortcuts/src/keyboard-shortcut-listener-react-application-hoc.injectable.tsx b/packages/business-features/keyboard-shortcuts/src/keyboard-shortcut-listener-react-application-hoc.injectable.tsx new file mode 100644 index 0000000000..ed22ef7fe8 --- /dev/null +++ b/packages/business-features/keyboard-shortcuts/src/keyboard-shortcut-listener-react-application-hoc.injectable.tsx @@ -0,0 +1,10 @@ +import { getInjectable } from "@ogre-tools/injectable"; +import { KeyboardShortcutListener } from "./keyboard-shortcut-listener"; +import { reactApplicationHigherOrderComponentInjectionToken } from "@k8slens/react-application"; + +export const keyboardShortcutListenerReactApplicationHocInjectable = getInjectable({ + id: "keyboard-shortcut-listener-react-application-hoc", + instantiate: () => KeyboardShortcutListener, + + injectionToken: reactApplicationHigherOrderComponentInjectionToken, +}); diff --git a/packages/business-features/keyboard-shortcuts/src/keyboard-shortcut-listener.tsx b/packages/business-features/keyboard-shortcuts/src/keyboard-shortcut-listener.tsx new file mode 100644 index 0000000000..5f2479d059 --- /dev/null +++ b/packages/business-features/keyboard-shortcuts/src/keyboard-shortcut-listener.tsx @@ -0,0 +1,41 @@ +import { withInjectables } from "@ogre-tools/injectable-react"; +import React, { useEffect } from "react"; + +import invokeShortcutInjectable, { InvokeShortcut } from "./invoke-shortcut.injectable"; + +export interface KeyboardShortcutListenerProps { + children: React.ReactNode; +} + +interface Dependencies { + invokeShortcut: InvokeShortcut; +} + +const NonInjectedKeyboardShortcutListener = ({ + children, + invokeShortcut, +}: KeyboardShortcutListenerProps & Dependencies) => { + useEffect(() => { + document.addEventListener("keydown", invokeShortcut); + + return () => { + document.removeEventListener("keydown", invokeShortcut); + }; + }); + + return <>{children}; +}; + +export const KeyboardShortcutListener = withInjectables< + Dependencies, + KeyboardShortcutListenerProps +>( + NonInjectedKeyboardShortcutListener, + + { + getProps: (di, props) => ({ + invokeShortcut: di.inject(invokeShortcutInjectable), + ...props, + }), + }, +); diff --git a/packages/business-features/keyboard-shortcuts/src/keyboard-shortcut-scope.tsx b/packages/business-features/keyboard-shortcuts/src/keyboard-shortcut-scope.tsx new file mode 100644 index 0000000000..0e725cc4d4 --- /dev/null +++ b/packages/business-features/keyboard-shortcuts/src/keyboard-shortcut-scope.tsx @@ -0,0 +1,12 @@ +import React from "react"; + +export interface KeyboardShortcutScopeProps { + id: string; + children: React.ReactNode; +} + +export const KeyboardShortcutScope = ({ id, children }: KeyboardShortcutScopeProps) => ( +
+ {children} +
+); diff --git a/packages/business-features/keyboard-shortcuts/src/keyboard-shortcuts.test.tsx b/packages/business-features/keyboard-shortcuts/src/keyboard-shortcuts.test.tsx new file mode 100644 index 0000000000..d2bbfb85a4 --- /dev/null +++ b/packages/business-features/keyboard-shortcuts/src/keyboard-shortcuts.test.tsx @@ -0,0 +1,353 @@ +import userEvent from "@testing-library/user-event"; +import type { RenderResult } from "@testing-library/react"; +import { render } from "@testing-library/react"; +import { createContainer, DiContainer, getInjectable } from "@ogre-tools/injectable"; +import { registerInjectableReact } from "@ogre-tools/injectable-react"; +import { registerMobX } from "@ogre-tools/injectable-extension-for-mobx"; +import { keyboardShortcutInjectionToken } from "./keyboard-shortcut-injection-token"; +import { registerFeature } from "@k8slens/feature-core"; +import { keyboardShortcutsFeature } from "./feature"; +import React from "react"; +import { computed, runInAction } from "mobx"; +import { KeyboardShortcutScope } from "./keyboard-shortcut-scope"; +import { Discover, discoverFor } from "@k8slens/react-testing-library-discovery"; +import { startApplicationInjectionToken } from "@k8slens/application"; +import { renderInjectionToken } from "@k8slens/react-application"; +import { reactApplicationChildrenInjectionToken } from "@k8slens/react-application"; +import platformInjectable from "./platform.injectable"; + +describe("keyboard-shortcuts", () => { + let di: DiContainer; + let invokeMock: jest.Mock; + let rendered: RenderResult; + + beforeEach(() => { + di = createContainer("irrelevant"); + + registerInjectableReact(di); + registerMobX(di); + + runInAction(() => { + registerFeature(di, keyboardShortcutsFeature); + }); + + invokeMock = jest.fn(); + + const someKeyboardShortcutInjectable = getInjectable({ + id: "some-keyboard-shortcut", + + instantiate: () => ({ + binding: "Escape", + invoke: () => invokeMock("esc-in-root"), + }), + + injectionToken: keyboardShortcutInjectionToken, + }); + + const someScopedKeyboardShortcutInjectable = getInjectable({ + id: "some-scoped-keyboard-shortcut", + + instantiate: () => ({ + binding: "Escape", + invoke: () => invokeMock("esc-in-scope"), + scope: "some-scope", + }), + + injectionToken: keyboardShortcutInjectionToken, + }); + + const someOtherKeyboardShortcutInjectable = getInjectable({ + id: "some-other-keyboard-shortcut", + + instantiate: () => ({ + binding: "something-else-than-esc", + invoke: () => invokeMock("something-else-than-esc"), + }), + + injectionToken: keyboardShortcutInjectionToken, + }); + + const childComponentForScopeInjectable = getInjectable({ + id: "some-child-component-for-scope", + + instantiate: () => ({ + id: "some-child-component-for-scope", + + enabled: computed(() => true), + + Component: () => ( + +
+ + ), + }), + + injectionToken: reactApplicationChildrenInjectionToken, + }); + + runInAction(() => { + di.register( + someKeyboardShortcutInjectable, + someScopedKeyboardShortcutInjectable, + someOtherKeyboardShortcutInjectable, + childComponentForScopeInjectable, + ); + }); + + di.override(renderInjectionToken, () => (application) => { + rendered = render(application); + }); + }); + + describe("when application is started", () => { + let discover: Discover; + + beforeEach(async () => { + const startApplication = di.inject(startApplicationInjectionToken); + + await startApplication(); + + discover = discoverFor(() => rendered); + }); + + it("renders", () => { + expect(rendered.baseElement).toMatchSnapshot(); + }); + + it("given focus is in the body, when pressing the shortcut, calls shortcut in global scope", () => { + userEvent.keyboard("{Escape}"); + + expect(invokeMock.mock.calls).toEqual([["esc-in-root"]]); + }); + + it("given focus inside a nested scope, when pressing the shortcut, calls only the callback for the scope", () => { + const result = discover.getSingleElement("keyboard-shortcut-scope", "some-scope"); + + const discoveredHtml = result.discovered as HTMLDivElement; + + discoveredHtml.focus(); + + userEvent.keyboard("{Escape}"); + + expect(invokeMock.mock.calls).toEqual([["esc-in-scope"]]); + }); + + it("given conflicting shortcut, when pressing the shortcut, calls both callbacks", () => { + const conflictingShortcutInjectable = getInjectable({ + id: "some-conflicting-keyboard-shortcut", + + instantiate: () => ({ + binding: "Escape", + invoke: () => invokeMock("conflicting-esc-in-root"), + }), + + injectionToken: keyboardShortcutInjectionToken, + }); + + runInAction(() => { + di.register(conflictingShortcutInjectable); + }); + + userEvent.keyboard("{Escape}"); + + expect(invokeMock.mock.calls).toEqual([["esc-in-root"], ["conflicting-esc-in-root"]]); + }); + + [ + { + scenario: "given shortcut without modifiers, when shortcut is pressed, calls the callback", + binding: { code: "Escape" }, + keyboard: "{Escape}", + shouldCallCallback: true, + }, + { + scenario: + "given shortcut without modifiers, when shortcut is pressed but with modifier, does not call the callback", + binding: { code: "F1" }, + keyboard: "{Meta>}[F1]", + shouldCallCallback: false, + }, + { + scenario: "given shortcut with meta modifier, when shortcut is pressed, calls the callback", + + binding: { meta: true, code: "F1" }, + keyboard: "{Meta>}[F1]", + shouldCallCallback: true, + }, + { + scenario: + "given shortcut with shift modifier, when shortcut is pressed, calls the callback", + + binding: { shift: true, code: "F1" }, + keyboard: "{Shift>}[F1]", + shouldCallCallback: true, + }, + { + scenario: "given shortcut with alt modifier, when shortcut is pressed, calls the callback", + binding: { altOrOption: true, code: "F1" }, + keyboard: "{Alt>}[F1]", + shouldCallCallback: true, + }, + { + scenario: "given shortcut with ctrl modifier, when shortcut is pressed, calls the callback", + binding: { ctrl: true, code: "F1" }, + keyboard: "{Control>}[F1]", + shouldCallCallback: true, + }, + { + scenario: "given shortcut with all modifiers, when shortcut is pressed, calls the callback", + + binding: { ctrl: true, altOrOption: true, shift: true, meta: true, code: "F1" }, + keyboard: "{Meta>}{Shift>}{Alt>}{Control>}[F1]", + shouldCallCallback: true, + }, + ].forEach(({ binding, keyboard, scenario, shouldCallCallback }) => { + // eslint-disable-next-line jest/valid-title + it(scenario, () => { + const invokeMock = jest.fn(); + + const shortcutInjectable = getInjectable({ + id: "shortcut", + + instantiate: () => ({ + binding, + invoke: invokeMock, + }), + + injectionToken: keyboardShortcutInjectionToken, + }); + + runInAction(() => { + di.register(shortcutInjectable); + }); + + userEvent.keyboard(keyboard); + + if (shouldCallCallback) { + // eslint-disable-next-line jest/no-conditional-expect + expect(invokeMock).toHaveBeenCalled(); + } else { + // eslint-disable-next-line jest/no-conditional-expect + expect(invokeMock).not.toHaveBeenCalled(); + } + }); + }); + }); + + describe("given in mac and keyboard shortcut with modifier for ctrl or command", () => { + beforeEach(async () => { + di.override(platformInjectable, () => "darwin"); + + invokeMock = jest.fn(); + + const shortcutInjectable = getInjectable({ + id: "shortcut", + + instantiate: () => ({ + binding: { code: "KeyK", ctrlOrCommand: true }, + invoke: invokeMock, + }), + + injectionToken: keyboardShortcutInjectionToken, + }); + + runInAction(() => { + di.register(shortcutInjectable); + }); + + const startApplication = di.inject(startApplicationInjectionToken); + + await startApplication(); + }); + + it("when pressing the keyboard shortcut with command, calls the callback", () => { + userEvent.keyboard("{Meta>}[KeyK]"); + + expect(invokeMock).toHaveBeenCalled(); + }); + + it("when pressing the keyboard shortcut with ctrl, does not call the callback", () => { + userEvent.keyboard("{Control>}[KeyK]"); + + expect(invokeMock).not.toHaveBeenCalled(); + }); + }); + + describe("given in windows and keyboard shortcut with modifier for ctrl or command", () => { + beforeEach(async () => { + di.override(platformInjectable, () => "win32"); + + invokeMock = jest.fn(); + + const shortcutInjectable = getInjectable({ + id: "shortcut", + + instantiate: () => ({ + binding: { code: "KeyK", ctrlOrCommand: true }, + invoke: invokeMock, + }), + + injectionToken: keyboardShortcutInjectionToken, + }); + + runInAction(() => { + di.register(shortcutInjectable); + }); + + const startApplication = di.inject(startApplicationInjectionToken); + + await startApplication(); + }); + + it("when pressing the keyboard shortcut with windows, does not call the callback", () => { + userEvent.keyboard("{Meta>}[KeyK]"); + + expect(invokeMock).not.toHaveBeenCalled(); + }); + + it("when pressing the keyboard shortcut with ctrl, calls the callback", () => { + userEvent.keyboard("{Control>}[KeyK]"); + + expect(invokeMock).toHaveBeenCalled(); + }); + }); + + describe("given in any other platform and keyboard shortcut with modifier for ctrl or command", () => { + beforeEach(async () => { + di.override(platformInjectable, () => "some-other-platform"); + + invokeMock = jest.fn(); + + const shortcutInjectable = getInjectable({ + id: "shortcut", + + instantiate: () => ({ + binding: { code: "KeyK", ctrlOrCommand: true }, + invoke: invokeMock, + }), + + injectionToken: keyboardShortcutInjectionToken, + }); + + runInAction(() => { + di.register(shortcutInjectable); + }); + + const startApplication = di.inject(startApplicationInjectionToken); + + await startApplication(); + }); + + it("when pressing the keyboard shortcut with meta, does not call the callback", () => { + userEvent.keyboard("{Meta>}[KeyK]"); + + expect(invokeMock).not.toHaveBeenCalled(); + }); + + it("when pressing the keyboard shortcut with ctrl, calls the callback", () => { + userEvent.keyboard("{Control>}[KeyK]"); + + expect(invokeMock).toHaveBeenCalled(); + }); + }); +}); diff --git a/packages/business-features/keyboard-shortcuts/src/platform.injectable.ts b/packages/business-features/keyboard-shortcuts/src/platform.injectable.ts new file mode 100644 index 0000000000..407af8a43d --- /dev/null +++ b/packages/business-features/keyboard-shortcuts/src/platform.injectable.ts @@ -0,0 +1,11 @@ +import { getInjectable } from "@ogre-tools/injectable"; + +export const allPlatforms = ["win32", "darwin", "linux"] as const; + +const platformInjectable = getInjectable({ + id: "platform", + instantiate: () => process.platform as (typeof allPlatforms)[number], + causesSideEffects: true, +}); + +export default platformInjectable; diff --git a/packages/business-features/keyboard-shortcuts/tsconfig.json b/packages/business-features/keyboard-shortcuts/tsconfig.json new file mode 100644 index 0000000000..9e140d79da --- /dev/null +++ b/packages/business-features/keyboard-shortcuts/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "@k8slens/typescript/config/base.json", + "include": ["**/*.ts", "**/*.tsx"], +} diff --git a/packages/business-features/keyboard-shortcuts/webpack.config.js b/packages/business-features/keyboard-shortcuts/webpack.config.js new file mode 100644 index 0000000000..1cda407f5a --- /dev/null +++ b/packages/business-features/keyboard-shortcuts/webpack.config.js @@ -0,0 +1 @@ +module.exports = require("@k8slens/webpack").configForReact; diff --git a/packages/cluster-settings/package.json b/packages/cluster-settings/package.json index 199347dcf4..aa9b8d6725 100644 --- a/packages/cluster-settings/package.json +++ b/packages/cluster-settings/package.json @@ -18,14 +18,16 @@ "clean": "rimraf dist/", "generate-types": "tsc --d --declarationDir ./dist --declarationMap --emitDeclarationOnly", "build": "npm run generate-types && swc ./src/index.ts -d ./dist", + "prepare": "npm run build", + "prepare:dev": "npm run build", "prepare:test": "npm run build" }, "devDependencies": { "@ogre-tools/injectable": "^15.1.2", "@swc/cli": "^0.1.61", - "@swc/core": "^1.3.37", + "@swc/core": "^1.3.44", "@types/node": "^16.18.11", "@types/semver": "^7.3.13", - "rimraf": "^4.1.2" + "rimraf": "^4.4.1" } } diff --git a/packages/core/package.json b/packages/core/package.json index ad6e7e45ae..e42f9853fe 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -48,8 +48,6 @@ "scripts": { "build": "cross-env NODE_ENV=production webpack --config webpack/library-bundle.ts --progress", "clean": "rimraf dist static/build", - "prepare:dev": "cross-env NODE_ENV=development webpack --config webpack/library-bundle.ts --progress", - "dev": "cross-env NODE_ENV=development webpack --config webpack/library-bundle.ts --watch", "test:unit": "jest --testPathIgnorePatterns integration", "test:watch": "func() { jest ${1} --watch --testPathIgnorePatterns integration; }; func", "lint": "PROD=true eslint --ext js,ts,tsx --max-warnings=0 .", @@ -98,7 +96,6 @@ ], "runtime": "@side/jest-runtime" }, - "build": {}, "nx": { "targets": { "build": { @@ -108,11 +105,6 @@ "outputs": [ "{workspaceRoot}/static/build/" ] - }, - "dev": { - "outputs": [ - "{workspaceRoot}/static/build/" - ] } } }, @@ -120,8 +112,8 @@ "@astronautlabs/jsonpath": "^1.1.0", "@hapi/call": "^9.0.1", "@hapi/subtext": "^7.1.0", - "@k8slens/cluster-settings": "^6.5.0-alpha.1", "@k8slens/node-fetch": "^6.5.0-alpha.1", + "@k8slens/react-application": "^1.0.0-alpha.0", "@kubernetes/client-node": "^0.18.1", "@material-ui/styles": "^4.11.5", "@ogre-tools/fp": "^15.1.2", @@ -136,7 +128,7 @@ "await-lock": "^2.2.2", "byline": "^5.0.0", "chokidar": "^3.5.3", - "conf": "^7.1.2", + "conf": "^10.2.0", "crypto-js": "^4.1.1", "electron-devtools-installer": "^3.2.0", "electron-updater": "^4.6.5", @@ -144,7 +136,6 @@ "filehound": "^1.17.6", "fs-extra": "^9.0.1", "glob-to-regexp": "^0.4.1", - "got": "^11.8.6", "grapheme-splitter": "^1.0.4", "handlebars": "^4.7.7", "history": "^4.10.1", @@ -191,12 +182,13 @@ "devDependencies": { "@async-fn/jest": "1.6.4", "@k8slens/messaging-fake-bridge": "^1.0.0-alpha.1", + "@k8slens/react-testing-library-discovery": "^1.0.0-alpha.0", "@material-ui/core": "^4.12.3", "@material-ui/icons": "^4.11.2", "@material-ui/lab": "^4.0.0-alpha.60", "@sentry/types": "^6.19.7", "@swc/cli": "^0.1.61", - "@swc/core": "^1.3.37", + "@swc/core": "^1.3.44", "@swc/jest": "^0.2.24", "@testing-library/dom": "^7.31.2", "@testing-library/jest-dom": "^5.16.5", @@ -215,7 +207,7 @@ "@types/hapi__call": "^9.0.0", "@types/hapi__subtext": "^7.0.0", "@types/http-proxy": "^1.17.9", - "@types/jest": "^28.1.6", + "@types/jest": "^29.5.0", "@types/js-yaml": "^4.0.5", "@types/lodash": "^4.14.191", "@types/marked": "^4.0.8", @@ -226,7 +218,7 @@ "@types/proper-lockfile": "^4.1.2", "@types/randomcolor": "^0.5.7", "@types/react": "^17.0.45", - "@types/react-beautiful-dnd": "^13.1.3", + "@types/react-beautiful-dnd": "^13.1.4", "@types/react-dom": "^17.0.16", "@types/react-router": "^5.1.19", "@types/react-router-dom": "^5.3.3", @@ -240,7 +232,7 @@ "@types/triple-beam": "^1.3.2", "@types/url-parse": "^1.4.8", "@types/uuid": "^8.3.4", - "@types/webpack": "^5.28.0", + "@types/webpack": "^5.28.1", "@types/webpack-dev-server": "^4.7.2", "@types/webpack-env": "^1.18.0", "@types/webpack-node-externals": "^2.5.3", @@ -273,7 +265,7 @@ "identity-obj-proxy": "^3.0.0", "ignore-loader": "^0.1.2", "include-media": "^1.4.9", - "jest": "^28.1.3", + "jest": "^29.5.0", "jest-canvas-mock": "^2.3.1", "jest-environment-jsdom": "^28.1.3", "jest-mock-extended": "^2.0.9", @@ -287,7 +279,7 @@ "node-gyp": "^8.3.0", "node-loader": "^2.0.0", "nodemon": "^2.0.20", - "playwright": "^1.30.0", + "playwright": "^1.32.1", "postcss": "^8.4.21", "postcss-loader": "^6.2.1", "query-string": "^7.1.3", @@ -300,7 +292,7 @@ "react-select-event": "^5.5.1", "react-table": "^7.8.0", "react-window": "^1.8.8", - "rimraf": "^4.1.2", + "rimraf": "^4.4.1", "sass": "^1.58.2", "sass-loader": "^12.6.0", "style-loader": "^3.3.1", @@ -313,9 +305,9 @@ "typedoc-plugin-markdown": "^3.13.6", "typescript": "^4.9.5", "typescript-plugin-css-modules": "^3.4.0", - "webpack": "^5.75.0", + "webpack": "^5.77.0", "webpack-cli": "^4.9.2", - "webpack-dev-server": "^4.11.1", + "webpack-dev-server": "^4.13.2", "webpack-node-externals": "^3.0.0", "xterm": "^4.19.0", "xterm-addon-fit": "^0.5.0" @@ -323,6 +315,7 @@ "peerDependencies": { "@k8slens/application": "^6.5.0-alpha.0", "@k8slens/application-for-electron-main": "^6.5.0-alpha.0", + "@k8slens/cluster-settings": "^6.5.0-alpha.1", "@k8slens/legacy-extensions": "^1.0.0-alpha.0", "@k8slens/messaging": "^1.0.0-alpha.1", "@k8slens/messaging-for-main": "^1.0.0-alpha.1", diff --git a/packages/core/src/common/__tests__/hotbar-store.test.ts b/packages/core/src/common/__tests__/hotbar-store.test.ts deleted file mode 100644 index 474ebc1618..0000000000 --- a/packages/core/src/common/__tests__/hotbar-store.test.ts +++ /dev/null @@ -1,356 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { anyObject } from "jest-mock-extended"; -import type { CatalogEntity, CatalogEntityData, CatalogEntityKindData } from "../catalog"; -import { getDiForUnitTesting } from "../../main/getDiForUnitTesting"; -import type { DiContainer } from "@ogre-tools/injectable"; -import hotbarStoreInjectable from "../hotbars/store.injectable"; -import type { HotbarStore } from "../hotbars/store"; -import catalogEntityRegistryInjectable from "../../main/catalog/entity-registry.injectable"; -import { computed } from "mobx"; -import hasCategoryForEntityInjectable from "../catalog/has-category-for-entity.injectable"; -import catalogCatalogEntityInjectable from "../catalog-entities/general-catalog-entities/implementations/catalog-catalog-entity.injectable"; -import loggerInjectable from "../logger.injectable"; -import type { Logger } from "../logger"; -import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable"; -import writeJsonSyncInjectable from "../fs/write-json-sync.injectable"; - -function getMockCatalogEntity(data: Partial & CatalogEntityKindData): CatalogEntity { - return { - getName: jest.fn(() => data.metadata?.name), - getId: jest.fn(() => data.metadata?.uid), - getSource: jest.fn(() => data.metadata?.source ?? "unknown"), - isEnabled: jest.fn(() => data.status?.enabled ?? true), - onContextMenuOpen: jest.fn(), - onSettingsOpen: jest.fn(), - metadata: {}, - spec: {}, - status: {}, - ...data, - } as CatalogEntity; -} - -describe("HotbarStore", () => { - let di: DiContainer; - let hotbarStore: HotbarStore; - let testCluster: CatalogEntity; - let minikubeCluster: CatalogEntity; - let awsCluster: CatalogEntity; - let loggerMock: jest.Mocked; - - beforeEach(async () => { - di = getDiForUnitTesting(); - - testCluster = getMockCatalogEntity({ - apiVersion: "v1", - kind: "Cluster", - status: { - phase: "Running", - }, - metadata: { - uid: "some-test-id", - name: "my-test-cluster", - source: "local", - labels: {}, - }, - }); - minikubeCluster = getMockCatalogEntity({ - apiVersion: "v1", - kind: "Cluster", - status: { - phase: "Running", - }, - metadata: { - uid: "some-minikube-id", - name: "my-minikube-cluster", - source: "local", - labels: {}, - }, - }); - awsCluster = getMockCatalogEntity({ - apiVersion: "v1", - kind: "Cluster", - status: { - phase: "Running", - }, - metadata: { - uid: "some-aws-id", - name: "my-aws-cluster", - source: "local", - labels: {}, - }, - }); - - di.override(hasCategoryForEntityInjectable, () => () => true); - - loggerMock = { - warn: jest.fn(), - debug: jest.fn(), - error: jest.fn(), - info: jest.fn(), - silly: jest.fn(), - }; - - di.override(loggerInjectable, () => loggerMock); - - di.override(directoryForUserDataInjectable, () => "/some-directory-for-user-data"); - - const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable); - const catalogCatalogEntity = di.inject(catalogCatalogEntityInjectable); - - catalogEntityRegistry.addComputedSource("some-id", computed(() => [ - testCluster, - minikubeCluster, - awsCluster, - catalogCatalogEntity, - ])); - }); - - describe("given no previous data in store, running all migrations", () => { - beforeEach(() => { - hotbarStore = di.inject(hotbarStoreInjectable); - - hotbarStore.load(); - }); - - describe("load", () => { - it("loads one hotbar by default", () => { - expect(hotbarStore.hotbars.length).toEqual(1); - }); - }); - - describe("add", () => { - it("adds a hotbar", () => { - hotbarStore.add({ name: "hottest" }); - expect(hotbarStore.hotbars.length).toEqual(2); - }); - }); - - describe("hotbar items", () => { - it("initially creates 12 empty cells", () => { - expect(hotbarStore.getActive().items.length).toEqual(12); - }); - - it("initially adds catalog entity as first item", () => { - expect(hotbarStore.getActive().items[0]?.entity.name).toEqual("Catalog"); - }); - - it("adds items", () => { - hotbarStore.addToHotbar(testCluster); - const items = hotbarStore.getActive().items.filter(Boolean); - - expect(items.length).toEqual(2); - }); - - it("removes items", () => { - hotbarStore.addToHotbar(testCluster); - hotbarStore.removeFromHotbar("some-test-id"); - hotbarStore.removeFromHotbar("catalog-entity"); - const items = hotbarStore.getActive().items.filter(Boolean); - - expect(items).toStrictEqual([]); - }); - - it("does nothing if removing with invalid uid", () => { - hotbarStore.addToHotbar(testCluster); - hotbarStore.removeFromHotbar("invalid uid"); - const items = hotbarStore.getActive().items.filter(Boolean); - - expect(items.length).toEqual(2); - }); - - it("moves item to empty cell", () => { - hotbarStore.addToHotbar(testCluster); - hotbarStore.addToHotbar(minikubeCluster); - hotbarStore.addToHotbar(awsCluster); - - expect(hotbarStore.getActive().items[6]).toBeNull(); - - hotbarStore.restackItems(1, 5); - - expect(hotbarStore.getActive().items[5]).toBeTruthy(); - expect(hotbarStore.getActive().items[5]?.entity.uid).toEqual("some-test-id"); - }); - - it("moves items down", () => { - hotbarStore.addToHotbar(testCluster); - hotbarStore.addToHotbar(minikubeCluster); - hotbarStore.addToHotbar(awsCluster); - - // aws -> catalog - hotbarStore.restackItems(3, 0); - - const items = hotbarStore.getActive().items.map(item => item?.entity.uid || null); - - expect(items.slice(0, 4)).toEqual(["some-aws-id", "catalog-entity", "some-test-id", "some-minikube-id"]); - }); - - it("moves items up", () => { - hotbarStore.addToHotbar(testCluster); - hotbarStore.addToHotbar(minikubeCluster); - hotbarStore.addToHotbar(awsCluster); - - // test -> aws - hotbarStore.restackItems(1, 3); - - const items = hotbarStore.getActive().items.map(item => item?.entity.uid || null); - - expect(items.slice(0, 4)).toEqual(["catalog-entity", "some-minikube-id", "some-aws-id", "some-test-id"]); - }); - - it("logs an error if cellIndex is out of bounds", () => { - hotbarStore.add({ name: "hottest", id: "hottest" }); - hotbarStore.setActiveHotbar("hottest"); - - hotbarStore.addToHotbar(testCluster, -1); - expect(loggerMock.error).toBeCalledWith("[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range", anyObject()); - - hotbarStore.addToHotbar(testCluster, 12); - expect(loggerMock.error).toBeCalledWith("[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range", anyObject()); - - hotbarStore.addToHotbar(testCluster, 13); - expect(loggerMock.error).toBeCalledWith("[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range", anyObject()); - }); - - it("throws an error if getId is invalid or returns not a string", () => { - expect(() => hotbarStore.addToHotbar({} as any)).toThrowError(TypeError); - expect(() => hotbarStore.addToHotbar({ getId: () => true } as any)).toThrowError(TypeError); - }); - - it("throws an error if getName is invalid or returns not a string", () => { - expect(() => hotbarStore.addToHotbar({ getId: () => "" } as any)).toThrowError(TypeError); - expect(() => hotbarStore.addToHotbar({ getId: () => "", getName: () => 4 } as any)).toThrowError(TypeError); - }); - - it("does nothing when item moved to same cell", () => { - hotbarStore.addToHotbar(testCluster); - hotbarStore.restackItems(1, 1); - - expect(hotbarStore.getActive().items[1]?.entity.uid).toEqual("some-test-id"); - }); - - it("new items takes first empty cell", () => { - hotbarStore.addToHotbar(testCluster); - hotbarStore.addToHotbar(awsCluster); - hotbarStore.restackItems(0, 3); - hotbarStore.addToHotbar(minikubeCluster); - - expect(hotbarStore.getActive().items[0]?.entity.uid).toEqual("some-minikube-id"); - }); - - it("throws if invalid arguments provided", () => { - hotbarStore.addToHotbar(testCluster); - - expect(() => hotbarStore.restackItems(-5, 0)).toThrow(); - expect(() => hotbarStore.restackItems(2, -1)).toThrow(); - expect(() => hotbarStore.restackItems(14, 1)).toThrow(); - expect(() => hotbarStore.restackItems(11, 112)).toThrow(); - }); - - it("checks if entity already pinned to hotbar", () => { - hotbarStore.addToHotbar(testCluster); - - expect(hotbarStore.isAddedToActive(testCluster)).toBeTruthy(); - expect(hotbarStore.isAddedToActive(awsCluster)).toBeFalsy(); - }); - }); - }); - - describe("given data from 5.0.0-beta.3 and version being 5.0.0-beta.10", () => { - beforeEach(() => { - const writeJsonSync = di.inject(writeJsonSyncInjectable); - - writeJsonSync("/some-directory-for-user-data/lens-hotbar-store.json", { - __internal__: { - migrations: { - version: "5.0.0-beta.3", - }, - }, - hotbars: [ - { - id: "3caac17f-aec2-4723-9694-ad204465d935", - name: "myhotbar", - items: [ - { - entity: { - uid: "some-aws-id", - }, - }, - { - entity: { - uid: "55b42c3c7ba3b04193416cda405269a5", - }, - }, - { - entity: { - uid: "176fd331968660832f62283219d7eb6e", - }, - }, - { - entity: { - uid: "61c4fb45528840ebad1badc25da41d14", - name: "user1-context", - source: "local", - }, - }, - { - entity: { - uid: "27d6f99fe9e7548a6e306760bfe19969", - name: "foo2", - source: "local", - }, - }, - null, - { - entity: { - uid: "c0b20040646849bb4dcf773e43a0bf27", - name: "multinode-demo", - source: "local", - }, - }, - null, - null, - null, - null, - null, - ], - }, - ], - }); - - di.override(storeMigrationVersionInjectable, () => "5.0.0-beta.10"); - - hotbarStore = di.inject(hotbarStoreInjectable); - - hotbarStore.load(); - }); - - it("allows to retrieve a hotbar", () => { - const hotbar = hotbarStore.findById("3caac17f-aec2-4723-9694-ad204465d935"); - - expect(hotbar?.id).toBe("3caac17f-aec2-4723-9694-ad204465d935"); - }); - - it("clears cells without entity", () => { - const items = hotbarStore.hotbars[0].items; - - expect(items[2]).toBeNull(); - }); - - it("adds extra data to cells with according entity", () => { - const items = hotbarStore.hotbars[0].items; - - expect(items[0]).toEqual({ - entity: { - name: "my-aws-cluster", - source: "local", - uid: "some-aws-id", - }, - }); - }); - }); -}); diff --git a/packages/core/src/common/__tests__/user-store.test.ts b/packages/core/src/common/__tests__/user-store.test.ts index fb8d020fb0..eee46fa9a4 100644 --- a/packages/core/src/common/__tests__/user-store.test.ts +++ b/packages/core/src/common/__tests__/user-store.test.ts @@ -2,11 +2,8 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { UserStore } from "../user-store"; -import userStoreInjectable from "../user-store/user-store.injectable"; import type { DiContainer } from "@ogre-tools/injectable"; import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import type { ClusterStoreModel } from "../cluster-store/cluster-store"; import { defaultThemeId } from "../vars"; import writeFileInjectable from "../fs/write-file.injectable"; import { getDiForUnitTesting } from "../../main/getDiForUnitTesting"; @@ -15,9 +12,16 @@ import releaseChannelInjectable from "../vars/release-channel.injectable"; import defaultUpdateChannelInjectable from "../../features/application-update/common/selected-update-channel/default-update-channel.injectable"; import writeJsonSyncInjectable from "../fs/write-json-sync.injectable"; import writeFileSyncInjectable from "../fs/write-file-sync.injectable"; +import type { UserPreferencesState } from "../../features/user-preferences/common/state.injectable"; +import userPreferencesStateInjectable from "../../features/user-preferences/common/state.injectable"; +import userPreferencesPersistentStorageInjectable from "../../features/user-preferences/common/storage.injectable"; +import type { ResetTheme } from "../../features/user-preferences/common/reset-theme.injectable"; +import resetThemeInjectable from "../../features/user-preferences/common/reset-theme.injectable"; +import type { ClusterStoreModel } from "../../features/cluster/storage/common/storage.injectable"; describe("user store tests", () => { - let userStore: UserStore; + let state: UserPreferencesState; + let resetTheme: ResetTheme; let di: DiContainer; beforeEach(async () => { @@ -33,6 +37,8 @@ describe("user store tests", () => { await di.inject(defaultUpdateChannelInjectable).init(); + state = di.inject(userPreferencesStateInjectable); + resetTheme = di.inject(resetThemeInjectable); }); describe("for an empty config", () => { @@ -42,25 +48,23 @@ describe("user store tests", () => { writeJsonSync("/some-directory-for-user-data/lens-user-store.json", {}); writeJsonSync("/some-directory-for-user-data/kube_config", {}); - userStore = di.inject(userStoreInjectable); - - userStore.load(); + di.inject(userPreferencesPersistentStorageInjectable).loadAndStartSyncing(); }); it("allows setting and getting preferences", () => { - userStore.httpsProxy = "abcd://defg"; + state.httpsProxy = "abcd://defg"; - expect(userStore.httpsProxy).toBe("abcd://defg"); - expect(userStore.colorTheme).toBe(defaultThemeId); + expect(state.httpsProxy).toBe("abcd://defg"); + expect(state.colorTheme).toBe(defaultThemeId); - userStore.colorTheme = "light"; - expect(userStore.colorTheme).toBe("light"); + state.colorTheme = "light"; + expect(state.colorTheme).toBe("light"); }); it("correctly resets theme to default value", async () => { - userStore.colorTheme = "some other theme"; - userStore.resetTheme(); - expect(userStore.colorTheme).toBe(defaultThemeId); + state.colorTheme = "some other theme"; + resetTheme(); + expect(state.colorTheme).toBe(defaultThemeId); }); }); @@ -92,18 +96,16 @@ describe("user store tests", () => { di.override(storeMigrationVersionInjectable, () => "10.0.0"); - userStore = di.inject(userStoreInjectable); - - userStore.load(); + di.inject(userPreferencesPersistentStorageInjectable).loadAndStartSyncing(); }); it("skips clusters for adding to kube-sync with files under extension_data/", () => { - expect(userStore.syncKubeconfigEntries.has("/some-directory-for-user-data/extension_data/foo/bar")).toBe(false); - expect(userStore.syncKubeconfigEntries.has("/some/other/path")).toBe(true); + expect(state.syncKubeconfigEntries.has("/some-directory-for-user-data/extension_data/foo/bar")).toBe(false); + expect(state.syncKubeconfigEntries.has("/some/other/path")).toBe(true); }); it("allows access to the colorTheme preference", () => { - expect(userStore.colorTheme).toBe("light"); + expect(state.colorTheme).toBe("light"); }); }); }); diff --git a/packages/core/src/common/base-store/base-store.ts b/packages/core/src/common/base-store/base-store.ts deleted file mode 100644 index a1dd26f0f7..0000000000 --- a/packages/core/src/common/base-store/base-store.ts +++ /dev/null @@ -1,156 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type Config from "conf"; -import type { Migrations, Options as ConfOptions } from "conf/dist/source/types"; -import type { IEqualsComparer } from "mobx"; -import { makeObservable, reaction } from "mobx"; -import { disposer, isPromiseLike } from "@k8slens/utilities"; -import { broadcastMessage } from "../ipc"; -import isEqual from "lodash/isEqual"; -import { kebabCase } from "lodash"; -import type { GetConfigurationFileModel } from "../get-configuration-file-model/get-configuration-file-model.injectable"; -import type { Logger } from "../logger"; -import type { PersistStateToConfig } from "./save-to-file"; -import type { GetBasenameOfPath } from "../path/get-basename.injectable"; -import type { EnlistMessageChannelListener } from "@k8slens/messaging"; -import { toJS } from "../utils"; - -export interface BaseStoreParams extends Omit, "migrations"> { - syncOptions?: { - fireImmediately?: boolean; - equals?: IEqualsComparer; - }; - configName: string; -} - -export interface IpcChannelPrefixes { - local: string; - remote: string; -} - -export interface BaseStoreDependencies { - readonly logger: Logger; - readonly storeMigrationVersion: string; - readonly directoryForUserData: string; - readonly migrations: Migrations>; - readonly ipcChannelPrefixes: IpcChannelPrefixes; - readonly shouldDisableSyncInListener: boolean; - getConfigurationFileModel: GetConfigurationFileModel; - persistStateToConfig: PersistStateToConfig; - getBasenameOfPath: GetBasenameOfPath; - enlistMessageChannelListener: EnlistMessageChannelListener; -} - -/** - * Note: T should only contain base JSON serializable types. - */ -export abstract class BaseStore { - private readonly syncDisposers = disposer(); - - readonly displayName = kebabCase(this.params.configName).toUpperCase(); - - /** - * @ignore - */ - protected readonly dependencies: BaseStoreDependencies; - - protected constructor( - dependencies: BaseStoreDependencies, - protected readonly params: BaseStoreParams, - ) { - this.dependencies = dependencies; - makeObservable(this); - } - - /** - * This must be called after the last child's constructor is finished (or just before it finishes) - */ - load() { - this.dependencies.logger.info(`[${this.displayName}]: LOADING ...`); - - const config = this.dependencies.getConfigurationFileModel({ - projectName: "lens", - projectVersion: this.dependencies.storeMigrationVersion, - cwd: this.cwd(), - ...this.params, - migrations: this.dependencies.migrations as Migrations, - }); - - const res = this.fromStore(config.store); - - if (isPromiseLike(res)) { - this.dependencies.logger.error(`${this.displayName} extends BaseStore's fromStore method returns a Promise or promise-like object. This is an error and must be fixed.`); - } - - this.startSyncing(config); - this.dependencies.logger.info(`[${this.displayName}]: LOADED from ${config.path}`); - } - - protected cwd() { - return this.dependencies.directoryForUserData; - } - - private startSyncing(config: Config) { - const name = this.dependencies.getBasenameOfPath(config.path); - - const disableSync = () => this.syncDisposers(); - const enableSync = () => { - this.syncDisposers.push( - reaction( - () => toJS(this.toJSON()), // unwrap possible observables and react to everything - model => { - this.dependencies.persistStateToConfig(config, model); - broadcastMessage(`${this.dependencies.ipcChannelPrefixes.remote}:${config.path}`, model); - }, - this.params.syncOptions, - ), - this.dependencies.enlistMessageChannelListener({ - id: this.displayName, - channel: { - id: `${this.dependencies.ipcChannelPrefixes.local}:${config.path}`, - }, - handler: (model) => { - this.dependencies.logger.silly(`[${this.displayName}]: syncing ${name}`, { model }); - - if (this.dependencies.shouldDisableSyncInListener) { - disableSync(); - } - - // todo: use "resourceVersion" if merge required (to avoid equality checks => better performance) - if (!isEqual(this.toJSON(), model)) { - this.fromStore(model as T); - } - - if (this.dependencies.shouldDisableSyncInListener) { - enableSync(); - } - }, - }), - ); - }; - - enableSync(); - } - - /** - * fromStore is called internally when a child class syncs with the file - * system. - * - * Note: This function **must** be synchronous. - * - * @param data the parsed information read from the stored JSON file - */ - protected abstract fromStore(data: T): void; - - /** - * toJSON is called when syncing the store to the filesystem. It should - * produce a JSON serializable object representation of the current state. - * - * It is recommended that a round trip is valid. Namely, calling - * `this.fromStore(this.toJSON())` shouldn't change the state. - */ - abstract toJSON(): T; -} diff --git a/packages/core/src/common/base-store/channel-prefix.ts b/packages/core/src/common/base-store/channel-prefix.ts deleted file mode 100644 index f2662c65e0..0000000000 --- a/packages/core/src/common/base-store/channel-prefix.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getInjectionToken } from "@ogre-tools/injectable"; -import type { IpcChannelPrefixes } from "./base-store"; - -export const baseStoreIpcChannelPrefixesInjectionToken = getInjectionToken({ - id: "base-store-ipc-channel-prefix-token", -}); diff --git a/packages/core/src/common/base-store/migrations.injectable.ts b/packages/core/src/common/base-store/migrations.injectable.ts deleted file mode 100644 index d97abee1ec..0000000000 --- a/packages/core/src/common/base-store/migrations.injectable.ts +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { InjectionToken } from "@ogre-tools/injectable"; -import { lifecycleEnum, getInjectable } from "@ogre-tools/injectable"; -import type Conf from "conf/dist/source"; -import type { Migrations } from "conf/dist/source/types"; -import loggerInjectable from "../logger.injectable"; -import { getOrInsert, iter } from "@k8slens/utilities"; - -export interface MigrationDeclaration { - version: string; - run(store: Conf>>): void; -} - -const storeMigrationsInjectable = getInjectable({ - id: "store-migrations", - instantiate: (di, token): Migrations> => { - const logger = di.inject(loggerInjectable); - const declarations = di.injectMany(token); - const migrations = new Map(); - - for (const decl of declarations) { - getOrInsert(migrations, decl.version, []).push(decl.run); - } - - return Object.fromEntries( - iter.map( - migrations, - ([v, fns]) => [v, (store) => { - logger.info(`Running ${v} migration for ${store.path}`); - - for (const fn of fns) { - fn(store); - } - }], - ), - ); - }, - lifecycle: lifecycleEnum.keyedSingleton({ - getInstanceKey: (di, token: InjectionToken) => token.id, - }), -}); - -export default storeMigrationsInjectable; diff --git a/packages/core/src/common/catalog-entities/web-link.ts b/packages/core/src/common/catalog-entities/web-link.ts index 833f05d65b..4b18ac7f73 100644 --- a/packages/core/src/common/catalog-entities/web-link.ts +++ b/packages/core/src/common/catalog-entities/web-link.ts @@ -4,10 +4,10 @@ */ import { getEnvironmentSpecificLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; +import removeWeblinkInjectable from "../../features/weblinks/common/remove.injectable"; import type { CatalogEntityContextMenuContext, CatalogEntityMetadata, CatalogEntityStatus } from "../catalog"; import { CatalogCategory, CatalogEntity, categoryVersion } from "../catalog/catalog-entity"; import productNameInjectable from "../vars/product-name.injectable"; -import weblinkStoreInjectable from "../weblinks-store/weblink-store.injectable"; export type WebLinkStatusPhase = "available" | "unavailable"; @@ -34,13 +34,13 @@ export class WebLink extends CatalogEntity weblinkStore.removeById(this.getId()), + onClick: () => removeWeblink(this.getId()), confirm: { message: `Remove Web Link "${this.getName()}" from ${productName}?`, }, diff --git a/packages/core/src/common/catalog/helpers.ts b/packages/core/src/common/catalog/helpers.ts new file mode 100644 index 0000000000..75cdf726aa --- /dev/null +++ b/packages/core/src/common/catalog/helpers.ts @@ -0,0 +1,83 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import type { CatalogEntity } from "./catalog-entity"; +import GraphemeSplitter from "grapheme-splitter"; +import { hasOwnProperty, hasTypedProperty, isObject, isString, iter } from "@k8slens/utilities"; + +function getNameParts(name: string): string[] { + const byWhitespace = name.split(/\s+/); + + if (byWhitespace.length > 1) { + return byWhitespace; + } + + const byDashes = name.split(/[-_]+/); + + if (byDashes.length > 1) { + return byDashes; + } + + return name.split(/@+/); +} + +export function limitGraphemeLengthOf(src: string, count: number): string { + const splitter = new GraphemeSplitter(); + + return iter + .chain(splitter.iterateGraphemes(src)) + .take(count) + .join(""); +} + +export function computeDefaultShortName(name: string) { + if (!name || typeof name !== "string") { + return "??"; + } + + const [rawFirst, rawSecond, rawThird] = getNameParts(name); + const splitter = new GraphemeSplitter(); + const first = splitter.iterateGraphemes(rawFirst); + const second = rawSecond ? splitter.iterateGraphemes(rawSecond): first; + const third = rawThird ? splitter.iterateGraphemes(rawThird) : iter.newEmpty(); + + return iter.chain(iter.take(first, 1)) + .concat(iter.take(second, 1)) + .concat(iter.take(third, 1)) + .join(""); +} + +export function getShortName(entity: CatalogEntity): string { + return entity.metadata.shortName || computeDefaultShortName(entity.getName()); +} + +export function getIconColourHash(entity: CatalogEntity): string { + return `${entity.metadata.name}-${entity.metadata.source}`; +} + +export function getIconBackground(entity: CatalogEntity): string | undefined { + if (isObject(entity.spec.icon)) { + if (hasTypedProperty(entity.spec.icon, "background", isString)) { + return entity.spec.icon.background; + } + + return hasOwnProperty(entity.spec.icon, "src") + ? "transparent" + : undefined; + } + + return undefined; +} + +export function getIconMaterial(entity: CatalogEntity): string | undefined { + if ( + isObject(entity.spec.icon) + && hasTypedProperty(entity.spec.icon, "material", isString) + ) { + return entity.spec.icon.material; + } + + return undefined; +} diff --git a/packages/core/src/common/cluster-store/cluster-store.injectable.ts b/packages/core/src/common/cluster-store/cluster-store.injectable.ts deleted file mode 100644 index 79eb02e36c..0000000000 --- a/packages/core/src/common/cluster-store/cluster-store.injectable.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import { ClusterStore } from "./cluster-store"; -import readClusterConfigSyncInjectable from "./read-cluster-config.injectable"; -import emitAppEventInjectable from "../app-event-bus/emit-event.injectable"; -import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable"; -import loggerInjectable from "../logger.injectable"; -import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable"; -import storeMigrationsInjectable from "../base-store/migrations.injectable"; -import { clusterStoreMigrationInjectionToken } from "./migration-token"; -import { baseStoreIpcChannelPrefixesInjectionToken } from "../base-store/channel-prefix"; -import { shouldBaseStoreDisableSyncInIpcListenerInjectionToken } from "../base-store/disable-sync"; -import { persistStateToConfigInjectionToken } from "../base-store/save-to-file"; -import getBasenameOfPathInjectable from "../path/get-basename.injectable"; -import { enlistMessageChannelListenerInjectionToken } from "@k8slens/messaging"; - -const clusterStoreInjectable = getInjectable({ - id: "cluster-store", - - instantiate: (di) => new ClusterStore({ - readClusterConfigSync: di.inject(readClusterConfigSyncInjectable), - emitAppEvent: di.inject(emitAppEventInjectable), - directoryForUserData: di.inject(directoryForUserDataInjectable), - getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable), - logger: di.inject(loggerInjectable), - storeMigrationVersion: di.inject(storeMigrationVersionInjectable), - migrations: di.inject(storeMigrationsInjectable, clusterStoreMigrationInjectionToken), - getBasenameOfPath: di.inject(getBasenameOfPathInjectable), - ipcChannelPrefixes: di.inject(baseStoreIpcChannelPrefixesInjectionToken), - persistStateToConfig: di.inject(persistStateToConfigInjectionToken), - enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken), - shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken), - }), -}); - -export default clusterStoreInjectable; diff --git a/packages/core/src/common/cluster-store/cluster-store.ts b/packages/core/src/common/cluster-store/cluster-store.ts deleted file mode 100644 index 8d283411c9..0000000000 --- a/packages/core/src/common/cluster-store/cluster-store.ts +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - - -import { action, comparer, computed, makeObservable, observable } from "mobx"; -import type { BaseStoreDependencies } from "../base-store/base-store"; -import { BaseStore } from "../base-store/base-store"; -import { Cluster } from "../cluster/cluster"; -import { toJS } from "../utils"; -import type { ClusterModel, ClusterId } from "../cluster-types"; -import type { ReadClusterConfigSync } from "./read-cluster-config.injectable"; -import type { EmitAppEvent } from "../app-event-bus/emit-event.injectable"; - -export interface ClusterStoreModel { - clusters?: ClusterModel[]; -} - -interface Dependencies extends BaseStoreDependencies { - readClusterConfigSync: ReadClusterConfigSync; - emitAppEvent: EmitAppEvent; -} - -export class ClusterStore extends BaseStore { - readonly clusters = observable.map(); - - constructor(protected readonly dependencies: Dependencies) { - super(dependencies, { - configName: "lens-cluster-store", - accessPropertiesByDotNotation: false, // To make dots safe in cluster context names - syncOptions: { - equals: comparer.structural, - }, - }); - - makeObservable(this); - } - - @computed get clustersList(): Cluster[] { - return Array.from(this.clusters.values()); - } - - @computed get connectedClustersList(): Cluster[] { - return this.clustersList.filter((c) => !c.disconnected); - } - - hasClusters() { - return this.clusters.size > 0; - } - - getById(id: ClusterId | undefined): Cluster | undefined { - if (id) { - return this.clusters.get(id); - } - - return undefined; - } - - addCluster(clusterOrModel: ClusterModel | Cluster): Cluster { - this.dependencies.emitAppEvent({ name: "cluster", action: "add" }); - - const cluster = clusterOrModel instanceof Cluster - ? clusterOrModel - : new Cluster( - clusterOrModel, - this.dependencies.readClusterConfigSync(clusterOrModel), - ); - - this.clusters.set(cluster.id, cluster); - - return cluster; - } - - @action - protected fromStore({ clusters = [] }: ClusterStoreModel = {}) { - const currentClusters = new Map(this.clusters); - const newClusters = new Map(); - - // update new clusters - for (const clusterModel of clusters) { - try { - let cluster = currentClusters.get(clusterModel.id); - - if (cluster) { - cluster.updateModel(clusterModel); - } else { - cluster = new Cluster( - clusterModel, - this.dependencies.readClusterConfigSync(clusterModel), - ); - } - newClusters.set(clusterModel.id, cluster); - } catch (error) { - this.dependencies.logger.warn(`[CLUSTER-STORE]: Failed to update/create a cluster: ${error}`); - } - } - - this.clusters.replace(newClusters); - } - - toJSON(): ClusterStoreModel { - return toJS({ - clusters: this.clustersList.map(cluster => cluster.toJSON()), - }); - } -} diff --git a/packages/core/src/common/cluster-store/read-cluster-config.injectable.ts b/packages/core/src/common/cluster-store/read-cluster-config.injectable.ts deleted file mode 100644 index 9fea013b16..0000000000 --- a/packages/core/src/common/cluster-store/read-cluster-config.injectable.ts +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import type { ClusterConfigData, ClusterModel } from "../cluster-types"; -import readFileSyncInjectable from "../fs/read-file-sync.injectable"; -import { loadConfigFromString, validateKubeConfig } from "../kube-helpers"; - -export type ReadClusterConfigSync = (model: ClusterModel) => ClusterConfigData; - -const readClusterConfigSyncInjectable = getInjectable({ - id: "read-cluster-config-sync", - instantiate: (di): ReadClusterConfigSync => { - const readFileSync = di.inject(readFileSyncInjectable); - - return ({ kubeConfigPath, contextName }) => { - const kubeConfigData = readFileSync(kubeConfigPath); - const { config } = loadConfigFromString(kubeConfigData); - const result = validateKubeConfig(config, contextName); - - if (result.error) { - throw result.error; - } - - return { clusterServerUrl: result.cluster.server }; - }; - }, -}); - -export default readClusterConfigSyncInjectable; diff --git a/packages/core/src/common/cluster-types.ts b/packages/core/src/common/cluster-types.ts index ba01489152..a8ce7da912 100644 --- a/packages/core/src/common/cluster-types.ts +++ b/packages/core/src/common/cluster-types.ts @@ -167,7 +167,7 @@ export enum ClusterMetricsResourceType { StatefulSet = "StatefulSet", Container = "Container", Ingress = "Ingress", - VolumeClaim = "VolumeClaim", + VolumeClaim = "PersistentVolumeClaim", ReplicaSet = "ReplicaSet", DaemonSet = "DaemonSet", Job = "Job", @@ -183,7 +183,6 @@ export const initialNodeShellImage = "docker.io/alpine:3.13"; * The data representing a cluster's state, for passing between main and renderer */ export interface ClusterState { - apiUrl: string; online: boolean; disconnected: boolean; accessible: boolean; diff --git a/packages/core/src/common/cluster/cluster.ts b/packages/core/src/common/cluster/cluster.ts index fc66d9aa29..f703ebc336 100644 --- a/packages/core/src/common/cluster/cluster.ts +++ b/packages/core/src/common/cluster/cluster.ts @@ -5,7 +5,7 @@ import { computed, observable, toJS, runInAction } from "mobx"; import type { KubeApiResource } from "../rbac"; -import type { ClusterState, ClusterId, ClusterMetadata, ClusterModel, ClusterPreferences, ClusterPrometheusPreferences, UpdateClusterModel, ClusterConfigData } from "../cluster-types"; +import type { ClusterState, ClusterId, ClusterMetadata, ClusterModel, ClusterPreferences, ClusterPrometheusPreferences, UpdateClusterModel } from "../cluster-types"; import { ClusterMetadataKey, clusterModelIdChecker, updateClusterModelChecker } from "../cluster-types"; import type { IObservableValue } from "mobx"; import { replaceObservableObject } from "../utils/replace-observable-object"; @@ -27,11 +27,6 @@ export class Cluster { */ readonly kubeConfigPath = observable.box() as IObservableValue; - /** - * Kubernetes API server URL - */ - readonly apiUrl: IObservableValue; - /** * Describes if we can detect that cluster is online */ @@ -122,7 +117,7 @@ export class Cluster { */ readonly prometheusPreferences = computed(() => pick(toJS(this.preferences), "prometheus", "prometheusProvider") as ClusterPrometheusPreferences); - constructor({ id, ...model }: ClusterModel, configData: ClusterConfigData) { + constructor({ id, ...model }: ClusterModel) { const { error } = clusterModelIdChecker.validate({ id }); if (error) { @@ -131,7 +126,6 @@ export class Cluster { this.id = id; this.updateModel(model); - this.apiUrl = observable.box(configData.clusterServerUrl); } /** @@ -187,7 +181,6 @@ export class Cluster { */ getState(): ClusterState { return { - apiUrl: this.apiUrl.get(), online: this.online.get(), ready: this.ready.get(), disconnected: this.disconnected.get(), @@ -207,7 +200,6 @@ export class Cluster { this.accessible.set(state.accessible); this.allowedNamespaces.replace(state.allowedNamespaces); this.resourcesToShow.replace(state.resourcesToShow); - this.apiUrl.set(state.apiUrl); this.disconnected.set(state.disconnected); this.isAdmin.set(state.isAdmin); this.isGlobalWatchEnabled.set(state.isGlobalWatchEnabled); diff --git a/packages/core/src/common/error-reporting/initialize-sentry-reporting.injectable.ts b/packages/core/src/common/error-reporting/initialize-sentry-reporting.injectable.ts index 677c18a586..3a72ef7b12 100644 --- a/packages/core/src/common/error-reporting/initialize-sentry-reporting.injectable.ts +++ b/packages/core/src/common/error-reporting/initialize-sentry-reporting.injectable.ts @@ -9,7 +9,7 @@ import isProductionInjectable from "../vars/is-production.injectable"; import sentryDataSourceNameInjectable from "../vars/sentry-dsn-url.injectable"; import { Dedupe, Offline } from "@sentry/integrations"; import { inspect } from "util"; -import userStoreInjectable from "../user-store/user-store.injectable"; +import userPreferencesStateInjectable from "../../features/user-preferences/common/state.injectable"; export type InitializeSentryReportingWith = (initSentry: (opts: BrowserOptions | ElectronMainOptions) => void) => void; @@ -20,7 +20,7 @@ const initializeSentryReportingWithInjectable = getInjectable({ instantiate: (di): InitializeSentryReportingWith => { const sentryDataSourceName = di.inject(sentryDataSourceNameInjectable); const isProduction = di.inject(isProductionInjectable); - const userStore = di.inject(userStoreInjectable); + const state = di.inject(userPreferencesStateInjectable); if (!sentryDataSourceName) { return () => {}; @@ -28,7 +28,7 @@ const initializeSentryReportingWithInjectable = getInjectable({ return (initSentry) => initSentry({ beforeSend: (event) => { - if (userStore.allowErrorReporting) { + if (state.allowErrorReporting) { return event; } diff --git a/packages/core/src/common/fetch/proxy-fetch.injectable.ts b/packages/core/src/common/fetch/proxy-fetch.injectable.ts index f13842c410..433670e003 100644 --- a/packages/core/src/common/fetch/proxy-fetch.injectable.ts +++ b/packages/core/src/common/fetch/proxy-fetch.injectable.ts @@ -4,7 +4,7 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import { HttpsProxyAgent } from "hpagent"; -import userStoreInjectable from "../user-store/user-store.injectable"; +import userPreferencesStateInjectable from "../../features/user-preferences/common/state.injectable"; import type { Fetch } from "./fetch.injectable"; import fetchInjectable from "./fetch.injectable"; @@ -12,7 +12,7 @@ const proxyFetchInjectable = getInjectable({ id: "proxy-fetch", instantiate: (di): Fetch => { const fetch = di.inject(fetchInjectable); - const { httpsProxy, allowUntrustedCAs } = di.inject(userStoreInjectable); + const { httpsProxy, allowUntrustedCAs } = di.inject(userPreferencesStateInjectable); const agent = httpsProxy ? new HttpsProxyAgent({ proxy: httpsProxy, diff --git a/packages/core/src/common/front-end-routing/routes/cluster/config/config-maps/config-maps-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/config/config-maps/config-maps-route.injectable.ts index 0ad7ed3d88..c6a525269c 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/config/config-maps/config-maps-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/config/config-maps/config-maps-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; const configMapsRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/config/horizontal-pod-autoscalers/horizontal-pod-autoscalers-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/config/horizontal-pod-autoscalers/horizontal-pod-autoscalers-route.injectable.ts index 00002620ee..cef3bdcb5a 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/config/horizontal-pod-autoscalers/horizontal-pod-autoscalers-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/config/horizontal-pod-autoscalers/horizontal-pod-autoscalers-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; const horizontalPodAutoscalersRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/config/leases/leases-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/config/leases/leases-route.injectable.ts index ea4eb2ae59..6da3564c8f 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/config/leases/leases-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/config/leases/leases-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; const leasesRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/config/limit-ranges/limit-ranges-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/config/limit-ranges/limit-ranges-route.injectable.ts index 0536e76004..a80a9e8ef4 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/config/limit-ranges/limit-ranges-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/config/limit-ranges/limit-ranges-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; const limitRangesRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/config/pod-disruption-budgets/pod-disruption-budgets-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/config/pod-disruption-budgets/pod-disruption-budgets-route.injectable.ts index 12ce0a2138..e297c7396e 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/config/pod-disruption-budgets/pod-disruption-budgets-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/config/pod-disruption-budgets/pod-disruption-budgets-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; const podDisruptionBudgetsRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/config/priority-classes/priority-classes-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/config/priority-classes/priority-classes-route.injectable.ts index 75194b0541..ea424ec86d 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/config/priority-classes/priority-classes-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/config/priority-classes/priority-classes-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; const priorityClassesRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/config/resource-quotas/resource-quotas-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/config/resource-quotas/resource-quotas-route.injectable.ts index 4905b70890..96704bbc80 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/config/resource-quotas/resource-quotas-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/config/resource-quotas/resource-quotas-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; const resourceQuotasRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/config/runtime-classes/runtime-classes-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/config/runtime-classes/runtime-classes-route.injectable.ts index beab83754f..a088ee2ba4 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/config/runtime-classes/runtime-classes-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/config/runtime-classes/runtime-classes-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; const runtimeClassesRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/config/secrets/secrets-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/config/secrets/secrets-route.injectable.ts index 7442de14b1..38343e4729 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/config/secrets/secrets-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/config/secrets/secrets-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; const secretsRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/config/vertical-pod-autoscalers/vertical-pod-autoscalers-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/config/vertical-pod-autoscalers/vertical-pod-autoscalers-route.injectable.ts index 57776c8237..07559a8d2a 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/config/vertical-pod-autoscalers/vertical-pod-autoscalers-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/config/vertical-pod-autoscalers/vertical-pod-autoscalers-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; const verticalPodAutoscalersRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/events/events-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/events/events-route.injectable.ts index 728f80c21d..fd4dfef75d 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/events/events-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/events/events-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token"; const eventsRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/namespaces/namespaces-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/namespaces/namespaces-route.injectable.ts index 0b57c8d573..6fb569fc11 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/namespaces/namespaces-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/namespaces/namespaces-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token"; const namespacesRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/network/endpoints/endpoints-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/network/endpoints/endpoints-route.injectable.ts index 50b3c72e4c..d8dcf8aa8f 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/network/endpoints/endpoints-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/network/endpoints/endpoints-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; const endpointsRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/network/ingress-class/ingress-classeses-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/network/ingress-class/ingress-classeses-route.injectable.ts index e036d4b6a6..efde1d8d7a 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/network/ingress-class/ingress-classeses-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/network/ingress-class/ingress-classeses-route.injectable.ts @@ -6,7 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; import { shouldShowResourceInjectionToken, -} from "../../../../../cluster-store/allowed-resources-injection-token"; +} from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; const ingressClassesesRouteInjectable = getInjectable({ id: "ingress-classes-route", diff --git a/packages/core/src/common/front-end-routing/routes/cluster/network/ingresses/ingresses-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/network/ingresses/ingresses-route.injectable.ts index 3a6669d99f..c2a6fe33cc 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/network/ingresses/ingresses-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/network/ingresses/ingresses-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { computedOr } from "@k8slens/utilities"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; diff --git a/packages/core/src/common/front-end-routing/routes/cluster/network/network-policies/network-policies-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/network/network-policies/network-policies-route.injectable.ts index 38a1b8a7e2..664eefa9f1 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/network/network-policies/network-policies-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/network/network-policies/network-policies-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; const networkPoliciesRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/network/services/services-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/network/services/services-route.injectable.ts index 223ded1e65..e9b130b318 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/network/services/services-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/network/services/services-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; const servicesRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/nodes/nodes-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/nodes/nodes-route.injectable.ts index e6ca61346e..1fe80a799b 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/nodes/nodes-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/nodes/nodes-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token"; const nodesRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/overview/cluster-overview-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/overview/cluster-overview-route.injectable.ts index e559c35079..bf9c3961ed 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/overview/cluster-overview-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/overview/cluster-overview-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../front-end-route-injection-token"; const clusterOverviewRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/storage/persistent-volume-claims/persistent-volume-claims-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/storage/persistent-volume-claims/persistent-volume-claims-route.injectable.ts index dbda527555..c8d7b64f8d 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/storage/persistent-volume-claims/persistent-volume-claims-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/storage/persistent-volume-claims/persistent-volume-claims-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; const persistentVolumeClaimsRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/storage/persistent-volumes/persistent-volumes-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/storage/persistent-volumes/persistent-volumes-route.injectable.ts index 7a06d9df58..a93ac0a3c8 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/storage/persistent-volumes/persistent-volumes-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/storage/persistent-volumes/persistent-volumes-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; const persistentVolumesRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/storage/storage-classes/storage-classes-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/storage/storage-classes/storage-classes-route.injectable.ts index 8702ab1602..0645c0f4eb 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/storage/storage-classes/storage-classes-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/storage/storage-classes/storage-classes-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; const storageClassesRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/user-management/cluster-role-bindings/cluster-role-bindings-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/user-management/cluster-role-bindings/cluster-role-bindings-route.injectable.ts index 0903d5fced..158563f8d5 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/user-management/cluster-role-bindings/cluster-role-bindings-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/user-management/cluster-role-bindings/cluster-role-bindings-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; const clusterRoleBindingsRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/user-management/cluster-roles/cluster-roles-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/user-management/cluster-roles/cluster-roles-route.injectable.ts index 9fce206667..db28d8dfff 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/user-management/cluster-roles/cluster-roles-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/user-management/cluster-roles/cluster-roles-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; const clusterRolesRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/user-management/pod-security-policies/pod-security-policies-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/user-management/pod-security-policies/pod-security-policies-route.injectable.ts index 2f35986916..27f9165be8 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/user-management/pod-security-policies/pod-security-policies-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/user-management/pod-security-policies/pod-security-policies-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; const podSecurityPoliciesRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/user-management/role-bindings/role-bindings-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/user-management/role-bindings/role-bindings-route.injectable.ts index 759c1b8eda..195210df23 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/user-management/role-bindings/role-bindings-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/user-management/role-bindings/role-bindings-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; const roleBindingsRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/user-management/roles/roles-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/user-management/roles/roles-route.injectable.ts index efe4cad810..807a12177c 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/user-management/roles/roles-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/user-management/roles/roles-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; const rolesRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/user-management/service-accounts/service-accounts-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/user-management/service-accounts/service-accounts-route.injectable.ts index 65d02135c7..aecc5a3640 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/user-management/service-accounts/service-accounts-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/user-management/service-accounts/service-accounts-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; const serviceAccountsRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/workloads/cron-jobs/cron-jobs-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/workloads/cron-jobs/cron-jobs-route.injectable.ts index 33453a2247..0899486298 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/workloads/cron-jobs/cron-jobs-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/workloads/cron-jobs/cron-jobs-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; const cronJobsRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/workloads/daemonsets/daemonsets-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/workloads/daemonsets/daemonsets-route.injectable.ts index f1ec2008fa..42f1329551 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/workloads/daemonsets/daemonsets-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/workloads/daemonsets/daemonsets-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; const daemonsetsRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/workloads/deployments/deployments-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/workloads/deployments/deployments-route.injectable.ts index 84c059780f..222f842981 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/workloads/deployments/deployments-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/workloads/deployments/deployments-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; const deploymentsRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/workloads/jobs/jobs-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/workloads/jobs/jobs-route.injectable.ts index 39cc89e88f..4933c69531 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/workloads/jobs/jobs-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/workloads/jobs/jobs-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; const jobsRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/workloads/pods/pods-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/workloads/pods/pods-route.injectable.ts index d013f872f0..6563896893 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/workloads/pods/pods-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/workloads/pods/pods-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; const podsRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/workloads/replicasets/replicasets-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/workloads/replicasets/replicasets-route.injectable.ts index b790ce13ec..bff60b2b8d 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/workloads/replicasets/replicasets-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/workloads/replicasets/replicasets-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; const replicasetsRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/workloads/replicationcontrollers/replicationcontrollers-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/workloads/replicationcontrollers/replicationcontrollers-route.injectable.ts index 77d87abc96..c6ce4afafe 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/workloads/replicationcontrollers/replicationcontrollers-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/workloads/replicationcontrollers/replicationcontrollers-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; const replicationControllersRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/routes/cluster/workloads/statefulsets/statefulsets-route.injectable.ts b/packages/core/src/common/front-end-routing/routes/cluster/workloads/statefulsets/statefulsets-route.injectable.ts index 72c81b3bee..0a0160ccd9 100644 --- a/packages/core/src/common/front-end-routing/routes/cluster/workloads/statefulsets/statefulsets-route.injectable.ts +++ b/packages/core/src/common/front-end-routing/routes/cluster/workloads/statefulsets/statefulsets-route.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldShowResourceInjectionToken } from "../../../../../cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { frontEndRouteInjectionToken } from "../../../../front-end-route-injection-token"; const statefulsetsRouteInjectable = getInjectable({ diff --git a/packages/core/src/common/front-end-routing/verify-that-all-routes-have-route-component.test.ts b/packages/core/src/common/front-end-routing/verify-that-all-routes-have-route-component.test.ts index 44f86f270d..a13a3f8929 100644 --- a/packages/core/src/common/front-end-routing/verify-that-all-routes-have-route-component.test.ts +++ b/packages/core/src/common/front-end-routing/verify-that-all-routes-have-route-component.test.ts @@ -6,22 +6,14 @@ import { getDiForUnitTesting } from "../../renderer/getDiForUnitTesting"; import { routeSpecificComponentInjectionToken } from "../../renderer/routes/route-specific-component-injection-token"; import { frontEndRouteInjectionToken } from "./front-end-route-injection-token"; import { filter, map } from "lodash/fp"; -import clusterStoreInjectable from "../cluster-store/cluster-store.injectable"; -import type { ClusterStore } from "../cluster-store/cluster-store"; import { pipeline } from "@ogre-tools/fp"; describe("verify-that-all-routes-have-component", () => { it("verify that routes have route component", () => { const rendererDi = getDiForUnitTesting(); - rendererDi.override(clusterStoreInjectable, () => ({ - getById: () => null, - } as unknown as ClusterStore)); - const routes = rendererDi.injectMany(frontEndRouteInjectionToken); - const routeComponents = rendererDi.injectMany( - routeSpecificComponentInjectionToken, - ); + const routeComponents = rendererDi.injectMany(routeSpecificComponentInjectionToken); const routesMissingComponent = pipeline( routes, diff --git a/packages/core/src/common/fs/fs.injectable.ts b/packages/core/src/common/fs/fs.injectable.ts index b42a51aad7..16fbbd73aa 100644 --- a/packages/core/src/common/fs/fs.injectable.ts +++ b/packages/core/src/common/fs/fs.injectable.ts @@ -22,6 +22,7 @@ const fsInjectable = getInjectable({ access, stat, unlink, + rename, }, ensureDir, ensureDirSync, @@ -58,6 +59,7 @@ const fsInjectable = getInjectable({ createReadStream, stat, unlink, + rename, }; }, causesSideEffects: true, diff --git a/packages/core/src/common/get-configuration-file-model/get-configuration-file-model.global-override-for-injectable.ts b/packages/core/src/common/get-configuration-file-model/get-configuration-file-model.global-override-for-injectable.ts index 95e2845825..5b83b7b573 100644 --- a/packages/core/src/common/get-configuration-file-model/get-configuration-file-model.global-override-for-injectable.ts +++ b/packages/core/src/common/get-configuration-file-model/get-configuration-file-model.global-override-for-injectable.ts @@ -10,7 +10,7 @@ import getConfigurationFileModelInjectable from "./get-configuration-file-model. import type Config from "conf"; import readJsonSyncInjectable from "../fs/read-json-sync.injectable"; import writeJsonSyncInjectable from "../fs/write-json-sync.injectable"; -import { get, set } from "lodash"; +import { get, has, set } from "lodash"; import semver from "semver"; const MIGRATION_KEY = `__internal__.migrations.version`; @@ -64,7 +64,7 @@ export default getGlobalOverride(getConfigurationFileModelInjectable, (di) => { path: configFilePath, get: (key: string) => get(store, key), set: (key: string, value: unknown) => { - let currentState: object; + let currentState: Partial>; try { currentState = readJsonSync(configFilePath); @@ -78,6 +78,25 @@ export default getGlobalOverride(getConfigurationFileModelInjectable, (di) => { }); store = readJsonSync(configFilePath); }, + delete: (key: string) => { + let currentState: Partial>; + + try { + currentState = readJsonSync(configFilePath); + } catch { + currentState = {}; + } + + delete currentState[key]; + + writeJsonSync(configFilePath, currentState); + store = readJsonSync(configFilePath); + }, + has: (key: string) => has(store, key), + clear: () => { + writeJsonSync(configFilePath, {}); + store = readJsonSync(configFilePath); + }, } as Partial as Config; // Migrate diff --git a/packages/core/src/common/hotbars/add-hotbar.injectable.ts b/packages/core/src/common/hotbars/add-hotbar.injectable.ts deleted file mode 100644 index 25ee0f588a..0000000000 --- a/packages/core/src/common/hotbars/add-hotbar.injectable.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import hotbarStoreInjectable from "./store.injectable"; -import type { CreateHotbarData, CreateHotbarOptions } from "./types"; - -export type AddHotbar = (data: CreateHotbarData, opts?: CreateHotbarOptions) => void; - -const addHotbarInjectable = getInjectable({ - id: "add-hotbar", - instantiate: (di): AddHotbar => { - const store = di.inject(hotbarStoreInjectable); - - return (data, opts) => store.add(data, opts); - }, -}); - -export default addHotbarInjectable; diff --git a/packages/core/src/common/hotbars/store.injectable.ts b/packages/core/src/common/hotbars/store.injectable.ts deleted file mode 100644 index ea25840e11..0000000000 --- a/packages/core/src/common/hotbars/store.injectable.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import catalogCatalogEntityInjectable from "../catalog-entities/general-catalog-entities/implementations/catalog-catalog-entity.injectable"; -import { HotbarStore } from "./store"; -import loggerInjectable from "../logger.injectable"; -import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable"; -import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable"; -import storeMigrationsInjectable from "../base-store/migrations.injectable"; -import { hotbarStoreMigrationInjectionToken } from "./migrations-token"; -import getBasenameOfPathInjectable from "../path/get-basename.injectable"; -import { baseStoreIpcChannelPrefixesInjectionToken } from "../base-store/channel-prefix"; -import { persistStateToConfigInjectionToken } from "../base-store/save-to-file"; -import { enlistMessageChannelListenerInjectionToken } from "@k8slens/messaging"; -import { shouldBaseStoreDisableSyncInIpcListenerInjectionToken } from "../base-store/disable-sync"; - -const hotbarStoreInjectable = getInjectable({ - id: "hotbar-store", - - instantiate: (di) => new HotbarStore({ - catalogCatalogEntity: di.inject(catalogCatalogEntityInjectable), - logger: di.inject(loggerInjectable), - directoryForUserData: di.inject(directoryForUserDataInjectable), - getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable), - storeMigrationVersion: di.inject(storeMigrationVersionInjectable), - migrations: di.inject(storeMigrationsInjectable, hotbarStoreMigrationInjectionToken), - getBasenameOfPath: di.inject(getBasenameOfPathInjectable), - ipcChannelPrefixes: di.inject(baseStoreIpcChannelPrefixesInjectionToken), - persistStateToConfig: di.inject(persistStateToConfigInjectionToken), - enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken), - shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken), - }), -}); - -export default hotbarStoreInjectable; diff --git a/packages/core/src/common/hotbars/store.ts b/packages/core/src/common/hotbars/store.ts deleted file mode 100644 index 709242f20e..0000000000 --- a/packages/core/src/common/hotbars/store.ts +++ /dev/null @@ -1,351 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { action, comparer, observable, makeObservable, computed } from "mobx"; -import type { BaseStoreDependencies } from "../base-store/base-store"; -import { BaseStore } from "../base-store/base-store"; -import { toJS } from "../utils"; -import type { CatalogEntity } from "../catalog"; -import { broadcastMessage } from "../ipc"; -import type { Hotbar, CreateHotbarData, CreateHotbarOptions } from "./types"; -import { defaultHotbarCells, getEmptyHotbar } from "./types"; -import { hotbarTooManyItemsChannel } from "../ipc/hotbar"; -import type { GeneralEntity } from "../catalog-entities"; -import type { Logger } from "../logger"; -import assert from "assert"; - -export interface HotbarStoreModel { - hotbars: Hotbar[]; - activeHotbarId: string; -} - -interface Dependencies extends BaseStoreDependencies { - readonly catalogCatalogEntity: GeneralEntity; - readonly logger: Logger; -} - -export class HotbarStore extends BaseStore { - @observable hotbars: Hotbar[] = []; - @observable private _activeHotbarId!: string; - - constructor(protected readonly dependencies: Dependencies) { - super(dependencies, { - configName: "lens-hotbar-store", - accessPropertiesByDotNotation: false, // To make dots safe in cluster context names - syncOptions: { - equals: comparer.structural, - }, - }); - makeObservable(this); - } - - @computed get activeHotbarId() { - return this._activeHotbarId; - } - - /** - * If `hotbar` points to a known hotbar, make it active. Otherwise, ignore - * @param hotbar The hotbar instance, or the index, or its ID - */ - setActiveHotbar(hotbar: Hotbar | number | string) { - if (typeof hotbar === "number") { - if (hotbar >= 0 && hotbar < this.hotbars.length) { - this._activeHotbarId = this.hotbars[hotbar].id; - } - } else if (typeof hotbar === "string") { - if (this.findById(hotbar)) { - this._activeHotbarId = hotbar; - } - } else { - if (this.hotbars.indexOf(hotbar) >= 0) { - this._activeHotbarId = hotbar.id; - } - } - } - - private hotbarIndexById(id: string) { - return this.hotbars.findIndex((hotbar) => hotbar.id === id); - } - - private hotbarIndex(hotbar: Hotbar) { - return this.hotbars.indexOf(hotbar); - } - - @computed get activeHotbarIndex() { - return this.hotbarIndexById(this.activeHotbarId); - } - - @action - protected fromStore(data: Partial = {}) { - if (!data.hotbars || !data.hotbars.length) { - const hotbar = getEmptyHotbar("Default"); - const { - metadata: { uid, name, source }, - } = this.dependencies.catalogCatalogEntity; - const initialItem = { entity: { uid, name, source }}; - - hotbar.items[0] = initialItem; - - this.hotbars = [hotbar]; - } else { - this.hotbars = data.hotbars; - } - - this.hotbars.forEach(ensureExactHotbarItemLength); - - if (data.activeHotbarId) { - this._activeHotbarId = data.activeHotbarId; - } - - if (!this._activeHotbarId) { - this._activeHotbarId = this.hotbars[0].id; - } - } - - toJSON(): HotbarStoreModel { - return toJS({ - hotbars: this.hotbars, - activeHotbarId: this.activeHotbarId, - }); - } - - getActive(): Hotbar { - const hotbar = this.findById(this.activeHotbarId); - - assert(hotbar, "There MUST always be an active hotbar"); - - return hotbar; - } - - findByName(name: string) { - return this.hotbars.find((hotbar) => hotbar.name === name); - } - - findById(id: string) { - return this.hotbars.find((hotbar) => hotbar.id === id); - } - - @action - add(data: CreateHotbarData, { setActive = false }: CreateHotbarOptions = {}) { - const hotbar = getEmptyHotbar(data.name, data.id); - - this.hotbars.push(hotbar); - - if (setActive) { - this._activeHotbarId = hotbar.id; - } - } - - @action - setHotbarName(id: string, name: string): void { - const index = this.hotbars.findIndex((hotbar) => hotbar.id === id); - - if (index < 0) { - return this.dependencies.logger.warn( - `[HOTBAR-STORE]: cannot setHotbarName: unknown id`, - { id }, - ); - } - - this.hotbars[index].name = name; - } - - @action - remove(hotbar: Hotbar) { - assert(this.hotbars.length >= 2, "Cannot remove the last hotbar"); - - this.hotbars = this.hotbars.filter((h) => h !== hotbar); - - if (this.activeHotbarId === hotbar.id) { - this.setActiveHotbar(0); - } - } - - @action - addToHotbar(item: CatalogEntity, cellIndex?: number) { - const hotbar = this.getActive(); - const uid = item.getId(); - const name = item.getName(); - - if (typeof uid !== "string") { - throw new TypeError("CatalogEntity's ID must be a string"); - } - - if (typeof name !== "string") { - throw new TypeError("CatalogEntity's NAME must be a string"); - } - - if (this.isAddedToActive(item)) { - return; - } - - const entity = { - uid, - name, - source: item.metadata.source, - }; - const newItem = { entity }; - - if (cellIndex === undefined) { - // Add item to empty cell - const emptyCellIndex = hotbar.items.indexOf(null); - - if (emptyCellIndex != -1) { - hotbar.items[emptyCellIndex] = newItem; - } else { - broadcastMessage(hotbarTooManyItemsChannel); - } - } else if (0 <= cellIndex && cellIndex < hotbar.items.length) { - hotbar.items[cellIndex] = newItem; - } else { - this.dependencies.logger.error( - `[HOTBAR-STORE]: cannot pin entity to hotbar outside of index range`, - { entityId: uid, hotbarId: hotbar.id, cellIndex }, - ); - } - } - - @action - removeFromHotbar(uid: string): void { - const hotbar = this.getActive(); - const index = hotbar.items.findIndex((item) => item?.entity.uid === uid); - - if (index < 0) { - return; - } - - hotbar.items[index] = null; - } - - /** - * Remove all hotbar items that reference the `uid`. - * @param uid The `EntityId` that each hotbar item refers to - * @returns A function that will (in an action) undo the removing of the hotbar items. This function will not complete if the hotbar has changed. - */ - @action - removeAllHotbarItems(uid: string) { - for (const hotbar of this.hotbars) { - const index = hotbar.items.findIndex((i) => i?.entity.uid === uid); - - if (index >= 0) { - hotbar.items[index] = null; - } - } - } - - findClosestEmptyIndex(from: number, direction = 1) { - let index = from; - const hotbar = this.getActive(); - - while (hotbar.items[index] != null) { - index += direction; - } - - return index; - } - - @action - restackItems(from: number, to: number): void { - const { items } = this.getActive(); - const source = items[from]; - const moveDown = from < to; - - if ( - from < 0 || - to < 0 || - from >= items.length || - to >= items.length || - isNaN(from) || - isNaN(to) - ) { - throw new Error("Invalid 'from' or 'to' arguments"); - } - - if (from == to) { - return; - } - - items.splice(from, 1, null); - - if (items[to] == null) { - items.splice(to, 1, source); - } else { - // Move cells up or down to closes empty cell - items.splice(this.findClosestEmptyIndex(to, moveDown ? -1 : 1), 1); - items.splice(to, 0, source); - } - } - - switchToPrevious() { - let index = this.activeHotbarIndex - 1; - - if (index < 0) { - index = this.hotbars.length - 1; - } - - this.setActiveHotbar(index); - } - - switchToNext() { - let index = this.activeHotbarIndex + 1; - - if (index >= this.hotbars.length) { - index = 0; - } - - this.setActiveHotbar(index); - } - - /** - * Checks if entity already pinned to the active hotbar - */ - isAddedToActive(entity: CatalogEntity | null | undefined): boolean { - if (!entity) { - return false; - } - - const indexInActiveHotbar = this.getActive().items.findIndex(item => item?.entity.uid === entity.getId()); - - return indexInActiveHotbar >= 0; - } - - getDisplayLabel(hotbar: Hotbar): string { - return `${this.getDisplayIndex(hotbar)}: ${hotbar.name}`; - } - - getDisplayIndex(hotbar: Hotbar): string { - const index = this.hotbarIndex(hotbar); - - if (index < 0) { - return "??"; - } - - return `${index + 1}`; - } -} - -/** - * This function ensures that there are always exactly `defaultHotbarCells` - * worth of items in the hotbar. - * @param hotbar The hotbar to modify - */ -function ensureExactHotbarItemLength(hotbar: Hotbar) { - // if there are not enough items - while (hotbar.items.length < defaultHotbarCells) { - hotbar.items.push(null); - } - - // if for some reason the hotbar was overfilled before, remove as many entries - // as needed, but prefer empty slots and items at the end first. - while (hotbar.items.length > defaultHotbarCells) { - const lastNull = hotbar.items.lastIndexOf(null); - - if (lastNull >= 0) { - hotbar.items.splice(lastNull, 1); - } else { - hotbar.items.length = defaultHotbarCells; - } - } -} diff --git a/packages/core/src/common/k8s-api/__tests__/api-manager.test.ts b/packages/core/src/common/k8s-api/__tests__/api-manager.test.ts index 48fdb3e544..ce117d9475 100644 --- a/packages/core/src/common/k8s-api/__tests__/api-manager.test.ts +++ b/packages/core/src/common/k8s-api/__tests__/api-manager.test.ts @@ -51,8 +51,6 @@ describe("ApiManager", () => { contextName: "some-context-name", id: "some-cluster-id", kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", })); apiManager = di.inject(apiManagerInjectable); diff --git a/packages/core/src/common/k8s-api/__tests__/kube-api-version-detection.test.ts b/packages/core/src/common/k8s-api/__tests__/kube-api-version-detection.test.ts index e3bcd70a81..e6efa606b9 100644 --- a/packages/core/src/common/k8s-api/__tests__/kube-api-version-detection.test.ts +++ b/packages/core/src/common/k8s-api/__tests__/kube-api-version-detection.test.ts @@ -43,8 +43,6 @@ describe("KubeApi", () => { contextName: "some-context-name", id: "some-cluster-id", kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", })); apiManager = di.inject(apiManagerInjectable); diff --git a/packages/core/src/common/k8s-api/__tests__/kube-api.test.ts b/packages/core/src/common/k8s-api/__tests__/kube-api.test.ts index e7240895cf..a5cce9f02c 100644 --- a/packages/core/src/common/k8s-api/__tests__/kube-api.test.ts +++ b/packages/core/src/common/k8s-api/__tests__/kube-api.test.ts @@ -62,8 +62,6 @@ describe("createKubeApiForRemoteCluster", () => { contextName: "some-context-name", id: "some-cluster-id", kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", })); fetchMock = asyncFn(); @@ -168,8 +166,6 @@ describe("KubeApi", () => { contextName: "some-context-name", id: "some-cluster-id", kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", })); di.override(apiKubeInjectable, () => createKubeJsonApi({ diff --git a/packages/core/src/common/kube-helpers.ts b/packages/core/src/common/kube-helpers.ts index 6deb9ab8a1..0e42425f1c 100644 --- a/packages/core/src/common/kube-helpers.ts +++ b/packages/core/src/common/kube-helpers.ts @@ -130,6 +130,16 @@ export function loadConfigFromString(content: string): ConfigResult { }; } +export function loadValidatedConfig(content: string, contextName: string): ValidateKubeConfigResult { + const { options, error } = loadToOptions(content); + + if (error) { + return { error }; + } + + return validateKubeConfig(loadFromOptions(options), contextName); +} + export interface SplitConfigEntry { config: KubeConfig; validationResult: ValidateKubeConfigResult; diff --git a/packages/core/src/common/kube-helpers/load-validated-config-from-file.injectable.ts b/packages/core/src/common/kube-helpers/load-validated-config-from-file.injectable.ts new file mode 100644 index 0000000000..ee0b9ed9b0 --- /dev/null +++ b/packages/core/src/common/kube-helpers/load-validated-config-from-file.injectable.ts @@ -0,0 +1,28 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import type { Cluster } from "../cluster/cluster"; +import readFileInjectable from "../fs/read-file.injectable"; +import type { ValidateKubeConfigResult } from "../kube-helpers"; +import { loadValidatedConfig } from "../kube-helpers"; +import resolveTildeInjectable from "../path/resolve-tilde.injectable"; + +export type LoadValidatedClusterConfig = (cluster: Cluster) => Promise; + +const loadValidatedClusterConfigInjectable = getInjectable({ + id: "load-validated-cluster-config", + instantiate: (di): LoadValidatedClusterConfig => { + const readFile = di.inject(readFileInjectable); + const resolveTilde = di.inject(resolveTildeInjectable); + + return async (cluster) => { + const data = await readFile(resolveTilde(cluster.kubeConfigPath.get())); + + return loadValidatedConfig(data, cluster.contextName.get()); + }; + }, +}); + +export default loadValidatedClusterConfigInjectable; diff --git a/packages/core/src/common/os/home-directory-path.injectable.ts b/packages/core/src/common/os/home-directory-path.injectable.ts index 83b4b0cdff..c8831eebab 100644 --- a/packages/core/src/common/os/home-directory-path.injectable.ts +++ b/packages/core/src/common/os/home-directory-path.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import userInfoInjectable from "../user-store/user-info.injectable"; +import userInfoInjectable from "../vars/user-info.injectable"; const homeDirectoryPathInjectable = getInjectable({ id: "home-directory-path", diff --git a/packages/core/src/common/persistent-storage/channel-prefix.ts b/packages/core/src/common/persistent-storage/channel-prefix.ts new file mode 100644 index 0000000000..aeb7da7a72 --- /dev/null +++ b/packages/core/src/common/persistent-storage/channel-prefix.ts @@ -0,0 +1,15 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getInjectionToken } from "@ogre-tools/injectable"; + +export interface IpcChannelPrefixes { + readonly local: string; + readonly remote: string; +} + +export const persistentStorageIpcChannelPrefixesInjectionToken = getInjectionToken({ + id: "persistent-storage-ipc-channel-prefix-token", +}); diff --git a/packages/core/src/common/persistent-storage/create.injectable.ts b/packages/core/src/common/persistent-storage/create.injectable.ts new file mode 100644 index 0000000000..da54076a5a --- /dev/null +++ b/packages/core/src/common/persistent-storage/create.injectable.ts @@ -0,0 +1,158 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { disposer, isPromiseLike } from "@k8slens/utilities"; +import { getInjectable } from "@ogre-tools/injectable"; +import type { Options } from "conf/dist/source"; +import { isEqual, kebabCase } from "lodash"; +import type { IEqualsComparer } from "mobx"; +import { reaction } from "mobx"; +import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; +import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable"; +import loggerInjectable from "../logger.injectable"; +import getBasenameOfPathInjectable from "../path/get-basename.injectable"; +import { enlistMessageChannelListenerInjectionToken, sendMessageToChannelInjectionToken } from "@k8slens/messaging"; +import type { MessageChannel } from "@k8slens/messaging"; +import { persistentStorageIpcChannelPrefixesInjectionToken } from "./channel-prefix"; +import { shouldPersistentStorageDisableSyncInIpcListenerInjectionToken } from "./disable-sync"; +import { persistStateToConfigInjectionToken } from "./save-to-file"; +import type { Migrations } from "./migrations.injectable"; +import { nextTick } from "process"; + +export interface PersistentStorage { + /** + * This method does the initial synchronous load from disk and then starts writing the state + * back to disk whenever it changes. + */ + loadAndStartSyncing: () => void; +} + +export interface PersistentStorageParams extends Omit, "migrations"> { + readonly syncOptions?: { + readonly fireImmediately?: boolean; + equals?: IEqualsComparer; + }; + readonly configName: string; + readonly migrations?: Migrations; + + /** + * fromStore is called internally when a child class syncs with the file + * system. + * + * Note: This function **must** be synchronous. + * + * @param data the parsed information read from the stored JSON file + */ + fromStore(data: Partial): void; + + /** + * toJSON is called when syncing the store to the filesystem. It should + * produce a JSON serializable object representation of the current state. + * + * It is recommended that a round trip is valid. Namely, calling + * `this.fromStore(this.toJSON())` shouldn't change the state. + */ + toJSON(): T; +} + +export type CreatePersistentStorage = (params: PersistentStorageParams) => PersistentStorage; + +const createPersistentStorageInjectable = getInjectable({ + id: "create-persistent-storage", + instantiate: (di): CreatePersistentStorage => { + const directoryForUserData = di.inject(directoryForUserDataInjectable); + const getConfigurationFileModel = di.inject(getConfigurationFileModelInjectable); + const logger = di.inject(loggerInjectable); + const getBasenameOfPath = di.inject(getBasenameOfPathInjectable); + const ipcChannelPrefixes = di.inject(persistentStorageIpcChannelPrefixesInjectionToken); + const persistStateToConfig = di.inject(persistStateToConfigInjectionToken); + const enlistMessageChannelListener = di.inject(enlistMessageChannelListenerInjectionToken); + const shouldDisableSyncInListener = di.inject(shouldPersistentStorageDisableSyncInIpcListenerInjectionToken); + const sendMessageToChannel = di.inject(sendMessageToChannelInjectionToken); + + return (rawParams: PersistentStorageParams) => { + const { + fromStore, + toJSON, + syncOptions, + migrations, + cwd = directoryForUserData, + ...params + } = rawParams; + const displayName = kebabCase(params.configName).toUpperCase(); + + const loadAndStartSyncing = () => { + logger.info(`[${displayName}]: LOADING ...`); + + const config = getConfigurationFileModel({ + projectName: "lens", + cwd, + migrations: migrations as Options["migrations"], + ...params, + }); + + const res = fromStore(config.store); + + if (isPromiseLike(res)) { + logger.error(`${displayName} extends BaseStore's fromStore method returns a Promise or promise-like object. This is an error and must be fixed.`); + } + + logger.info(`[${displayName}]: LOADED from ${config.path}`); + + const syncDisposers = disposer(); + const sendChannel: MessageChannel = { + id: `${ipcChannelPrefixes.remote}:${config.path}`, + }; + const receiveChannel: MessageChannel = { + id: `${ipcChannelPrefixes.local}:${config.path}`, + }; + const name = getBasenameOfPath(config.path); + + const disableSync = () => syncDisposers(); + const enableSync = () => { + syncDisposers.push( + reaction( + () => toJSON(), + model => { + persistStateToConfig(config, model); + sendMessageToChannel(sendChannel, model); + }, + syncOptions, + ), + enlistMessageChannelListener({ + id: "persistent-storage-sync", + channel: receiveChannel, + handler: (model) => { + logger.silly(`[${displayName}]: syncing ${name}`, { model }); + + if (shouldDisableSyncInListener) { + disableSync(); + } + + // todo: use "resourceVersion" if merge required (to avoid equality checks => better performance) + if (!isEqual(toJSON(), model)) { + fromStore(model); + } + + if (shouldDisableSyncInListener) { + nextTick(() => { + enableSync(); + }); + } + }, + }), + ); + }; + + enableSync(); + }; + + return { + loadAndStartSyncing, + }; + }; + }, +}); + +export default createPersistentStorageInjectable; diff --git a/packages/core/src/common/base-store/disable-sync.ts b/packages/core/src/common/persistent-storage/disable-sync.ts similarity index 54% rename from packages/core/src/common/base-store/disable-sync.ts rename to packages/core/src/common/persistent-storage/disable-sync.ts index ce7abd16a1..db24561936 100644 --- a/packages/core/src/common/base-store/disable-sync.ts +++ b/packages/core/src/common/persistent-storage/disable-sync.ts @@ -5,6 +5,6 @@ import { getInjectionToken } from "@ogre-tools/injectable"; -export const shouldBaseStoreDisableSyncInIpcListenerInjectionToken = getInjectionToken({ - id: "should-base-store-disable-sync-in-ipc-listener-token", +export const shouldPersistentStorageDisableSyncInIpcListenerInjectionToken = getInjectionToken({ + id: "should-persistent-storage-disable-sync-in-ipc-listener-token", }); diff --git a/packages/core/src/common/persistent-storage/migrations.injectable.ts b/packages/core/src/common/persistent-storage/migrations.injectable.ts new file mode 100644 index 0000000000..e4bd43d1de --- /dev/null +++ b/packages/core/src/common/persistent-storage/migrations.injectable.ts @@ -0,0 +1,58 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import type { InjectionToken } from "@ogre-tools/injectable"; +import { lifecycleEnum, getInjectable } from "@ogre-tools/injectable"; +import loggerInjectable from "../logger.injectable"; +import { getOrInsert, iter, object } from "@k8slens/utilities"; + +export type AllowedSetValue = T extends (...args: any[]) => any + ? never + : T extends undefined | symbol + ? never + : T; + +export interface MigrationStore { + readonly path: string; + get(key: string): unknown; + delete(key: string): void; + has(key: string): boolean; + clear(): void; + set(key: string, value: AllowedSetValue): void; +} + +export type Migrations = Partial void>>; + +export interface MigrationDeclaration { + version: string; + run(store: MigrationStore): void; +} + +const persistentStorageMigrationsInjectable = getInjectable({ + id: "persistent-storage-migrations", + instantiate: (di, token) => { + const logger = di.inject(loggerInjectable); + const declarations = di.injectMany(token); + const migrations = new Map(); + + for (const decl of declarations) { + getOrInsert(migrations, decl.version, []).push(decl.run); + } + + return iter.chain(migrations.entries()) + .map(([v, fns]) => [v, (store: MigrationStore) => { + logger.info(`Running ${v} migration for ${store.path}`); + + for (const fn of fns) { + fn(store); + } + }] as const) + .collect(object.fromEntries); + }, + lifecycle: lifecycleEnum.keyedSingleton({ + getInstanceKey: (di, token: InjectionToken) => token.id, + }), +}); + +export default persistentStorageMigrationsInjectable; diff --git a/packages/core/src/common/base-store/save-to-file.ts b/packages/core/src/common/persistent-storage/save-to-file.ts similarity index 100% rename from packages/core/src/common/base-store/save-to-file.ts rename to packages/core/src/common/persistent-storage/save-to-file.ts diff --git a/packages/core/src/common/persistent-storage/storage-migration-version.injectable.ts b/packages/core/src/common/persistent-storage/storage-migration-version.injectable.ts new file mode 100644 index 0000000000..6568e8f983 --- /dev/null +++ b/packages/core/src/common/persistent-storage/storage-migration-version.injectable.ts @@ -0,0 +1,32 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import type { InjectionToken } from "@ogre-tools/injectable"; +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import * as semver from "semver"; +import type { MigrationDeclaration } from "./migrations.injectable"; + +/** + * NOTE: not all stores can use this computed version, namely if any migration uses a range for + * the version selector. + */ +const storageMigrationVersionInjectable = getInjectable({ + id: "storage-migration-version", + instantiate: (di, token) => { + const declarations = di.injectMany(token); + + return declarations.reduce((version, decl) => { + if (semver.gte(decl.version, version)) { + return decl.version; + } + + return version; + }, "1.0.0"); + }, + lifecycle: lifecycleEnum.keyedSingleton({ + getInstanceKey: (di, token: InjectionToken) => token.id, + }), +}); + +export default storageMigrationVersionInjectable; diff --git a/packages/core/src/common/protocol-handler/router.ts b/packages/core/src/common/protocol-handler/router.ts index 0018c385c2..7a48dbb343 100644 --- a/packages/core/src/common/protocol-handler/router.ts +++ b/packages/core/src/common/protocol-handler/router.ts @@ -10,13 +10,13 @@ import { isDefined, iter } from "@k8slens/utilities"; import { pathToRegexp } from "path-to-regexp"; import type Url from "url-parse"; import { RoutingError, RoutingErrorType } from "./error"; -import type { ExtensionsStore } from "../../extensions/extensions-store/extensions-store"; import type { ExtensionLoader } from "../../extensions/extension-loader"; import type { LensExtension } from "../../extensions/lens-extension"; import type { RouteHandler, RouteParams } from "./registration"; import { when } from "mobx"; import { ipcRenderer } from "electron"; import type { Logger } from "../logger"; +import type { IsExtensionEnabled } from "../../features/extensions/enabled/common/is-enabled.injectable"; // IPC channel for protocol actions. Main broadcasts the open-url events to this channel. export const ProtocolHandlerIpcPrefix = "protocol-handler"; @@ -65,8 +65,8 @@ export function foldAttemptResults(mainAttempt: RouteAttempt, rendererAttempt: R export interface LensProtocolRouterDependencies { readonly extensionLoader: ExtensionLoader; - readonly extensionsStore: ExtensionsStore; readonly logger: Logger; + isExtensionEnabled: IsExtensionEnabled; } export abstract class LensProtocolRouter { @@ -209,7 +209,7 @@ export abstract class LensProtocolRouter { return name; } - if (!this.dependencies.extensionsStore.isEnabled(extension)) { + if (!this.dependencies.isExtensionEnabled(extension)) { this.dependencies.logger.info(`${LensProtocolRouter.LoggingPrefix}: Extension ${name} matched, but not enabled`); return name; diff --git a/packages/core/src/common/user-store/index.ts b/packages/core/src/common/user-store/index.ts deleted file mode 100644 index 026167519b..0000000000 --- a/packages/core/src/common/user-store/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export * from "./user-store"; -export type { KubeconfigSyncEntry, KubeconfigSyncValue, UserPreferencesModel } from "./preferences-helpers"; diff --git a/packages/core/src/common/user-store/migrations-token.ts b/packages/core/src/common/user-store/migrations-token.ts deleted file mode 100644 index f3959beb3a..0000000000 --- a/packages/core/src/common/user-store/migrations-token.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { getInjectionToken } from "@ogre-tools/injectable"; -import type { MigrationDeclaration } from "../base-store/migrations.injectable"; - -export const userStoreMigrationInjectionToken = getInjectionToken({ - id: "user-store-migration-token", -}); diff --git a/packages/core/src/common/user-store/user-store.injectable.ts b/packages/core/src/common/user-store/user-store.injectable.ts deleted file mode 100644 index 10806007a2..0000000000 --- a/packages/core/src/common/user-store/user-store.injectable.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import { UserStore } from "./user-store"; -import selectedUpdateChannelInjectable from "../../features/application-update/common/selected-update-channel/selected-update-channel.injectable"; -import emitAppEventInjectable from "../app-event-bus/emit-event.injectable"; -import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable"; -import loggerInjectable from "../logger.injectable"; -import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable"; -import storeMigrationsInjectable from "../base-store/migrations.injectable"; -import { userStoreMigrationInjectionToken } from "./migrations-token"; -import { baseStoreIpcChannelPrefixesInjectionToken } from "../base-store/channel-prefix"; -import { shouldBaseStoreDisableSyncInIpcListenerInjectionToken } from "../base-store/disable-sync"; -import { persistStateToConfigInjectionToken } from "../base-store/save-to-file"; -import getBasenameOfPathInjectable from "../path/get-basename.injectable"; -import { enlistMessageChannelListenerInjectionToken } from "@k8slens/messaging"; -import userStorePreferenceDescriptorsInjectable from "./preference-descriptors.injectable"; - -const userStoreInjectable = getInjectable({ - id: "user-store", - - instantiate: (di) => new UserStore({ - selectedUpdateChannel: di.inject(selectedUpdateChannelInjectable), - emitAppEvent: di.inject(emitAppEventInjectable), - directoryForUserData: di.inject(directoryForUserDataInjectable), - getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable), - logger: di.inject(loggerInjectable), - storeMigrationVersion: di.inject(storeMigrationVersionInjectable), - migrations: di.inject(storeMigrationsInjectable, userStoreMigrationInjectionToken), - getBasenameOfPath: di.inject(getBasenameOfPathInjectable), - ipcChannelPrefixes: di.inject(baseStoreIpcChannelPrefixesInjectionToken), - persistStateToConfig: di.inject(persistStateToConfigInjectionToken), - enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken), - shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken), - preferenceDescriptors: di.inject(userStorePreferenceDescriptorsInjectable), - }), -}); - -export default userStoreInjectable; diff --git a/packages/core/src/common/user-store/user-store.ts b/packages/core/src/common/user-store/user-store.ts deleted file mode 100644 index 4ffa31fac1..0000000000 --- a/packages/core/src/common/user-store/user-store.ts +++ /dev/null @@ -1,156 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { action, observable, makeObservable, isObservableArray, isObservableSet, isObservableMap } from "mobx"; -import type { BaseStoreDependencies } from "../base-store/base-store"; -import { BaseStore } from "../base-store/base-store"; -import { getOrInsertSet, toggle, object } from "@k8slens/utilities"; -import type { UserPreferencesModel, StoreType } from "./preferences-helpers"; -import type { EmitAppEvent } from "../app-event-bus/emit-event.injectable"; - -// TODO: Remove coupling with Feature -import type { SelectedUpdateChannel } from "../../features/application-update/common/selected-update-channel/selected-update-channel.injectable"; -import type { ReleaseChannel } from "../../features/application-update/common/update-channels"; -import type { PreferenceDescriptors } from "./preference-descriptors.injectable"; -import { toJS } from "../utils"; - -export interface UserStoreModel { - preferences: UserPreferencesModel; -} - -interface Dependencies extends BaseStoreDependencies { - readonly selectedUpdateChannel: SelectedUpdateChannel; - readonly preferenceDescriptors: PreferenceDescriptors; - emitAppEvent: EmitAppEvent; -} - -export class UserStore extends BaseStore /* implements UserStoreFlatModel (when strict null is enabled) */ { - constructor(protected readonly dependencies: Dependencies) { - super(dependencies, { - configName: "lens-user-store", - }); - - makeObservable(this); - } - - /** - * @deprecated No longer used - */ - @observable seenContexts = observable.set(); - - /** - * @deprecated No longer used - */ - @observable newContexts = observable.set(); - - @observable allowErrorReporting!: StoreType; - @observable allowUntrustedCAs!: StoreType; - @observable colorTheme!: StoreType; - @observable terminalTheme!: StoreType; - @observable localeTimezone!: StoreType; - @observable downloadMirror!: StoreType; - @observable httpsProxy!: StoreType; - @observable shell!: StoreType; - @observable downloadBinariesPath!: StoreType; - @observable kubectlBinariesPath!: StoreType; - @observable terminalCopyOnSelect!: StoreType; - @observable terminalConfig!: StoreType; - @observable extensionRegistryUrl!: StoreType; - - /** - * Download kubectl binaries matching cluster version - */ - @observable downloadKubectlBinaries!: StoreType; - - /** - * Whether the application should open itself at login. - */ - @observable openAtLogin!: StoreType; - - /** - * The column IDs under each configurable table ID that have been configured - * to not be shown - */ - @observable hiddenTableColumns!: StoreType; - - /** - * Monaco editor configs - */ - @observable editorConfiguration!: StoreType; - - /** - * The set of file/folder paths to be synced - */ - @observable syncKubeconfigEntries!: StoreType; - - /** - * Checks if a column (by ID) for a table (by ID) is configured to be hidden - * @param tableId The ID of the table to be checked against - * @param columnIds The list of IDs the check if one is hidden - * @returns true if at least one column under the table is set to hidden - */ - isTableColumnHidden(tableId: string, ...columnIds: (string | undefined)[]): boolean { - if (columnIds.length === 0) { - return false; - } - - const config = this.hiddenTableColumns.get(tableId); - - if (!config) { - return false; - } - - return columnIds.some(columnId => columnId && config.has(columnId)); - } - - /** - * Toggles the hidden configuration of a table's column - */ - toggleTableColumnVisibility(tableId: string, columnId: string) { - toggle(getOrInsertSet(this.hiddenTableColumns, tableId), columnId); - } - - @action - resetTheme() { - this.colorTheme = this.dependencies.preferenceDescriptors.colorTheme.fromStore(undefined); - } - - @action - protected fromStore({ preferences }: Partial = {}) { - this.dependencies.logger.debug("UserStore.fromStore()", { preferences }); - - for (const [key, { fromStore }] of object.entries(this.dependencies.preferenceDescriptors)) { - const curVal = this[key]; - const newVal = fromStore((preferences)?.[key] as never) as never; - - if (isObservableArray(curVal)) { - curVal.replace(newVal); - } else if (isObservableSet(curVal) || isObservableMap(curVal)) { - curVal.replace(newVal); - } else { - this[key] = newVal; - } - } - - // TODO: Switch to action-based saving instead saving stores by reaction - if (preferences?.updateChannel) { - this.dependencies.selectedUpdateChannel.setValue(preferences?.updateChannel as ReleaseChannel); - } - } - - toJSON(): UserStoreModel { - const preferences = object.fromEntries( - object.entries(this.dependencies.preferenceDescriptors) - .map(([key, { toStore }]) => [key, toStore(this[key] as never)]), - ) as UserPreferencesModel; - - return toJS({ - preferences: { - ...preferences, - updateChannel: this.dependencies.selectedUpdateChannel.value.get().id, - }, - }); - } -} diff --git a/packages/core/src/common/user-store/current-timezone.global-override-for-injectable.ts b/packages/core/src/common/vars/current-timezone.global-override-for-injectable.ts similarity index 100% rename from packages/core/src/common/user-store/current-timezone.global-override-for-injectable.ts rename to packages/core/src/common/vars/current-timezone.global-override-for-injectable.ts diff --git a/packages/core/src/common/user-store/current-timezone.injectable.ts b/packages/core/src/common/vars/current-timezone.injectable.ts similarity index 100% rename from packages/core/src/common/user-store/current-timezone.injectable.ts rename to packages/core/src/common/vars/current-timezone.injectable.ts diff --git a/packages/core/src/common/user-store/user-info.global-override-for-injectable.ts b/packages/core/src/common/vars/user-info.global-override-for-injectable.ts similarity index 100% rename from packages/core/src/common/user-store/user-info.global-override-for-injectable.ts rename to packages/core/src/common/vars/user-info.global-override-for-injectable.ts diff --git a/packages/core/src/common/user-store/user-info.injectable.ts b/packages/core/src/common/vars/user-info.injectable.ts similarity index 100% rename from packages/core/src/common/user-store/user-info.injectable.ts rename to packages/core/src/common/vars/user-info.injectable.ts diff --git a/packages/core/src/common/weblinks-store/weblink-store.injectable.ts b/packages/core/src/common/weblinks-store/weblink-store.injectable.ts deleted file mode 100644 index 843c8716e5..0000000000 --- a/packages/core/src/common/weblinks-store/weblink-store.injectable.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import { baseStoreIpcChannelPrefixesInjectionToken } from "../base-store/channel-prefix"; -import { shouldBaseStoreDisableSyncInIpcListenerInjectionToken } from "../base-store/disable-sync"; -import storeMigrationsInjectable from "../base-store/migrations.injectable"; -import { persistStateToConfigInjectionToken } from "../base-store/save-to-file"; -import getConfigurationFileModelInjectable from "../get-configuration-file-model/get-configuration-file-model.injectable"; -import loggerInjectable from "../logger.injectable"; -import getBasenameOfPathInjectable from "../path/get-basename.injectable"; -import { enlistMessageChannelListenerInjectionToken } from "@k8slens/messaging"; -import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable"; -import { weblinkStoreMigrationInjectionToken } from "./migration-token"; -import { WeblinkStore } from "./weblink-store"; - -const weblinkStoreInjectable = getInjectable({ - id: "weblink-store", - instantiate: (di) => new WeblinkStore({ - directoryForUserData: di.inject(directoryForUserDataInjectable), - getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable), - logger: di.inject(loggerInjectable), - storeMigrationVersion: di.inject(storeMigrationVersionInjectable), - migrations: di.inject(storeMigrationsInjectable, weblinkStoreMigrationInjectionToken), - getBasenameOfPath: di.inject(getBasenameOfPathInjectable), - ipcChannelPrefixes: di.inject(baseStoreIpcChannelPrefixesInjectionToken), - persistStateToConfig: di.inject(persistStateToConfigInjectionToken), - enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken), - shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken), - }), -}); - -export default weblinkStoreInjectable; diff --git a/packages/core/src/common/weblinks-store/weblink-store.ts b/packages/core/src/common/weblinks-store/weblink-store.ts deleted file mode 100644 index 2044ca08c1..0000000000 --- a/packages/core/src/common/weblinks-store/weblink-store.ts +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { action, comparer, observable, makeObservable } from "mobx"; -import type { BaseStoreDependencies } from "../base-store/base-store"; -import { BaseStore } from "../base-store/base-store"; -import * as uuid from "uuid"; -import { toJS } from "../utils"; - -export interface WeblinkData { - id: string; - name: string; - url: string; -} - -export interface WeblinkCreateOptions { - id?: string; - name: string; - url: string; -} - - -export interface WeblinkStoreModel { - weblinks: WeblinkData[]; -} - -export class WeblinkStore extends BaseStore { - @observable weblinks: WeblinkData[] = []; - - constructor(deps: BaseStoreDependencies) { - super(deps, { - configName: "lens-weblink-store", - accessPropertiesByDotNotation: false, // To make dots safe in cluster context names - syncOptions: { - equals: comparer.structural, - }, - }); - makeObservable(this); - this.load(); - } - - @action - protected fromStore(data: Partial = {}) { - this.weblinks = data.weblinks || []; - } - - add(data: WeblinkCreateOptions) { - const { - id = uuid.v4(), - name, - url, - } = data; - const weblink: WeblinkData = { id, name, url }; - - this.weblinks.push(weblink); - - return weblink; - } - - @action - removeById(id: string) { - this.weblinks = this.weblinks.filter((w) => w.id !== id); - } - - toJSON(): WeblinkStoreModel { - const model: WeblinkStoreModel = { - weblinks: this.weblinks, - }; - - return toJS(model); - } -} diff --git a/packages/core/src/extensions/__tests__/extension-loader.test.ts b/packages/core/src/extensions/__tests__/extension-loader.test.ts index 4ded8cdbb9..9be0507f6d 100644 --- a/packages/core/src/extensions/__tests__/extension-loader.test.ts +++ b/packages/core/src/extensions/__tests__/extension-loader.test.ts @@ -5,14 +5,16 @@ import type { ExtensionLoader } from "../extension-loader"; import extensionLoaderInjectable from "../extension-loader/extension-loader.injectable"; +import type { ObservableMap } from "mobx"; import { runInAction } from "mobx"; -import updateExtensionsStateInjectable from "../extension-loader/update-extensions-state/update-extensions-state.injectable"; import { delay } from "@k8slens/utilities"; import { getDiForUnitTesting } from "../../renderer/getDiForUnitTesting"; import ipcRendererInjectable from "../../renderer/utils/channel/ipc-renderer.injectable"; import type { IpcRenderer } from "electron"; import directoryForUserDataInjectable from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; import currentlyInClusterFrameInjectable from "../../renderer/routes/currently-in-cluster-frame.injectable"; +import type { LensExtensionState } from "../../features/extensions/enabled/common/state.injectable"; +import enabledExtensionsStateInjectable from "../../features/extensions/enabled/common/state.injectable"; const manifestPath = "manifest/path"; const manifestPath2 = "manifest/path2"; @@ -20,7 +22,7 @@ const manifestPath3 = "manifest/path3"; describe("ExtensionLoader", () => { let extensionLoader: ExtensionLoader; - let updateExtensionStateMock: jest.Mock; + let enabledExtensionsState: ObservableMap; beforeEach(() => { const di = getDiForUnitTesting(); @@ -106,69 +108,60 @@ describe("ExtensionLoader", () => { }, }) as unknown as IpcRenderer); - updateExtensionStateMock = jest.fn(); - - di.override(updateExtensionsStateInjectable, () => updateExtensionStateMock); - extensionLoader = di.inject(extensionLoaderInjectable); + enabledExtensionsState = di.inject(enabledExtensionsStateInjectable); }); it("renderer updates extension after ipc broadcast", async () => { - expect(extensionLoader.userExtensions).toMatchInlineSnapshot(`Map {}`); + expect(extensionLoader.userExtensions).toEqual(new Map()); await extensionLoader.init(); await delay(10); // Assert the extensions after the extension broadcast event - expect(extensionLoader.userExtensions).toMatchInlineSnapshot(` - Map { - "manifest/path" => Object { - "absolutePath": "/test/1", - "id": "manifest/path", - "isBundled": false, - "isEnabled": true, - "manifest": Object { - "name": "TestExtension", - "version": "1.0.0", + expect(extensionLoader.userExtensions).toEqual( + new Map([ + ["manifest/path", { + absolutePath: "/test/1", + id: "manifest/path", + isBundled: false, + isEnabled: true, + manifest: { + name: "TestExtension", + version: "1.0.0", }, - "manifestPath": "manifest/path", - }, - "manifest/path3" => Object { - "absolutePath": "/test/3", - "id": "manifest/path3", - "isBundled": false, - "isEnabled": true, - "manifest": Object { - "name": "TestExtension3", - "version": "3.0.0", + manifestPath: "manifest/path", + }], + ["manifest/path3", { + absolutePath: "/test/3", + id: "manifest/path3", + isBundled: false, + isEnabled: true, + manifest: { + name: "TestExtension3", + version: "3.0.0", }, - "manifestPath": "manifest/path3", - }, - } - `); + manifestPath: "manifest/path3", + }], + ]), + ); }); it("updates ExtensionsStore after isEnabled is changed", async () => { await extensionLoader.init(); - expect(updateExtensionStateMock).not.toHaveBeenCalled(); - runInAction(() => { extensionLoader.setIsEnabled("manifest/path", false); }); - expect(updateExtensionStateMock).toHaveBeenCalledWith( - expect.objectContaining({ - "manifest/path": { - enabled: false, - name: "TestExtension", - }, - - "manifest/path2": { - enabled: true, - name: "TestExtension2", - }, - }), - ); + expect(enabledExtensionsState.size).toBe(2); + expect(enabledExtensionsState.get("manifest/path")).toMatchObject({ + enabled: false, + name: "TestExtension", + }); + expect(enabledExtensionsState.get("manifest/path2")).toMatchObject({ + enabled: true, + name: "TestExtension2", + }); }); }); diff --git a/packages/core/src/extensions/base-extension-store.ts b/packages/core/src/extensions/base-extension-store.ts new file mode 100644 index 0000000000..703edfafd0 --- /dev/null +++ b/packages/core/src/extensions/base-extension-store.ts @@ -0,0 +1,103 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import * as path from "path"; +import type { LensExtension } from "./lens-extension"; +import type { StaticThis } from "../common/utils/singleton"; +import { getOrInsertWith } from "@k8slens/utilities"; +import { getLegacyGlobalDiForExtensionApi } from "./as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; +import type { PersistentStorage, PersistentStorageParams } from "../common/persistent-storage/create.injectable"; +import createPersistentStorageInjectable from "../common/persistent-storage/create.injectable"; +import directoryForUserDataInjectable from "../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; +import assert from "assert"; +import type { Options } from "conf"; +import type { Migrations } from "../common/persistent-storage/migrations.injectable"; + +export interface ExtensionStoreParams extends Omit, "migrations" | "cwd" | "fromStore" | "toJSON"> { + migrations?: Options["migrations"]; + cwd?: string; +} + +export abstract class BaseExtensionStore { + private static readonly instances = new WeakMap(); + + /** + * @deprecated This is a form of global shared state. Just call `new Store(...)` + */ + static createInstance(this: StaticThis, ...args: R): T { + return getOrInsertWith(BaseExtensionStore.instances, this, () => new this(...args)) as T; + } + + /** + * @deprecated This is a form of global shared state. Just call `new Store(...)` + */ + static getInstance(this: StaticThis, strict?: true): T; + static getInstance(this: StaticThis, strict: false): T | undefined; + static getInstance(this: StaticThis, strict = true): T | undefined { + if (!BaseExtensionStore.instances.has(this) && strict) { + throw new TypeError(`instance of ${this.name} is not created`); + } + + return BaseExtensionStore.instances.get(this) as (T | undefined); + } + + protected persistentStorage?: PersistentStorage; + private readonly dependencies = (() => { + const di = getLegacyGlobalDiForExtensionApi(); + + return { + createPersistentStorage: di.inject(createPersistentStorageInjectable), + directoryForUserData: di.inject(directoryForUserDataInjectable), + } as const; + })(); + + constructor(protected readonly rawParams: ExtensionStoreParams) { } + + /** + * @deprecated This is a form of global shared state. Just call `new Store(...)` + */ + static resetInstance() { + BaseExtensionStore.instances.delete(this); + } + + protected extension?: LensExtension; + + loadExtension(extension: LensExtension) { + this.extension = extension; + const { + projectVersion = this.extension.version, + cwd: _cwd, // This is ignored to maintain backwards compatibility + migrations = {}, + ...params + } = this.rawParams; + + this.persistentStorage = this.dependencies.createPersistentStorage({ + ...params, + cwd: this.cwd(), + projectVersion, + migrations: migrations as Migrations, + fromStore: (data) => this.fromStore(data), + toJSON: () => this.toJSON(), + }); + + this.persistentStorage.loadAndStartSyncing(); + } + + /** + * @deprecated Never use this method. Instead call {@link BaseExtensionStore.loadExtension} + */ + load() { + this.persistentStorage?.loadAndStartSyncing(); + } + + protected cwd() { + assert(this.extension, "cwd can only be called in loadExtension"); + + return this.rawParams.cwd ?? path.join(this.dependencies.directoryForUserData, "extension-store", this.extension.storeName); + } + + abstract fromStore(data: Partial): void; + abstract toJSON(): T; +} diff --git a/packages/core/src/extensions/common-api/app.ts b/packages/core/src/extensions/common-api/app.ts index 8c190b984b..9e0c84f4e0 100644 --- a/packages/core/src/extensions/common-api/app.ts +++ b/packages/core/src/extensions/common-api/app.ts @@ -8,21 +8,21 @@ import isLinuxInjectable from "../../common/vars/is-linux.injectable"; import isMacInjectable from "../../common/vars/is-mac.injectable"; import isSnapPackageInjectable from "../../common/vars/is-snap-package.injectable"; import isWindowsInjectable from "../../common/vars/is-windows.injectable"; -import { asLegacyGlobalFunctionForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api"; import { getLegacyGlobalDiForExtensionApi } from "../as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; -import getEnabledExtensionsInjectable from "./get-enabled-extensions/get-enabled-extensions.injectable"; import { issuesTrackerUrl } from "../../common/vars"; import { buildVersionInjectionToken } from "../../common/vars/build-semantic-version.injectable"; import { asLegacyGlobalForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; -import userStoreInjectable from "../../common/user-store/user-store.injectable"; +import enabledExtensionsInjectable from "../../features/extensions/enabled/common/enabled-extensions.injectable"; +import userPreferencesStateInjectable from "../../features/user-preferences/common/state.injectable"; -const userStore = asLegacyGlobalForExtensionApi(userStoreInjectable); +const userStore = asLegacyGlobalForExtensionApi(userPreferencesStateInjectable); +const enabledExtensions = asLegacyGlobalForExtensionApi(enabledExtensionsInjectable); export const App = { Preferences: { getKubectlPath: () => userStore.kubectlBinariesPath, }, - getEnabledExtensions: asLegacyGlobalFunctionForExtensionApi(getEnabledExtensionsInjectable), + getEnabledExtensions: () => enabledExtensions.get(), get version() { const di = getLegacyGlobalDiForExtensionApi(); @@ -54,7 +54,7 @@ export const App = { return di.inject(isLinuxInjectable); }, /** - * @deprecated This value is now `""` and is left here for backwards compatability. + * @deprecated This value is now `""` and is left here for backwards compatibility. */ slackUrl: "", issuesTrackerUrl, diff --git a/packages/core/src/extensions/common-api/get-enabled-extensions/get-enabled-extensions.injectable.ts b/packages/core/src/extensions/common-api/get-enabled-extensions/get-enabled-extensions.injectable.ts deleted file mode 100644 index 747dfa4f42..0000000000 --- a/packages/core/src/extensions/common-api/get-enabled-extensions/get-enabled-extensions.injectable.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import extensionsStoreInjectable from "../../extensions-store/extensions-store.injectable"; - -const getEnabledExtensionsInjectable = getInjectable({ - id: "get-enabled-extensions", - - instantiate: (di) => () => - di.inject(extensionsStoreInjectable).enabledExtensions, -}); - -export default getEnabledExtensionsInjectable; diff --git a/packages/core/src/extensions/common-api/k8s-api.ts b/packages/core/src/extensions/common-api/k8s-api.ts index 20e687ff6d..dfb96860c0 100644 --- a/packages/core/src/extensions/common-api/k8s-api.ts +++ b/packages/core/src/extensions/common-api/k8s-api.ts @@ -32,11 +32,13 @@ import type { KubeJsonApi as InternalKubeJsonApi } from "../../common/k8s-api/ku import createKubeJsonApiInjectable from "../../common/k8s-api/create-kube-json-api.injectable"; import type { RequestInit } from "@k8slens/node-fetch"; import createKubeJsonApiForClusterInjectable from "../../common/k8s-api/create-kube-json-api-for-cluster.injectable"; +import getPodsByOwnerIdInjectable from "../../renderer/components/+workloads-pods/get-pods-by-owner-id.injectable"; export const apiManager = asLegacyGlobalForExtensionApi(apiManagerInjectable); export const forCluster = asLegacyGlobalFunctionForExtensionApi(createKubeApiForClusterInjectable); export const forRemoteCluster = asLegacyGlobalFunctionForExtensionApi(createKubeApiForRemoteClusterInjectable); export const createResourceStack = asLegacyGlobalFunctionForExtensionApi(createResourceStackInjectable); +export const getPodsByOwnerId = asLegacyGlobalFunctionForExtensionApi(getPodsByOwnerIdInjectable); const getKubeApiDeps = (): KubeApiDependencies => { const di = getLegacyGlobalDiForExtensionApi(); @@ -50,7 +52,7 @@ const getKubeApiDeps = (): KubeApiDependencies => { export interface ExternalKubeApiOptions { /** * If `true` then on creation of the `KubeApi`instance a call to `apiManager.registerApi` will be - * made. This is `true` by default to maintain backwards compatability. + * made. This is `true` by default to maintain backwards compatibility. * * Setting this to `false` might make `KubeObject`'s details drawer stop working. * @@ -200,7 +202,7 @@ export type { } from "../../common/k8s-api/kube-object.store"; /** - * @deprecated This type is only present for backwards compatable typescript support + * @deprecated This type is only present for backwards compatible typescript support */ export interface IgnoredKubeApiOptions { /** diff --git a/packages/core/src/extensions/common-api/stores.ts b/packages/core/src/extensions/common-api/stores.ts index 023fc5c7a4..4585a1661d 100644 --- a/packages/core/src/extensions/common-api/stores.ts +++ b/packages/core/src/extensions/common-api/stores.ts @@ -3,10 +3,13 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { BaseStoreParams } from "../../common/base-store/base-store"; -import { ExtensionStore } from "../extension-store"; +import type { PersistentStorageParams } from "../../common/persistent-storage/create.injectable"; +import type { ExtensionStoreParams } from "../base-extension-store"; +import { BaseExtensionStore as ExtensionStore } from "../base-extension-store"; -export { - BaseStoreParams, - ExtensionStore, +export type { + ExtensionStoreParams, + PersistentStorageParams, }; + +export { ExtensionStore }; diff --git a/packages/core/src/extensions/extension-discovery/extension-discovery.injectable.ts b/packages/core/src/extensions/extension-discovery/extension-discovery.injectable.ts index 5e2a4cd84d..92b1c8cf77 100644 --- a/packages/core/src/extensions/extension-discovery/extension-discovery.injectable.ts +++ b/packages/core/src/extensions/extension-discovery/extension-discovery.injectable.ts @@ -6,7 +6,6 @@ import { getInjectable } from "@ogre-tools/injectable"; import { ExtensionDiscovery } from "./extension-discovery"; import extensionLoaderInjectable from "../extension-loader/extension-loader.injectable"; import isCompatibleExtensionInjectable from "./is-compatible-extension/is-compatible-extension.injectable"; -import extensionsStoreInjectable from "../extensions-store/extensions-store.injectable"; import extensionInstallationStateStoreInjectable from "../extension-installation-state-store/extension-installation-state-store.injectable"; import installExtensionInjectable from "../install-extension/install-extension.injectable"; import extensionPackageRootDirectoryInjectable from "../install-extension/extension-package-root-directory.injectable"; @@ -28,13 +27,14 @@ import joinPathsInjectable from "../../common/path/join-paths.injectable"; import removePathInjectable from "../../common/fs/remove.injectable"; import homeDirectoryPathInjectable from "../../common/os/home-directory-path.injectable"; import lensResourcesDirInjectable from "../../common/vars/lens-resources-dir.injectable"; +import isExtensionEnabledInjectable from "../../features/extensions/enabled/common/is-enabled.injectable"; const extensionDiscoveryInjectable = getInjectable({ id: "extension-discovery", instantiate: (di) => new ExtensionDiscovery({ extensionLoader: di.inject(extensionLoaderInjectable), - extensionsStore: di.inject(extensionsStoreInjectable), + isExtensionEnabled: di.inject(isExtensionEnabledInjectable), extensionInstallationStateStore: di.inject(extensionInstallationStateStoreInjectable), isCompatibleExtension: di.inject(isCompatibleExtensionInjectable), installExtension: di.inject(installExtensionInjectable), diff --git a/packages/core/src/extensions/extension-discovery/extension-discovery.ts b/packages/core/src/extensions/extension-discovery/extension-discovery.ts index 88864d8663..7a39fa7467 100644 --- a/packages/core/src/extensions/extension-discovery/extension-discovery.ts +++ b/packages/core/src/extensions/extension-discovery/extension-discovery.ts @@ -9,7 +9,6 @@ import { makeObservable, observable, reaction, when } from "mobx"; import { broadcastMessage, ipcMainHandle, ipcRendererOn } from "../../common/ipc"; import { toJS } from "../../common/utils"; import { isErrnoException } from "@k8slens/utilities"; -import type { ExtensionsStore } from "../extensions-store/extensions-store"; import type { ExtensionLoader } from "../extension-loader"; import type { InstalledExtension, LensExtensionId, LensExtensionManifest } from "@k8slens/legacy-extensions"; import type { ExtensionInstallationStateStore } from "../extension-installation-state-store/extension-installation-state-store"; @@ -31,10 +30,10 @@ import type { GetDirnameOfPath } from "../../common/path/get-dirname.injectable" import type { GetRelativePath } from "../../common/path/get-relative-path.injectable"; import type { RemovePath } from "../../common/fs/remove.injectable"; import type TypedEventEmitter from "typed-emitter"; +import type { IsExtensionEnabled } from "../../features/extensions/enabled/common/is-enabled.injectable"; interface Dependencies { readonly extensionLoader: ExtensionLoader; - readonly extensionsStore: ExtensionsStore; readonly extensionInstallationStateStore: ExtensionInstallationStateStore; readonly extensionPackageRootDirectory: string; readonly resourcesDirectory: string; @@ -42,6 +41,7 @@ interface Dependencies { readonly isProduction: boolean; readonly fileSystemSeparator: string; readonly homeDirectoryPath: string; + isExtensionEnabled: IsExtensionEnabled; isCompatibleExtension: (manifest: LensExtensionManifest) => boolean; installExtension: (name: string) => Promise; readJsonFile: ReadJson; @@ -334,7 +334,7 @@ export class ExtensionDiscovery { try { const manifest = await this.dependencies.readJsonFile(manifestPath) as unknown as LensExtensionManifest; const id = isBundled ? manifestPath : this.getInstalledManifestPath(manifest.name); - const isEnabled = this.dependencies.extensionsStore.isEnabled({ id, isBundled }); + const isEnabled = this.dependencies.isExtensionEnabled({ id, isBundled }); const extensionDir = this.dependencies.getDirnameOfPath(manifestPath); const npmPackage = this.dependencies.joinPaths(extensionDir, `${manifest.name}-${manifest.version}.tgz`); const absolutePath = this.dependencies.isProduction && await this.dependencies.pathExists(npmPackage) diff --git a/packages/core/src/extensions/extension-loader/extension-loader.injectable.ts b/packages/core/src/extensions/extension-loader/extension-loader.injectable.ts index e635a6cca1..12187c38c0 100644 --- a/packages/core/src/extensions/extension-loader/extension-loader.injectable.ts +++ b/packages/core/src/extensions/extension-loader/extension-loader.injectable.ts @@ -4,7 +4,6 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import { ExtensionLoader } from "./extension-loader"; -import updateExtensionsStateInjectable from "./update-extensions-state/update-extensions-state.injectable"; import { createExtensionInstanceInjectionToken } from "./create-extension-instance.token"; import extensionInstancesInjectable from "./extension-instances.injectable"; import type { LensExtension } from "../lens-extension"; @@ -14,6 +13,7 @@ import joinPathsInjectable from "../../common/path/join-paths.injectable"; import getDirnameOfPathInjectable from "../../common/path/get-dirname.injectable"; import { bundledExtensionInjectionToken } from "@k8slens/legacy-extensions"; import { extensionEntryPointNameInjectionToken } from "./entry-point-name"; +import updateExtensionsStateInjectable from "../../features/extensions/enabled/common/update-state.injectable"; const extensionLoaderInjectable = getInjectable({ id: "extension-loader", diff --git a/packages/core/src/extensions/extension-loader/extension-loader.ts b/packages/core/src/extensions/extension-loader/extension-loader.ts index 7d41ec1fda..4a24ad23a1 100644 --- a/packages/core/src/extensions/extension-loader/extension-loader.ts +++ b/packages/core/src/extensions/extension-loader/extension-loader.ts @@ -10,7 +10,6 @@ import { action, computed, makeObservable, toJS, observable, observe, reaction, import { broadcastMessage, ipcMainOn, ipcRendererOn, ipcMainHandle } from "../../common/ipc"; import { isDefined } from "@k8slens/utilities"; import type { LensExtension } from "../lens-extension"; -import type { LensExtensionState } from "../extensions-store/extensions-store"; import { extensionLoaderFromMainChannel, extensionLoaderFromRendererChannel } from "../../common/ipc/extension-handling"; import { requestExtensionLoaderInitialState } from "../../renderer/ipc"; import assert from "assert"; @@ -21,6 +20,7 @@ import type { Logger } from "../../common/logger"; import type { JoinPaths } from "../../common/path/join-paths.injectable"; import type { GetDirnameOfPath } from "../../common/path/get-dirname.injectable"; import type { LensExtensionId, BundledExtension, InstalledExtension, LensExtensionConstructor } from "@k8slens/legacy-extensions"; +import type { UpdateExtensionsState } from "../../features/extensions/enabled/common/update-state.injectable"; const logModule = "[EXTENSIONS-LOADER]"; @@ -29,7 +29,7 @@ interface Dependencies { readonly bundledExtensions: BundledExtension[]; readonly logger: Logger; readonly extensionEntryPointName: "main" | "renderer"; - updateExtensionsState: (extensionsState: Record) => void; + updateExtensionsState: UpdateExtensionsState; createExtensionInstance: CreateExtensionInstance; getExtension: (instance: LensExtension) => Extension; joinPaths: JoinPaths; @@ -125,13 +125,11 @@ export class ExtensionLoader { // Transform userExtensions to a state object for storing into ExtensionsStore @computed get storeState() { - return Object.fromEntries( - Array.from(this.userExtensions) - .map(([extId, extension]) => [extId, { - enabled: extension.isEnabled, - name: extension.manifest.name, - }]), - ); + return Array.from(this.userExtensions) + .map(([extId, extension]) => [extId, { + enabled: extension.isEnabled, + name: extension.manifest.name, + }] as const); } @action diff --git a/packages/core/src/extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store-injection-token.ts b/packages/core/src/extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store-injection-token.ts index d7635a71b2..297238787e 100644 --- a/packages/core/src/extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store-injection-token.ts +++ b/packages/core/src/extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store-injection-token.ts @@ -3,8 +3,8 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectionToken } from "@ogre-tools/injectable"; -import type { MigrationDeclaration } from "../../../common/base-store/migrations.injectable"; +import type { MigrationDeclaration } from "../../../common/persistent-storage/migrations.injectable"; -export const fileSystemProvisionerStoreInjectionToken = getInjectionToken({ - id: "file-system-provisioner-store-injection-token", +export const fileSystemProvisionerStoreMigrationDeclarationInjectionToken = getInjectionToken({ + id: "file-system-provisioner-store-migration-declaration", }); diff --git a/packages/core/src/extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store.injectable.ts b/packages/core/src/extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store.injectable.ts index 2295608520..fe78674d11 100644 --- a/packages/core/src/extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store.injectable.ts +++ b/packages/core/src/extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store.injectable.ts @@ -3,36 +3,36 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { FileSystemProvisionerStore } from "./file-system-provisioner-store"; -import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import getConfigurationFileModelInjectable from "../../../common/get-configuration-file-model/get-configuration-file-model.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; -import storeMigrationVersionInjectable from "../../../common/vars/store-migration-version.injectable"; -import { baseStoreIpcChannelPrefixesInjectionToken } from "../../../common/base-store/channel-prefix"; -import { shouldBaseStoreDisableSyncInIpcListenerInjectionToken } from "../../../common/base-store/disable-sync"; -import { persistStateToConfigInjectionToken } from "../../../common/base-store/save-to-file"; -import getBasenameOfPathInjectable from "../../../common/path/get-basename.injectable"; -import { enlistMessageChannelListenerInjectionToken } from "@k8slens/messaging"; -import ensureHashedDirectoryForExtensionInjectable from "./ensure-hashed-directory-for-extension.injectable"; import { registeredExtensionsInjectable } from "./registered-extensions.injectable"; +import createPersistentStorageInjectable from "../../../common/persistent-storage/create.injectable"; +import { action } from "mobx"; +import { object } from "@k8slens/utilities"; +import storeMigrationVersionInjectable from "../../../common/vars/store-migration-version.injectable"; const fileSystemProvisionerStoreInjectable = getInjectable({ id: "file-system-provisioner-store", - instantiate: (di) => new FileSystemProvisionerStore({ - directoryForUserData: di.inject(directoryForUserDataInjectable), - getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable), - logger: di.inject(loggerInjectable), - storeMigrationVersion: di.inject(storeMigrationVersionInjectable), - migrations: {}, - getBasenameOfPath: di.inject(getBasenameOfPathInjectable), - ipcChannelPrefixes: di.inject(baseStoreIpcChannelPrefixesInjectionToken), - persistStateToConfig: di.inject(persistStateToConfigInjectionToken), - enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken), - shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken), - ensureHashedDirectoryForExtension: di.inject(ensureHashedDirectoryForExtensionInjectable), - registeredExtensions: di.inject(registeredExtensionsInjectable), - }), + instantiate: (di) => { + const registeredExtensions = di.inject(registeredExtensionsInjectable); + const createPersistentStorage = di.inject(createPersistentStorageInjectable); + const storeMigrationVersion = di.inject(storeMigrationVersionInjectable); + + const store = createPersistentStorage({ + configName: "lens-filesystem-provisioner-store", + accessPropertiesByDotNotation: false, // To make dots safe in cluster context names + projectVersion: storeMigrationVersion, + fromStore: action(({ extensions = {}}) => { + registeredExtensions.replace(object.entries(extensions)); + }), + toJSON: () => ({ + extensions: Object.fromEntries(registeredExtensions), + }), + }); + + return { + load: () => store.loadAndStartSyncing(), + }; + }, }); export default fileSystemProvisionerStoreInjectable; diff --git a/packages/core/src/extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store.ts b/packages/core/src/extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store.ts deleted file mode 100644 index 6c48210479..0000000000 --- a/packages/core/src/extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store.ts +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { LensExtensionId } from "@k8slens/legacy-extensions"; -import type { ObservableMap } from "mobx"; -import { action, makeObservable } from "mobx"; -import type { BaseStoreDependencies } from "../../../common/base-store/base-store"; -import { BaseStore } from "../../../common/base-store/base-store"; -import type { EnsureHashedDirectoryForExtension } from "./ensure-hashed-directory-for-extension.injectable"; - -interface FSProvisionModel { - extensions: Record; // extension names to paths -} - -interface Dependencies extends BaseStoreDependencies { - ensureHashedDirectoryForExtension: EnsureHashedDirectoryForExtension; - registeredExtensions: ObservableMap; -} - -export class FileSystemProvisionerStore extends BaseStore { - constructor(protected readonly dependencies: Dependencies) { - super(dependencies, { - configName: "lens-filesystem-provisioner-store", - accessPropertiesByDotNotation: false, // To make dots safe in cluster context names - }); - - makeObservable(this); - } - - /** - * This function retrieves the saved path to the folder which the extension - * can saves files to. If the folder is not present then it is created. - * @param extensionName the name of the extension requesting the path - * @returns path to the folder that the extension can safely write files to. - */ - async requestDirectory(extensionName: string): Promise { - return this.dependencies.ensureHashedDirectoryForExtension(extensionName); - } - - @action - protected fromStore({ extensions }: FSProvisionModel = { extensions: {}}): void { - this.dependencies.registeredExtensions.merge(extensions); - } - - toJSON(): FSProvisionModel { - return { - extensions: Object.fromEntries(this.dependencies.registeredExtensions.toJSON()), - }; - } -} diff --git a/packages/core/src/extensions/extension-loader/update-extensions-state/update-extensions-state.injectable.ts b/packages/core/src/extensions/extension-loader/update-extensions-state/update-extensions-state.injectable.ts deleted file mode 100644 index 754375dd5a..0000000000 --- a/packages/core/src/extensions/extension-loader/update-extensions-state/update-extensions-state.injectable.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import extensionsStoreInjectable from "../../extensions-store/extensions-store.injectable"; - -const updateExtensionsStateInjectable = getInjectable({ - id: "update-extensions-state", - instantiate: (di) => di.inject(extensionsStoreInjectable).mergeState, -}); - -export default updateExtensionsStateInjectable; diff --git a/packages/core/src/extensions/extension-store.ts b/packages/core/src/extensions/extension-store.ts deleted file mode 100644 index fe9cff2a02..0000000000 --- a/packages/core/src/extensions/extension-store.ts +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { BaseStoreParams } from "../common/base-store/base-store"; -import { BaseStore } from "../common/base-store/base-store"; -import * as path from "path"; -import type { LensExtension } from "./lens-extension"; -import assert from "assert"; -import type { StaticThis } from "../common/utils/singleton"; -import { getOrInsertWith } from "@k8slens/utilities"; -import { getLegacyGlobalDiForExtensionApi } from "./as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; -import directoryForUserDataInjectable from "../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import getConfigurationFileModelInjectable from "../common/get-configuration-file-model/get-configuration-file-model.injectable"; -import loggerInjectable from "../common/logger.injectable"; -import storeMigrationVersionInjectable from "../common/vars/store-migration-version.injectable"; -import type { Migrations } from "conf/dist/source/types"; -import { baseStoreIpcChannelPrefixesInjectionToken } from "../common/base-store/channel-prefix"; -import { shouldBaseStoreDisableSyncInIpcListenerInjectionToken } from "../common/base-store/disable-sync"; -import { persistStateToConfigInjectionToken } from "../common/base-store/save-to-file"; -import getBasenameOfPathInjectable from "../common/path/get-basename.injectable"; -import { enlistMessageChannelListenerInjectionToken } from "@k8slens/messaging"; - -export interface ExtensionStoreParams extends BaseStoreParams { - migrations?: Migrations; -} - -export abstract class ExtensionStore extends BaseStore { - private static readonly instances = new WeakMap(); - - /** - * @deprecated This is a form of global shared state. Just call `new Store(...)` - */ - static createInstance(this: StaticThis, ...args: R): T { - return getOrInsertWith(ExtensionStore.instances, this, () => new this(...args)) as T; - } - - /** - * @deprecated This is a form of global shared state. Just call `new Store(...)` - */ - static getInstance(this: StaticThis, strict?: true): T; - static getInstance(this: StaticThis, strict: false): T | undefined; - static getInstance(this: StaticThis, strict = true): T | undefined { - if (!ExtensionStore.instances.has(this) && strict) { - throw new TypeError(`instance of ${this.name} is not created`); - } - - return ExtensionStore.instances.get(this) as (T | undefined); - } - - constructor({ migrations, ...params }: ExtensionStoreParams) { - const di = getLegacyGlobalDiForExtensionApi(); - - super({ - directoryForUserData: di.inject(directoryForUserDataInjectable), - getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable), - logger: di.inject(loggerInjectable), - storeMigrationVersion: di.inject(storeMigrationVersionInjectable), - migrations: migrations as Migrations>, - getBasenameOfPath: di.inject(getBasenameOfPathInjectable), - ipcChannelPrefixes: di.inject(baseStoreIpcChannelPrefixesInjectionToken), - persistStateToConfig: di.inject(persistStateToConfigInjectionToken), - enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken), - shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken), - }, params); - } - - /** - * @deprecated This is a form of global shared state. Just call `new Store(...)` - */ - static resetInstance() { - ExtensionStore.instances.delete(this); - } - - protected extension?: LensExtension; - - loadExtension(extension: LensExtension) { - this.extension = extension; - - this.params.projectVersion ??= this.extension.version; - - return super.load(); - } - - load() { - if (!this.extension) { return; } - - return super.load(); - } - - protected cwd() { - assert(this.extension, "must call this.load() first"); - - return path.join(super.cwd(), "extension-store", this.extension.storeName); - } -} diff --git a/packages/core/src/extensions/extensions-store/extensions-store.injectable.ts b/packages/core/src/extensions/extensions-store/extensions-store.injectable.ts deleted file mode 100644 index 9e08a96aac..0000000000 --- a/packages/core/src/extensions/extensions-store/extensions-store.injectable.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import directoryForUserDataInjectable from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import { baseStoreIpcChannelPrefixesInjectionToken } from "../../common/base-store/channel-prefix"; -import { shouldBaseStoreDisableSyncInIpcListenerInjectionToken } from "../../common/base-store/disable-sync"; -import { persistStateToConfigInjectionToken } from "../../common/base-store/save-to-file"; -import getConfigurationFileModelInjectable from "../../common/get-configuration-file-model/get-configuration-file-model.injectable"; -import loggerInjectable from "../../common/logger.injectable"; -import getBasenameOfPathInjectable from "../../common/path/get-basename.injectable"; -import { enlistMessageChannelListenerInjectionToken } from "@k8slens/messaging"; -import storeMigrationVersionInjectable from "../../common/vars/store-migration-version.injectable"; -import { ExtensionsStore } from "./extensions-store"; - -const extensionsStoreInjectable = getInjectable({ - id: "extensions-store", - instantiate: (di) => new ExtensionsStore({ - directoryForUserData: di.inject(directoryForUserDataInjectable), - getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable), - logger: di.inject(loggerInjectable), - storeMigrationVersion: di.inject(storeMigrationVersionInjectable), - migrations: {}, - getBasenameOfPath: di.inject(getBasenameOfPathInjectable), - ipcChannelPrefixes: di.inject(baseStoreIpcChannelPrefixesInjectionToken), - persistStateToConfig: di.inject(persistStateToConfigInjectionToken), - enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken), - shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken), - }), -}); - -export default extensionsStoreInjectable; diff --git a/packages/core/src/extensions/extensions-store/extensions-store.ts b/packages/core/src/extensions/extensions-store/extensions-store.ts deleted file mode 100644 index 17766d26d7..0000000000 --- a/packages/core/src/extensions/extensions-store/extensions-store.ts +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { LensExtensionId } from "@k8slens/legacy-extensions"; -import { action, computed, makeObservable, observable } from "mobx"; -import type { BaseStoreDependencies } from "../../common/base-store/base-store"; -import { BaseStore } from "../../common/base-store/base-store"; - -export interface LensExtensionsStoreModel { - extensions: Record; -} - -export interface LensExtensionState { - enabled?: boolean; - name: string; -} - -export interface IsEnabledExtensionDescriptor { - id: string; - isBundled: boolean; -} - -export class ExtensionsStore extends BaseStore { - constructor(deps: BaseStoreDependencies) { - super(deps, { - configName: "lens-extensions", - }); - makeObservable(this); - this.load(); - } - - @computed - get enabledExtensions() { - return Array.from(this.state.values()) - .filter(({ enabled }) => enabled) - .map(({ name }) => name); - } - - protected state = observable.map(); - - isEnabled({ id, isBundled }: IsEnabledExtensionDescriptor): boolean { - // By default false, so that copied extensions are disabled by default. - // If user installs the extension from the UI, the Extensions component will specifically enable it. - return isBundled || Boolean(this.state.get(id)?.enabled); - } - - mergeState = action((extensionsState: Record | [LensExtensionId, LensExtensionState][]) => { - this.state.merge(extensionsState); - }); - - @action - protected fromStore({ extensions }: LensExtensionsStoreModel) { - this.state.merge(extensions); - } - - toJSON(): LensExtensionsStoreModel { - return { - extensions: Object.fromEntries(this.state.toJSON()), - }; - } -} diff --git a/packages/core/src/extensions/lens-extension-set-dependencies.ts b/packages/core/src/extensions/lens-extension-set-dependencies.ts index 7b7c62597a..aa22b7a1a1 100644 --- a/packages/core/src/extensions/lens-extension-set-dependencies.ts +++ b/packages/core/src/extensions/lens-extension-set-dependencies.ts @@ -10,13 +10,13 @@ import type { Route } from "../common/front-end-routing/front-end-route-injectio import type { CatalogEntityRegistry as MainCatalogEntityRegistry } from "../main/catalog"; import type { CatalogEntityRegistry as RendererCatalogEntityRegistry } from "../renderer/api/catalog/entity/registry"; import type { GetExtensionPageParameters } from "../renderer/routes/get-extension-page-parameters.injectable"; -import type { FileSystemProvisionerStore } from "./extension-loader/file-system-provisioner-store/file-system-provisioner-store"; import type { NavigateForExtension } from "../main/start-main-application/lens-window/navigate-for-extension.injectable"; import type { Logger } from "../common/logger"; +import type { EnsureHashedDirectoryForExtension } from "./extension-loader/file-system-provisioner-store/ensure-hashed-directory-for-extension.injectable"; export interface LensExtensionDependencies { - readonly fileSystemProvisionerStore: FileSystemProvisionerStore; readonly logger: Logger; + ensureHashedDirectoryForExtension: EnsureHashedDirectoryForExtension; } export interface LensMainExtensionDependencies extends LensExtensionDependencies { diff --git a/packages/core/src/extensions/lens-extension.ts b/packages/core/src/extensions/lens-extension.ts index 83ba4ebaec..ec5eca0cd9 100644 --- a/packages/core/src/extensions/lens-extension.ts +++ b/packages/core/src/extensions/lens-extension.ts @@ -83,7 +83,7 @@ export class LensExtension< */ async getExtensionFileFolder(): Promise { // storeName is read from the manifest and has a fallback to the manifest name, which equals id - return this[lensExtensionDependencies].fileSystemProvisionerStore.requestDirectory(this.storeName); + return this[lensExtensionDependencies].ensureHashedDirectoryForExtension(this.storeName); } @action diff --git a/packages/core/src/extensions/renderer-api/k8s-api.ts b/packages/core/src/extensions/renderer-api/k8s-api.ts index 1dfdc0e075..98e2cfcdfb 100644 --- a/packages/core/src/extensions/renderer-api/k8s-api.ts +++ b/packages/core/src/extensions/renderer-api/k8s-api.ts @@ -37,7 +37,7 @@ import namespaceApiInjectable from "../../common/k8s-api/endpoints/namespace.api import kubeEventApiInjectable from "../../common/k8s-api/endpoints/events.api.injectable"; import roleBindingApiInjectable from "../../common/k8s-api/endpoints/role-binding.api.injectable"; import customResourceDefinitionApiInjectable from "../../common/k8s-api/endpoints/custom-resource-definition.api.injectable"; -import { shouldShowResourceInjectionToken } from "../../common/cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { asLegacyGlobalFunctionForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api"; import requestMetricsInjectable from "../../common/k8s-api/endpoints/metrics.api/request-metrics.injectable"; diff --git a/packages/core/src/features/__snapshots__/extension-special-characters-in-page-registrations.test.tsx.snap b/packages/core/src/features/__snapshots__/extension-special-characters-in-page-registrations.test.tsx.snap index 03e2a2cd8a..a63146ddcc 100644 --- a/packages/core/src/features/__snapshots__/extension-special-characters-in-page-registrations.test.tsx.snap +++ b/packages/core/src/features/__snapshots__/extension-special-characters-in-page-registrations.test.tsx.snap @@ -230,7 +230,7 @@ exports[`extension special characters in page registrations renders 1`] = ` class="HotbarSelector" > mac", - "root -> mac -> about", - "root -> mac -> separator-1", - "root -> mac -> navigate-to-preferences", - "root -> mac -> navigate-to-extensions", - "root -> mac -> separator-2", - "root -> mac -> services", - "root -> mac -> separator-3", - "root -> mac -> hide", - "root -> mac -> hide-others", - "root -> mac -> unhide", - "root -> mac -> separator-4", - "root -> mac -> quit", - "root -> file", - "root -> file -> add-cluster", - "root -> file -> separator-1-for-file", - "root -> file -> close-window", - "root -> edit", - "root -> edit -> undo", - "root -> edit -> redo", - "root -> edit -> separator-1-in-edit", - "root -> edit -> cut", - "root -> edit -> copy", - "root -> edit -> paste", - "root -> edit -> delete", - "root -> edit -> separator-2-in-edit", - "root -> edit -> selectAll", - "root -> view", - "root -> view -> navigate-to-catalog", - "root -> view -> open-command-palette", - "root -> view -> separator-1-for-view", - "root -> view -> go-back", - "root -> view -> go-forward", - "root -> view -> reload", - "root -> view -> toggle-dev-tools", - "root -> view -> separator-2-for-view", - "root -> view -> reset-zoom", - "root -> view -> zoom-in", - "root -> view -> zoom-out", - "root -> view -> separator-3-for-view", - "root -> view -> toggle-full-screen", - "root -> help", - "root -> help -> navigate-to-welcome", - "root -> help -> open-documentation", - "root -> help -> open-support", -] +"[ + 'root', + 'root -> mac', + 'root -> mac -> about', + 'root -> mac -> separator-1', + 'root -> mac -> navigate-to-preferences', + 'root -> mac -> navigate-to-extensions', + 'root -> mac -> separator-2', + 'root -> mac -> services', + 'root -> mac -> separator-3', + 'root -> mac -> hide', + 'root -> mac -> hide-others', + 'root -> mac -> unhide', + 'root -> mac -> separator-4', + 'root -> mac -> quit', + 'root -> file', + 'root -> file -> add-cluster', + 'root -> file -> separator-1-for-file', + 'root -> file -> close-window', + 'root -> edit', + 'root -> edit -> undo', + 'root -> edit -> redo', + 'root -> edit -> separator-1-in-edit', + 'root -> edit -> cut', + 'root -> edit -> copy', + 'root -> edit -> paste', + 'root -> edit -> delete', + 'root -> edit -> separator-2-in-edit', + 'root -> edit -> selectAll', + 'root -> view', + 'root -> view -> navigate-to-catalog', + 'root -> view -> open-command-palette', + 'root -> view -> separator-1-for-view', + 'root -> view -> go-back', + 'root -> view -> go-forward', + 'root -> view -> reload', + 'root -> view -> toggle-dev-tools', + 'root -> view -> separator-2-for-view', + 'root -> view -> reset-zoom', + 'root -> view -> zoom-in', + 'root -> view -> zoom-out', + 'root -> view -> separator-3-for-view', + 'root -> view -> toggle-full-screen', + 'root -> help', + 'root -> help -> navigate-to-welcome', + 'root -> help -> open-documentation', + 'root -> help -> open-support' +]" `; exports[`application-menu, given platform is 'linux' given enough time passes populates application menu 1`] = ` -Array [ - "root", - "root -> file", - "root -> file -> add-cluster", - "root -> file -> navigate-to-preferences", - "root -> file -> navigate-to-extensions", - "root -> file -> quit", - "root -> edit", - "root -> edit -> undo", - "root -> edit -> redo", - "root -> edit -> separator-1-in-edit", - "root -> edit -> cut", - "root -> edit -> copy", - "root -> edit -> paste", - "root -> edit -> delete", - "root -> edit -> separator-2-in-edit", - "root -> edit -> selectAll", - "root -> view", - "root -> view -> navigate-to-catalog", - "root -> view -> open-command-palette", - "root -> view -> separator-1-for-view", - "root -> view -> go-back", - "root -> view -> go-forward", - "root -> view -> reload", - "root -> view -> toggle-dev-tools", - "root -> view -> separator-2-for-view", - "root -> view -> reset-zoom", - "root -> view -> zoom-in", - "root -> view -> zoom-out", - "root -> view -> separator-3-for-view", - "root -> view -> toggle-full-screen", - "root -> help", - "root -> help -> navigate-to-welcome", - "root -> help -> open-documentation", - "root -> help -> open-support", - "root -> help -> about", -] +"[ + 'root', + 'root -> file', + 'root -> file -> add-cluster', + 'root -> file -> navigate-to-preferences', + 'root -> file -> navigate-to-extensions', + 'root -> file -> quit', + 'root -> edit', + 'root -> edit -> undo', + 'root -> edit -> redo', + 'root -> edit -> separator-1-in-edit', + 'root -> edit -> cut', + 'root -> edit -> copy', + 'root -> edit -> paste', + 'root -> edit -> delete', + 'root -> edit -> separator-2-in-edit', + 'root -> edit -> selectAll', + 'root -> view', + 'root -> view -> navigate-to-catalog', + 'root -> view -> open-command-palette', + 'root -> view -> separator-1-for-view', + 'root -> view -> go-back', + 'root -> view -> go-forward', + 'root -> view -> reload', + 'root -> view -> toggle-dev-tools', + 'root -> view -> separator-2-for-view', + 'root -> view -> reset-zoom', + 'root -> view -> zoom-in', + 'root -> view -> zoom-out', + 'root -> view -> separator-3-for-view', + 'root -> view -> toggle-full-screen', + 'root -> help', + 'root -> help -> navigate-to-welcome', + 'root -> help -> open-documentation', + 'root -> help -> open-support', + 'root -> help -> about' +]" `; exports[`application-menu, given platform is 'win32' given enough time passes populates application menu 1`] = ` -Array [ - "root", - "root -> file", - "root -> file -> add-cluster", - "root -> file -> navigate-to-preferences", - "root -> file -> navigate-to-extensions", - "root -> file -> quit", - "root -> edit", - "root -> edit -> undo", - "root -> edit -> redo", - "root -> edit -> separator-1-in-edit", - "root -> edit -> cut", - "root -> edit -> copy", - "root -> edit -> paste", - "root -> edit -> delete", - "root -> edit -> separator-2-in-edit", - "root -> edit -> selectAll", - "root -> view", - "root -> view -> navigate-to-catalog", - "root -> view -> open-command-palette", - "root -> view -> separator-1-for-view", - "root -> view -> go-back", - "root -> view -> go-forward", - "root -> view -> reload", - "root -> view -> toggle-dev-tools", - "root -> view -> separator-2-for-view", - "root -> view -> reset-zoom", - "root -> view -> zoom-in", - "root -> view -> zoom-out", - "root -> view -> separator-3-for-view", - "root -> view -> toggle-full-screen", - "root -> help", - "root -> help -> navigate-to-welcome", - "root -> help -> open-documentation", - "root -> help -> open-support", - "root -> help -> about", -] +"[ + 'root', + 'root -> file', + 'root -> file -> add-cluster', + 'root -> file -> navigate-to-preferences', + 'root -> file -> navigate-to-extensions', + 'root -> file -> quit', + 'root -> edit', + 'root -> edit -> undo', + 'root -> edit -> redo', + 'root -> edit -> separator-1-in-edit', + 'root -> edit -> cut', + 'root -> edit -> copy', + 'root -> edit -> paste', + 'root -> edit -> delete', + 'root -> edit -> separator-2-in-edit', + 'root -> edit -> selectAll', + 'root -> view', + 'root -> view -> navigate-to-catalog', + 'root -> view -> open-command-palette', + 'root -> view -> separator-1-for-view', + 'root -> view -> go-back', + 'root -> view -> go-forward', + 'root -> view -> reload', + 'root -> view -> toggle-dev-tools', + 'root -> view -> separator-2-for-view', + 'root -> view -> reset-zoom', + 'root -> view -> zoom-in', + 'root -> view -> zoom-out', + 'root -> view -> separator-3-for-view', + 'root -> view -> toggle-full-screen', + 'root -> help', + 'root -> help -> navigate-to-welcome', + 'root -> help -> open-documentation', + 'root -> help -> open-support', + 'root -> help -> about' +]" `; diff --git a/packages/core/src/features/application-menu/application-menu.test.ts b/packages/core/src/features/application-menu/application-menu.test.ts index 84dddb2531..7f17c9eceb 100644 --- a/packages/core/src/features/application-menu/application-menu.test.ts +++ b/packages/core/src/features/application-menu/application-menu.test.ts @@ -8,6 +8,7 @@ import populateApplicationMenuInjectable from "./main/populate-application-menu. import { advanceFakeTime, testUsingFakeTime } from "../../test-utils/use-fake-time"; import { getCompositePaths } from "../../common/utils/composite/get-composite-paths/get-composite-paths"; import platformInjectable, { allPlatforms } from "../../common/vars/platform.injectable"; +import { inspect } from "util"; describe.each(allPlatforms)("application-menu, given platform is '%s'", (platform) => { let builder: ApplicationBuilder; @@ -53,7 +54,14 @@ describe.each(allPlatforms)("application-menu, given platform is '%s'", (platfor }); it("populates application menu", () => { - expect(applicationMenuPaths.map(x => x.join(" -> "))).toMatchSnapshot(); + expect(inspect(applicationMenuPaths.map(x => x.join(" -> ")), { + compact: false, + breakLength: Infinity, + colors: false, + depth: Infinity, + maxArrayLength: Infinity, + maxStringLength: Infinity, + })).toMatchSnapshot(); }); }); }); diff --git a/packages/core/src/features/application-update/__snapshots__/installing-update.test.ts.snap b/packages/core/src/features/application-update/__snapshots__/installing-update.test.ts.snap index cd0d55e25f..30b66e8970 100644 --- a/packages/core/src/features/application-update/__snapshots__/installing-update.test.ts.snap +++ b/packages/core/src/features/application-update/__snapshots__/installing-update.test.ts.snap @@ -231,7 +231,7 @@ exports[`installing update when started renders 1`] = ` class="HotbarSelector" > { let builder: ApplicationBuilder; let rendered: RenderResult; let windowDi: DiContainer; - let cluster: Cluster; let clusterEntity: KubernetesCluster; let localClusterEntity: KubernetesCluster; let otherEntity: WebLink; @@ -26,20 +25,7 @@ describe("opening catalog entity details panel", () => { beforeEach(async () => { builder = getApplicationBuilder(); - builder.beforeWindowStart(({ windowDi }) => { - // TODO: remove once ClusterStore can be used without overriding it - windowDi.override(getClusterByIdInjectable, () => (clusterId) => { - if (clusterId === cluster?.id) { - return cluster; - } - - return undefined; - }); - }); - - testUsingFakeTime(); - - builder.afterWindowStart(({ windowDi }) => { + builder.afterWindowStart(async ({ windowDi }) => { clusterEntity = new KubernetesCluster({ metadata: { labels: {}, @@ -82,12 +68,33 @@ describe("opening catalog entity details panel", () => { phase: "available", }, }); - cluster = new Cluster({ - contextName: clusterEntity.spec.kubeconfigContext, + + const writeJsonFile = windowDi.inject(writeJsonFileInjectable); + const addCluster = windowDi.inject(addClusterInjectable); + + await writeJsonFile(clusterEntity.spec.kubeconfigPath, { + contexts: [{ + name: clusterEntity.spec.kubeconfigContext, + context: { + cluster: "some-cluster", + user: "some-user", + }, + }], + clusters: [{ + name: "some-cluster", + cluster: { + server: "https://localhost:9999", + }, + }], + users: [{ + name: "some-user", + }], + }); + + addCluster({ id: clusterEntity.getId(), kubeConfigPath: clusterEntity.spec.kubeconfigPath, - }, { - clusterServerUrl: "https://localhost:9999", + contextName: clusterEntity.spec.kubeconfigContext, }); // TODO: replace with proper entity source once syncing entities between main and windows is injectable diff --git a/packages/core/src/features/cluster/activation/main/request-activation.injectable.ts b/packages/core/src/features/cluster/activation/main/request-activation.injectable.ts index c498040fc0..0bc51a6f6f 100644 --- a/packages/core/src/features/cluster/activation/main/request-activation.injectable.ts +++ b/packages/core/src/features/cluster/activation/main/request-activation.injectable.ts @@ -3,8 +3,8 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import getClusterByIdInjectable from "../../../../common/cluster-store/get-by-id.injectable"; import clusterConnectionInjectable from "../../../../main/cluster/cluster-connection.injectable"; +import getClusterByIdInjectable from "../../storage/common/get-by-id.injectable"; import { requestClusterActivationInjectionToken } from "../common/request-token"; const requestClusterActivationInjectable = getInjectable({ diff --git a/packages/core/src/features/cluster/activation/main/request-deactivation.injectable.ts b/packages/core/src/features/cluster/activation/main/request-deactivation.injectable.ts index 05fc00911c..45585419dc 100644 --- a/packages/core/src/features/cluster/activation/main/request-deactivation.injectable.ts +++ b/packages/core/src/features/cluster/activation/main/request-deactivation.injectable.ts @@ -5,8 +5,8 @@ import { getInjectable } from "@ogre-tools/injectable"; import emitAppEventInjectable from "../../../../common/app-event-bus/emit-event.injectable"; import clusterFramesInjectable from "../../../../common/cluster-frames.injectable"; -import getClusterByIdInjectable from "../../../../common/cluster-store/get-by-id.injectable"; import clusterConnectionInjectable from "../../../../main/cluster/cluster-connection.injectable"; +import getClusterByIdInjectable from "../../storage/common/get-by-id.injectable"; import { requestClusterDeactivationInjectionToken } from "../common/request-token"; const requestClusterDeactivationInjectable = getInjectable({ diff --git a/packages/core/src/features/cluster/connections/main/api-url.injectable.ts b/packages/core/src/features/cluster/connections/main/api-url.injectable.ts new file mode 100644 index 0000000000..5cb0b761ec --- /dev/null +++ b/packages/core/src/features/cluster/connections/main/api-url.injectable.ts @@ -0,0 +1,48 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; +import { URL } from "url"; +import type { Cluster } from "../../../../common/cluster/cluster"; +import statInjectable from "../../../../common/fs/stat.injectable"; +import loadValidatedClusterConfigInjectable from "../../../../common/kube-helpers/load-validated-config-from-file.injectable"; + +interface ClusterApiUrlState { + url: URL; + lastReadMtimeMs: number; +} + +const clusterApiUrlInjectable = getInjectable({ + id: "cluster-api-url", + instantiate: (di, cluster): () => Promise => { + const loadValidatedClusterConfig = di.inject(loadValidatedClusterConfigInjectable); + const stat = di.inject(statInjectable); + + let state: ClusterApiUrlState | undefined; + + return async () => { + const stats = await stat(cluster.kubeConfigPath.get()); + + if (!state || state.lastReadMtimeMs >= stats.mtimeMs) { + const result = await loadValidatedClusterConfig(cluster); + + if (result.error) { + throw result.error; + } + + state = { + url: new URL(result.cluster.server), + lastReadMtimeMs: stats.mtimeMs, + }; + } + + return state.url; + }; + }, + lifecycle: lifecycleEnum.keyedSingleton({ + getInstanceKey: (di, cluster: Cluster) => cluster.id, + }), +}); + +export default clusterApiUrlInjectable; diff --git a/packages/core/src/features/cluster/delete-dialog/__snapshots__/delete-cluster-dialog.test.tsx.snap b/packages/core/src/features/cluster/delete-dialog/__snapshots__/delete-cluster-dialog.test.tsx.snap index 980e1a5461..41695a2f1f 100644 --- a/packages/core/src/features/cluster/delete-dialog/__snapshots__/delete-cluster-dialog.test.tsx.snap +++ b/packages/core/src/features/cluster/delete-dialog/__snapshots__/delete-cluster-dialog.test.tsx.snap @@ -542,7 +542,7 @@ exports[`Deleting a cluster when an internal kubeconfig cluster is used when the class="HotbarSelector" > { clusterName: "some-current-context-cluster", }, kubeConfigPath: "./temp-kube-config", - }, { - clusterServerUrl: currentClusterServerUrl, }); nonCurrentCluster = new Cluster({ id: "some-non-current-context-cluster", @@ -119,8 +117,6 @@ describe("Deleting a cluster", () => { clusterName: "some-non-current-context-cluster", }, kubeConfigPath: "./temp-kube-config", - }, { - clusterServerUrl: currentClusterServerUrl, }); }); @@ -197,8 +193,6 @@ describe("Deleting a cluster", () => { clusterName: "some-cluster", }, kubeConfigPath: joinPaths(directoryForKubeConfigs, "some-cluster.json"), - }, { - clusterServerUrl: singleClusterServerUrl, }); }); @@ -233,8 +227,6 @@ describe("Deleting a cluster", () => { clusterName: "some-cluster", }, kubeConfigPath: "./temp-kube-config", - }, { - clusterServerUrl: singleClusterServerUrl, }); }); diff --git a/packages/core/src/features/cluster/delete-dialog/main/delete-channel-listener.injectable.ts b/packages/core/src/features/cluster/delete-dialog/main/delete-channel-listener.injectable.ts index 30d976c59c..5b908a56c0 100644 --- a/packages/core/src/features/cluster/delete-dialog/main/delete-channel-listener.injectable.ts +++ b/packages/core/src/features/cluster/delete-dialog/main/delete-channel-listener.injectable.ts @@ -4,7 +4,6 @@ */ import emitAppEventInjectable from "../../../../common/app-event-bus/emit-event.injectable"; import clusterFramesInjectable from "../../../../common/cluster-frames.injectable"; -import clusterStoreInjectable from "../../../../common/cluster-store/cluster-store.injectable"; import directoryForLensLocalStorageInjectable from "../../../../common/directory-for-lens-local-storage/directory-for-lens-local-storage.injectable"; import removePathInjectable from "../../../../common/fs/remove.injectable"; import joinPathsInjectable from "../../../../common/path/join-paths.injectable"; @@ -12,22 +11,23 @@ import clusterConnectionInjectable from "../../../../main/cluster/cluster-connec import { noop } from "@k8slens/utilities"; import { getRequestChannelListenerInjectable } from "@k8slens/messaging"; import { deleteClusterChannel } from "../common/delete-channel"; +import clustersStateInjectable from "../../storage/common/state.injectable"; const deleteClusterChannelListenerInjectable = getRequestChannelListenerInjectable({ id: "delete-cluster-channel-listener", channel: deleteClusterChannel, getHandler: (di) => { const emitAppEvent = di.inject(emitAppEventInjectable); - const clusterStore = di.inject(clusterStoreInjectable); const clusterFrames = di.inject(clusterFramesInjectable); const joinPaths = di.inject(joinPathsInjectable); const directoryForLensLocalStorage = di.inject(directoryForLensLocalStorageInjectable); const deleteFile = di.inject(removePathInjectable); + const clustersState = di.inject(clustersStateInjectable); return async (clusterId) => { emitAppEvent({ name: "cluster", action: "remove" }); - const cluster = clusterStore.getById(clusterId); + const cluster = clustersState.get(clusterId); if (!cluster) { return; @@ -37,9 +37,7 @@ const deleteClusterChannelListenerInjectable = getRequestChannelListenerInjectab clusterConnection.disconnect(); clusterFrames.delete(cluster.id); - - // Remove from the cluster store as well, this should clear any old settings - clusterStore.clusters.delete(cluster.id); + clustersState.delete(cluster.id); // remove the local storage file const localStorageFilePath = joinPaths(directoryForLensLocalStorage, `${cluster.id}.json`); diff --git a/packages/core/src/features/cluster/refresh-accessibility-technical.test.ts b/packages/core/src/features/cluster/refresh-accessibility-technical.test.ts index 73ff6151b9..4851cca084 100644 --- a/packages/core/src/features/cluster/refresh-accessibility-technical.test.ts +++ b/packages/core/src/features/cluster/refresh-accessibility-technical.test.ts @@ -6,7 +6,6 @@ import type { AsyncFnMock } from "@async-fn/jest"; import asyncFn from "@async-fn/jest"; import type { AuthorizationV1Api, CoreV1Api, V1APIGroupList, V1APIVersions, V1NamespaceList, V1SelfSubjectAccessReview, V1SelfSubjectRulesReview } from "@kubernetes/client-node"; -import clusterStoreInjectable from "../../common/cluster-store/cluster-store.injectable"; import type { Cluster } from "../../common/cluster/cluster"; import createAuthorizationApiInjectable from "../../common/cluster/create-authorization-api.injectable"; import writeJsonFileInjectable from "../../common/fs/write-json-file.injectable"; @@ -26,6 +25,7 @@ import type { KubeAuthProxy } from "../../main/kube-auth-proxy/create-kube-auth- import createKubeAuthProxyInjectable from "../../main/kube-auth-proxy/create-kube-auth-proxy.injectable"; import type { Mocked } from "../../test-utils/mock-interface"; import { flushPromises } from "@k8slens/test-utils"; +import addClusterInjectable from "./storage/common/add.injectable"; describe("Refresh Cluster Accessibility Technical Tests", () => { let builder: ApplicationBuilder; @@ -79,7 +79,7 @@ describe("Refresh Cluster Accessibility Technical Tests", () => { beforeEach(async () => { const mainDi = builder.mainDi; - const clusterStore = mainDi.inject(clusterStoreInjectable); + const addCluster = mainDi.inject(addClusterInjectable); const writeJsonFile = mainDi.inject(writeJsonFileInjectable); await writeJsonFile("/some-kube-config-path", { @@ -103,13 +103,11 @@ describe("Refresh Cluster Accessibility Technical Tests", () => { }], }); - clusterStore.addCluster({ + cluster = addCluster({ contextName: "some-cluster-context", id: "some-cluster-id", kubeConfigPath: "/some-kube-config-path", }); - - cluster = clusterStore.getById("some-cluster-id") ?? (() => { throw new Error("missing cluster"); })(); clusterConnection = mainDi.inject(clusterConnectionInjectable, cluster); refreshPromise = clusterConnection.refreshAccessibilityAndMetadata(); }); diff --git a/packages/core/src/common/cluster-store/allowed-resources-injection-token.ts b/packages/core/src/features/cluster/showing-kube-resources/common/allowed-resources-injection-token.ts similarity index 84% rename from packages/core/src/common/cluster-store/allowed-resources-injection-token.ts rename to packages/core/src/features/cluster/showing-kube-resources/common/allowed-resources-injection-token.ts index 5b71038d04..317ed5a0ec 100644 --- a/packages/core/src/common/cluster-store/allowed-resources-injection-token.ts +++ b/packages/core/src/features/cluster/showing-kube-resources/common/allowed-resources-injection-token.ts @@ -5,7 +5,7 @@ import { getInjectionToken } from "@ogre-tools/injectable"; import type { IComputedValue } from "mobx"; -import type { KubeApiResourceDescriptor } from "../rbac"; +import type { KubeApiResourceDescriptor } from "../../../../common/rbac"; export const shouldShowResourceInjectionToken = getInjectionToken, KubeApiResourceDescriptor>({ id: "should-show-resource", diff --git a/packages/core/src/features/cluster/state-sync/main/handle-initial.injectable.ts b/packages/core/src/features/cluster/state-sync/main/handle-initial.injectable.ts index db65024973..75a68273a0 100644 --- a/packages/core/src/features/cluster/state-sync/main/handle-initial.injectable.ts +++ b/packages/core/src/features/cluster/state-sync/main/handle-initial.injectable.ts @@ -2,17 +2,17 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ -import clusterStoreInjectable from "../../../../common/cluster-store/cluster-store.injectable"; import { getRequestChannelListenerInjectable } from "@k8slens/messaging"; +import clustersInjectable from "../../storage/common/clusters.injectable"; import { initialClusterStatesChannel } from "../common/channels"; const handleInitialClusterStateSyncInjectable = getRequestChannelListenerInjectable({ id: "handle-initial-cluster-state-sync", channel: initialClusterStatesChannel, getHandler: (di) => { - const clusterStore = di.inject(clusterStoreInjectable); + const clusters = di.inject(clustersInjectable); - return () => clusterStore.clustersList.map(cluster => ({ + return () => clusters.get().map(cluster => ({ clusterId: cluster.id, state: cluster.getState(), })); diff --git a/packages/core/src/features/cluster/state-sync/main/setup-sync.injectable.ts b/packages/core/src/features/cluster/state-sync/main/setup-sync.injectable.ts index 348450958d..927c93c148 100644 --- a/packages/core/src/features/cluster/state-sync/main/setup-sync.injectable.ts +++ b/packages/core/src/features/cluster/state-sync/main/setup-sync.injectable.ts @@ -5,22 +5,22 @@ import { getInjectable } from "@ogre-tools/injectable"; import { isEqual } from "lodash"; import { autorun } from "mobx"; -import clusterStoreInjectable from "../../../../common/cluster-store/cluster-store.injectable"; import type { ClusterId, ClusterState } from "../../../../common/cluster-types"; import { beforeApplicationIsLoadingInjectionToken } from "@k8slens/application"; -import initClusterStoreInjectable from "../../store/main/init.injectable"; +import initClusterStoreInjectable from "../../storage/main/init.injectable"; import emitClusterStateUpdateInjectable from "./emit-update.injectable"; +import clustersInjectable from "../../storage/common/clusters.injectable"; const setupClusterStateBroadcastingInjectable = getInjectable({ id: "setup-cluster-state-broadcasting", instantiate: (di) => ({ run: () => { const emitClusterStateUpdate = di.inject(emitClusterStateUpdateInjectable); - const clusterStore = di.inject(clusterStoreInjectable); + const clusters = di.inject(clustersInjectable); const prevStates = new Map(); autorun(() => { - for (const cluster of clusterStore.clusters.values()) { + for (const cluster of clusters.get()) { const prevState = prevStates.get(cluster.id); const curState = cluster.getState(); diff --git a/packages/core/src/features/cluster/state-sync/renderer/listener.injectable.ts b/packages/core/src/features/cluster/state-sync/renderer/listener.injectable.ts index a3778d096e..ec329f2c9d 100644 --- a/packages/core/src/features/cluster/state-sync/renderer/listener.injectable.ts +++ b/packages/core/src/features/cluster/state-sync/renderer/listener.injectable.ts @@ -2,8 +2,8 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ -import getClusterByIdInjectable from "../../../../common/cluster-store/get-by-id.injectable"; import { getMessageChannelListenerInjectable } from "@k8slens/messaging"; +import getClusterByIdInjectable from "../../storage/common/get-by-id.injectable"; import { clusterStateSyncChannel } from "../common/channels"; const clusterStateListenerInjectable = getMessageChannelListenerInjectable({ diff --git a/packages/core/src/features/cluster/state-sync/renderer/setup-sync.injectable.ts b/packages/core/src/features/cluster/state-sync/renderer/setup-sync.injectable.ts index da43ace234..43e1025f0a 100644 --- a/packages/core/src/features/cluster/state-sync/renderer/setup-sync.injectable.ts +++ b/packages/core/src/features/cluster/state-sync/renderer/setup-sync.injectable.ts @@ -3,9 +3,9 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import getClusterByIdInjectable from "../../../../common/cluster-store/get-by-id.injectable"; import { beforeFrameStartsSecondInjectionToken } from "../../../../renderer/before-frame-starts/tokens"; -import initClusterStoreInjectable from "../../store/renderer/init.injectable"; +import getClusterByIdInjectable from "../../storage/common/get-by-id.injectable"; +import initClusterStoreInjectable from "../../storage/renderer/init.injectable"; import requestInitialClusterStatesInjectable from "./request-initial.injectable"; const setupClusterStateSyncInjectable = getInjectable({ diff --git a/packages/core/src/common/__tests__/cluster-store.test.ts b/packages/core/src/features/cluster/storage/cluster-storage.test.ts similarity index 62% rename from packages/core/src/common/__tests__/cluster-store.test.ts rename to packages/core/src/features/cluster/storage/cluster-storage.test.ts index 5b052548a5..4178b3e7d4 100644 --- a/packages/core/src/common/__tests__/cluster-store.test.ts +++ b/packages/core/src/features/cluster/storage/cluster-storage.test.ts @@ -3,29 +3,35 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { ClusterStore } from "../cluster-store/cluster-store"; -import type { GetCustomKubeConfigFilePath } from "../app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable"; -import getCustomKubeConfigFilePathInjectable from "../app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable"; -import clusterStoreInjectable from "../cluster-store/cluster-store.injectable"; +import type { GetCustomKubeConfigFilePath } from "../../../common/app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable"; +import getCustomKubeConfigFilePathInjectable from "../../../common/app-paths/get-custom-kube-config-directory/get-custom-kube-config-directory.injectable"; import type { DiContainer } from "@ogre-tools/injectable"; -import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import { getDiForUnitTesting } from "../../main/getDiForUnitTesting"; +import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; +import { getDiForUnitTesting } from "../../../main/getDiForUnitTesting"; import assert from "assert"; -import directoryForTempInjectable from "../app-paths/directory-for-temp/directory-for-temp.injectable"; -import kubectlBinaryNameInjectable from "../../main/kubectl/binary-name.injectable"; -import kubectlDownloadingNormalizedArchInjectable from "../../main/kubectl/normalized-arch.injectable"; -import normalizedPlatformInjectable from "../vars/normalized-platform.injectable"; -import storeMigrationVersionInjectable from "../vars/store-migration-version.injectable"; -import type { WriteJsonSync } from "../fs/write-json-sync.injectable"; -import writeJsonSyncInjectable from "../fs/write-json-sync.injectable"; -import type { ReadFileSync } from "../fs/read-file-sync.injectable"; -import readFileSyncInjectable from "../fs/read-file-sync.injectable"; +import directoryForTempInjectable from "../../../common/app-paths/directory-for-temp/directory-for-temp.injectable"; +import kubectlBinaryNameInjectable from "../../../main/kubectl/binary-name.injectable"; +import kubectlDownloadingNormalizedArchInjectable from "../../../main/kubectl/normalized-arch.injectable"; +import normalizedPlatformInjectable from "../../../common/vars/normalized-platform.injectable"; +import storeMigrationVersionInjectable from "../../../common/vars/store-migration-version.injectable"; +import type { WriteJsonSync } from "../../../common/fs/write-json-sync.injectable"; +import writeJsonSyncInjectable from "../../../common/fs/write-json-sync.injectable"; +import type { ReadFileSync } from "../../../common/fs/read-file-sync.injectable"; +import readFileSyncInjectable from "../../../common/fs/read-file-sync.injectable"; import { readFileSync } from "fs"; -import type { WriteFileSync } from "../fs/write-file-sync.injectable"; -import writeFileSyncInjectable from "../fs/write-file-sync.injectable"; -import type { WriteBufferSync } from "../fs/write-buffer-sync.injectable"; -import writeBufferSyncInjectable from "../fs/write-buffer-sync.injectable"; -import { Cluster } from "../cluster/cluster"; +import type { WriteFileSync } from "../../../common/fs/write-file-sync.injectable"; +import writeFileSyncInjectable from "../../../common/fs/write-file-sync.injectable"; +import type { WriteBufferSync } from "../../../common/fs/write-buffer-sync.injectable"; +import writeBufferSyncInjectable from "../../../common/fs/write-buffer-sync.injectable"; +import clustersPersistentStorageInjectable from "./common/storage.injectable"; +import type { PersistentStorage } from "../../../common/persistent-storage/create.injectable"; +import type { AddCluster } from "./common/add.injectable"; +import addClusterInjectable from "./common/add.injectable"; +import type { GetClusterById } from "./common/get-by-id.injectable"; +import getClusterByIdInjectable from "./common/get-by-id.injectable"; +import type { IComputedValue } from "mobx"; +import clustersInjectable from "./common/clusters.injectable"; +import type { Cluster } from "../../../common/cluster/cluster"; // NOTE: this is intended to read the actual file system const testDataIcon = readFileSync("test-data/cluster-store-migration-icon.png"); @@ -54,15 +60,18 @@ users: token: kubeconfig-user-q4lm4:xxxyyyy `; -describe("cluster-store", () => { +describe("cluster storage technical tests", () => { let di: DiContainer; - let clusterStore: ClusterStore; + let clustersPersistentStorage: PersistentStorage; let writeJsonSync: WriteJsonSync; let writeFileSync: WriteFileSync; let writeBufferSync: WriteBufferSync; let readFileSync: ReadFileSync; let getCustomKubeConfigFilePath: GetCustomKubeConfigFilePath; let writeFileSyncAndReturnPath: (filePath: string, contents: string) => string; + let addCluster: AddCluster; + let getClusterById: GetClusterById; + let clusters: IComputedValue; beforeEach(async () => { di = getDiForUnitTesting(); @@ -76,6 +85,9 @@ describe("cluster-store", () => { writeFileSync = di.inject(writeFileSyncInjectable); writeBufferSync = di.inject(writeBufferSyncInjectable); readFileSync = di.inject(readFileSyncInjectable); + addCluster = di.inject(addClusterInjectable); + getClusterById = di.inject(getClusterByIdInjectable); + clusters = di.inject(clustersInjectable); writeFileSyncAndReturnPath = (filePath, contents) => (writeFileSync(filePath, contents), filePath); }); @@ -84,13 +96,13 @@ describe("cluster-store", () => { getCustomKubeConfigFilePath = di.inject(getCustomKubeConfigFilePathInjectable); writeJsonSync("/some-directory-for-user-data/lens-cluster-store.json", {}); - clusterStore = di.inject(clusterStoreInjectable); - clusterStore.load(); + clustersPersistentStorage = di.inject(clustersPersistentStorageInjectable); + clustersPersistentStorage.loadAndStartSyncing(); }); describe("with foo cluster added", () => { beforeEach(() => { - const cluster = new Cluster({ + addCluster({ id: "foo", contextName: "foo", preferences: { @@ -102,15 +114,11 @@ describe("cluster-store", () => { getCustomKubeConfigFilePath("foo"), kubeconfig, ), - }, { - clusterServerUrl, }); - - clusterStore.addCluster(cluster); }); it("adds new cluster to store", async () => { - const storedCluster = clusterStore.getById("foo"); + const storedCluster = getClusterById("foo"); assert(storedCluster); @@ -124,9 +132,7 @@ describe("cluster-store", () => { describe("with prod and dev clusters added", () => { beforeEach(() => { - const store = clusterStore; - - store.addCluster({ + addCluster({ id: "prod", contextName: "foo", preferences: { @@ -137,7 +143,7 @@ describe("cluster-store", () => { kubeconfig, ), }); - store.addCluster({ + addCluster({ id: "dev", contextName: "foo2", preferences: { @@ -151,8 +157,7 @@ describe("cluster-store", () => { }); it("check if store can contain multiple clusters", () => { - expect(clusterStore.hasClusters()).toBeTruthy(); - expect(clusterStore.clusters.size).toBe(2); + expect(clusters.get().length).toBe(2); }); it("check if cluster's kubeconfig file saved", () => { @@ -199,11 +204,11 @@ describe("cluster-store", () => { getCustomKubeConfigFilePath = di.inject(getCustomKubeConfigFilePathInjectable); - clusterStore = di.inject(clusterStoreInjectable); - clusterStore.load(); + clustersPersistentStorage = di.inject(clustersPersistentStorageInjectable); + clustersPersistentStorage.loadAndStartSyncing(); }); it("allows to retrieve a cluster", () => { - const storedCluster = clusterStore.getById("cluster1"); + const storedCluster = getClusterById("cluster1"); assert(storedCluster); @@ -212,7 +217,7 @@ describe("cluster-store", () => { }); it("allows getting all of the clusters", async () => { - const storedClusters = clusterStore.clustersList; + const storedClusters = clusters.get(); expect(storedClusters.length).toBe(3); expect(storedClusters[0].id).toBe("cluster1"); @@ -223,47 +228,6 @@ describe("cluster-store", () => { }); }); - describe("config with invalid cluster kubeconfig", () => { - beforeEach(() => { - writeFileSync("/invalid-kube-config", invalidKubeconfig); - writeFileSync("/valid-kube-config", kubeconfig); - writeJsonSync("/some-directory-for-user-data/lens-cluster-store.json", { - __internal__: { - migrations: { - version: "99.99.99", - }, - }, - clusters: [ - { - id: "cluster1", - kubeConfigPath: "/invalid-kube-config", - contextName: "test", - preferences: { terminalCWD: "/foo" }, - workspace: "foo", - }, - { - id: "cluster2", - kubeConfigPath: "/valid-kube-config", - contextName: "foo", - preferences: { terminalCWD: "/foo" }, - workspace: "default", - }, - ], - }); - - getCustomKubeConfigFilePath = di.inject(getCustomKubeConfigFilePathInjectable); - - clusterStore = di.inject(clusterStoreInjectable); - clusterStore.load(); - }); - - it("does not enable clusters with invalid kubeconfig", () => { - const storedClusters = clusterStore.clustersList; - - expect(storedClusters.length).toBe(1); - }); - }); - describe("pre 3.6.0-beta.1 config with an existing cluster", () => { beforeEach(() => { di.override(storeMigrationVersionInjectable, () => "3.6.0"); @@ -290,48 +254,22 @@ describe("cluster-store", () => { writeBufferSync("/some-directory-for-user-data/icon_path", testDataIcon); - clusterStore = di.inject(clusterStoreInjectable); - clusterStore.load(); + clustersPersistentStorage = di.inject(clustersPersistentStorageInjectable); + clustersPersistentStorage.loadAndStartSyncing(); }); it("migrates to modern format with kubeconfig in a file", async () => { - const configPath = clusterStore.clustersList[0].kubeConfigPath.get(); + const configPath = clusters.get()[0].kubeConfigPath.get(); expect(readFileSync(configPath)).toBe(minimalValidKubeConfig); }); it("migrates to modern format with icon not in file", async () => { - expect(clusterStore.clustersList[0].preferences.icon).toMatch(/data:;base64,/); + expect(clusters.get()[0].preferences.icon).toMatch(/data:;base64,/); }); }); }); -const invalidKubeconfig = JSON.stringify({ - apiVersion: "v1", - clusters: [{ - cluster: { - server: "https://localhost", - }, - name: "test2", - }], - contexts: [{ - context: { - cluster: "test", - user: "test", - }, - name: "test", - }], - "current-context": "test", - kind: "Config", - preferences: {}, - users: [{ - user: { - token: "kubeconfig-user-q4lm4:xxxyyyy", - }, - name: "test", - }], -}); - const minimalValidKubeConfig = JSON.stringify({ apiVersion: "v1", clusters: [ diff --git a/packages/core/src/features/cluster/storage/common/add.injectable.ts b/packages/core/src/features/cluster/storage/common/add.injectable.ts new file mode 100644 index 0000000000..ff4ad8d828 --- /dev/null +++ b/packages/core/src/features/cluster/storage/common/add.injectable.ts @@ -0,0 +1,29 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { action } from "mobx"; +import emitAppEventInjectable from "../../../../common/app-event-bus/emit-event.injectable"; +import type { ClusterModel } from "../../../../common/cluster-types"; +import { Cluster } from "../../../../common/cluster/cluster"; +import clustersStateInjectable from "./state.injectable"; +import { setAndGet } from "@k8slens/utilities"; + +export type AddCluster = (clusterModel: ClusterModel) => Cluster; + +const addClusterInjectable = getInjectable({ + id: "add-cluster", + instantiate: (di): AddCluster => { + const clustersState = di.inject(clustersStateInjectable); + const emitAppEvent = di.inject(emitAppEventInjectable); + + return action((clusterModel) => { + emitAppEvent({ name: "cluster", action: "add" }); + + return setAndGet(clustersState, clusterModel.id, new Cluster(clusterModel)); + }); + }, +}); + +export default addClusterInjectable; diff --git a/packages/core/src/features/cluster/storage/common/clusters.injectable.ts b/packages/core/src/features/cluster/storage/common/clusters.injectable.ts new file mode 100644 index 0000000000..d251cf784f --- /dev/null +++ b/packages/core/src/features/cluster/storage/common/clusters.injectable.ts @@ -0,0 +1,18 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { computed } from "mobx"; +import clustersStateInjectable from "./state.injectable"; + +const clustersInjectable = getInjectable({ + id: "clusters", + instantiate: (di) => { + const clustersState = di.inject(clustersStateInjectable); + + return computed(() => [...clustersState.values()]); + }, +}); + +export default clustersInjectable; diff --git a/packages/core/src/common/cluster-store/get-by-id.injectable.ts b/packages/core/src/features/cluster/storage/common/get-by-id.injectable.ts similarity index 59% rename from packages/core/src/common/cluster-store/get-by-id.injectable.ts rename to packages/core/src/features/cluster/storage/common/get-by-id.injectable.ts index 534bdb5e76..2e3128f4d6 100644 --- a/packages/core/src/common/cluster-store/get-by-id.injectable.ts +++ b/packages/core/src/features/cluster/storage/common/get-by-id.injectable.ts @@ -3,18 +3,18 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import type { ClusterId } from "../cluster-types"; -import type { Cluster } from "../cluster/cluster"; -import clusterStoreInjectable from "./cluster-store.injectable"; +import type { ClusterId } from "../../../../common/cluster-types"; +import type { Cluster } from "../../../../common/cluster/cluster"; +import clustersStateInjectable from "./state.injectable"; export type GetClusterById = (id: ClusterId) => Cluster | undefined; const getClusterByIdInjectable = getInjectable({ id: "get-cluster-by-id", instantiate: (di): GetClusterById => { - const store = di.inject(clusterStoreInjectable); + const clustersState = di.inject(clustersStateInjectable); - return (id) => store.getById(id); + return (id) => clustersState.get(id); }, }); diff --git a/packages/core/src/common/cluster-store/migration-token.ts b/packages/core/src/features/cluster/storage/common/migration-token.ts similarity index 76% rename from packages/core/src/common/cluster-store/migration-token.ts rename to packages/core/src/features/cluster/storage/common/migration-token.ts index 86489509a2..8fa8064cf6 100644 --- a/packages/core/src/common/cluster-store/migration-token.ts +++ b/packages/core/src/features/cluster/storage/common/migration-token.ts @@ -4,7 +4,7 @@ */ import { getInjectionToken } from "@ogre-tools/injectable"; -import type { MigrationDeclaration } from "../base-store/migrations.injectable"; +import type { MigrationDeclaration } from "../../../../common/persistent-storage/migrations.injectable"; export const clusterStoreMigrationInjectionToken = getInjectionToken({ id: "cluster-store-migration", diff --git a/packages/core/src/features/cluster/storage/common/state.injectable.ts b/packages/core/src/features/cluster/storage/common/state.injectable.ts new file mode 100644 index 0000000000..a3234dcb62 --- /dev/null +++ b/packages/core/src/features/cluster/storage/common/state.injectable.ts @@ -0,0 +1,15 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { observable } from "mobx"; +import type { ClusterId } from "../../../../common/cluster-types"; +import type { Cluster } from "../../../../common/cluster/cluster"; + +const clustersStateInjectable = getInjectable({ + id: "clusters-state", + instantiate: () => observable.map(), +}); + +export default clustersStateInjectable; diff --git a/packages/core/src/features/cluster/storage/common/storage.injectable.ts b/packages/core/src/features/cluster/storage/common/storage.injectable.ts new file mode 100644 index 0000000000..eb11d4b4e4 --- /dev/null +++ b/packages/core/src/features/cluster/storage/common/storage.injectable.ts @@ -0,0 +1,67 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { iter } from "@k8slens/utilities"; +import { getInjectable } from "@ogre-tools/injectable"; +import { comparer, action } from "mobx"; +import { clusterStoreMigrationInjectionToken } from "./migration-token"; +import type { ClusterId, ClusterModel } from "../../../../common/cluster-types"; +import { Cluster } from "../../../../common/cluster/cluster"; +import loggerInjectable from "../../../../common/logger.injectable"; +import createPersistentStorageInjectable from "../../../../common/persistent-storage/create.injectable"; +import persistentStorageMigrationsInjectable from "../../../../common/persistent-storage/migrations.injectable"; +import storeMigrationVersionInjectable from "../../../../common/vars/store-migration-version.injectable"; +import clustersStateInjectable from "./state.injectable"; + +export interface ClusterStoreModel { + clusters?: ClusterModel[]; +} + +const clustersPersistentStorageInjectable = getInjectable({ + id: "clusters-persistent-storage", + instantiate: (di) => { + const createPersistentStorage = di.inject(createPersistentStorageInjectable); + const clustersState = di.inject(clustersStateInjectable); + const logger = di.inject(loggerInjectable); + + return createPersistentStorage({ + configName: "lens-cluster-store", + accessPropertiesByDotNotation: false, // To make dots safe in cluster context names + syncOptions: { + equals: comparer.structural, + }, + projectVersion: di.inject(storeMigrationVersionInjectable), + migrations: di.inject(persistentStorageMigrationsInjectable, clusterStoreMigrationInjectionToken), + fromStore: action(({ clusters = [] }) => { + const currentClusters = new Map(clustersState); + const newClusters = new Map(); + + for (const clusterModel of clusters) { + try { + let cluster = currentClusters.get(clusterModel.id); + + if (cluster) { + cluster.updateModel(clusterModel); + } else { + cluster = new Cluster(clusterModel); + } + + newClusters.set(clusterModel.id, cluster); + } catch (error) { + logger.warn(`[CLUSTER-STORE]: Failed to update/create a cluster: ${error}`); + } + } + + clustersState.replace(newClusters); + }), + toJSON: () => ({ + clusters: iter.chain(clustersState.values()) + .map(cluster => cluster.toJSON()) + .toArray(), + }), + }); + }, +}); + +export default clustersPersistentStorageInjectable; diff --git a/packages/core/src/features/cluster/store/main/init.injectable.ts b/packages/core/src/features/cluster/storage/main/init.injectable.ts similarity index 60% rename from packages/core/src/features/cluster/store/main/init.injectable.ts rename to packages/core/src/features/cluster/storage/main/init.injectable.ts index c7a6bc1c9a..7a6c906386 100644 --- a/packages/core/src/features/cluster/store/main/init.injectable.ts +++ b/packages/core/src/features/cluster/storage/main/init.injectable.ts @@ -3,19 +3,19 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import clusterStoreInjectable from "../../../../common/cluster-store/cluster-store.injectable"; import { beforeApplicationIsLoadingInjectionToken } from "@k8slens/application"; -import initUserStoreInjectable from "../../../../main/stores/init-user-store.injectable"; +import clustersPersistentStorageInjectable from "../common/storage.injectable"; +import loadUserPreferencesStorageInjectable from "../../../user-preferences/main/load-storage.injectable"; const initClusterStoreInjectable = getInjectable({ id: "init-cluster-store", instantiate: (di) => ({ run: () => { - const clusterStore = di.inject(clusterStoreInjectable); + const storage = di.inject(clustersPersistentStorageInjectable); - clusterStore.load(); + storage.loadAndStartSyncing(); }, - runAfter: initUserStoreInjectable, + runAfter: loadUserPreferencesStorageInjectable, }), injectionToken: beforeApplicationIsLoadingInjectionToken, }); diff --git a/packages/core/src/features/cluster/store/renderer/init.injectable.ts b/packages/core/src/features/cluster/storage/renderer/init.injectable.ts similarity index 67% rename from packages/core/src/features/cluster/store/renderer/init.injectable.ts rename to packages/core/src/features/cluster/storage/renderer/init.injectable.ts index f937796bae..cdedc3fceb 100644 --- a/packages/core/src/features/cluster/store/renderer/init.injectable.ts +++ b/packages/core/src/features/cluster/storage/renderer/init.injectable.ts @@ -3,17 +3,17 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import clusterStoreInjectable from "../../../../common/cluster-store/cluster-store.injectable"; import { beforeFrameStartsSecondInjectionToken } from "../../../../renderer/before-frame-starts/tokens"; -import initUserStoreInjectable from "../../../../renderer/stores/init-user-store.injectable"; +import initUserStoreInjectable from "../../../user-preferences/renderer/load-storage.injectable"; +import clustersPersistentStorageInjectable from "../common/storage.injectable"; const initClusterStoreInjectable = getInjectable({ id: "init-cluster-store", instantiate: (di) => ({ run: () => { - const clusterStore = di.inject(clusterStoreInjectable); + const storage = di.inject(clustersPersistentStorageInjectable); - clusterStore.load(); + storage.loadAndStartSyncing(); }, runAfter: initUserStoreInjectable, }), diff --git a/packages/core/src/features/cluster/visibility-of-sidebar-items.test.tsx b/packages/core/src/features/cluster/visibility-of-sidebar-items.test.tsx index bc96800562..46a8916241 100644 --- a/packages/core/src/features/cluster/visibility-of-sidebar-items.test.tsx +++ b/packages/core/src/features/cluster/visibility-of-sidebar-items.test.tsx @@ -13,7 +13,7 @@ import { frontEndRouteInjectionToken } from "../../common/front-end-routing/fron import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import { navigateToRouteInjectionToken } from "../../common/front-end-routing/navigate-to-route-injection-token"; -import { shouldShowResourceInjectionToken } from "../../common/cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "./showing-kube-resources/common/allowed-resources-injection-token"; describe("cluster - visibility of sidebar items", () => { let builder: ApplicationBuilder; diff --git a/packages/core/src/features/command-pallet/__snapshots__/keyboard-shortcuts.test.tsx.snap b/packages/core/src/features/command-pallet/__snapshots__/keyboard-shortcuts.test.tsx.snap index 0269032531..d21ce102d2 100644 --- a/packages/core/src/features/command-pallet/__snapshots__/keyboard-shortcuts.test.tsx.snap +++ b/packages/core/src/features/command-pallet/__snapshots__/keyboard-shortcuts.test.tsx.snap @@ -322,7 +322,7 @@ exports[`Command Pallet: keyboard shortcut tests when on linux renders 1`] = ` class="HotbarSelector" > { let builder: ApplicationBuilder; @@ -19,23 +19,11 @@ describe("Showing correct entity settings", () => { let clusterEntity: KubernetesCluster; let localClusterEntity: KubernetesCluster; let otherEntity: WebLink; - let cluster: Cluster; beforeEach(async () => { builder = getApplicationBuilder(); - builder.beforeWindowStart(({ windowDi }) => { - // TODO: remove once ClusterStore can be used without overriding it - windowDi.override(getClusterByIdInjectable, () => (clusterId) => { - if (clusterId === cluster.id) { - return cluster; - } - - return undefined; - }); - }); - - builder.afterWindowStart(({ windowDi }) => { + builder.afterWindowStart(async ({ windowDi }) => { clusterEntity = new KubernetesCluster({ metadata: { labels: {}, @@ -78,14 +66,34 @@ describe("Showing correct entity settings", () => { phase: "available", }, }); - cluster = new Cluster({ - contextName: clusterEntity.spec.kubeconfigContext, - id: clusterEntity.getId(), - kubeConfigPath: clusterEntity.spec.kubeconfigPath, - }, { - clusterServerUrl: "https://localhost:9999", + + const writeJsonFile = windowDi.inject(writeJsonFileInjectable); + const addCluster = windowDi.inject(addClusterInjectable); + + await writeJsonFile(clusterEntity.spec.kubeconfigPath, { + contexts: [{ + name: clusterEntity.spec.kubeconfigContext, + context: { + cluster: "some-cluster", + user: "some-user", + }, + }], + clusters: [{ + name: "some-cluster", + cluster: { + server: "https://localhost:9999", + }, + }], + users: [{ + name: "some-user", + }], }); + addCluster({ + id: clusterEntity.getId(), + kubeConfigPath: clusterEntity.spec.kubeconfigPath, + contextName: clusterEntity.spec.kubeconfigContext, + }); // TODO: replace with proper entity source once syncing entities between main and windows is injectable const catalogEntityRegistry = windowDi.inject(catalogEntityRegistryInjectable); diff --git a/packages/core/src/features/extensions/__snapshots__/navigation-using-application-menu.test.ts.snap b/packages/core/src/features/extensions/__snapshots__/navigation-using-application-menu.test.ts.snap index 53dc28e961..dbaf12b978 100644 --- a/packages/core/src/features/extensions/__snapshots__/navigation-using-application-menu.test.ts.snap +++ b/packages/core/src/features/extensions/__snapshots__/navigation-using-application-menu.test.ts.snap @@ -230,7 +230,7 @@ exports[`extensions - navigation using application menu renders 1`] = ` class="HotbarSelector" > { + const state = di.inject(enabledExtensionsStateInjectable); + + return computed(() => ( + iter.chain(state.values()) + .filter(({ enabled }) => enabled) + .map(({ name }) => name) + .toArray() + )); + }, +}); + +export default enabledExtensionsInjectable; diff --git a/packages/core/src/features/extensions/enabled/common/is-enabled.injectable.ts b/packages/core/src/features/extensions/enabled/common/is-enabled.injectable.ts new file mode 100644 index 0000000000..bb7f531cb3 --- /dev/null +++ b/packages/core/src/features/extensions/enabled/common/is-enabled.injectable.ts @@ -0,0 +1,24 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import enabledExtensionsStateInjectable from "./state.injectable"; + +export interface IsEnabledExtensionDescriptor { + readonly id: string; + readonly isBundled: boolean; +} + +export type IsExtensionEnabled = (desc: IsEnabledExtensionDescriptor) => boolean; + +const isExtensionEnabledInjectable = getInjectable({ + id: "is-extension-enabled", + instantiate: (di): IsExtensionEnabled => { + const state = di.inject(enabledExtensionsStateInjectable); + + return ({ id, isBundled }) => isBundled || (state.get(id)?.enabled ?? false); + }, +}); + +export default isExtensionEnabledInjectable; diff --git a/packages/core/src/features/extensions/enabled/common/migrations.ts b/packages/core/src/features/extensions/enabled/common/migrations.ts new file mode 100644 index 0000000000..eef1c4c996 --- /dev/null +++ b/packages/core/src/features/extensions/enabled/common/migrations.ts @@ -0,0 +1,11 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getInjectionToken } from "@ogre-tools/injectable"; +import type { MigrationDeclaration } from "../../../../common/persistent-storage/migrations.injectable"; + +export const enabledExtensionsMigrationDeclarationInjectionToken = getInjectionToken({ + id: "enabled-extensions-migration-declaration", +}); diff --git a/packages/core/src/features/extensions/enabled/common/state.injectable.ts b/packages/core/src/features/extensions/enabled/common/state.injectable.ts new file mode 100644 index 0000000000..6cf87ace88 --- /dev/null +++ b/packages/core/src/features/extensions/enabled/common/state.injectable.ts @@ -0,0 +1,19 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import type { LensExtensionId } from "@k8slens/legacy-extensions"; +import { getInjectable } from "@ogre-tools/injectable"; +import { observable } from "mobx"; + +export interface LensExtensionState { + enabled?: boolean; + name: string; +} + +const enabledExtensionsStateInjectable = getInjectable({ + id: "enabled-extensions-state", + instantiate: () => observable.map(), +}); + +export default enabledExtensionsStateInjectable; diff --git a/packages/core/src/features/extensions/enabled/common/storage.injectable.ts b/packages/core/src/features/extensions/enabled/common/storage.injectable.ts new file mode 100644 index 0000000000..47a9930ccf --- /dev/null +++ b/packages/core/src/features/extensions/enabled/common/storage.injectable.ts @@ -0,0 +1,39 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import type { LensExtensionId } from "@k8slens/legacy-extensions"; +import { getInjectable } from "@ogre-tools/injectable"; +import { action, toJS } from "mobx"; +import createPersistentStorageInjectable from "../../../../common/persistent-storage/create.injectable"; +import persistentStorageMigrationsInjectable from "../../../../common/persistent-storage/migrations.injectable"; +import storageMigrationVersionInjectable from "../../../../common/persistent-storage/storage-migration-version.injectable"; +import { enabledExtensionsMigrationDeclarationInjectionToken } from "./migrations"; +import type { LensExtensionState } from "./state.injectable"; +import enabledExtensionsStateInjectable from "./state.injectable"; + +interface EnabledExtensionsStorageModal { + extensions: [LensExtensionId, LensExtensionState][]; +} + +const enabledExtensionsPersistentStorageInjectable = getInjectable({ + id: "enabled-extensions-persistent-storage", + instantiate: (di) => { + const createPersistentStorage = di.inject(createPersistentStorageInjectable); + const state = di.inject(enabledExtensionsStateInjectable); + + return createPersistentStorage({ + configName: "lens-extensions", + fromStore: action(({ extensions = [] }) => { + state.replace(extensions); + }), + toJSON: () => ({ + extensions: [...toJS(state)], + }), + projectVersion: di.inject(storageMigrationVersionInjectable, enabledExtensionsMigrationDeclarationInjectionToken), + migrations: di.inject(persistentStorageMigrationsInjectable, enabledExtensionsMigrationDeclarationInjectionToken), + }); + }, +}); + +export default enabledExtensionsPersistentStorageInjectable; diff --git a/packages/core/src/features/extensions/enabled/common/update-state.injectable.ts b/packages/core/src/features/extensions/enabled/common/update-state.injectable.ts new file mode 100644 index 0000000000..e12c2d32d9 --- /dev/null +++ b/packages/core/src/features/extensions/enabled/common/update-state.injectable.ts @@ -0,0 +1,23 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import type { LensExtensionId } from "@k8slens/legacy-extensions"; +import { getInjectable } from "@ogre-tools/injectable"; +import type { IObservableMapInitialValues } from "mobx"; +import { action } from "mobx"; +import type { LensExtensionState } from "./state.injectable"; +import enabledExtensionsStateInjectable from "./state.injectable"; + +export type UpdateExtensionsState = (state: IObservableMapInitialValues) => void; + +const updateExtensionsStateInjectable = getInjectable({ + id: "update-extensions-state", + instantiate: (di): UpdateExtensionsState => { + const state = di.inject(enabledExtensionsStateInjectable); + + return action((newState) => state.merge(newState)); + }, +}); + +export default updateExtensionsStateInjectable; diff --git a/packages/core/src/features/extensions/enabled/main/load-storage.injectable.ts b/packages/core/src/features/extensions/enabled/main/load-storage.injectable.ts new file mode 100644 index 0000000000..e3c301627b --- /dev/null +++ b/packages/core/src/features/extensions/enabled/main/load-storage.injectable.ts @@ -0,0 +1,21 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { beforeApplicationIsLoadingInjectionToken } from "@k8slens/application"; +import { getInjectable } from "@ogre-tools/injectable"; +import enabledExtensionsPersistentStorageInjectable from "../common/storage.injectable"; + +const loadEnabledExtensionsStorageInjectable = getInjectable({ + id: "load-enabled-extensions-storage", + instantiate: (di) => ({ + run: () => { + const storage = di.inject(enabledExtensionsPersistentStorageInjectable); + + storage.loadAndStartSyncing(); + }, + }), + injectionToken: beforeApplicationIsLoadingInjectionToken, +}); + +export default loadEnabledExtensionsStorageInjectable; diff --git a/packages/core/src/features/extensions/enabled/main/v6.5.0-migration.injectable.ts b/packages/core/src/features/extensions/enabled/main/v6.5.0-migration.injectable.ts new file mode 100644 index 0000000000..f658180535 --- /dev/null +++ b/packages/core/src/features/extensions/enabled/main/v6.5.0-migration.injectable.ts @@ -0,0 +1,26 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { isObject } from "@k8slens/utilities"; +import { getInjectable } from "@ogre-tools/injectable"; +import { enabledExtensionsMigrationDeclarationInjectionToken } from "../common/migrations"; + +const enabledExtensionsMigrationV650Injectable = getInjectable({ + id: "enabled-extensions-migration-v650", + instantiate: () => ({ + version: "6.5.0", + run: (store) => { + const extensions = store.get("extensions"); + + if (!isObject(extensions)) { + store.delete("extensions"); + } else { + store.set("extensions", Object.entries(extensions)); + } + }, + }), + injectionToken: enabledExtensionsMigrationDeclarationInjectionToken, +}); + +export default enabledExtensionsMigrationV650Injectable; diff --git a/packages/core/src/features/extensions/enabled/renderer/load-storage.injectable.ts b/packages/core/src/features/extensions/enabled/renderer/load-storage.injectable.ts new file mode 100644 index 0000000000..50be3cd88d --- /dev/null +++ b/packages/core/src/features/extensions/enabled/renderer/load-storage.injectable.ts @@ -0,0 +1,21 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { beforeFrameStartsSecondInjectionToken } from "../../../../renderer/before-frame-starts/tokens"; +import enabledExtensionsPersistentStorageInjectable from "../common/storage.injectable"; + +const loadEnabledExtensionsStorageInjectable = getInjectable({ + id: "load-enabled-extensions-storage", + instantiate: (di) => ({ + run: () => { + const storage = di.inject(enabledExtensionsPersistentStorageInjectable); + + storage.loadAndStartSyncing(); + }, + }), + injectionToken: beforeFrameStartsSecondInjectionToken, +}); + +export default loadEnabledExtensionsStorageInjectable; diff --git a/packages/core/src/features/extensions/stopping/main/stop-all.injectable.ts b/packages/core/src/features/extensions/stopping/main/stop-all.injectable.ts new file mode 100644 index 0000000000..ce3d69d723 --- /dev/null +++ b/packages/core/src/features/extensions/stopping/main/stop-all.injectable.ts @@ -0,0 +1,25 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import extensionInjectable from "../../../../extensions/extension-loader/extension/extension.injectable"; +import extensionsInjectable from "../../../../extensions/extensions.injectable"; + +const stopAllExtensionsInjectable = getInjectable({ + id: "stop-all-extensions", + instantiate: (di) => { + const extensionInstances = di.inject(extensionsInjectable); + + return async () => { + for (const instance of extensionInstances.get()) { + const extension = di.inject(extensionInjectable, instance); + + await instance.disable(); + extension.deregister(); + } + }; + }, +}); + +export default stopAllExtensionsInjectable; diff --git a/packages/core/src/features/helm-charts/__snapshots__/add-custom-helm-repository-in-preferences.test.ts.snap b/packages/core/src/features/helm-charts/__snapshots__/add-custom-helm-repository-in-preferences.test.ts.snap index 7932686bbf..7de4de3f87 100644 --- a/packages/core/src/features/helm-charts/__snapshots__/add-custom-helm-repository-in-preferences.test.ts.snap +++ b/packages/core/src/features/helm-charts/__snapshots__/add-custom-helm-repository-in-preferences.test.ts.snap @@ -649,7 +649,7 @@ exports[`add custom helm repository in preferences when navigating to preference class="HotbarSelector" >
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
+ class="chartIcon intro-logo imageNotLoaded" + data-testid="image-container" + style="background-image: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNzIyLjggNzAyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogICAgICAgICAgPGcgZmlsbD0iIzhlOTI5NyI+CiAgICAgICAgICAgIDxwYXRoIGQ9Im0zMTggMjk5LjVjMi4xIDEuNiA0LjggMi41IDcuNiAyLjUgNi45IDAgMTIuNi01LjUgMTIuOS0xMi4zbC4zLS4yIDQuMy03Ni43Yy01LjIuNi0xMC40IDEuNS0xNS42IDIuNy0yOC41IDYuNS01My4yIDIwLjUtNzIuNiAzOS41bDYyLjkgNDQuNnoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTMwOS41IDQxMS45Yy0xLjQtNS45LTYuNi05LjktMTIuNC0xMC0uOCAwLTEuNy4xLTIuNS4ybC0uMS0uMi03NS41IDEyLjhjMTEuNyAzMi4yIDMzLjQgNTguNSA2MC44IDc2LjFsMjkuMi03MC43LS4yLS4zYzEuMS0yLjQgMS40LTUuMi43LTcuOXoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTI4NC40IDM1Ny41YzIuNS0uNyA0LjktMi4yIDYuNy00LjQgNC4zLTUuNCAzLjYtMTMuMi0xLjYtMTcuOGwuMS0uMy01Ny40LTUxLjRjLTE3IDI3LjgtMjUuMSA2MS4xLTIxLjQgOTUuM2w3My42LTIxLjJ6Ii8+CiAgICAgICAgICAgIDxwYXRoIGQ9Im0zNDAuMiAzODAgMjEuMiAxMC4yIDIxLjEtMTAuMSA1LjMtMjIuOS0xNC42LTE4LjJoLTIzLjZsLTE0LjYgMTguMnoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTM4NC4yIDI4OS40Yy4xIDIuNiAxIDUuMiAyLjggNy41IDQuMyA1LjQgMTIuMSA2LjQgMTcuNyAyLjRsLjIuMSA2Mi41LTQ0LjNjLTIzLjYtMjMuMS01NC40LTM4LjItODcuNi00Mi4yeiIvPgogICAgICAgICAgICA8cGF0aCBkPSJtNDkwLjMgMjgzLjctNTcuMSA1MS4xdi4yYy0yIDEuNy0zLjUgNC4xLTQuMSA2LjgtMS41IDYuOCAyLjUgMTMuNSA5LjIgMTUuM2wuMS4zIDc0IDIxLjNjMS42LTE2IC42LTMyLjUtMy4yLTQ5LTMuOS0xNi44LTEwLjQtMzIuMi0xOC45LTQ2eiIvPgogICAgICAgICAgICA8cGF0aCBkPSJtMzcyLjggNDM5LjZjLTEuMi0yLjMtMy4yLTQuMy01LjgtNS41LTItLjktNC0xLjQtNi0xLjMtNC41LjItOC43IDIuNi0xMC45IDYuOGgtLjFsLTM3LjEgNjcuMWMyNS43IDguOCA1NC4xIDEwLjcgODIuNSA0LjIgNS4xLTEuMiAxMC0yLjUgMTQuOS00LjJsLTM3LjMtNjcuMXoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTcxMS43IDQyNS02MC40LTI2Mi4yYy0zLjItMTMuNy0xMi41LTI1LjMtMjUuMy0zMS40bC0yNDQuNC0xMTYuOGMtNy4xLTMuNC0xNC44LTQuOS0yMi43LTQuNS02LjIuMy0xMi4zIDEuOS0xNy45IDQuNWwtMjQ0LjMgMTE2LjdjLTEyLjggNi4xLTIyLjEgMTcuNy0yNS4zIDMxLjRsLTYwLjIgMjYyLjNjLTIuOCAxMi4yLS41IDI1IDYuMyAzNS41LjggMS4zIDEuNyAyLjUgMi43IDMuN2wxNjkuMSAyMTAuM2M4LjkgMTEgMjIuMyAxNy40IDM2LjUgMTcuNGwyNzEuMi0uMWMxNC4yIDAgMjcuNy02LjQgMzYuNS0xNy40bDE2OS4xLTIxMC4zYzguOS0xMC45IDEyLjItMjUuNCA5LjEtMzkuMXptLTkzLTMuMmMtMS44IDcuOC0xMC4yIDEyLjYtMTguOSAxMC43LS4xIDAtLjIgMC0uMiAwLS4xIDAtLjItLjEtLjMtLjEtMS4yLS4zLTIuNy0uNS0zLjgtLjgtNS0xLjMtOC42LTMuMy0xMy4xLTUuMS05LjctMy41LTE3LjctNi40LTI1LjUtNy41LTQtLjMtNiAxLjYtOC4yIDMtMS4xLS4yLTQuNC0uOC02LjItMS4xLTE0IDQ0LTQzLjkgODIuMi04NC4zIDEwNi4xLjcgMS43IDEuOSA1LjMgMi40IDUuOS0uOSAyLjUtMi4zIDQuOC0xLjEgOC42IDIuOCA3LjQgNy40IDE0LjYgMTMgMjMuMiAyLjcgNCA1LjQgNy4xIDcuOCAxMS43LjYgMS4xIDEuMyAyLjggMS45IDMuOSAzLjggOCAxIDE3LjMtNi4yIDIwLjgtNy4zIDMuNS0xNi4zLS4yLTIwLjItOC4zLS42LTEuMS0xLjMtMi43LTEuOC0zLjgtMi4xLTQuNy0yLjgtOC44LTQuMi0xMy40LTMuMy05LjctNi0xNy44LTEwLTI0LjYtMi4yLTMuMy01LTMuNy03LjUtNC41LS41LS44LTIuMi00LTMuMS01LjYtOC4xIDMuMS0xNi40IDUuNi0yNS4xIDcuNi0zNy45IDguNi03NS45IDUuMS0xMDkuOS03LjlsLTMuMyA2Yy0yLjUuNy00LjggMS4zLTYuMyAzLjEtNS4zIDYuNC03LjUgMTYuNi0xMS4zIDI2LjMtMS41IDQuNi0yLjEgOC43LTQuMiAxMy40LS41IDEuMS0xLjMgMi42LTEuOCAzLjctMy45IDguMS0xMi45IDExLjctMjAuMiA4LjItNy4yLTMuNS0xMC0xMi43LTYuMi0yMC44LjYtMS4yIDEuMy0yLjggMS45LTMuOSAyLjQtNC42IDUuMi03LjcgNy44LTExLjcgNS41LTguNyAxMC40LTE2LjQgMTMuMi0yMy44LjctMi40LS4zLTUuOC0xLjMtOC4zbDIuNy02LjRjLTM4LjktMjMuMS02OS43LTU5LjgtODQuMy0xMDUuM2wtNi40IDEuMWMtMS43LTEtNS4xLTMuMi04LjQtMy03LjggMS4xLTE1LjggNC0yNS41IDcuNS00LjUgMS43LTguMSAzLjctMTMuMSA1LTEuMS4zLTIuNi42LTMuOC44LS4xIDAtLjIuMS0uMy4xcy0uMiAwLS4yIDBjLTguNyAxLjktMTcuMS0yLjktMTguOS0xMC43czMuOC0xNS43IDEyLjQtMTcuOGMuMSAwIC4yIDAgLjItLjFoLjFjMS4yLS4zIDIuOC0uNyAzLjktLjkgNS4xLTEgOS4yLS43IDE0LTEuMSAxMC4yLTEuMSAxOC43LTEuOSAyNi4yLTQuMyAyLjQtMSA0LjctNC4zIDYuMy02LjNsNi4xLTEuOGMtNi45LTQ3LjUgNC44LTk0LjIgMjkuOC0xMzEuOWwtNC43LTQuMmMtLjMtMS44LS43LTYtMi45LTguNC01LjgtNS40LTEzLTkuOS0yMS44LTE1LjMtNC4yLTIuNC04LTQtMTIuMS03LjEtLjktLjctMi4xLTEuNy0zLTIuNC0uMS0uMS0uMS0uMS0uMi0uMi03LTUuNi04LjYtMTUuMi0zLjYtMjEuNiAyLjgtMy42IDcuMi01LjMgMTEuNy01LjIgMy41LjEgNy4xIDEuNCAxMC4yIDMuOCAxIC44IDIuNCAxLjggMy4yIDIuNiAzLjkgMy40IDYuMyA2LjcgOS42IDEwLjIgNy4yIDcuMyAxMy4yIDEzLjQgMTkuNyAxNy44IDMuNCAyIDYuMSAxLjIgOC43LjguOC42IDMuNyAyLjYgNS4zIDMuOCAyNC45LTI2LjQgNTcuNi00NiA5NS42LTU0LjYgOC44LTIgMTcuNy0zLjMgMjYuNC00LjFsLjMtNi4yYzEuOS0xLjkgNC4xLTQuNiA0LjgtNy42LjYtNy45LS40LTE2LjMtMS42LTI2LjUtLjctNC44LTEuOC04LjctMi0xMy45IDAtMS4xIDAtMi41IDAtMy44IDAtLjEgMC0uMyAwLS40IDAtOSA2LjUtMTYuMiAxNC42LTE2LjJzMTQuNiA3LjMgMTQuNiAxNi4yYzAgMS4zLjEgMyAwIDQuMi0uMiA1LjItMS4zIDkuMS0yIDEzLjktMS4yIDEwLjItMi4zIDE4LjctMS43IDI2LjUuNiAzLjkgMi45IDUuNSA0LjggNy4zIDAgMS4xLjIgNC42LjMgNi41IDQ2LjUgNC4xIDg5LjcgMjUuNCAxMjEuNCA1OC43bDUuNi00YzEuOS4xIDYgLjcgOC45LTEgNi41LTQuNCAxMi41LTEwLjUgMTkuNy0xNy44IDMuMy0zLjUgNS43LTYuOCA5LjctMTAuMi45LS44IDIuMy0xLjggMy4yLTIuNiA3LTUuNiAxNi44LTUgMjEuOCAxLjNzMy40IDE2LTMuNiAyMS42Yy0xIC44LTIuMyAxLjktMy4yIDIuNi00LjIgMy4xLTggNC43LTEyLjIgNy4xLTguNyA1LjQtMTYgOS45LTIxLjggMTUuMy0yLjcgMi45LTIuNSA1LjctMi44IDguMy0uOC43LTMuNyAzLjMtNS4yIDQuNyAxMi42IDE4LjggMjIuMSA0MC4xIDI3LjQgNjMuMyA1LjMgMjMuMSA2LjEgNDYuMSAzLjEgNjguM2w1LjkgMS43YzEuMSAxLjUgMy4yIDUuMiA2LjMgNi4zIDcuNSAyLjQgMTYgMy4yIDI2LjIgNC4zIDQuOC40IDguOS4yIDE0IDEuMSAxLjIuMiAzIC43IDQuMiAxIDguOSAyLjQgMTQuNCAxMC40IDEyLjYgMTguMnoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTQyOCA0MDEuN2MtMS0uMi0yLS4zLTMtLjItMS43LjEtMy4zLjUtNC45IDEuMy02LjIgMy05IDEwLjQtNi4yIDE2LjdsLS4xLjEgMjkuNiA3MS40YzI4LjUtMTguMiA0OS44LTQ1LjMgNjEtNzYuNmwtNzYuMi0xMi45eiIvPgogICAgICAgICAgPC9nPgogICAgICAgIDwvc3ZnPg==);" + />
@@ -1917,44 +1816,11 @@ exports[`installing helm chart from new tab given tab for installing chart was n class="TableCell icon" >
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
+ class="chartIcon intro-logo imageNotLoaded" + data-testid="image-container" + style="background-image: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNzIyLjggNzAyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogICAgICAgICAgPGcgZmlsbD0iIzhlOTI5NyI+CiAgICAgICAgICAgIDxwYXRoIGQ9Im0zMTggMjk5LjVjMi4xIDEuNiA0LjggMi41IDcuNiAyLjUgNi45IDAgMTIuNi01LjUgMTIuOS0xMi4zbC4zLS4yIDQuMy03Ni43Yy01LjIuNi0xMC40IDEuNS0xNS42IDIuNy0yOC41IDYuNS01My4yIDIwLjUtNzIuNiAzOS41bDYyLjkgNDQuNnoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTMwOS41IDQxMS45Yy0xLjQtNS45LTYuNi05LjktMTIuNC0xMC0uOCAwLTEuNy4xLTIuNS4ybC0uMS0uMi03NS41IDEyLjhjMTEuNyAzMi4yIDMzLjQgNTguNSA2MC44IDc2LjFsMjkuMi03MC43LS4yLS4zYzEuMS0yLjQgMS40LTUuMi43LTcuOXoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTI4NC40IDM1Ny41YzIuNS0uNyA0LjktMi4yIDYuNy00LjQgNC4zLTUuNCAzLjYtMTMuMi0xLjYtMTcuOGwuMS0uMy01Ny40LTUxLjRjLTE3IDI3LjgtMjUuMSA2MS4xLTIxLjQgOTUuM2w3My42LTIxLjJ6Ii8+CiAgICAgICAgICAgIDxwYXRoIGQ9Im0zNDAuMiAzODAgMjEuMiAxMC4yIDIxLjEtMTAuMSA1LjMtMjIuOS0xNC42LTE4LjJoLTIzLjZsLTE0LjYgMTguMnoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTM4NC4yIDI4OS40Yy4xIDIuNiAxIDUuMiAyLjggNy41IDQuMyA1LjQgMTIuMSA2LjQgMTcuNyAyLjRsLjIuMSA2Mi41LTQ0LjNjLTIzLjYtMjMuMS01NC40LTM4LjItODcuNi00Mi4yeiIvPgogICAgICAgICAgICA8cGF0aCBkPSJtNDkwLjMgMjgzLjctNTcuMSA1MS4xdi4yYy0yIDEuNy0zLjUgNC4xLTQuMSA2LjgtMS41IDYuOCAyLjUgMTMuNSA5LjIgMTUuM2wuMS4zIDc0IDIxLjNjMS42LTE2IC42LTMyLjUtMy4yLTQ5LTMuOS0xNi44LTEwLjQtMzIuMi0xOC45LTQ2eiIvPgogICAgICAgICAgICA8cGF0aCBkPSJtMzcyLjggNDM5LjZjLTEuMi0yLjMtMy4yLTQuMy01LjgtNS41LTItLjktNC0xLjQtNi0xLjMtNC41LjItOC43IDIuNi0xMC45IDYuOGgtLjFsLTM3LjEgNjcuMWMyNS43IDguOCA1NC4xIDEwLjcgODIuNSA0LjIgNS4xLTEuMiAxMC0yLjUgMTQuOS00LjJsLTM3LjMtNjcuMXoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTcxMS43IDQyNS02MC40LTI2Mi4yYy0zLjItMTMuNy0xMi41LTI1LjMtMjUuMy0zMS40bC0yNDQuNC0xMTYuOGMtNy4xLTMuNC0xNC44LTQuOS0yMi43LTQuNS02LjIuMy0xMi4zIDEuOS0xNy45IDQuNWwtMjQ0LjMgMTE2LjdjLTEyLjggNi4xLTIyLjEgMTcuNy0yNS4zIDMxLjRsLTYwLjIgMjYyLjNjLTIuOCAxMi4yLS41IDI1IDYuMyAzNS41LjggMS4zIDEuNyAyLjUgMi43IDMuN2wxNjkuMSAyMTAuM2M4LjkgMTEgMjIuMyAxNy40IDM2LjUgMTcuNGwyNzEuMi0uMWMxNC4yIDAgMjcuNy02LjQgMzYuNS0xNy40bDE2OS4xLTIxMC4zYzguOS0xMC45IDEyLjItMjUuNCA5LjEtMzkuMXptLTkzLTMuMmMtMS44IDcuOC0xMC4yIDEyLjYtMTguOSAxMC43LS4xIDAtLjIgMC0uMiAwLS4xIDAtLjItLjEtLjMtLjEtMS4yLS4zLTIuNy0uNS0zLjgtLjgtNS0xLjMtOC42LTMuMy0xMy4xLTUuMS05LjctMy41LTE3LjctNi40LTI1LjUtNy41LTQtLjMtNiAxLjYtOC4yIDMtMS4xLS4yLTQuNC0uOC02LjItMS4xLTE0IDQ0LTQzLjkgODIuMi04NC4zIDEwNi4xLjcgMS43IDEuOSA1LjMgMi40IDUuOS0uOSAyLjUtMi4zIDQuOC0xLjEgOC42IDIuOCA3LjQgNy40IDE0LjYgMTMgMjMuMiAyLjcgNCA1LjQgNy4xIDcuOCAxMS43LjYgMS4xIDEuMyAyLjggMS45IDMuOSAzLjggOCAxIDE3LjMtNi4yIDIwLjgtNy4zIDMuNS0xNi4zLS4yLTIwLjItOC4zLS42LTEuMS0xLjMtMi43LTEuOC0zLjgtMi4xLTQuNy0yLjgtOC44LTQuMi0xMy40LTMuMy05LjctNi0xNy44LTEwLTI0LjYtMi4yLTMuMy01LTMuNy03LjUtNC41LS41LS44LTIuMi00LTMuMS01LjYtOC4xIDMuMS0xNi40IDUuNi0yNS4xIDcuNi0zNy45IDguNi03NS45IDUuMS0xMDkuOS03LjlsLTMuMyA2Yy0yLjUuNy00LjggMS4zLTYuMyAzLjEtNS4zIDYuNC03LjUgMTYuNi0xMS4zIDI2LjMtMS41IDQuNi0yLjEgOC43LTQuMiAxMy40LS41IDEuMS0xLjMgMi42LTEuOCAzLjctMy45IDguMS0xMi45IDExLjctMjAuMiA4LjItNy4yLTMuNS0xMC0xMi43LTYuMi0yMC44LjYtMS4yIDEuMy0yLjggMS45LTMuOSAyLjQtNC42IDUuMi03LjcgNy44LTExLjcgNS41LTguNyAxMC40LTE2LjQgMTMuMi0yMy44LjctMi40LS4zLTUuOC0xLjMtOC4zbDIuNy02LjRjLTM4LjktMjMuMS02OS43LTU5LjgtODQuMy0xMDUuM2wtNi40IDEuMWMtMS43LTEtNS4xLTMuMi04LjQtMy03LjggMS4xLTE1LjggNC0yNS41IDcuNS00LjUgMS43LTguMSAzLjctMTMuMSA1LTEuMS4zLTIuNi42LTMuOC44LS4xIDAtLjIuMS0uMy4xcy0uMiAwLS4yIDBjLTguNyAxLjktMTcuMS0yLjktMTguOS0xMC43czMuOC0xNS43IDEyLjQtMTcuOGMuMSAwIC4yIDAgLjItLjFoLjFjMS4yLS4zIDIuOC0uNyAzLjktLjkgNS4xLTEgOS4yLS43IDE0LTEuMSAxMC4yLTEuMSAxOC43LTEuOSAyNi4yLTQuMyAyLjQtMSA0LjctNC4zIDYuMy02LjNsNi4xLTEuOGMtNi45LTQ3LjUgNC44LTk0LjIgMjkuOC0xMzEuOWwtNC43LTQuMmMtLjMtMS44LS43LTYtMi45LTguNC01LjgtNS40LTEzLTkuOS0yMS44LTE1LjMtNC4yLTIuNC04LTQtMTIuMS03LjEtLjktLjctMi4xLTEuNy0zLTIuNC0uMS0uMS0uMS0uMS0uMi0uMi03LTUuNi04LjYtMTUuMi0zLjYtMjEuNiAyLjgtMy42IDcuMi01LjMgMTEuNy01LjIgMy41LjEgNy4xIDEuNCAxMC4yIDMuOCAxIC44IDIuNCAxLjggMy4yIDIuNiAzLjkgMy40IDYuMyA2LjcgOS42IDEwLjIgNy4yIDcuMyAxMy4yIDEzLjQgMTkuNyAxNy44IDMuNCAyIDYuMSAxLjIgOC43LjguOC42IDMuNyAyLjYgNS4zIDMuOCAyNC45LTI2LjQgNTcuNi00NiA5NS42LTU0LjYgOC44LTIgMTcuNy0zLjMgMjYuNC00LjFsLjMtNi4yYzEuOS0xLjkgNC4xLTQuNiA0LjgtNy42LjYtNy45LS40LTE2LjMtMS42LTI2LjUtLjctNC44LTEuOC04LjctMi0xMy45IDAtMS4xIDAtMi41IDAtMy44IDAtLjEgMC0uMyAwLS40IDAtOSA2LjUtMTYuMiAxNC42LTE2LjJzMTQuNiA3LjMgMTQuNiAxNi4yYzAgMS4zLjEgMyAwIDQuMi0uMiA1LjItMS4zIDkuMS0yIDEzLjktMS4yIDEwLjItMi4zIDE4LjctMS43IDI2LjUuNiAzLjkgMi45IDUuNSA0LjggNy4zIDAgMS4xLjIgNC42LjMgNi41IDQ2LjUgNC4xIDg5LjcgMjUuNCAxMjEuNCA1OC43bDUuNi00YzEuOS4xIDYgLjcgOC45LTEgNi41LTQuNCAxMi41LTEwLjUgMTkuNy0xNy44IDMuMy0zLjUgNS43LTYuOCA5LjctMTAuMi45LS44IDIuMy0xLjggMy4yLTIuNiA3LTUuNiAxNi44LTUgMjEuOCAxLjNzMy40IDE2LTMuNiAyMS42Yy0xIC44LTIuMyAxLjktMy4yIDIuNi00LjIgMy4xLTggNC43LTEyLjIgNy4xLTguNyA1LjQtMTYgOS45LTIxLjggMTUuMy0yLjcgMi45LTIuNSA1LjctMi44IDguMy0uOC43LTMuNyAzLjMtNS4yIDQuNyAxMi42IDE4LjggMjIuMSA0MC4xIDI3LjQgNjMuMyA1LjMgMjMuMSA2LjEgNDYuMSAzLjEgNjguM2w1LjkgMS43YzEuMSAxLjUgMy4yIDUuMiA2LjMgNi4zIDcuNSAyLjQgMTYgMy4yIDI2LjIgNC4zIDQuOC40IDguOS4yIDE0IDEuMSAxLjIuMiAzIC43IDQuMiAxIDguOSAyLjQgMTQuNCAxMC40IDEyLjYgMTguMnoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTQyOCA0MDEuN2MtMS0uMi0yLS4zLTMtLjItMS43LjEtMy4zLjUtNC45IDEuMy02LjIgMy05IDEwLjQtNi4yIDE2LjdsLS4xLjEgMjkuNiA3MS40YzI4LjUtMTguMiA0OS44LTQ1LjMgNjEtNzYuNmwtNzYuMi0xMi45eiIvPgogICAgICAgICAgPC9nPgogICAgICAgIDwvc3ZnPg==);" + />
@@ -14739,44 +13811,11 @@ exports[`installing helm chart from new tab given tab for installing chart was n class="TableCell icon" >
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
+ class="chartIcon intro-logo imageNotLoaded" + data-testid="image-container" + style="background-image: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNzIyLjggNzAyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogICAgICAgICAgPGcgZmlsbD0iIzhlOTI5NyI+CiAgICAgICAgICAgIDxwYXRoIGQ9Im0zMTggMjk5LjVjMi4xIDEuNiA0LjggMi41IDcuNiAyLjUgNi45IDAgMTIuNi01LjUgMTIuOS0xMi4zbC4zLS4yIDQuMy03Ni43Yy01LjIuNi0xMC40IDEuNS0xNS42IDIuNy0yOC41IDYuNS01My4yIDIwLjUtNzIuNiAzOS41bDYyLjkgNDQuNnoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTMwOS41IDQxMS45Yy0xLjQtNS45LTYuNi05LjktMTIuNC0xMC0uOCAwLTEuNy4xLTIuNS4ybC0uMS0uMi03NS41IDEyLjhjMTEuNyAzMi4yIDMzLjQgNTguNSA2MC44IDc2LjFsMjkuMi03MC43LS4yLS4zYzEuMS0yLjQgMS40LTUuMi43LTcuOXoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTI4NC40IDM1Ny41YzIuNS0uNyA0LjktMi4yIDYuNy00LjQgNC4zLTUuNCAzLjYtMTMuMi0xLjYtMTcuOGwuMS0uMy01Ny40LTUxLjRjLTE3IDI3LjgtMjUuMSA2MS4xLTIxLjQgOTUuM2w3My42LTIxLjJ6Ii8+CiAgICAgICAgICAgIDxwYXRoIGQ9Im0zNDAuMiAzODAgMjEuMiAxMC4yIDIxLjEtMTAuMSA1LjMtMjIuOS0xNC42LTE4LjJoLTIzLjZsLTE0LjYgMTguMnoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTM4NC4yIDI4OS40Yy4xIDIuNiAxIDUuMiAyLjggNy41IDQuMyA1LjQgMTIuMSA2LjQgMTcuNyAyLjRsLjIuMSA2Mi41LTQ0LjNjLTIzLjYtMjMuMS01NC40LTM4LjItODcuNi00Mi4yeiIvPgogICAgICAgICAgICA8cGF0aCBkPSJtNDkwLjMgMjgzLjctNTcuMSA1MS4xdi4yYy0yIDEuNy0zLjUgNC4xLTQuMSA2LjgtMS41IDYuOCAyLjUgMTMuNSA5LjIgMTUuM2wuMS4zIDc0IDIxLjNjMS42LTE2IC42LTMyLjUtMy4yLTQ5LTMuOS0xNi44LTEwLjQtMzIuMi0xOC45LTQ2eiIvPgogICAgICAgICAgICA8cGF0aCBkPSJtMzcyLjggNDM5LjZjLTEuMi0yLjMtMy4yLTQuMy01LjgtNS41LTItLjktNC0xLjQtNi0xLjMtNC41LjItOC43IDIuNi0xMC45IDYuOGgtLjFsLTM3LjEgNjcuMWMyNS43IDguOCA1NC4xIDEwLjcgODIuNSA0LjIgNS4xLTEuMiAxMC0yLjUgMTQuOS00LjJsLTM3LjMtNjcuMXoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTcxMS43IDQyNS02MC40LTI2Mi4yYy0zLjItMTMuNy0xMi41LTI1LjMtMjUuMy0zMS40bC0yNDQuNC0xMTYuOGMtNy4xLTMuNC0xNC44LTQuOS0yMi43LTQuNS02LjIuMy0xMi4zIDEuOS0xNy45IDQuNWwtMjQ0LjMgMTE2LjdjLTEyLjggNi4xLTIyLjEgMTcuNy0yNS4zIDMxLjRsLTYwLjIgMjYyLjNjLTIuOCAxMi4yLS41IDI1IDYuMyAzNS41LjggMS4zIDEuNyAyLjUgMi43IDMuN2wxNjkuMSAyMTAuM2M4LjkgMTEgMjIuMyAxNy40IDM2LjUgMTcuNGwyNzEuMi0uMWMxNC4yIDAgMjcuNy02LjQgMzYuNS0xNy40bDE2OS4xLTIxMC4zYzguOS0xMC45IDEyLjItMjUuNCA5LjEtMzkuMXptLTkzLTMuMmMtMS44IDcuOC0xMC4yIDEyLjYtMTguOSAxMC43LS4xIDAtLjIgMC0uMiAwLS4xIDAtLjItLjEtLjMtLjEtMS4yLS4zLTIuNy0uNS0zLjgtLjgtNS0xLjMtOC42LTMuMy0xMy4xLTUuMS05LjctMy41LTE3LjctNi40LTI1LjUtNy41LTQtLjMtNiAxLjYtOC4yIDMtMS4xLS4yLTQuNC0uOC02LjItMS4xLTE0IDQ0LTQzLjkgODIuMi04NC4zIDEwNi4xLjcgMS43IDEuOSA1LjMgMi40IDUuOS0uOSAyLjUtMi4zIDQuOC0xLjEgOC42IDIuOCA3LjQgNy40IDE0LjYgMTMgMjMuMiAyLjcgNCA1LjQgNy4xIDcuOCAxMS43LjYgMS4xIDEuMyAyLjggMS45IDMuOSAzLjggOCAxIDE3LjMtNi4yIDIwLjgtNy4zIDMuNS0xNi4zLS4yLTIwLjItOC4zLS42LTEuMS0xLjMtMi43LTEuOC0zLjgtMi4xLTQuNy0yLjgtOC44LTQuMi0xMy40LTMuMy05LjctNi0xNy44LTEwLTI0LjYtMi4yLTMuMy01LTMuNy03LjUtNC41LS41LS44LTIuMi00LTMuMS01LjYtOC4xIDMuMS0xNi40IDUuNi0yNS4xIDcuNi0zNy45IDguNi03NS45IDUuMS0xMDkuOS03LjlsLTMuMyA2Yy0yLjUuNy00LjggMS4zLTYuMyAzLjEtNS4zIDYuNC03LjUgMTYuNi0xMS4zIDI2LjMtMS41IDQuNi0yLjEgOC43LTQuMiAxMy40LS41IDEuMS0xLjMgMi42LTEuOCAzLjctMy45IDguMS0xMi45IDExLjctMjAuMiA4LjItNy4yLTMuNS0xMC0xMi43LTYuMi0yMC44LjYtMS4yIDEuMy0yLjggMS45LTMuOSAyLjQtNC42IDUuMi03LjcgNy44LTExLjcgNS41LTguNyAxMC40LTE2LjQgMTMuMi0yMy44LjctMi40LS4zLTUuOC0xLjMtOC4zbDIuNy02LjRjLTM4LjktMjMuMS02OS43LTU5LjgtODQuMy0xMDUuM2wtNi40IDEuMWMtMS43LTEtNS4xLTMuMi04LjQtMy03LjggMS4xLTE1LjggNC0yNS41IDcuNS00LjUgMS43LTguMSAzLjctMTMuMSA1LTEuMS4zLTIuNi42LTMuOC44LS4xIDAtLjIuMS0uMy4xcy0uMiAwLS4yIDBjLTguNyAxLjktMTcuMS0yLjktMTguOS0xMC43czMuOC0xNS43IDEyLjQtMTcuOGMuMSAwIC4yIDAgLjItLjFoLjFjMS4yLS4zIDIuOC0uNyAzLjktLjkgNS4xLTEgOS4yLS43IDE0LTEuMSAxMC4yLTEuMSAxOC43LTEuOSAyNi4yLTQuMyAyLjQtMSA0LjctNC4zIDYuMy02LjNsNi4xLTEuOGMtNi45LTQ3LjUgNC44LTk0LjIgMjkuOC0xMzEuOWwtNC43LTQuMmMtLjMtMS44LS43LTYtMi45LTguNC01LjgtNS40LTEzLTkuOS0yMS44LTE1LjMtNC4yLTIuNC04LTQtMTIuMS03LjEtLjktLjctMi4xLTEuNy0zLTIuNC0uMS0uMS0uMS0uMS0uMi0uMi03LTUuNi04LjYtMTUuMi0zLjYtMjEuNiAyLjgtMy42IDcuMi01LjMgMTEuNy01LjIgMy41LjEgNy4xIDEuNCAxMC4yIDMuOCAxIC44IDIuNCAxLjggMy4yIDIuNiAzLjkgMy40IDYuMyA2LjcgOS42IDEwLjIgNy4yIDcuMyAxMy4yIDEzLjQgMTkuNyAxNy44IDMuNCAyIDYuMSAxLjIgOC43LjguOC42IDMuNyAyLjYgNS4zIDMuOCAyNC45LTI2LjQgNTcuNi00NiA5NS42LTU0LjYgOC44LTIgMTcuNy0zLjMgMjYuNC00LjFsLjMtNi4yYzEuOS0xLjkgNC4xLTQuNiA0LjgtNy42LjYtNy45LS40LTE2LjMtMS42LTI2LjUtLjctNC44LTEuOC04LjctMi0xMy45IDAtMS4xIDAtMi41IDAtMy44IDAtLjEgMC0uMyAwLS40IDAtOSA2LjUtMTYuMiAxNC42LTE2LjJzMTQuNiA3LjMgMTQuNiAxNi4yYzAgMS4zLjEgMyAwIDQuMi0uMiA1LjItMS4zIDkuMS0yIDEzLjktMS4yIDEwLjItMi4zIDE4LjctMS43IDI2LjUuNiAzLjkgMi45IDUuNSA0LjggNy4zIDAgMS4xLjIgNC42LjMgNi41IDQ2LjUgNC4xIDg5LjcgMjUuNCAxMjEuNCA1OC43bDUuNi00YzEuOS4xIDYgLjcgOC45LTEgNi41LTQuNCAxMi41LTEwLjUgMTkuNy0xNy44IDMuMy0zLjUgNS43LTYuOCA5LjctMTAuMi45LS44IDIuMy0xLjggMy4yLTIuNiA3LTUuNiAxNi44LTUgMjEuOCAxLjNzMy40IDE2LTMuNiAyMS42Yy0xIC44LTIuMyAxLjktMy4yIDIuNi00LjIgMy4xLTggNC43LTEyLjIgNy4xLTguNyA1LjQtMTYgOS45LTIxLjggMTUuMy0yLjcgMi45LTIuNSA1LjctMi44IDguMy0uOC43LTMuNyAzLjMtNS4yIDQuNyAxMi42IDE4LjggMjIuMSA0MC4xIDI3LjQgNjMuMyA1LjMgMjMuMSA2LjEgNDYuMSAzLjEgNjguM2w1LjkgMS43YzEuMSAxLjUgMy4yIDUuMiA2LjMgNi4zIDcuNSAyLjQgMTYgMy4yIDI2LjIgNC4zIDQuOC40IDguOS4yIDE0IDEuMSAxLjIuMiAzIC43IDQuMiAxIDguOSAyLjQgMTQuNCAxMC40IDEyLjYgMTguMnoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTQyOCA0MDEuN2MtMS0uMi0yLS4zLTMtLjItMS43LjEtMy4zLjUtNC45IDEuMy02LjIgMy05IDEwLjQtNi4yIDE2LjdsLS4xLjEgMjkuNiA3MS40YzI4LjUtMTguMiA0OS44LTQ1LjMgNjEtNzYuNmwtNzYuMi0xMi45eiIvPgogICAgICAgICAgPC9nPgogICAgICAgIDwvc3ZnPg==);" + />
@@ -3450,44 +3217,11 @@ exports[`opening dock tab for installing helm chart given application is started class="TableCell icon" >
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
+ class="chartIcon intro-logo imageNotLoaded" + data-testid="image-container" + style="background-image: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNzIyLjggNzAyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogICAgICAgICAgPGcgZmlsbD0iIzhlOTI5NyI+CiAgICAgICAgICAgIDxwYXRoIGQ9Im0zMTggMjk5LjVjMi4xIDEuNiA0LjggMi41IDcuNiAyLjUgNi45IDAgMTIuNi01LjUgMTIuOS0xMi4zbC4zLS4yIDQuMy03Ni43Yy01LjIuNi0xMC40IDEuNS0xNS42IDIuNy0yOC41IDYuNS01My4yIDIwLjUtNzIuNiAzOS41bDYyLjkgNDQuNnoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTMwOS41IDQxMS45Yy0xLjQtNS45LTYuNi05LjktMTIuNC0xMC0uOCAwLTEuNy4xLTIuNS4ybC0uMS0uMi03NS41IDEyLjhjMTEuNyAzMi4yIDMzLjQgNTguNSA2MC44IDc2LjFsMjkuMi03MC43LS4yLS4zYzEuMS0yLjQgMS40LTUuMi43LTcuOXoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTI4NC40IDM1Ny41YzIuNS0uNyA0LjktMi4yIDYuNy00LjQgNC4zLTUuNCAzLjYtMTMuMi0xLjYtMTcuOGwuMS0uMy01Ny40LTUxLjRjLTE3IDI3LjgtMjUuMSA2MS4xLTIxLjQgOTUuM2w3My42LTIxLjJ6Ii8+CiAgICAgICAgICAgIDxwYXRoIGQ9Im0zNDAuMiAzODAgMjEuMiAxMC4yIDIxLjEtMTAuMSA1LjMtMjIuOS0xNC42LTE4LjJoLTIzLjZsLTE0LjYgMTguMnoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTM4NC4yIDI4OS40Yy4xIDIuNiAxIDUuMiAyLjggNy41IDQuMyA1LjQgMTIuMSA2LjQgMTcuNyAyLjRsLjIuMSA2Mi41LTQ0LjNjLTIzLjYtMjMuMS01NC40LTM4LjItODcuNi00Mi4yeiIvPgogICAgICAgICAgICA8cGF0aCBkPSJtNDkwLjMgMjgzLjctNTcuMSA1MS4xdi4yYy0yIDEuNy0zLjUgNC4xLTQuMSA2LjgtMS41IDYuOCAyLjUgMTMuNSA5LjIgMTUuM2wuMS4zIDc0IDIxLjNjMS42LTE2IC42LTMyLjUtMy4yLTQ5LTMuOS0xNi44LTEwLjQtMzIuMi0xOC45LTQ2eiIvPgogICAgICAgICAgICA8cGF0aCBkPSJtMzcyLjggNDM5LjZjLTEuMi0yLjMtMy4yLTQuMy01LjgtNS41LTItLjktNC0xLjQtNi0xLjMtNC41LjItOC43IDIuNi0xMC45IDYuOGgtLjFsLTM3LjEgNjcuMWMyNS43IDguOCA1NC4xIDEwLjcgODIuNSA0LjIgNS4xLTEuMiAxMC0yLjUgMTQuOS00LjJsLTM3LjMtNjcuMXoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTcxMS43IDQyNS02MC40LTI2Mi4yYy0zLjItMTMuNy0xMi41LTI1LjMtMjUuMy0zMS40bC0yNDQuNC0xMTYuOGMtNy4xLTMuNC0xNC44LTQuOS0yMi43LTQuNS02LjIuMy0xMi4zIDEuOS0xNy45IDQuNWwtMjQ0LjMgMTE2LjdjLTEyLjggNi4xLTIyLjEgMTcuNy0yNS4zIDMxLjRsLTYwLjIgMjYyLjNjLTIuOCAxMi4yLS41IDI1IDYuMyAzNS41LjggMS4zIDEuNyAyLjUgMi43IDMuN2wxNjkuMSAyMTAuM2M4LjkgMTEgMjIuMyAxNy40IDM2LjUgMTcuNGwyNzEuMi0uMWMxNC4yIDAgMjcuNy02LjQgMzYuNS0xNy40bDE2OS4xLTIxMC4zYzguOS0xMC45IDEyLjItMjUuNCA5LjEtMzkuMXptLTkzLTMuMmMtMS44IDcuOC0xMC4yIDEyLjYtMTguOSAxMC43LS4xIDAtLjIgMC0uMiAwLS4xIDAtLjItLjEtLjMtLjEtMS4yLS4zLTIuNy0uNS0zLjgtLjgtNS0xLjMtOC42LTMuMy0xMy4xLTUuMS05LjctMy41LTE3LjctNi40LTI1LjUtNy41LTQtLjMtNiAxLjYtOC4yIDMtMS4xLS4yLTQuNC0uOC02LjItMS4xLTE0IDQ0LTQzLjkgODIuMi04NC4zIDEwNi4xLjcgMS43IDEuOSA1LjMgMi40IDUuOS0uOSAyLjUtMi4zIDQuOC0xLjEgOC42IDIuOCA3LjQgNy40IDE0LjYgMTMgMjMuMiAyLjcgNCA1LjQgNy4xIDcuOCAxMS43LjYgMS4xIDEuMyAyLjggMS45IDMuOSAzLjggOCAxIDE3LjMtNi4yIDIwLjgtNy4zIDMuNS0xNi4zLS4yLTIwLjItOC4zLS42LTEuMS0xLjMtMi43LTEuOC0zLjgtMi4xLTQuNy0yLjgtOC44LTQuMi0xMy40LTMuMy05LjctNi0xNy44LTEwLTI0LjYtMi4yLTMuMy01LTMuNy03LjUtNC41LS41LS44LTIuMi00LTMuMS01LjYtOC4xIDMuMS0xNi40IDUuNi0yNS4xIDcuNi0zNy45IDguNi03NS45IDUuMS0xMDkuOS03LjlsLTMuMyA2Yy0yLjUuNy00LjggMS4zLTYuMyAzLjEtNS4zIDYuNC03LjUgMTYuNi0xMS4zIDI2LjMtMS41IDQuNi0yLjEgOC43LTQuMiAxMy40LS41IDEuMS0xLjMgMi42LTEuOCAzLjctMy45IDguMS0xMi45IDExLjctMjAuMiA4LjItNy4yLTMuNS0xMC0xMi43LTYuMi0yMC44LjYtMS4yIDEuMy0yLjggMS45LTMuOSAyLjQtNC42IDUuMi03LjcgNy44LTExLjcgNS41LTguNyAxMC40LTE2LjQgMTMuMi0yMy44LjctMi40LS4zLTUuOC0xLjMtOC4zbDIuNy02LjRjLTM4LjktMjMuMS02OS43LTU5LjgtODQuMy0xMDUuM2wtNi40IDEuMWMtMS43LTEtNS4xLTMuMi04LjQtMy03LjggMS4xLTE1LjggNC0yNS41IDcuNS00LjUgMS43LTguMSAzLjctMTMuMSA1LTEuMS4zLTIuNi42LTMuOC44LS4xIDAtLjIuMS0uMy4xcy0uMiAwLS4yIDBjLTguNyAxLjktMTcuMS0yLjktMTguOS0xMC43czMuOC0xNS43IDEyLjQtMTcuOGMuMSAwIC4yIDAgLjItLjFoLjFjMS4yLS4zIDIuOC0uNyAzLjktLjkgNS4xLTEgOS4yLS43IDE0LTEuMSAxMC4yLTEuMSAxOC43LTEuOSAyNi4yLTQuMyAyLjQtMSA0LjctNC4zIDYuMy02LjNsNi4xLTEuOGMtNi45LTQ3LjUgNC44LTk0LjIgMjkuOC0xMzEuOWwtNC43LTQuMmMtLjMtMS44LS43LTYtMi45LTguNC01LjgtNS40LTEzLTkuOS0yMS44LTE1LjMtNC4yLTIuNC04LTQtMTIuMS03LjEtLjktLjctMi4xLTEuNy0zLTIuNC0uMS0uMS0uMS0uMS0uMi0uMi03LTUuNi04LjYtMTUuMi0zLjYtMjEuNiAyLjgtMy42IDcuMi01LjMgMTEuNy01LjIgMy41LjEgNy4xIDEuNCAxMC4yIDMuOCAxIC44IDIuNCAxLjggMy4yIDIuNiAzLjkgMy40IDYuMyA2LjcgOS42IDEwLjIgNy4yIDcuMyAxMy4yIDEzLjQgMTkuNyAxNy44IDMuNCAyIDYuMSAxLjIgOC43LjguOC42IDMuNyAyLjYgNS4zIDMuOCAyNC45LTI2LjQgNTcuNi00NiA5NS42LTU0LjYgOC44LTIgMTcuNy0zLjMgMjYuNC00LjFsLjMtNi4yYzEuOS0xLjkgNC4xLTQuNiA0LjgtNy42LjYtNy45LS40LTE2LjMtMS42LTI2LjUtLjctNC44LTEuOC04LjctMi0xMy45IDAtMS4xIDAtMi41IDAtMy44IDAtLjEgMC0uMyAwLS40IDAtOSA2LjUtMTYuMiAxNC42LTE2LjJzMTQuNiA3LjMgMTQuNiAxNi4yYzAgMS4zLjEgMyAwIDQuMi0uMiA1LjItMS4zIDkuMS0yIDEzLjktMS4yIDEwLjItMi4zIDE4LjctMS43IDI2LjUuNiAzLjkgMi45IDUuNSA0LjggNy4zIDAgMS4xLjIgNC42LjMgNi41IDQ2LjUgNC4xIDg5LjcgMjUuNCAxMjEuNCA1OC43bDUuNi00YzEuOS4xIDYgLjcgOC45LTEgNi41LTQuNCAxMi41LTEwLjUgMTkuNy0xNy44IDMuMy0zLjUgNS43LTYuOCA5LjctMTAuMi45LS44IDIuMy0xLjggMy4yLTIuNiA3LTUuNiAxNi44LTUgMjEuOCAxLjNzMy40IDE2LTMuNiAyMS42Yy0xIC44LTIuMyAxLjktMy4yIDIuNi00LjIgMy4xLTggNC43LTEyLjIgNy4xLTguNyA1LjQtMTYgOS45LTIxLjggMTUuMy0yLjcgMi45LTIuNSA1LjctMi44IDguMy0uOC43LTMuNyAzLjMtNS4yIDQuNyAxMi42IDE4LjggMjIuMSA0MC4xIDI3LjQgNjMuMyA1LjMgMjMuMSA2LjEgNDYuMSAzLjEgNjguM2w1LjkgMS43YzEuMSAxLjUgMy4yIDUuMiA2LjMgNi4zIDcuNSAyLjQgMTYgMy4yIDI2LjIgNC4zIDQuOC40IDguOS4yIDE0IDEuMSAxLjIuMiAzIC43IDQuMiAxIDguOSAyLjQgMTQuNCAxMC40IDEyLjYgMTguMnoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTQyOCA0MDEuN2MtMS0uMi0yLS4zLTMtLjItMS43LjEtMy4zLjUtNC45IDEuMy02LjIgMy05IDEwLjQtNi4yIDE2LjdsLS4xLjEgMjkuNiA3MS40YzI4LjUtMTguMiA0OS44LTQ1LjMgNjEtNzYuNmwtNzYuMi0xMi45eiIvPgogICAgICAgICAgPC9nPgogICAgICAgIDwvc3ZnPg==);" + />
@@ -4407,44 +4073,11 @@ exports[`opening dock tab for installing helm chart given application is started class="TableCell icon" >
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
+ class="chartIcon intro-logo imageNotLoaded" + data-testid="image-container" + style="background-image: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNzIyLjggNzAyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogICAgICAgICAgPGcgZmlsbD0iIzhlOTI5NyI+CiAgICAgICAgICAgIDxwYXRoIGQ9Im0zMTggMjk5LjVjMi4xIDEuNiA0LjggMi41IDcuNiAyLjUgNi45IDAgMTIuNi01LjUgMTIuOS0xMi4zbC4zLS4yIDQuMy03Ni43Yy01LjIuNi0xMC40IDEuNS0xNS42IDIuNy0yOC41IDYuNS01My4yIDIwLjUtNzIuNiAzOS41bDYyLjkgNDQuNnoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTMwOS41IDQxMS45Yy0xLjQtNS45LTYuNi05LjktMTIuNC0xMC0uOCAwLTEuNy4xLTIuNS4ybC0uMS0uMi03NS41IDEyLjhjMTEuNyAzMi4yIDMzLjQgNTguNSA2MC44IDc2LjFsMjkuMi03MC43LS4yLS4zYzEuMS0yLjQgMS40LTUuMi43LTcuOXoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTI4NC40IDM1Ny41YzIuNS0uNyA0LjktMi4yIDYuNy00LjQgNC4zLTUuNCAzLjYtMTMuMi0xLjYtMTcuOGwuMS0uMy01Ny40LTUxLjRjLTE3IDI3LjgtMjUuMSA2MS4xLTIxLjQgOTUuM2w3My42LTIxLjJ6Ii8+CiAgICAgICAgICAgIDxwYXRoIGQ9Im0zNDAuMiAzODAgMjEuMiAxMC4yIDIxLjEtMTAuMSA1LjMtMjIuOS0xNC42LTE4LjJoLTIzLjZsLTE0LjYgMTguMnoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTM4NC4yIDI4OS40Yy4xIDIuNiAxIDUuMiAyLjggNy41IDQuMyA1LjQgMTIuMSA2LjQgMTcuNyAyLjRsLjIuMSA2Mi41LTQ0LjNjLTIzLjYtMjMuMS01NC40LTM4LjItODcuNi00Mi4yeiIvPgogICAgICAgICAgICA8cGF0aCBkPSJtNDkwLjMgMjgzLjctNTcuMSA1MS4xdi4yYy0yIDEuNy0zLjUgNC4xLTQuMSA2LjgtMS41IDYuOCAyLjUgMTMuNSA5LjIgMTUuM2wuMS4zIDc0IDIxLjNjMS42LTE2IC42LTMyLjUtMy4yLTQ5LTMuOS0xNi44LTEwLjQtMzIuMi0xOC45LTQ2eiIvPgogICAgICAgICAgICA8cGF0aCBkPSJtMzcyLjggNDM5LjZjLTEuMi0yLjMtMy4yLTQuMy01LjgtNS41LTItLjktNC0xLjQtNi0xLjMtNC41LjItOC43IDIuNi0xMC45IDYuOGgtLjFsLTM3LjEgNjcuMWMyNS43IDguOCA1NC4xIDEwLjcgODIuNSA0LjIgNS4xLTEuMiAxMC0yLjUgMTQuOS00LjJsLTM3LjMtNjcuMXoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTcxMS43IDQyNS02MC40LTI2Mi4yYy0zLjItMTMuNy0xMi41LTI1LjMtMjUuMy0zMS40bC0yNDQuNC0xMTYuOGMtNy4xLTMuNC0xNC44LTQuOS0yMi43LTQuNS02LjIuMy0xMi4zIDEuOS0xNy45IDQuNWwtMjQ0LjMgMTE2LjdjLTEyLjggNi4xLTIyLjEgMTcuNy0yNS4zIDMxLjRsLTYwLjIgMjYyLjNjLTIuOCAxMi4yLS41IDI1IDYuMyAzNS41LjggMS4zIDEuNyAyLjUgMi43IDMuN2wxNjkuMSAyMTAuM2M4LjkgMTEgMjIuMyAxNy40IDM2LjUgMTcuNGwyNzEuMi0uMWMxNC4yIDAgMjcuNy02LjQgMzYuNS0xNy40bDE2OS4xLTIxMC4zYzguOS0xMC45IDEyLjItMjUuNCA5LjEtMzkuMXptLTkzLTMuMmMtMS44IDcuOC0xMC4yIDEyLjYtMTguOSAxMC43LS4xIDAtLjIgMC0uMiAwLS4xIDAtLjItLjEtLjMtLjEtMS4yLS4zLTIuNy0uNS0zLjgtLjgtNS0xLjMtOC42LTMuMy0xMy4xLTUuMS05LjctMy41LTE3LjctNi40LTI1LjUtNy41LTQtLjMtNiAxLjYtOC4yIDMtMS4xLS4yLTQuNC0uOC02LjItMS4xLTE0IDQ0LTQzLjkgODIuMi04NC4zIDEwNi4xLjcgMS43IDEuOSA1LjMgMi40IDUuOS0uOSAyLjUtMi4zIDQuOC0xLjEgOC42IDIuOCA3LjQgNy40IDE0LjYgMTMgMjMuMiAyLjcgNCA1LjQgNy4xIDcuOCAxMS43LjYgMS4xIDEuMyAyLjggMS45IDMuOSAzLjggOCAxIDE3LjMtNi4yIDIwLjgtNy4zIDMuNS0xNi4zLS4yLTIwLjItOC4zLS42LTEuMS0xLjMtMi43LTEuOC0zLjgtMi4xLTQuNy0yLjgtOC44LTQuMi0xMy40LTMuMy05LjctNi0xNy44LTEwLTI0LjYtMi4yLTMuMy01LTMuNy03LjUtNC41LS41LS44LTIuMi00LTMuMS01LjYtOC4xIDMuMS0xNi40IDUuNi0yNS4xIDcuNi0zNy45IDguNi03NS45IDUuMS0xMDkuOS03LjlsLTMuMyA2Yy0yLjUuNy00LjggMS4zLTYuMyAzLjEtNS4zIDYuNC03LjUgMTYuNi0xMS4zIDI2LjMtMS41IDQuNi0yLjEgOC43LTQuMiAxMy40LS41IDEuMS0xLjMgMi42LTEuOCAzLjctMy45IDguMS0xMi45IDExLjctMjAuMiA4LjItNy4yLTMuNS0xMC0xMi43LTYuMi0yMC44LjYtMS4yIDEuMy0yLjggMS45LTMuOSAyLjQtNC42IDUuMi03LjcgNy44LTExLjcgNS41LTguNyAxMC40LTE2LjQgMTMuMi0yMy44LjctMi40LS4zLTUuOC0xLjMtOC4zbDIuNy02LjRjLTM4LjktMjMuMS02OS43LTU5LjgtODQuMy0xMDUuM2wtNi40IDEuMWMtMS43LTEtNS4xLTMuMi04LjQtMy03LjggMS4xLTE1LjggNC0yNS41IDcuNS00LjUgMS43LTguMSAzLjctMTMuMSA1LTEuMS4zLTIuNi42LTMuOC44LS4xIDAtLjIuMS0uMy4xcy0uMiAwLS4yIDBjLTguNyAxLjktMTcuMS0yLjktMTguOS0xMC43czMuOC0xNS43IDEyLjQtMTcuOGMuMSAwIC4yIDAgLjItLjFoLjFjMS4yLS4zIDIuOC0uNyAzLjktLjkgNS4xLTEgOS4yLS43IDE0LTEuMSAxMC4yLTEuMSAxOC43LTEuOSAyNi4yLTQuMyAyLjQtMSA0LjctNC4zIDYuMy02LjNsNi4xLTEuOGMtNi45LTQ3LjUgNC44LTk0LjIgMjkuOC0xMzEuOWwtNC43LTQuMmMtLjMtMS44LS43LTYtMi45LTguNC01LjgtNS40LTEzLTkuOS0yMS44LTE1LjMtNC4yLTIuNC04LTQtMTIuMS03LjEtLjktLjctMi4xLTEuNy0zLTIuNC0uMS0uMS0uMS0uMS0uMi0uMi03LTUuNi04LjYtMTUuMi0zLjYtMjEuNiAyLjgtMy42IDcuMi01LjMgMTEuNy01LjIgMy41LjEgNy4xIDEuNCAxMC4yIDMuOCAxIC44IDIuNCAxLjggMy4yIDIuNiAzLjkgMy40IDYuMyA2LjcgOS42IDEwLjIgNy4yIDcuMyAxMy4yIDEzLjQgMTkuNyAxNy44IDMuNCAyIDYuMSAxLjIgOC43LjguOC42IDMuNyAyLjYgNS4zIDMuOCAyNC45LTI2LjQgNTcuNi00NiA5NS42LTU0LjYgOC44LTIgMTcuNy0zLjMgMjYuNC00LjFsLjMtNi4yYzEuOS0xLjkgNC4xLTQuNiA0LjgtNy42LjYtNy45LS40LTE2LjMtMS42LTI2LjUtLjctNC44LTEuOC04LjctMi0xMy45IDAtMS4xIDAtMi41IDAtMy44IDAtLjEgMC0uMyAwLS40IDAtOSA2LjUtMTYuMiAxNC42LTE2LjJzMTQuNiA3LjMgMTQuNiAxNi4yYzAgMS4zLjEgMyAwIDQuMi0uMiA1LjItMS4zIDkuMS0yIDEzLjktMS4yIDEwLjItMi4zIDE4LjctMS43IDI2LjUuNiAzLjkgMi45IDUuNSA0LjggNy4zIDAgMS4xLjIgNC42LjMgNi41IDQ2LjUgNC4xIDg5LjcgMjUuNCAxMjEuNCA1OC43bDUuNi00YzEuOS4xIDYgLjcgOC45LTEgNi41LTQuNCAxMi41LTEwLjUgMTkuNy0xNy44IDMuMy0zLjUgNS43LTYuOCA5LjctMTAuMi45LS44IDIuMy0xLjggMy4yLTIuNiA3LTUuNiAxNi44LTUgMjEuOCAxLjNzMy40IDE2LTMuNiAyMS42Yy0xIC44LTIuMyAxLjktMy4yIDIuNi00LjIgMy4xLTggNC43LTEyLjIgNy4xLTguNyA1LjQtMTYgOS45LTIxLjggMTUuMy0yLjcgMi45LTIuNSA1LjctMi44IDguMy0uOC43LTMuNyAzLjMtNS4yIDQuNyAxMi42IDE4LjggMjIuMSA0MC4xIDI3LjQgNjMuMyA1LjMgMjMuMSA2LjEgNDYuMSAzLjEgNjguM2w1LjkgMS43YzEuMSAxLjUgMy4yIDUuMiA2LjMgNi4zIDcuNSAyLjQgMTYgMy4yIDI2LjIgNC4zIDQuOC40IDguOS4yIDE0IDEuMSAxLjIuMiAzIC43IDQuMiAxIDguOSAyLjQgMTQuNCAxMC40IDEyLjYgMTguMnoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTQyOCA0MDEuN2MtMS0uMi0yLS4zLTMtLjItMS43LjEtMy4zLjUtNC45IDEuMy02LjIgMy05IDEwLjQtNi4yIDE2LjdsLS4xLjEgMjkuNiA3MS40YzI4LjUtMTguMiA0OS44LTQ1LjMgNjEtNzYuNmwtNzYuMi0xMi45eiIvPgogICAgICAgICAgPC9nPgogICAgICAgIDwvc3ZnPg==);" + />
@@ -5354,44 +4919,11 @@ exports[`opening dock tab for installing helm chart given application is started class="TableCell icon" >
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
+ class="chartIcon intro-logo imageNotLoaded" + data-testid="image-container" + style="background-image: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNzIyLjggNzAyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogICAgICAgICAgPGcgZmlsbD0iIzhlOTI5NyI+CiAgICAgICAgICAgIDxwYXRoIGQ9Im0zMTggMjk5LjVjMi4xIDEuNiA0LjggMi41IDcuNiAyLjUgNi45IDAgMTIuNi01LjUgMTIuOS0xMi4zbC4zLS4yIDQuMy03Ni43Yy01LjIuNi0xMC40IDEuNS0xNS42IDIuNy0yOC41IDYuNS01My4yIDIwLjUtNzIuNiAzOS41bDYyLjkgNDQuNnoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTMwOS41IDQxMS45Yy0xLjQtNS45LTYuNi05LjktMTIuNC0xMC0uOCAwLTEuNy4xLTIuNS4ybC0uMS0uMi03NS41IDEyLjhjMTEuNyAzMi4yIDMzLjQgNTguNSA2MC44IDc2LjFsMjkuMi03MC43LS4yLS4zYzEuMS0yLjQgMS40LTUuMi43LTcuOXoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTI4NC40IDM1Ny41YzIuNS0uNyA0LjktMi4yIDYuNy00LjQgNC4zLTUuNCAzLjYtMTMuMi0xLjYtMTcuOGwuMS0uMy01Ny40LTUxLjRjLTE3IDI3LjgtMjUuMSA2MS4xLTIxLjQgOTUuM2w3My42LTIxLjJ6Ii8+CiAgICAgICAgICAgIDxwYXRoIGQ9Im0zNDAuMiAzODAgMjEuMiAxMC4yIDIxLjEtMTAuMSA1LjMtMjIuOS0xNC42LTE4LjJoLTIzLjZsLTE0LjYgMTguMnoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTM4NC4yIDI4OS40Yy4xIDIuNiAxIDUuMiAyLjggNy41IDQuMyA1LjQgMTIuMSA2LjQgMTcuNyAyLjRsLjIuMSA2Mi41LTQ0LjNjLTIzLjYtMjMuMS01NC40LTM4LjItODcuNi00Mi4yeiIvPgogICAgICAgICAgICA8cGF0aCBkPSJtNDkwLjMgMjgzLjctNTcuMSA1MS4xdi4yYy0yIDEuNy0zLjUgNC4xLTQuMSA2LjgtMS41IDYuOCAyLjUgMTMuNSA5LjIgMTUuM2wuMS4zIDc0IDIxLjNjMS42LTE2IC42LTMyLjUtMy4yLTQ5LTMuOS0xNi44LTEwLjQtMzIuMi0xOC45LTQ2eiIvPgogICAgICAgICAgICA8cGF0aCBkPSJtMzcyLjggNDM5LjZjLTEuMi0yLjMtMy4yLTQuMy01LjgtNS41LTItLjktNC0xLjQtNi0xLjMtNC41LjItOC43IDIuNi0xMC45IDYuOGgtLjFsLTM3LjEgNjcuMWMyNS43IDguOCA1NC4xIDEwLjcgODIuNSA0LjIgNS4xLTEuMiAxMC0yLjUgMTQuOS00LjJsLTM3LjMtNjcuMXoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTcxMS43IDQyNS02MC40LTI2Mi4yYy0zLjItMTMuNy0xMi41LTI1LjMtMjUuMy0zMS40bC0yNDQuNC0xMTYuOGMtNy4xLTMuNC0xNC44LTQuOS0yMi43LTQuNS02LjIuMy0xMi4zIDEuOS0xNy45IDQuNWwtMjQ0LjMgMTE2LjdjLTEyLjggNi4xLTIyLjEgMTcuNy0yNS4zIDMxLjRsLTYwLjIgMjYyLjNjLTIuOCAxMi4yLS41IDI1IDYuMyAzNS41LjggMS4zIDEuNyAyLjUgMi43IDMuN2wxNjkuMSAyMTAuM2M4LjkgMTEgMjIuMyAxNy40IDM2LjUgMTcuNGwyNzEuMi0uMWMxNC4yIDAgMjcuNy02LjQgMzYuNS0xNy40bDE2OS4xLTIxMC4zYzguOS0xMC45IDEyLjItMjUuNCA5LjEtMzkuMXptLTkzLTMuMmMtMS44IDcuOC0xMC4yIDEyLjYtMTguOSAxMC43LS4xIDAtLjIgMC0uMiAwLS4xIDAtLjItLjEtLjMtLjEtMS4yLS4zLTIuNy0uNS0zLjgtLjgtNS0xLjMtOC42LTMuMy0xMy4xLTUuMS05LjctMy41LTE3LjctNi40LTI1LjUtNy41LTQtLjMtNiAxLjYtOC4yIDMtMS4xLS4yLTQuNC0uOC02LjItMS4xLTE0IDQ0LTQzLjkgODIuMi04NC4zIDEwNi4xLjcgMS43IDEuOSA1LjMgMi40IDUuOS0uOSAyLjUtMi4zIDQuOC0xLjEgOC42IDIuOCA3LjQgNy40IDE0LjYgMTMgMjMuMiAyLjcgNCA1LjQgNy4xIDcuOCAxMS43LjYgMS4xIDEuMyAyLjggMS45IDMuOSAzLjggOCAxIDE3LjMtNi4yIDIwLjgtNy4zIDMuNS0xNi4zLS4yLTIwLjItOC4zLS42LTEuMS0xLjMtMi43LTEuOC0zLjgtMi4xLTQuNy0yLjgtOC44LTQuMi0xMy40LTMuMy05LjctNi0xNy44LTEwLTI0LjYtMi4yLTMuMy01LTMuNy03LjUtNC41LS41LS44LTIuMi00LTMuMS01LjYtOC4xIDMuMS0xNi40IDUuNi0yNS4xIDcuNi0zNy45IDguNi03NS45IDUuMS0xMDkuOS03LjlsLTMuMyA2Yy0yLjUuNy00LjggMS4zLTYuMyAzLjEtNS4zIDYuNC03LjUgMTYuNi0xMS4zIDI2LjMtMS41IDQuNi0yLjEgOC43LTQuMiAxMy40LS41IDEuMS0xLjMgMi42LTEuOCAzLjctMy45IDguMS0xMi45IDExLjctMjAuMiA4LjItNy4yLTMuNS0xMC0xMi43LTYuMi0yMC44LjYtMS4yIDEuMy0yLjggMS45LTMuOSAyLjQtNC42IDUuMi03LjcgNy44LTExLjcgNS41LTguNyAxMC40LTE2LjQgMTMuMi0yMy44LjctMi40LS4zLTUuOC0xLjMtOC4zbDIuNy02LjRjLTM4LjktMjMuMS02OS43LTU5LjgtODQuMy0xMDUuM2wtNi40IDEuMWMtMS43LTEtNS4xLTMuMi04LjQtMy03LjggMS4xLTE1LjggNC0yNS41IDcuNS00LjUgMS43LTguMSAzLjctMTMuMSA1LTEuMS4zLTIuNi42LTMuOC44LS4xIDAtLjIuMS0uMy4xcy0uMiAwLS4yIDBjLTguNyAxLjktMTcuMS0yLjktMTguOS0xMC43czMuOC0xNS43IDEyLjQtMTcuOGMuMSAwIC4yIDAgLjItLjFoLjFjMS4yLS4zIDIuOC0uNyAzLjktLjkgNS4xLTEgOS4yLS43IDE0LTEuMSAxMC4yLTEuMSAxOC43LTEuOSAyNi4yLTQuMyAyLjQtMSA0LjctNC4zIDYuMy02LjNsNi4xLTEuOGMtNi45LTQ3LjUgNC44LTk0LjIgMjkuOC0xMzEuOWwtNC43LTQuMmMtLjMtMS44LS43LTYtMi45LTguNC01LjgtNS40LTEzLTkuOS0yMS44LTE1LjMtNC4yLTIuNC04LTQtMTIuMS03LjEtLjktLjctMi4xLTEuNy0zLTIuNC0uMS0uMS0uMS0uMS0uMi0uMi03LTUuNi04LjYtMTUuMi0zLjYtMjEuNiAyLjgtMy42IDcuMi01LjMgMTEuNy01LjIgMy41LjEgNy4xIDEuNCAxMC4yIDMuOCAxIC44IDIuNCAxLjggMy4yIDIuNiAzLjkgMy40IDYuMyA2LjcgOS42IDEwLjIgNy4yIDcuMyAxMy4yIDEzLjQgMTkuNyAxNy44IDMuNCAyIDYuMSAxLjIgOC43LjguOC42IDMuNyAyLjYgNS4zIDMuOCAyNC45LTI2LjQgNTcuNi00NiA5NS42LTU0LjYgOC44LTIgMTcuNy0zLjMgMjYuNC00LjFsLjMtNi4yYzEuOS0xLjkgNC4xLTQuNiA0LjgtNy42LjYtNy45LS40LTE2LjMtMS42LTI2LjUtLjctNC44LTEuOC04LjctMi0xMy45IDAtMS4xIDAtMi41IDAtMy44IDAtLjEgMC0uMyAwLS40IDAtOSA2LjUtMTYuMiAxNC42LTE2LjJzMTQuNiA3LjMgMTQuNiAxNi4yYzAgMS4zLjEgMyAwIDQuMi0uMiA1LjItMS4zIDkuMS0yIDEzLjktMS4yIDEwLjItMi4zIDE4LjctMS43IDI2LjUuNiAzLjkgMi45IDUuNSA0LjggNy4zIDAgMS4xLjIgNC42LjMgNi41IDQ2LjUgNC4xIDg5LjcgMjUuNCAxMjEuNCA1OC43bDUuNi00YzEuOS4xIDYgLjcgOC45LTEgNi41LTQuNCAxMi41LTEwLjUgMTkuNy0xNy44IDMuMy0zLjUgNS43LTYuOCA5LjctMTAuMi45LS44IDIuMy0xLjggMy4yLTIuNiA3LTUuNiAxNi44LTUgMjEuOCAxLjNzMy40IDE2LTMuNiAyMS42Yy0xIC44LTIuMyAxLjktMy4yIDIuNi00LjIgMy4xLTggNC43LTEyLjIgNy4xLTguNyA1LjQtMTYgOS45LTIxLjggMTUuMy0yLjcgMi45LTIuNSA1LjctMi44IDguMy0uOC43LTMuNyAzLjMtNS4yIDQuNyAxMi42IDE4LjggMjIuMSA0MC4xIDI3LjQgNjMuMyA1LjMgMjMuMSA2LjEgNDYuMSAzLjEgNjguM2w1LjkgMS43YzEuMSAxLjUgMy4yIDUuMiA2LjMgNi4zIDcuNSAyLjQgMTYgMy4yIDI2LjIgNC4zIDQuOC40IDguOS4yIDE0IDEuMSAxLjIuMiAzIC43IDQuMiAxIDguOSAyLjQgMTQuNCAxMC40IDEyLjYgMTguMnoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTQyOCA0MDEuN2MtMS0uMi0yLS4zLTMtLjItMS43LjEtMy4zLjUtNC45IDEuMy02LjIgMy05IDEwLjQtNi4yIDE2LjdsLS4xLjEgMjkuNiA3MS40YzI4LjUtMTguMiA0OS44LTQ1LjMgNjEtNzYuNmwtNzYuMi0xMi45eiIvPgogICAgICAgICAgPC9nPgogICAgICAgIDwvc3ZnPg==);" + />
@@ -6305,44 +5769,11 @@ exports[`opening dock tab for installing helm chart given application is started class="TableCell icon" >
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
+ class="chartIcon intro-logo imageNotLoaded" + data-testid="image-container" + style="background-image: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNzIyLjggNzAyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogICAgICAgICAgPGcgZmlsbD0iIzhlOTI5NyI+CiAgICAgICAgICAgIDxwYXRoIGQ9Im0zMTggMjk5LjVjMi4xIDEuNiA0LjggMi41IDcuNiAyLjUgNi45IDAgMTIuNi01LjUgMTIuOS0xMi4zbC4zLS4yIDQuMy03Ni43Yy01LjIuNi0xMC40IDEuNS0xNS42IDIuNy0yOC41IDYuNS01My4yIDIwLjUtNzIuNiAzOS41bDYyLjkgNDQuNnoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTMwOS41IDQxMS45Yy0xLjQtNS45LTYuNi05LjktMTIuNC0xMC0uOCAwLTEuNy4xLTIuNS4ybC0uMS0uMi03NS41IDEyLjhjMTEuNyAzMi4yIDMzLjQgNTguNSA2MC44IDc2LjFsMjkuMi03MC43LS4yLS4zYzEuMS0yLjQgMS40LTUuMi43LTcuOXoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTI4NC40IDM1Ny41YzIuNS0uNyA0LjktMi4yIDYuNy00LjQgNC4zLTUuNCAzLjYtMTMuMi0xLjYtMTcuOGwuMS0uMy01Ny40LTUxLjRjLTE3IDI3LjgtMjUuMSA2MS4xLTIxLjQgOTUuM2w3My42LTIxLjJ6Ii8+CiAgICAgICAgICAgIDxwYXRoIGQ9Im0zNDAuMiAzODAgMjEuMiAxMC4yIDIxLjEtMTAuMSA1LjMtMjIuOS0xNC42LTE4LjJoLTIzLjZsLTE0LjYgMTguMnoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTM4NC4yIDI4OS40Yy4xIDIuNiAxIDUuMiAyLjggNy41IDQuMyA1LjQgMTIuMSA2LjQgMTcuNyAyLjRsLjIuMSA2Mi41LTQ0LjNjLTIzLjYtMjMuMS01NC40LTM4LjItODcuNi00Mi4yeiIvPgogICAgICAgICAgICA8cGF0aCBkPSJtNDkwLjMgMjgzLjctNTcuMSA1MS4xdi4yYy0yIDEuNy0zLjUgNC4xLTQuMSA2LjgtMS41IDYuOCAyLjUgMTMuNSA5LjIgMTUuM2wuMS4zIDc0IDIxLjNjMS42LTE2IC42LTMyLjUtMy4yLTQ5LTMuOS0xNi44LTEwLjQtMzIuMi0xOC45LTQ2eiIvPgogICAgICAgICAgICA8cGF0aCBkPSJtMzcyLjggNDM5LjZjLTEuMi0yLjMtMy4yLTQuMy01LjgtNS41LTItLjktNC0xLjQtNi0xLjMtNC41LjItOC43IDIuNi0xMC45IDYuOGgtLjFsLTM3LjEgNjcuMWMyNS43IDguOCA1NC4xIDEwLjcgODIuNSA0LjIgNS4xLTEuMiAxMC0yLjUgMTQuOS00LjJsLTM3LjMtNjcuMXoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTcxMS43IDQyNS02MC40LTI2Mi4yYy0zLjItMTMuNy0xMi41LTI1LjMtMjUuMy0zMS40bC0yNDQuNC0xMTYuOGMtNy4xLTMuNC0xNC44LTQuOS0yMi43LTQuNS02LjIuMy0xMi4zIDEuOS0xNy45IDQuNWwtMjQ0LjMgMTE2LjdjLTEyLjggNi4xLTIyLjEgMTcuNy0yNS4zIDMxLjRsLTYwLjIgMjYyLjNjLTIuOCAxMi4yLS41IDI1IDYuMyAzNS41LjggMS4zIDEuNyAyLjUgMi43IDMuN2wxNjkuMSAyMTAuM2M4LjkgMTEgMjIuMyAxNy40IDM2LjUgMTcuNGwyNzEuMi0uMWMxNC4yIDAgMjcuNy02LjQgMzYuNS0xNy40bDE2OS4xLTIxMC4zYzguOS0xMC45IDEyLjItMjUuNCA5LjEtMzkuMXptLTkzLTMuMmMtMS44IDcuOC0xMC4yIDEyLjYtMTguOSAxMC43LS4xIDAtLjIgMC0uMiAwLS4xIDAtLjItLjEtLjMtLjEtMS4yLS4zLTIuNy0uNS0zLjgtLjgtNS0xLjMtOC42LTMuMy0xMy4xLTUuMS05LjctMy41LTE3LjctNi40LTI1LjUtNy41LTQtLjMtNiAxLjYtOC4yIDMtMS4xLS4yLTQuNC0uOC02LjItMS4xLTE0IDQ0LTQzLjkgODIuMi04NC4zIDEwNi4xLjcgMS43IDEuOSA1LjMgMi40IDUuOS0uOSAyLjUtMi4zIDQuOC0xLjEgOC42IDIuOCA3LjQgNy40IDE0LjYgMTMgMjMuMiAyLjcgNCA1LjQgNy4xIDcuOCAxMS43LjYgMS4xIDEuMyAyLjggMS45IDMuOSAzLjggOCAxIDE3LjMtNi4yIDIwLjgtNy4zIDMuNS0xNi4zLS4yLTIwLjItOC4zLS42LTEuMS0xLjMtMi43LTEuOC0zLjgtMi4xLTQuNy0yLjgtOC44LTQuMi0xMy40LTMuMy05LjctNi0xNy44LTEwLTI0LjYtMi4yLTMuMy01LTMuNy03LjUtNC41LS41LS44LTIuMi00LTMuMS01LjYtOC4xIDMuMS0xNi40IDUuNi0yNS4xIDcuNi0zNy45IDguNi03NS45IDUuMS0xMDkuOS03LjlsLTMuMyA2Yy0yLjUuNy00LjggMS4zLTYuMyAzLjEtNS4zIDYuNC03LjUgMTYuNi0xMS4zIDI2LjMtMS41IDQuNi0yLjEgOC43LTQuMiAxMy40LS41IDEuMS0xLjMgMi42LTEuOCAzLjctMy45IDguMS0xMi45IDExLjctMjAuMiA4LjItNy4yLTMuNS0xMC0xMi43LTYuMi0yMC44LjYtMS4yIDEuMy0yLjggMS45LTMuOSAyLjQtNC42IDUuMi03LjcgNy44LTExLjcgNS41LTguNyAxMC40LTE2LjQgMTMuMi0yMy44LjctMi40LS4zLTUuOC0xLjMtOC4zbDIuNy02LjRjLTM4LjktMjMuMS02OS43LTU5LjgtODQuMy0xMDUuM2wtNi40IDEuMWMtMS43LTEtNS4xLTMuMi04LjQtMy03LjggMS4xLTE1LjggNC0yNS41IDcuNS00LjUgMS43LTguMSAzLjctMTMuMSA1LTEuMS4zLTIuNi42LTMuOC44LS4xIDAtLjIuMS0uMy4xcy0uMiAwLS4yIDBjLTguNyAxLjktMTcuMS0yLjktMTguOS0xMC43czMuOC0xNS43IDEyLjQtMTcuOGMuMSAwIC4yIDAgLjItLjFoLjFjMS4yLS4zIDIuOC0uNyAzLjktLjkgNS4xLTEgOS4yLS43IDE0LTEuMSAxMC4yLTEuMSAxOC43LTEuOSAyNi4yLTQuMyAyLjQtMSA0LjctNC4zIDYuMy02LjNsNi4xLTEuOGMtNi45LTQ3LjUgNC44LTk0LjIgMjkuOC0xMzEuOWwtNC43LTQuMmMtLjMtMS44LS43LTYtMi45LTguNC01LjgtNS40LTEzLTkuOS0yMS44LTE1LjMtNC4yLTIuNC04LTQtMTIuMS03LjEtLjktLjctMi4xLTEuNy0zLTIuNC0uMS0uMS0uMS0uMS0uMi0uMi03LTUuNi04LjYtMTUuMi0zLjYtMjEuNiAyLjgtMy42IDcuMi01LjMgMTEuNy01LjIgMy41LjEgNy4xIDEuNCAxMC4yIDMuOCAxIC44IDIuNCAxLjggMy4yIDIuNiAzLjkgMy40IDYuMyA2LjcgOS42IDEwLjIgNy4yIDcuMyAxMy4yIDEzLjQgMTkuNyAxNy44IDMuNCAyIDYuMSAxLjIgOC43LjguOC42IDMuNyAyLjYgNS4zIDMuOCAyNC45LTI2LjQgNTcuNi00NiA5NS42LTU0LjYgOC44LTIgMTcuNy0zLjMgMjYuNC00LjFsLjMtNi4yYzEuOS0xLjkgNC4xLTQuNiA0LjgtNy42LjYtNy45LS40LTE2LjMtMS42LTI2LjUtLjctNC44LTEuOC04LjctMi0xMy45IDAtMS4xIDAtMi41IDAtMy44IDAtLjEgMC0uMyAwLS40IDAtOSA2LjUtMTYuMiAxNC42LTE2LjJzMTQuNiA3LjMgMTQuNiAxNi4yYzAgMS4zLjEgMyAwIDQuMi0uMiA1LjItMS4zIDkuMS0yIDEzLjktMS4yIDEwLjItMi4zIDE4LjctMS43IDI2LjUuNiAzLjkgMi45IDUuNSA0LjggNy4zIDAgMS4xLjIgNC42LjMgNi41IDQ2LjUgNC4xIDg5LjcgMjUuNCAxMjEuNCA1OC43bDUuNi00YzEuOS4xIDYgLjcgOC45LTEgNi41LTQuNCAxMi41LTEwLjUgMTkuNy0xNy44IDMuMy0zLjUgNS43LTYuOCA5LjctMTAuMi45LS44IDIuMy0xLjggMy4yLTIuNiA3LTUuNiAxNi44LTUgMjEuOCAxLjNzMy40IDE2LTMuNiAyMS42Yy0xIC44LTIuMyAxLjktMy4yIDIuNi00LjIgMy4xLTggNC43LTEyLjIgNy4xLTguNyA1LjQtMTYgOS45LTIxLjggMTUuMy0yLjcgMi45LTIuNSA1LjctMi44IDguMy0uOC43LTMuNyAzLjMtNS4yIDQuNyAxMi42IDE4LjggMjIuMSA0MC4xIDI3LjQgNjMuMyA1LjMgMjMuMSA2LjEgNDYuMSAzLjEgNjguM2w1LjkgMS43YzEuMSAxLjUgMy4yIDUuMiA2LjMgNi4zIDcuNSAyLjQgMTYgMy4yIDI2LjIgNC4zIDQuOC40IDguOS4yIDE0IDEuMSAxLjIuMiAzIC43IDQuMiAxIDguOSAyLjQgMTQuNCAxMC40IDEyLjYgMTguMnoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTQyOCA0MDEuN2MtMS0uMi0yLS4zLTMtLjItMS43LjEtMy4zLjUtNC45IDEuMy02LjIgMy05IDEwLjQtNi4yIDE2LjdsLS4xLjEgMjkuNiA3MS40YzI4LjUtMTguMiA0OS44LTQ1LjMgNjEtNzYuNmwtNzYuMi0xMi45eiIvPgogICAgICAgICAgPC9nPgogICAgICAgIDwvc3ZnPg==);" + />
@@ -7262,44 +6625,11 @@ exports[`opening dock tab for installing helm chart given application is started class="TableCell icon" >
-
- - - - - - - - - - - - - -
+
-
- - - - - - - - - - - - - -
+
{ + const computeHotbarIndex = di.inject(computeHotbarIndexInjectable); + const activeHotbarId = di.inject(activeHotbarIdInjectable); + + return computed(() => { + const activeId = activeHotbarId.get(); + + return (activeId && computeHotbarIndex(activeId)) || 0; + }); + }, +}); + +export default activeHotbarIndexInjectable; diff --git a/packages/core/src/features/hotbar/storage/common/active-id.injectable.ts b/packages/core/src/features/hotbar/storage/common/active-id.injectable.ts new file mode 100644 index 0000000000..2514bc834d --- /dev/null +++ b/packages/core/src/features/hotbar/storage/common/active-id.injectable.ts @@ -0,0 +1,13 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { observable } from "mobx"; + +const activeHotbarIdInjectable = getInjectable({ + id: "active-hotbar-id", + instantiate: () => observable.box(), +}); + +export default activeHotbarIdInjectable; diff --git a/packages/core/src/features/hotbar/storage/common/active.injectable.ts b/packages/core/src/features/hotbar/storage/common/active.injectable.ts new file mode 100644 index 0000000000..433a6f4323 --- /dev/null +++ b/packages/core/src/features/hotbar/storage/common/active.injectable.ts @@ -0,0 +1,24 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { computed } from "mobx"; +import activeHotbarIdInjectable from "./active-id.injectable"; +import hotbarsStateInjectable from "./state.injectable"; + +const activeHotbarInjectable = getInjectable({ + id: "active-hotbar", + instantiate: (di) => { + const state = di.inject(hotbarsStateInjectable); + const activeHotbarId = di.inject(activeHotbarIdInjectable); + + return computed(() => { + const id = activeHotbarId.get(); + + return (id && state.get(id)) || undefined; + }); + }, +}); + +export default activeHotbarInjectable; diff --git a/packages/core/src/features/hotbar/storage/common/add.injectable.ts b/packages/core/src/features/hotbar/storage/common/add.injectable.ts new file mode 100644 index 0000000000..19cbadc7c6 --- /dev/null +++ b/packages/core/src/features/hotbar/storage/common/add.injectable.ts @@ -0,0 +1,33 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { action } from "mobx"; +import type { CreateHotbarData, CreateHotbarOptions } from "./types"; +import activeHotbarIdInjectable from "./active-id.injectable"; +import hotbarsStateInjectable from "./state.injectable"; +import createHotbarInjectable from "./create-hotbar.injectable"; + +export type AddHotbar = (data: CreateHotbarData, { setActive }?: CreateHotbarOptions) => void; + +const addHotbarInjectable = getInjectable({ + id: "add-hotbar", + instantiate: (di): AddHotbar => { + const state = di.inject(hotbarsStateInjectable); + const activeHotbarId = di.inject(activeHotbarIdInjectable); + const createHotbar = di.inject(createHotbarInjectable); + + return action((data, { setActive = false } = {}) => { + const hotbar = createHotbar(data); + + state.set(hotbar.id, hotbar); + + if (setActive) { + activeHotbarId.set(hotbar.id); + } + }); + }, +}); + +export default addHotbarInjectable; diff --git a/packages/core/src/features/hotbar/storage/common/compute-display-index.injectable.ts b/packages/core/src/features/hotbar/storage/common/compute-display-index.injectable.ts new file mode 100644 index 0000000000..c67b75db39 --- /dev/null +++ b/packages/core/src/features/hotbar/storage/common/compute-display-index.injectable.ts @@ -0,0 +1,19 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import computeHotbarIndexInjectable from "./compute-hotbar-index.injectable"; + +export type ComputeDisplayIndex = (hotbarId: string) => string; + +const computeDisplayIndexInjectable = getInjectable({ + id: "compute-display-index", + instantiate: (di): ComputeDisplayIndex => { + const computeHotbarIndex = di.inject(computeHotbarIndexInjectable); + + return (hotbarId) => `${computeHotbarIndex(hotbarId) + 1}`; + }, +}); + +export default computeDisplayIndexInjectable; diff --git a/packages/core/src/features/hotbar/storage/common/compute-display-label.injectable.ts b/packages/core/src/features/hotbar/storage/common/compute-display-label.injectable.ts new file mode 100644 index 0000000000..5900563cb2 --- /dev/null +++ b/packages/core/src/features/hotbar/storage/common/compute-display-label.injectable.ts @@ -0,0 +1,20 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import type { Hotbar } from "./hotbar"; +import computeDisplayIndexInjectable from "./compute-display-index.injectable"; + +export type ComputeHotbarDisplayLabel = (hotbar: Hotbar) => string; + +const computeHotbarDisplayLabelInjectable = getInjectable({ + id: "compute-hotbar-display-label", + instantiate: (di): ComputeHotbarDisplayLabel => { + const computeDisplayIndex = di.inject(computeDisplayIndexInjectable); + + return (hotbar) => `${computeDisplayIndex(hotbar.id)}: ${hotbar.name}`; + }, +}); + +export default computeHotbarDisplayLabelInjectable; diff --git a/packages/core/src/features/hotbar/storage/common/compute-hotbar-index.injectable.ts b/packages/core/src/features/hotbar/storage/common/compute-hotbar-index.injectable.ts new file mode 100644 index 0000000000..b54954c618 --- /dev/null +++ b/packages/core/src/features/hotbar/storage/common/compute-hotbar-index.injectable.ts @@ -0,0 +1,31 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import hotbarsStateInjectable from "./state.injectable"; + +export type ComputeHotbarIndex = (hotbarId: string) => number; + +const computeHotbarIndexInjectable = getInjectable({ + id: "compute-hotbar-index", + instantiate: (di): ComputeHotbarIndex => { + const state = di.inject(hotbarsStateInjectable); + + return (hotbarId) => { + let i = 0; + + for (const hotbar of state.values()) { + if (hotbar.id === hotbarId) { + return i; + } + + i += 1; + } + + return 0; + }; + }, +}); + +export default computeHotbarIndexInjectable; diff --git a/packages/core/src/features/hotbar/storage/common/create-hotbar.injectable.ts b/packages/core/src/features/hotbar/storage/common/create-hotbar.injectable.ts new file mode 100644 index 0000000000..8d35af255b --- /dev/null +++ b/packages/core/src/features/hotbar/storage/common/create-hotbar.injectable.ts @@ -0,0 +1,24 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import type { CreateHotbarData } from "./types"; +import prefixedLoggerInjectable from "../../../../common/logger/prefixed-logger.injectable"; +import type { HotbarDependencies } from "./hotbar"; +import { Hotbar } from "./hotbar"; + +export type CreateHotbar = (data: CreateHotbarData) => Hotbar; + +const createHotbarInjectable = getInjectable({ + id: "create-hotbar", + instantiate: (di): CreateHotbar => { + const deps: HotbarDependencies = { + logger: di.inject(prefixedLoggerInjectable, "HOTBAR"), + }; + + return (data) => new Hotbar(deps, data); + }, +}); + +export default createHotbarInjectable; diff --git a/packages/core/src/features/hotbar/storage/common/find-by-name.injectable.ts b/packages/core/src/features/hotbar/storage/common/find-by-name.injectable.ts new file mode 100644 index 0000000000..321bc74b2e --- /dev/null +++ b/packages/core/src/features/hotbar/storage/common/find-by-name.injectable.ts @@ -0,0 +1,21 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { iter } from "@k8slens/utilities"; +import { getInjectable } from "@ogre-tools/injectable"; +import type { Hotbar } from "./hotbar"; +import hotbarsStateInjectable from "./state.injectable"; + +export type FindHotbarByName = (name: string) => Hotbar | undefined; + +const findHotbarByNameInjectable = getInjectable({ + id: "find-hotbar-by-name", + instantiate: (di): FindHotbarByName => { + const state = di.inject(hotbarsStateInjectable); + + return (name) => iter.find(state.values(), hotbar => hotbar.name.get() === name); + }, +}); + +export default findHotbarByNameInjectable; diff --git a/packages/core/src/features/hotbar/storage/common/get-by-id.injectable.ts b/packages/core/src/features/hotbar/storage/common/get-by-id.injectable.ts new file mode 100644 index 0000000000..00fe43814f --- /dev/null +++ b/packages/core/src/features/hotbar/storage/common/get-by-id.injectable.ts @@ -0,0 +1,20 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import type { Hotbar } from "./hotbar"; +import hotbarsStateInjectable from "./state.injectable"; + +export type GetHotbarById = (id: string) => Hotbar | undefined; + +const getHotbarByIdInjectable = getInjectable({ + id: "get-hotbar-by-id", + instantiate: (di): GetHotbarById => { + const state = di.inject(hotbarsStateInjectable); + + return (id) => state.get(id); + }, +}); + +export default getHotbarByIdInjectable; diff --git a/packages/core/src/features/hotbar/storage/common/hotbar.ts b/packages/core/src/features/hotbar/storage/common/hotbar.ts new file mode 100644 index 0000000000..efb2e60579 --- /dev/null +++ b/packages/core/src/features/hotbar/storage/common/hotbar.ts @@ -0,0 +1,175 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { type IObservableValue, type IObservableArray, observable, runInAction, toJS } from "mobx"; +import type { CatalogEntity } from "../../../../common/catalog"; +import { getShortName } from "../../../../common/catalog/helpers"; +import type { HotbarItem, CreateHotbarData } from "./types"; +import { defaultHotbarCells } from "./types"; +import { broadcastMessage } from "../../../../common/ipc"; +import { hotbarTooManyItemsChannel } from "../../../../common/ipc/hotbar"; +import * as uuid from "uuid"; +import type { Logger } from "../../../../common/logger"; +import { tuple } from "@k8slens/utilities"; + +export interface HotbarDependencies { + readonly logger: Logger; +} + +export interface HotbarData { + readonly id: string; + readonly name: string; + readonly items: (HotbarItem | null)[]; +} + +export class Hotbar { + readonly id: string; + readonly name: IObservableValue; + readonly items: IObservableArray; + + constructor(private readonly dependencies: HotbarDependencies, data: CreateHotbarData) { + this.id = data.id ?? uuid.v4(); + this.name = observable.box(data.name); + this.items = observable.array(data.items ?? tuple.filled(defaultHotbarCells, null)); + } + + isFull() { + for (const item of this.items) { + if (!item) { + return false; + } + } + + return true; + } + + hasEntity(entityId: string) { + return this.items.findIndex(item => item?.entity.uid === entityId) >= 0; + } + + private findClosestEmptyIndex(from: number, direction = 1) { + let index = from; + + while (this.items[index] != null) { + index += direction; + } + + return index; + } + + restack(from: number, to: number) { + runInAction(() => { + const source = this.items[from]; + const moveDown = from < to; + + if ( + from < 0 || + to < 0 || + from >= this.items.length || + to >= this.items.length || + isNaN(from) || + isNaN(to) + ) { + throw new Error("Invalid 'from' or 'to' arguments"); + } + + if (from == to) { + return; + } + + this.items.splice(from, 1, null); + + if (this.items[to] == null) { + this.items.splice(to, 1, source); + } else { + // Move cells up or down to closes empty cell + this.items.splice(this.findClosestEmptyIndex(to, moveDown ? -1 : 1), 1); + this.items.splice(to, 0, source); + } + }); + } + + toggleEntity(item: CatalogEntity) { + runInAction(() => { + if (this.hasEntity(item.getId())) { + this.removeEntity(item.getId()); + } else { + this.addEntity(item); + } + }); + } + + removeEntity(uid: string) { + runInAction(() => { + const index = this.items.findIndex((item) => item?.entity.uid === uid); + + if (index < 0) { + return; + } + + this.items[index] = null; + }); + } + + addEntity(item: CatalogEntity, cellIndex?: number) { + const uid = item.getId(); + const name = item.getName(); + const shortName = getShortName(item); + + if (typeof uid !== "string") { + throw new TypeError("CatalogEntity's ID must be a string"); + } + + if (typeof name !== "string") { + throw new TypeError("CatalogEntity's NAME must be a string"); + } + + if (typeof shortName !== "string") { + throw new TypeError("CatalogEntity's SHORT_NAME must be a string"); + } + + if (this.hasEntity(item.getId())) { + return; + } + + const entity = { + uid, + name, + source: item.metadata.source, + shortName, + }; + const newItem = { entity }; + + if (cellIndex === undefined) { + // Add item to empty cell + const emptyCellIndex = this.items.indexOf(null); + + if (emptyCellIndex >= 0) { + runInAction(() => { + this.items[emptyCellIndex] = newItem; + }); + } else { + broadcastMessage(hotbarTooManyItemsChannel); + } + } else if (0 <= cellIndex && cellIndex < this.items.length) { + runInAction(() => { + this.items[cellIndex] = newItem; + }); + } else { + this.dependencies.logger.error( + "cannot pin entity to hotbar outside of index range", + { entityId: uid, hotbarId: this.id, cellIndex }, + ); + } + } + + toJSON(): HotbarData { + return { + id: this.id, + items: toJS(this.items), + name: this.name.get(), + }; + } +} diff --git a/packages/core/src/features/hotbar/storage/common/hotbars.injectable.ts b/packages/core/src/features/hotbar/storage/common/hotbars.injectable.ts new file mode 100644 index 0000000000..2a13b4fc27 --- /dev/null +++ b/packages/core/src/features/hotbar/storage/common/hotbars.injectable.ts @@ -0,0 +1,18 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { computed } from "mobx"; +import hotbarsStateInjectable from "./state.injectable"; + +const hotbarsInjectable = getInjectable({ + id: "hotbars", + instantiate: (di) => { + const state = di.inject(hotbarsStateInjectable); + + return computed(() => [...state.values()]); + }, +}); + +export default hotbarsInjectable; diff --git a/packages/core/src/common/hotbars/migrations-token.ts b/packages/core/src/features/hotbar/storage/common/migrations-token.ts similarity index 76% rename from packages/core/src/common/hotbars/migrations-token.ts rename to packages/core/src/features/hotbar/storage/common/migrations-token.ts index 5441844933..ede5cd1966 100644 --- a/packages/core/src/common/hotbars/migrations-token.ts +++ b/packages/core/src/features/hotbar/storage/common/migrations-token.ts @@ -4,7 +4,7 @@ */ import { getInjectionToken } from "@ogre-tools/injectable"; -import type { MigrationDeclaration } from "../base-store/migrations.injectable"; +import type { MigrationDeclaration } from "../../../../common/persistent-storage/migrations.injectable"; export const hotbarStoreMigrationInjectionToken = getInjectionToken({ id: "hotbar-store-migration-token", diff --git a/packages/core/src/features/hotbar/storage/common/remove-entity-from-all.injectable.ts b/packages/core/src/features/hotbar/storage/common/remove-entity-from-all.injectable.ts new file mode 100644 index 0000000000..5890a0e995 --- /dev/null +++ b/packages/core/src/features/hotbar/storage/common/remove-entity-from-all.injectable.ts @@ -0,0 +1,24 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { action } from "mobx"; +import hotbarsInjectable from "./hotbars.injectable"; + +export type RemoveEntityFromAllHotbars = (entityId: string) => void; + +const removeEntityFromAllHotbarsInjectable = getInjectable({ + id: "remove-entity-from-all-hotbars", + instantiate: (di): RemoveEntityFromAllHotbars => { + const hotbars = di.inject(hotbarsInjectable); + + return action((entityId) => { + for (const hotbar of hotbars.get()) { + hotbar.removeEntity(entityId); + } + }); + }, +}); + +export default removeEntityFromAllHotbarsInjectable; diff --git a/packages/core/src/features/hotbar/storage/common/remove.injectable.ts b/packages/core/src/features/hotbar/storage/common/remove.injectable.ts new file mode 100644 index 0000000000..d06c19b2e0 --- /dev/null +++ b/packages/core/src/features/hotbar/storage/common/remove.injectable.ts @@ -0,0 +1,33 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { iter } from "@k8slens/utilities"; +import { getInjectable } from "@ogre-tools/injectable"; +import assert from "assert"; +import { action } from "mobx"; +import activeHotbarIdInjectable from "./active-id.injectable"; +import type { Hotbar } from "./hotbar"; +import hotbarsStateInjectable from "./state.injectable"; + +export type RemoveHotbar = (hotbar: Hotbar) => void; + +const removeHotbarInjectable = getInjectable({ + id: "remove-hotbar", + instantiate: (di): RemoveHotbar => { + const state = di.inject(hotbarsStateInjectable); + const activeHotbarId = di.inject(activeHotbarIdInjectable); + + return action((hotbar) => { + assert(state.size >= 2, "Cannot remove the last hotbar"); + + state.delete(hotbar.id); + + if (activeHotbarId.get() === hotbar.id) { + activeHotbarId.set(iter.first(state.values())?.id); + } + }); + }, +}); + +export default removeHotbarInjectable; diff --git a/packages/core/src/features/hotbar/storage/common/set-as-active.injectable.ts b/packages/core/src/features/hotbar/storage/common/set-as-active.injectable.ts new file mode 100644 index 0000000000..226914fcae --- /dev/null +++ b/packages/core/src/features/hotbar/storage/common/set-as-active.injectable.ts @@ -0,0 +1,40 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { iter } from "@k8slens/utilities"; +import { getInjectable } from "@ogre-tools/injectable"; +import { action } from "mobx"; +import activeHotbarIdInjectable from "./active-id.injectable"; +import type { Hotbar } from "./hotbar"; +import hotbarsStateInjectable from "./state.injectable"; + +export type SetAsActiveHotbar = (desc: Hotbar | number | string) => void; + +const setAsActiveHotbarInjectable = getInjectable({ + id: "set-as-active-hotbar", + instantiate: (di): SetAsActiveHotbar => { + const hotbarsState = di.inject(hotbarsStateInjectable); + const activeHotbarId = di.inject(activeHotbarIdInjectable); + + return action((desc) => { + if (typeof desc === "number") { + const hotbar = iter.nth(hotbarsState.values(), desc); + + if (hotbar) { + activeHotbarId.set(hotbar.id); + } + } else if (typeof desc === "string") { + if (hotbarsState.has(desc)) { + activeHotbarId.set(desc); + } + } else { + if (hotbarsState.has(desc.id)) { + activeHotbarId.set(desc.id); + } + } + }); + }, +}); + +export default setAsActiveHotbarInjectable; diff --git a/packages/core/src/features/hotbar/storage/common/state.injectable.ts b/packages/core/src/features/hotbar/storage/common/state.injectable.ts new file mode 100644 index 0000000000..c544172d9a --- /dev/null +++ b/packages/core/src/features/hotbar/storage/common/state.injectable.ts @@ -0,0 +1,14 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { observable } from "mobx"; +import type { Hotbar } from "./hotbar"; + +const hotbarsStateInjectable = getInjectable({ + id: "hotbars-state", + instantiate: () => observable.map(), +}); + +export default hotbarsStateInjectable; diff --git a/packages/core/src/features/hotbar/storage/common/storage.injectable.ts b/packages/core/src/features/hotbar/storage/common/storage.injectable.ts new file mode 100644 index 0000000000..3bdc2bd3b6 --- /dev/null +++ b/packages/core/src/features/hotbar/storage/common/storage.injectable.ts @@ -0,0 +1,116 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { iter } from "@k8slens/utilities"; +import { getInjectable } from "@ogre-tools/injectable"; +import { action, comparer } from "mobx"; +import catalogCatalogEntityInjectable from "../../../../common/catalog-entities/general-catalog-entities/implementations/catalog-catalog-entity.injectable"; +import { hotbarStoreMigrationInjectionToken } from "./migrations-token"; +import { defaultHotbarCells } from "./types"; +import createPersistentStorageInjectable from "../../../../common/persistent-storage/create.injectable"; +import persistentStorageMigrationsInjectable from "../../../../common/persistent-storage/migrations.injectable"; +import storeMigrationVersionInjectable from "../../../../common/vars/store-migration-version.injectable"; +import activeHotbarIdInjectable from "./active-id.injectable"; +import createHotbarInjectable from "./create-hotbar.injectable"; +import type { Hotbar, HotbarData } from "./hotbar"; +import hotbarsStateInjectable from "./state.injectable"; + +export interface HotbarStoreModel { + hotbars: HotbarData[]; + activeHotbarId: string | undefined; +} + +const hotbarsPersistentStorageInjectable = getInjectable({ + id: "hotbars-persistent-storage", + instantiate: (di) => { + const state = di.inject(hotbarsStateInjectable); + const createPersistentStorage = di.inject(createPersistentStorageInjectable); + const catalogCatalogEntity = di.inject(catalogCatalogEntityInjectable); + const activeHotbarId = di.inject(activeHotbarIdInjectable); + const createHotbar = di.inject(createHotbarInjectable); + + return createPersistentStorage({ + configName: "lens-hotbar-store", + accessPropertiesByDotNotation: false, // To make dots safe in cluster context names + syncOptions: { + equals: comparer.structural, + }, + projectVersion: di.inject(storeMigrationVersionInjectable), + migrations: di.inject(persistentStorageMigrationsInjectable, hotbarStoreMigrationInjectionToken), + fromStore: action((data) => { + if (!data.hotbars || !data.hotbars.length) { + const hotbar = createHotbar({ + name: "Default", + }); + const { + metadata: { + uid, + name, + source, + }, + } = catalogCatalogEntity; + + hotbar.items[0] = { + entity: { + uid, + name, + source, + }, + }; + state.replace([[hotbar.id, hotbar]]); + } else { + state.replace(data.hotbars.map((hotbar) => [hotbar.id, createHotbar(hotbar)])); + } + + for (const hotbar of state.values()) { + ensureExactHotbarItemLength(hotbar); + } + + if (data.activeHotbarId) { + activeHotbarId.set(data.activeHotbarId); + } + + const firstHotbarId = iter.first(state.values())?.id; + + if (!activeHotbarId.get()) { + activeHotbarId.set(firstHotbarId); + } else if (!iter.find(state.values(), hotbar => hotbar.id === activeHotbarId.get())) { + activeHotbarId.set(firstHotbarId); + } + }), + toJSON: () => ({ + hotbars: iter.chain(state.values()) + .map(hotbar => hotbar.toJSON()) + .toArray(), + activeHotbarId: activeHotbarId.get(), + }), + }); + }, +}); + +export default hotbarsPersistentStorageInjectable; + +/** + * This function ensures that there are always exactly `defaultHotbarCells` + * worth of items in the hotbar. + * @param hotbar The hotbar to modify + */ +function ensureExactHotbarItemLength(hotbar: Hotbar) { + // if there are not enough items + while (hotbar.items.length < defaultHotbarCells) { + hotbar.items.push(null); + } + + // if for some reason the hotbar was overfilled before, remove as many entries + // as needed, but prefer empty slots and items at the end first. + while (hotbar.items.length > defaultHotbarCells) { + const lastNull = hotbar.items.lastIndexOf(null); + + if (lastNull >= 0) { + hotbar.items.splice(lastNull, 1); + } else { + hotbar.items.length = defaultHotbarCells; + } + } +} diff --git a/packages/core/src/features/hotbar/storage/common/switch-to-next.injectable.ts b/packages/core/src/features/hotbar/storage/common/switch-to-next.injectable.ts new file mode 100644 index 0000000000..5648c5bfc4 --- /dev/null +++ b/packages/core/src/features/hotbar/storage/common/switch-to-next.injectable.ts @@ -0,0 +1,32 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { action } from "mobx"; +import activeHotbarIndexInjectable from "./active-hotbar-index.injectable"; +import setAsActiveHotbarInjectable from "./set-as-active.injectable"; +import hotbarsStateInjectable from "./state.injectable"; + +export type SwitchToNextHotbar = () => void; + +const switchToNextHotbarInjectable = getInjectable({ + id: "switch-to-next-hotbar", + instantiate: (di): SwitchToNextHotbar => { + const setAsActiveHotbar = di.inject(setAsActiveHotbarInjectable); + const activeHotbarIndex = di.inject(activeHotbarIndexInjectable); + const state = di.inject(hotbarsStateInjectable); + + return action(() => { + const index = activeHotbarIndex.get() + 1; + + if (index >= state.size) { + setAsActiveHotbar(0); + } else { + setAsActiveHotbar(index); + } + }); + }, +}); + +export default switchToNextHotbarInjectable; diff --git a/packages/core/src/features/hotbar/storage/common/switch-to-previous.injectable.ts b/packages/core/src/features/hotbar/storage/common/switch-to-previous.injectable.ts new file mode 100644 index 0000000000..d18a12c27c --- /dev/null +++ b/packages/core/src/features/hotbar/storage/common/switch-to-previous.injectable.ts @@ -0,0 +1,32 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { action } from "mobx"; +import activeHotbarIndexInjectable from "./active-hotbar-index.injectable"; +import setAsActiveHotbarInjectable from "./set-as-active.injectable"; +import hotbarsStateInjectable from "./state.injectable"; + +export type SwitchToPreviousHotbar = () => void; + +const switchToPreviousHotbarInjectable = getInjectable({ + id: "switch-to-previous-hotbar", + instantiate: (di): SwitchToPreviousHotbar => { + const setAsActiveHotbar = di.inject(setAsActiveHotbarInjectable); + const activeHotbarIndex = di.inject(activeHotbarIndexInjectable); + const state = di.inject(hotbarsStateInjectable); + + return action(() => { + const index = activeHotbarIndex.get() - 1; + + if (index < 0) { + setAsActiveHotbar(state.size - 1); + } else { + setAsActiveHotbar(index); + } + }); + }, +}); + +export default switchToPreviousHotbarInjectable; diff --git a/packages/core/src/features/hotbar/storage/common/toggling.injectable.ts b/packages/core/src/features/hotbar/storage/common/toggling.injectable.ts new file mode 100644 index 0000000000..4daf9ca41e --- /dev/null +++ b/packages/core/src/features/hotbar/storage/common/toggling.injectable.ts @@ -0,0 +1,25 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import activeHotbarInjectable from "./active.injectable"; +import type { Hotbar } from "./hotbar"; + +export type ActiveHotbarModel = Pick; + +const activeHotbarModelInjectable = getInjectable({ + id: "active-hotbar-model", + instantiate: (di): ActiveHotbarModel => { + const activeHotbar = di.inject(activeHotbarInjectable); + + return { + hasEntity: (entityId) => activeHotbar.get()?.hasEntity(entityId) ?? false, + toggleEntity: (entity) => activeHotbar.get()?.toggleEntity(entity), + addEntity: (entity) => activeHotbar.get()?.addEntity(entity), + removeEntity: (entityId) => activeHotbar.get()?.removeEntity(entityId), + }; + }, +}); + +export default activeHotbarModelInjectable; diff --git a/packages/core/src/common/hotbars/types.ts b/packages/core/src/features/hotbar/storage/common/types.ts similarity index 56% rename from packages/core/src/common/hotbars/types.ts rename to packages/core/src/features/hotbar/storage/common/types.ts index 2925c785a0..3153fdd2fa 100644 --- a/packages/core/src/common/hotbars/types.ts +++ b/packages/core/src/features/hotbar/storage/common/types.ts @@ -3,9 +3,6 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import * as uuid from "uuid"; -import type { Tuple } from "@k8slens/utilities"; -import { tuple } from "@k8slens/utilities"; export interface HotbarItem { entity: { @@ -18,12 +15,10 @@ export interface HotbarItem { }; } -export type Hotbar = Required; - export interface CreateHotbarData { id?: string; name: string; - items?: Tuple; + items?: (HotbarItem | null)[]; } export interface CreateHotbarOptions { @@ -31,11 +26,3 @@ export interface CreateHotbarOptions { } export const defaultHotbarCells = 12; // Number is chosen to easy hit any item with keyboard - -export function getEmptyHotbar(name: string, id: string = uuid.v4()): Hotbar { - return { - id, - items: tuple.filled(defaultHotbarCells, null), - name, - }; -} diff --git a/packages/core/src/features/hotbar/storage/main/5.0.0-alpha.0.injectable.ts b/packages/core/src/features/hotbar/storage/main/5.0.0-alpha.0.injectable.ts new file mode 100644 index 0000000000..9450b9528f --- /dev/null +++ b/packages/core/src/features/hotbar/storage/main/5.0.0-alpha.0.injectable.ts @@ -0,0 +1,30 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +// Cleans up a store that had the state related data stored +import catalogCatalogEntityInjectable from "../../../../common/catalog-entities/general-catalog-entities/implementations/catalog-catalog-entity.injectable"; +import { getInjectable } from "@ogre-tools/injectable"; +import { hotbarStoreMigrationInjectionToken } from "../common/migrations-token"; +import createHotbarInjectable from "../common/create-hotbar.injectable"; + +const v500Alpha0HotbarStoreMigrationInjectable = getInjectable({ + id: "v5.0.0-alpha.0-hotbar-store-migration", + instantiate: (di) => ({ + version: "5.0.0-alpha.0", + run(store) { + const catalogCatalogEntity = di.inject(catalogCatalogEntityInjectable); + const createHotbar = di.inject(createHotbarInjectable); + const hotbar = createHotbar({ name: "default" }); + + hotbar.addEntity(catalogCatalogEntity); + + store.set("hotbars", [hotbar.toJSON()]); + }, + }), + injectionToken: hotbarStoreMigrationInjectionToken, +}); + +export default v500Alpha0HotbarStoreMigrationInjectable; + diff --git a/packages/core/src/main/hotbar-store/migrations/5.0.0-alpha.2.injectable.ts b/packages/core/src/features/hotbar/storage/main/5.0.0-alpha.2.injectable.ts similarity index 76% rename from packages/core/src/main/hotbar-store/migrations/5.0.0-alpha.2.injectable.ts rename to packages/core/src/features/hotbar/storage/main/5.0.0-alpha.2.injectable.ts index 550f46a940..adf7ff4f30 100644 --- a/packages/core/src/main/hotbar-store/migrations/5.0.0-alpha.2.injectable.ts +++ b/packages/core/src/features/hotbar/storage/main/5.0.0-alpha.2.injectable.ts @@ -4,10 +4,10 @@ */ // Cleans up a store that had the state related data stored -import type { Hotbar } from "../../../common/hotbars/types"; import * as uuid from "uuid"; import { getInjectable } from "@ogre-tools/injectable"; -import { hotbarStoreMigrationInjectionToken } from "../../../common/hotbars/migrations-token"; +import { hotbarStoreMigrationInjectionToken } from "../common/migrations-token"; +import type { HotbarData } from "../common/hotbar"; const v500Alpha2HotbarStoreMigrationInjectable = getInjectable({ id: "v5.0.0-alpha.2-hotbar-store-migration", @@ -15,7 +15,7 @@ const v500Alpha2HotbarStoreMigrationInjectable = getInjectable({ version: "5.0.0-alpha.2", run(store) { const rawHotbars = store.get("hotbars"); - const hotbars: Hotbar[] = Array.isArray(rawHotbars) ? rawHotbars : []; + const hotbars: HotbarData[] = Array.isArray(rawHotbars) ? rawHotbars : []; store.set("hotbars", hotbars.map(({ id, ...rest }) => ({ id: id || uuid.v4(), diff --git a/packages/core/src/features/hotbar/storage/main/5.0.0-beta.10.injectable.ts b/packages/core/src/features/hotbar/storage/main/5.0.0-beta.10.injectable.ts new file mode 100644 index 0000000000..a777a81b6a --- /dev/null +++ b/packages/core/src/features/hotbar/storage/main/5.0.0-beta.10.injectable.ts @@ -0,0 +1,168 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import * as uuid from "uuid"; +import directoryForUserDataInjectable from "../../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; +import catalogCatalogEntityInjectable from "../../../../common/catalog-entities/general-catalog-entities/implementations/catalog-catalog-entity.injectable"; +import { isDefined, isErrnoException } from "@k8slens/utilities"; +import joinPathsInjectable from "../../../../common/path/join-paths.injectable"; +import { getInjectable } from "@ogre-tools/injectable"; +import { hotbarStoreMigrationInjectionToken } from "../common/migrations-token"; +import readJsonSyncInjectable from "../../../../common/fs/read-json-sync.injectable"; +import loggerInjectable from "../../../../common/logger.injectable"; +import { generateNewIdFor } from "../../../../common/utils/generate-new-id-for"; +import type { ClusterModel } from "../../../../common/cluster-types"; +import { defaultHotbarCells } from "../common/types"; +import type { HotbarData } from "../common/hotbar"; +import createHotbarInjectable from "../common/create-hotbar.injectable"; + +interface Pre500WorkspaceStoreModel { + workspaces: { + id: string; + name: string; + }[]; +} + +interface Pre500ClusterModel extends ClusterModel { + workspace?: string; + workspaces?: string[]; +} + +interface Pre500ClusterStoreModel { + clusters?: Pre500ClusterModel[]; +} + +const v500Beta10HotbarStoreMigrationInjectable = getInjectable({ + id: "v5.0.0-beta.10-hotbar-store-migration", + instantiate: (di) => ({ + version: "5.0.0-beta.10", + run(store) { + const userDataPath = di.inject(directoryForUserDataInjectable); + const joinPaths = di.inject(joinPathsInjectable); + const readJsonSync = di.inject(readJsonSyncInjectable); + const catalogCatalogEntity = di.inject(catalogCatalogEntityInjectable); + const logger = di.inject(loggerInjectable); + const createHotbar = di.inject(createHotbarInjectable); + const rawHotbars = store.get("hotbars"); + const hotbars: HotbarData[] = Array.isArray(rawHotbars) ? rawHotbars.filter(h => h && typeof h === "object") : []; + + // Hotbars might be empty, if some of the previous migrations weren't run + if (hotbars.length === 0) { + const hotbar = createHotbar({ name: "default" }); + + hotbar.addEntity(catalogCatalogEntity); + hotbars.push(hotbar.toJSON()); + } + + try { + const workspaceStoreData: Pre500WorkspaceStoreModel = readJsonSync(joinPaths(userDataPath, "lens-workspace-store.json")); + const { clusters = [] }: Pre500ClusterStoreModel = readJsonSync(joinPaths(userDataPath, "lens-cluster-store.json")); + const workspaceHotbars = new Map(); // mapping from WorkspaceId to HotBar + + for (const { id, name } of workspaceStoreData.workspaces) { + logger.info(`Creating new hotbar for ${name}`); + workspaceHotbars.set(id, { + id: uuid.v4(), + items: [], + name: `Workspace: ${name}`, + }); + } + + { + // grab the default named hotbar or the first. + const defaultHotbarIndex = Math.max(0, hotbars.findIndex(hotbar => hotbar.name === "default")); + const [{ name, id, items }] = hotbars.splice(defaultHotbarIndex, 1); + + workspaceHotbars.set("default", { + name, + id, + items: items.filter(isDefined), + }); + } + + for (const cluster of clusters) { + const uid = generateNewIdFor(cluster); + + for (const workspaceId of cluster.workspaces ?? [cluster.workspace].filter(isDefined)) { + const workspaceHotbar = workspaceHotbars.get(workspaceId); + + if (!workspaceHotbar) { + logger.info(`Cluster ${uid} has unknown workspace ID, skipping`); + continue; + } + + logger.info(`Adding cluster ${uid} to ${workspaceHotbar.name}`); + + if (workspaceHotbar?.items.length < defaultHotbarCells) { + workspaceHotbar.items.push({ + entity: { + uid: generateNewIdFor(cluster), + name: cluster.preferences?.clusterName || cluster.contextName, + }, + }); + } + } + } + + for (const hotbar of workspaceHotbars.values()) { + if (hotbar.items.length === 0) { + logger.info(`Skipping ${hotbar.name} due to it being empty`); + continue; + } + + while (hotbar.items.length < defaultHotbarCells) { + hotbar.items.push(null); + } + + hotbars.push(hotbar); + } + + /** + * Finally, make sure that the catalog entity hotbar item is in place. + * Just in case something else removed it. + * + * if every hotbar has elements that all not the `catalog-entity` item + */ + if (hotbars.every(hotbar => hotbar.items.every(item => item?.entity?.uid !== "catalog-entity"))) { + // note, we will add a new whole hotbar here called "default" if that was previously removed + const defaultHotbarIndex = hotbars.findIndex(hotbar => hotbar.name === "default"); + + if (defaultHotbarIndex >= 0) { + const defaultHotbar = createHotbar(hotbars[defaultHotbarIndex]); + + if (defaultHotbar.isFull()) { + // making a new hotbar is less destructive if the first hotbar + // called "default" is full than overriding a hotbar item + const hotbar = createHotbar({ name: "initial" }); + + hotbar.addEntity(catalogCatalogEntity); + hotbars.unshift(hotbar.toJSON()); + } else { + defaultHotbar.addEntity(catalogCatalogEntity); + hotbars[defaultHotbarIndex] = defaultHotbar.toJSON(); + } + } else { + const hotbar = createHotbar({ name: "default" }); + + hotbar.addEntity(catalogCatalogEntity); + hotbars.unshift(hotbar.toJSON()); + } + } + + } catch (error) { + // ignore files being missing + if (isErrnoException(error) && error.code !== "ENOENT") { + throw error; + } + } + + store.set("hotbars", hotbars); + }, + }), + injectionToken: hotbarStoreMigrationInjectionToken, +}); + +export default v500Beta10HotbarStoreMigrationInjectable; + diff --git a/packages/core/src/features/hotbar/storage/main/5.0.0-beta.5.injectable.ts b/packages/core/src/features/hotbar/storage/main/5.0.0-beta.5.injectable.ts new file mode 100644 index 0000000000..75f3868d7b --- /dev/null +++ b/packages/core/src/features/hotbar/storage/main/5.0.0-beta.5.injectable.ts @@ -0,0 +1,51 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getInjectable } from "@ogre-tools/injectable"; +import catalogEntityRegistryInjectable from "../../../../main/catalog/entity-registry.injectable"; +import type { HotbarData } from "../common/hotbar"; +import { hotbarStoreMigrationInjectionToken } from "../common/migrations-token"; + +const v500Beta5HotbarStoreMigrationInjectable = getInjectable({ + id: "v500-beta5-hotbar-store-migration", + instantiate: (di) => ({ + version: "5.0.0-beta.5", + run(store) { + const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable); + const rawHotbars = store.get("hotbars"); + const hotbars: HotbarData[] = Array.isArray(rawHotbars) ? rawHotbars : []; + + for (const hotbar of hotbars) { + for (let i = 0; i < hotbar.items.length; i += 1) { + const item = hotbar.items[i]; + + if (!item) { + continue; + } + + const entity = catalogEntityRegistry.findById(item.entity.uid); + + if (!entity) { + // Clear disabled item + hotbar.items[i] = null; + } else { + // Save additional data + item.entity = { + ...item.entity, + name: entity.metadata.name, + source: entity.metadata.source, + }; + } + } + } + + store.set("hotbars", hotbars); + }, + }), + injectionToken: hotbarStoreMigrationInjectionToken, +}); + +export default v500Beta5HotbarStoreMigrationInjectable; + diff --git a/packages/core/src/features/hotbar/store/main/init.injectable.ts b/packages/core/src/features/hotbar/storage/main/load-storage.injectable.ts similarity index 66% rename from packages/core/src/features/hotbar/store/main/init.injectable.ts rename to packages/core/src/features/hotbar/storage/main/load-storage.injectable.ts index cb45c396ac..d541f80249 100644 --- a/packages/core/src/features/hotbar/store/main/init.injectable.ts +++ b/packages/core/src/features/hotbar/storage/main/load-storage.injectable.ts @@ -3,21 +3,21 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import hotbarStoreInjectable from "../../../../common/hotbars/store.injectable"; import { onLoadOfApplicationInjectionToken } from "@k8slens/application"; import setupSyncingOfGeneralCatalogEntitiesInjectable from "../../../../main/start-main-application/runnables/setup-syncing-of-general-catalog-entities.injectable"; +import hotbarsPersistentStorageInjectable from "../common/storage.injectable"; -const initHotbarStoreInjectable = getInjectable({ - id: "init-hotbar-store", +const loadHotbarStorageInjectable = getInjectable({ + id: "load-hotbar-storage", instantiate: (di) => ({ run: () => { - const hotbarStore = di.inject(hotbarStoreInjectable); + const storage = di.inject(hotbarsPersistentStorageInjectable); - hotbarStore.load(); + storage.loadAndStartSyncing(); }, runAfter: setupSyncingOfGeneralCatalogEntitiesInjectable, }), injectionToken: onLoadOfApplicationInjectionToken, }); -export default initHotbarStoreInjectable; +export default loadHotbarStorageInjectable; diff --git a/packages/core/src/features/hotbar/store/renderer/init.injectable.ts b/packages/core/src/features/hotbar/storage/renderer/init.injectable.ts similarity index 53% rename from packages/core/src/features/hotbar/store/renderer/init.injectable.ts rename to packages/core/src/features/hotbar/storage/renderer/init.injectable.ts index 239807dfda..ebc787bd4a 100644 --- a/packages/core/src/features/hotbar/store/renderer/init.injectable.ts +++ b/packages/core/src/features/hotbar/storage/renderer/init.injectable.ts @@ -3,21 +3,21 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import hotbarStoreInjectable from "../../../../common/hotbars/store.injectable"; import { beforeFrameStartsSecondInjectionToken } from "../../../../renderer/before-frame-starts/tokens"; -import initClusterStoreInjectable from "../../../cluster/store/renderer/init.injectable"; +import initClusterStoreInjectable from "../../../cluster/storage/renderer/init.injectable"; +import hotbarsPersistentStorageInjectable from "../common/storage.injectable"; -const initHotbarStoreInjectable = getInjectable({ - id: "init-hotbar-store", +const loadHotbarStorageInjectable = getInjectable({ + id: "load-hotbar-storage", instantiate: (di) => ({ run: () => { - const hotbarStore = di.inject(hotbarStoreInjectable); + const storage = di.inject(hotbarsPersistentStorageInjectable); - hotbarStore.load(); + storage.loadAndStartSyncing(); }, runAfter: initClusterStoreInjectable, }), injectionToken: beforeFrameStartsSecondInjectionToken, }); -export default initHotbarStoreInjectable; +export default loadHotbarStorageInjectable; diff --git a/packages/core/src/features/hotbar/storage/storage-technical.test.ts b/packages/core/src/features/hotbar/storage/storage-technical.test.ts new file mode 100644 index 0000000000..54e6875c0a --- /dev/null +++ b/packages/core/src/features/hotbar/storage/storage-technical.test.ts @@ -0,0 +1,373 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { anyObject } from "jest-mock-extended"; +import type { CatalogEntity, CatalogEntityData, CatalogEntityKindData } from "../../../common/catalog"; +import { getDiForUnitTesting } from "../../../main/getDiForUnitTesting"; +import type { DiContainer } from "@ogre-tools/injectable"; +import catalogEntityRegistryInjectable from "../../../main/catalog/entity-registry.injectable"; +import type { IComputedValue } from "mobx"; +import { computed } from "mobx"; +import hasCategoryForEntityInjectable from "../../../common/catalog/has-category-for-entity.injectable"; +import catalogCatalogEntityInjectable from "../../../common/catalog-entities/general-catalog-entities/implementations/catalog-catalog-entity.injectable"; +import loggerInjectable from "../../../common/logger.injectable"; +import type { Logger } from "../../../common/logger"; +import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; +import storeMigrationVersionInjectable from "../../../common/vars/store-migration-version.injectable"; +import writeJsonSyncInjectable from "../../../common/fs/write-json-sync.injectable"; +import type { SetAsActiveHotbar } from "./common/set-as-active.injectable"; +import setAsActiveHotbarInjectable from "./common/set-as-active.injectable"; +import hotbarsPersistentStorageInjectable from "./common/storage.injectable"; +import type { Hotbar } from "./common/hotbar"; +import hotbarsInjectable from "./common/hotbars.injectable"; +import activeHotbarInjectable from "./common/active.injectable"; +import type { AddHotbar } from "./common/add.injectable"; +import type { GetHotbarById } from "./common/get-by-id.injectable"; +import getHotbarByIdInjectable from "./common/get-by-id.injectable"; +import addHotbarInjectable from "./common/add.injectable"; +import { defaultHotbarCells } from "./common/types"; + +function getMockCatalogEntity(data: Partial & CatalogEntityKindData): CatalogEntity { + return { + getName: jest.fn(() => data.metadata?.name), + getId: jest.fn(() => data.metadata?.uid), + getSource: jest.fn(() => data.metadata?.source ?? "unknown"), + isEnabled: jest.fn(() => data.status?.enabled ?? true), + onContextMenuOpen: jest.fn(), + onSettingsOpen: jest.fn(), + metadata: {}, + spec: {}, + status: {}, + ...data, + } as CatalogEntity; +} + +describe("Hotbars technical tests", () => { + let di: DiContainer; + let testCluster: CatalogEntity; + let minikubeCluster: CatalogEntity; + let awsCluster: CatalogEntity; + let loggerMock: jest.Mocked; + let setAsActiveHotbar: SetAsActiveHotbar; + let hotbars: IComputedValue; + let activeHotbar: IComputedValue; + let addHotbar: AddHotbar; + let getHotbarById: GetHotbarById; + + beforeEach(async () => { + di = getDiForUnitTesting(); + + testCluster = getMockCatalogEntity({ + apiVersion: "v1", + kind: "Cluster", + status: { + phase: "Running", + }, + metadata: { + uid: "some-test-id", + name: "my-test-cluster", + source: "local", + labels: {}, + }, + }); + minikubeCluster = getMockCatalogEntity({ + apiVersion: "v1", + kind: "Cluster", + status: { + phase: "Running", + }, + metadata: { + uid: "some-minikube-id", + name: "my-minikube-cluster", + source: "local", + labels: {}, + }, + }); + awsCluster = getMockCatalogEntity({ + apiVersion: "v1", + kind: "Cluster", + status: { + phase: "Running", + }, + metadata: { + uid: "some-aws-id", + name: "my-aws-cluster", + source: "local", + labels: {}, + }, + }); + + di.override(hasCategoryForEntityInjectable, () => () => true); + + loggerMock = { + warn: jest.fn(), + debug: jest.fn(), + error: jest.fn(), + info: jest.fn(), + silly: jest.fn(), + }; + + di.override(loggerInjectable, () => loggerMock); + + di.override(directoryForUserDataInjectable, () => "/some-directory-for-user-data"); + + const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable); + const catalogCatalogEntity = di.inject(catalogCatalogEntityInjectable); + + catalogEntityRegistry.addComputedSource("some-id", computed(() => [ + testCluster, + minikubeCluster, + awsCluster, + catalogCatalogEntity, + ])); + + setAsActiveHotbar = di.inject(setAsActiveHotbarInjectable); + hotbars = di.inject(hotbarsInjectable); + activeHotbar = di.inject(activeHotbarInjectable); + addHotbar = di.inject(addHotbarInjectable); + getHotbarById = di.inject(getHotbarByIdInjectable); + }); + + describe("given no previous data in store, running all migrations", () => { + beforeEach(() => { + di.override(storeMigrationVersionInjectable, () => "9999.0.0"); + di.inject(hotbarsPersistentStorageInjectable).loadAndStartSyncing(); + }); + + describe("load", () => { + it("loads one hotbar by default", () => { + expect(hotbars.get().length).toEqual(1); + }); + }); + + describe("add", () => { + it("adds a hotbar", () => { + addHotbar({ name: "hottest" }); + expect(hotbars.get().length).toEqual(2); + }); + }); + + describe("hotbar items", () => { + it("initially creates default number of empty cells", () => { + expect(activeHotbar.get()?.items?.length).toEqual(defaultHotbarCells); + }); + + it("initially adds catalog entity as first item", () => { + expect(activeHotbar.get()?.items[0]?.entity.name).toEqual("Catalog"); + }); + + it("adds items", () => { + activeHotbar.get()?.addEntity(testCluster); + const items = activeHotbar.get()?.items.filter(Boolean); + + expect(items?.length).toEqual(2); + }); + + it("removes items", () => { + activeHotbar.get()?.addEntity(testCluster); + activeHotbar.get()?.removeEntity("some-test-id"); + activeHotbar.get()?.removeEntity("catalog-entity"); + const items = activeHotbar.get()?.items.filter(Boolean); + + expect(items).toStrictEqual([]); + }); + + it("does nothing if removing with invalid uid", () => { + activeHotbar.get()?.addEntity(testCluster); + activeHotbar.get()?.removeEntity("invalid uid"); + const items = activeHotbar.get()?.items.filter(Boolean); + + expect(items?.length).toEqual(2); + }); + + it("moves item to empty cell", () => { + activeHotbar.get()?.addEntity(testCluster); + activeHotbar.get()?.addEntity(minikubeCluster); + activeHotbar.get()?.addEntity(awsCluster); + + expect(activeHotbar.get()?.items[6]).toBeNull(); + + activeHotbar.get()?.restack(1, 5); + + expect(activeHotbar.get()?.items[5]).toBeTruthy(); + expect(activeHotbar.get()?.items[5]?.entity.uid).toEqual("some-test-id"); + }); + + it("moves items down", () => { + activeHotbar.get()?.addEntity(testCluster); + activeHotbar.get()?.addEntity(minikubeCluster); + activeHotbar.get()?.addEntity(awsCluster); + + // aws -> catalog + activeHotbar.get()?.restack(3, 0); + + const items = activeHotbar.get()?.items.map(item => item?.entity.uid || null); + + expect(items?.slice(0, 4)).toEqual(["some-aws-id", "catalog-entity", "some-test-id", "some-minikube-id"]); + }); + + it("moves items up", () => { + activeHotbar.get()?.addEntity(testCluster); + activeHotbar.get()?.addEntity(minikubeCluster); + activeHotbar.get()?.addEntity(awsCluster); + + // test -> aws + activeHotbar.get()?.restack(1, 3); + + const items = activeHotbar.get()?.items.map(item => item?.entity.uid || null); + + expect(items?.slice(0, 4)).toEqual(["catalog-entity", "some-minikube-id", "some-aws-id", "some-test-id"]); + }); + + it("logs an error if cellIndex is out of bounds", () => { + addHotbar({ name: "hottest", id: "hottest" }); + setAsActiveHotbar("hottest"); + + activeHotbar.get()?.addEntity(testCluster, -1); + expect(loggerMock.error).toBeCalledWith("[HOTBAR]: cannot pin entity to hotbar outside of index range", anyObject()); + + activeHotbar.get()?.addEntity(testCluster, 12); + expect(loggerMock.error).toBeCalledWith("[HOTBAR]: cannot pin entity to hotbar outside of index range", anyObject()); + + activeHotbar.get()?.addEntity(testCluster, 13); + expect(loggerMock.error).toBeCalledWith("[HOTBAR]: cannot pin entity to hotbar outside of index range", anyObject()); + }); + + it("throws an error if getId is invalid or returns not a string", () => { + expect(() => activeHotbar.get()?.addEntity({} as any)).toThrowError(TypeError); + expect(() => activeHotbar.get()?.addEntity({ getId: () => true } as any)).toThrowError(TypeError); + }); + + it("throws an error if getName is invalid or returns not a string", () => { + expect(() => activeHotbar.get()?.addEntity({ getId: () => "" } as any)).toThrowError(TypeError); + expect(() => activeHotbar.get()?.addEntity({ getId: () => "", getName: () => 4 } as any)).toThrowError(TypeError); + }); + + it("does nothing when item moved to same cell", () => { + activeHotbar.get()?.addEntity(testCluster); + activeHotbar.get()?.restack(1, 1); + + expect(activeHotbar.get()?.items[1]?.entity.uid).toEqual("some-test-id"); + }); + + it("new items takes first empty cell", () => { + activeHotbar.get()?.addEntity(testCluster); + activeHotbar.get()?.addEntity(awsCluster); + activeHotbar.get()?.restack(0, 3); + activeHotbar.get()?.addEntity(minikubeCluster); + + expect(activeHotbar.get()?.items[0]?.entity.uid).toEqual("some-minikube-id"); + }); + + it("throws if invalid arguments provided", () => { + activeHotbar.get()?.addEntity(testCluster); + + expect(() => activeHotbar.get()?.restack(-5, 0)).toThrow(); + expect(() => activeHotbar.get()?.restack(2, -1)).toThrow(); + expect(() => activeHotbar.get()?.restack(14, 1)).toThrow(); + expect(() => activeHotbar.get()?.restack(11, 112)).toThrow(); + }); + + it("checks if entity already pinned to hotbar", () => { + activeHotbar.get()?.addEntity(testCluster); + + expect(activeHotbar.get()?.hasEntity(testCluster.getId())).toBeTruthy(); + expect(activeHotbar.get()?.hasEntity(awsCluster.getId())).toBeFalsy(); + }); + }); + }); + + describe("given data from 5.0.0-beta.3 and version being 5.0.0-beta.10", () => { + beforeEach(() => { + const writeJsonSync = di.inject(writeJsonSyncInjectable); + + writeJsonSync("/some-directory-for-user-data/lens-hotbar-store.json", { + __internal__: { + migrations: { + version: "5.0.0-beta.3", + }, + }, + hotbars: [ + { + id: "3caac17f-aec2-4723-9694-ad204465d935", + name: "myhotbar", + items: [ + { + entity: { + uid: "some-aws-id", + }, + }, + { + entity: { + uid: "55b42c3c7ba3b04193416cda405269a5", + }, + }, + { + entity: { + uid: "176fd331968660832f62283219d7eb6e", + }, + }, + { + entity: { + uid: "61c4fb45528840ebad1badc25da41d14", + name: "user1-context", + source: "local", + }, + }, + { + entity: { + uid: "27d6f99fe9e7548a6e306760bfe19969", + name: "foo2", + source: "local", + }, + }, + null, + { + entity: { + uid: "c0b20040646849bb4dcf773e43a0bf27", + name: "multinode-demo", + source: "local", + }, + }, + null, + null, + null, + null, + null, + ], + }, + ], + }); + + di.override(storeMigrationVersionInjectable, () => "5.0.0-beta.10"); + + di.inject(hotbarsPersistentStorageInjectable).loadAndStartSyncing(); + }); + + it("allows to retrieve a hotbar", () => { + const hotbar = getHotbarById("3caac17f-aec2-4723-9694-ad204465d935"); + + expect(hotbar?.id).toBe("3caac17f-aec2-4723-9694-ad204465d935"); + }); + + it("clears cells without entity", () => { + const items = hotbars.get()[0].items; + + expect(items[2]).toBeNull(); + }); + + it("adds extra data to cells with according entity", () => { + const items = hotbars.get()[0].items; + + expect(items[0]).toEqual({ + entity: { + name: "my-aws-cluster", + source: "local", + uid: "some-aws-id", + }, + }); + }); + }); +}); diff --git a/packages/core/src/features/pod-logs/download-logs.test.tsx b/packages/core/src/features/pod-logs/download-logs.test.tsx index 9cd378a6d4..87bcf740d3 100644 --- a/packages/core/src/features/pod-logs/download-logs.test.tsx +++ b/packages/core/src/features/pod-logs/download-logs.test.tsx @@ -190,12 +190,12 @@ describe("download logs options in logs dock tab", () => { it("logs have been called with query", () => { expect(callForLogsMock).toHaveBeenCalledWith( { name: "dockerExporter", namespace: "default" }, - { "previous": true, "timestamps": false }, + { "previous": true, "timestamps": false, container: "docker-exporter" }, ); }); it("shows save dialog with proper attributes", async () => { - expect(openSaveFileDialogMock).toHaveBeenCalledWith("dockerExporter.log", "all-logs", "text/plain"); + expect(openSaveFileDialogMock).toHaveBeenCalledWith("docker-exporter.log", "all-logs", "text/plain"); }); it("doesn't block download dropdown for interaction after click", async () => { @@ -265,7 +265,7 @@ describe("download logs options in logs dock tab", () => { it("logs have been called", () => { expect(callForLogsMock).toHaveBeenCalledWith( { name: "dockerExporter", namespace: "default" }, - { "previous": true, "timestamps": false }, + { "previous": true, "timestamps": false, container: "docker-exporter" }, ); }); diff --git a/packages/core/src/features/preferences/__snapshots__/closing-preferences.test.tsx.snap b/packages/core/src/features/preferences/__snapshots__/closing-preferences.test.tsx.snap index 23530d3cb7..90b64e5302 100644 --- a/packages/core/src/features/preferences/__snapshots__/closing-preferences.test.tsx.snap +++ b/packages/core/src/features/preferences/__snapshots__/closing-preferences.test.tsx.snap @@ -709,7 +709,7 @@ exports[`preferences - closing-preferences given accessing preferences directly class="HotbarSelector" > { diff --git a/packages/core/src/features/preferences/hiding-of-empty-branches.test.tsx b/packages/core/src/features/preferences/hiding-of-empty-branches.test.tsx index 0bc2d67d24..9e4ef01a5c 100644 --- a/packages/core/src/features/preferences/hiding-of-empty-branches.test.tsx +++ b/packages/core/src/features/preferences/hiding-of-empty-branches.test.tsx @@ -10,8 +10,8 @@ import { runInAction } from "mobx"; import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import { preferenceItemInjectionToken } from "./renderer/preference-items/preference-item-injection-token"; -import type { Discover } from "../../renderer/components/test-utils/discovery-of-html-elements"; -import { discoverFor } from "../../renderer/components/test-utils/discovery-of-html-elements"; +import type { Discover } from "@k8slens/react-testing-library-discovery"; +import { discoverFor } from "@k8slens/react-testing-library-discovery"; describe("preferences - hiding-of-empty-branches, given in preferences page", () => { let builder: ApplicationBuilder; diff --git a/packages/core/src/features/preferences/navigation-to-application-preferences.test.tsx b/packages/core/src/features/preferences/navigation-to-application-preferences.test.tsx index bcb4db587d..47673b4637 100644 --- a/packages/core/src/features/preferences/navigation-to-application-preferences.test.tsx +++ b/packages/core/src/features/preferences/navigation-to-application-preferences.test.tsx @@ -7,8 +7,8 @@ import type { RenderResult } from "@testing-library/react"; import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import navigateToProxyPreferencesInjectable from "./common/navigate-to-proxy-preferences.injectable"; -import type { Discover } from "../../renderer/components/test-utils/discovery-of-html-elements"; -import { discoverFor } from "../../renderer/components/test-utils/discovery-of-html-elements"; +import type { Discover } from "@k8slens/react-testing-library-discovery"; +import { discoverFor } from "@k8slens/react-testing-library-discovery"; import type { FakeExtensionOptions } from "../../renderer/components/test-utils/get-extension-fake"; describe("preferences - navigation to application preferences", () => { diff --git a/packages/core/src/features/preferences/navigation-to-editor-preferences.test.ts b/packages/core/src/features/preferences/navigation-to-editor-preferences.test.ts index 018a27210a..51f93afa3f 100644 --- a/packages/core/src/features/preferences/navigation-to-editor-preferences.test.ts +++ b/packages/core/src/features/preferences/navigation-to-editor-preferences.test.ts @@ -5,8 +5,8 @@ import type { RenderResult } from "@testing-library/react"; import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; -import type { Discover } from "../../renderer/components/test-utils/discovery-of-html-elements"; -import { discoverFor } from "../../renderer/components/test-utils/discovery-of-html-elements"; +import type { Discover } from "@k8slens/react-testing-library-discovery"; +import { discoverFor } from "@k8slens/react-testing-library-discovery"; describe("preferences - navigation to editor preferences", () => { let applicationBuilder: ApplicationBuilder; diff --git a/packages/core/src/features/preferences/navigation-to-extension-specific-preferences.test.tsx b/packages/core/src/features/preferences/navigation-to-extension-specific-preferences.test.tsx index 84c8f4e395..e7a04a4b04 100644 --- a/packages/core/src/features/preferences/navigation-to-extension-specific-preferences.test.tsx +++ b/packages/core/src/features/preferences/navigation-to-extension-specific-preferences.test.tsx @@ -8,8 +8,8 @@ import { getApplicationBuilder } from "../../renderer/components/test-utils/get- import React from "react"; import "@testing-library/jest-dom/extend-expect"; import type { FakeExtensionOptions } from "../../renderer/components/test-utils/get-extension-fake"; -import type { Discover } from "../../renderer/components/test-utils/discovery-of-html-elements"; -import { discoverFor } from "../../renderer/components/test-utils/discovery-of-html-elements"; +import type { Discover } from "@k8slens/react-testing-library-discovery"; +import { discoverFor } from "@k8slens/react-testing-library-discovery"; import logErrorInjectable from "../../common/log-error.injectable"; describe("preferences - navigation to extension specific preferences", () => { diff --git a/packages/core/src/features/preferences/navigation-to-kubernetes-preferences.test.ts b/packages/core/src/features/preferences/navigation-to-kubernetes-preferences.test.ts index 15c853d3c3..06c1fda87e 100644 --- a/packages/core/src/features/preferences/navigation-to-kubernetes-preferences.test.ts +++ b/packages/core/src/features/preferences/navigation-to-kubernetes-preferences.test.ts @@ -7,8 +7,8 @@ import type { ApplicationBuilder } from "../../renderer/components/test-utils/ge import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import requestPublicHelmRepositoriesInjectable from "../helm-charts/child-features/preferences/renderer/adding-of-public-helm-repository/public-helm-repositories/request-public-helm-repositories.injectable"; import getActiveHelmRepositoriesInjectable from "../../main/helm/repositories/get-active-helm-repositories/get-active-helm-repositories.injectable"; -import type { Discover } from "../../renderer/components/test-utils/discovery-of-html-elements"; -import { discoverFor } from "../../renderer/components/test-utils/discovery-of-html-elements"; +import type { Discover } from "@k8slens/react-testing-library-discovery"; +import { discoverFor } from "@k8slens/react-testing-library-discovery"; describe("preferences - navigation to kubernetes preferences", () => { let builder: ApplicationBuilder; diff --git a/packages/core/src/features/preferences/navigation-to-proxy-preferences.test.ts b/packages/core/src/features/preferences/navigation-to-proxy-preferences.test.ts index 2372b78187..fa22e15d17 100644 --- a/packages/core/src/features/preferences/navigation-to-proxy-preferences.test.ts +++ b/packages/core/src/features/preferences/navigation-to-proxy-preferences.test.ts @@ -5,8 +5,8 @@ import type { RenderResult } from "@testing-library/react"; import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; -import type { Discover } from "../../renderer/components/test-utils/discovery-of-html-elements"; -import { discoverFor } from "../../renderer/components/test-utils/discovery-of-html-elements"; +import type { Discover } from "@k8slens/react-testing-library-discovery"; +import { discoverFor } from "@k8slens/react-testing-library-discovery"; describe("preferences - navigation to proxy preferences", () => { let applicationBuilder: ApplicationBuilder; diff --git a/packages/core/src/features/preferences/navigation-to-telemetry-preferences.test.tsx b/packages/core/src/features/preferences/navigation-to-telemetry-preferences.test.tsx index 58d0121a9e..ce8158cc4c 100644 --- a/packages/core/src/features/preferences/navigation-to-telemetry-preferences.test.tsx +++ b/packages/core/src/features/preferences/navigation-to-telemetry-preferences.test.tsx @@ -9,8 +9,8 @@ import { getApplicationBuilder } from "../../renderer/components/test-utils/get- import navigateToTelemetryPreferencesInjectable from "./common/navigate-to-telemetry-preferences.injectable"; import sentryDataSourceNameInjectable from "../../common/vars/sentry-dsn-url.injectable"; import type { FakeExtensionOptions } from "../../renderer/components/test-utils/get-extension-fake"; -import type { Discover } from "../../renderer/components/test-utils/discovery-of-html-elements"; -import { discoverFor } from "../../renderer/components/test-utils/discovery-of-html-elements"; +import type { Discover } from "@k8slens/react-testing-library-discovery"; +import { discoverFor } from "@k8slens/react-testing-library-discovery"; describe("preferences - navigation to telemetry preferences", () => { let builder: ApplicationBuilder; diff --git a/packages/core/src/features/preferences/navigation-to-terminal-preferences.test.ts b/packages/core/src/features/preferences/navigation-to-terminal-preferences.test.ts index 3023852481..1542659d17 100644 --- a/packages/core/src/features/preferences/navigation-to-terminal-preferences.test.ts +++ b/packages/core/src/features/preferences/navigation-to-terminal-preferences.test.ts @@ -5,8 +5,8 @@ import type { RenderResult } from "@testing-library/react"; import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; -import type { Discover } from "../../renderer/components/test-utils/discovery-of-html-elements"; -import { discoverFor } from "../../renderer/components/test-utils/discovery-of-html-elements"; +import type { Discover } from "@k8slens/react-testing-library-discovery"; +import { discoverFor } from "@k8slens/react-testing-library-discovery"; describe("preferences - navigation to terminal preferences", () => { let applicationBuilder: ApplicationBuilder; diff --git a/packages/core/src/features/preferences/navigation-using-application-menu.test.ts b/packages/core/src/features/preferences/navigation-using-application-menu.test.ts index ae143fe468..020bc25259 100644 --- a/packages/core/src/features/preferences/navigation-using-application-menu.test.ts +++ b/packages/core/src/features/preferences/navigation-using-application-menu.test.ts @@ -6,8 +6,8 @@ import type { RenderResult } from "@testing-library/react"; import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; -import type { Discover } from "../../renderer/components/test-utils/discovery-of-html-elements"; -import { discoverFor } from "../../renderer/components/test-utils/discovery-of-html-elements"; +import type { Discover } from "@k8slens/react-testing-library-discovery"; +import { discoverFor } from "@k8slens/react-testing-library-discovery"; describe("preferences - navigation using application menu", () => { let applicationBuilder: ApplicationBuilder; diff --git a/packages/core/src/features/preferences/navigation-using-tray.test.ts b/packages/core/src/features/preferences/navigation-using-tray.test.ts index e48dba482e..63d2bd5290 100644 --- a/packages/core/src/features/preferences/navigation-using-tray.test.ts +++ b/packages/core/src/features/preferences/navigation-using-tray.test.ts @@ -5,8 +5,8 @@ import type { RenderResult } from "@testing-library/react"; import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; -import type { Discover } from "../../renderer/components/test-utils/discovery-of-html-elements"; -import { discoverFor } from "../../renderer/components/test-utils/discovery-of-html-elements"; +import type { Discover } from "@k8slens/react-testing-library-discovery"; +import { discoverFor } from "@k8slens/react-testing-library-discovery"; describe("show-about-using-tray", () => { let applicationBuilder: ApplicationBuilder; diff --git a/packages/core/src/features/preferences/renderer/preference-items/application/extension-install-registry/extension-install-registry.tsx b/packages/core/src/features/preferences/renderer/preference-items/application/extension-install-registry/extension-install-registry.tsx index ce6aed0a5f..addd5d8a4e 100644 --- a/packages/core/src/features/preferences/renderer/preference-items/application/extension-install-registry/extension-install-registry.tsx +++ b/packages/core/src/features/preferences/renderer/preference-items/application/extension-install-registry/extension-install-registry.tsx @@ -6,16 +6,16 @@ import React from "react"; import { SubTitle } from "../../../../../../renderer/components/layout/sub-title"; import { Select } from "../../../../../../renderer/components/select"; import { withInjectables } from "@ogre-tools/injectable-react"; -import { defaultExtensionRegistryUrl, defaultExtensionRegistryUrlLocation } from "../../../../../../common/user-store/preferences-helpers"; import { Input } from "../../../../../../renderer/components/input"; import { isUrl } from "../../../../../../renderer/components/input/input_validators"; -import type { UserStore } from "../../../../../../common/user-store"; import { runInAction } from "mobx"; -import userStoreInjectable from "../../../../../../common/user-store/user-store.injectable"; import { observer } from "mobx-react"; +import type { UserPreferencesState } from "../../../../../user-preferences/common/state.injectable"; +import userPreferencesStateInjectable from "../../../../../user-preferences/common/state.injectable"; +import { defaultExtensionRegistryUrlLocation, defaultExtensionRegistryUrl } from "../../../../../user-preferences/common/preferences-helpers"; interface Dependencies { - userStore: UserStore; + state: UserPreferencesState; } const extensionInstallRegistryOptions = [ @@ -33,8 +33,8 @@ const extensionInstallRegistryOptions = [ }, ] as const; -const NonInjectedExtensionInstallRegistry = observer(({ userStore }: Dependencies) => { - const [customUrl, setCustomUrl] = React.useState(userStore.extensionRegistryUrl.customUrl || ""); +const NonInjectedExtensionInstallRegistry = observer(({ state }: Dependencies) => { + const [customUrl, setCustomUrl] = React.useState(state.extensionRegistryUrl.customUrl || ""); return (
@@ -42,14 +42,14 @@ const NonInjectedExtensionInstallRegistry = observer(({ userStore }: Dependencie - (userStore.colorTheme = value?.value ?? defaultTheme.name) + (state.colorTheme = value?.value ?? defaultTheme.name) } themeName="lens" /> @@ -53,7 +53,7 @@ const NonInjectedTheme = observer(({ export const Theme = withInjectables(NonInjectedTheme, { getProps: (di) => ({ - userStore: di.inject(userStoreInjectable), + state: di.inject(userPreferencesStateInjectable), defaultTheme: di.inject(defaultLensThemeInjectable), themes: di.injectMany(lensThemeDeclarationInjectionToken), }), diff --git a/packages/core/src/features/preferences/renderer/preference-items/application/timezone/timezone.tsx b/packages/core/src/features/preferences/renderer/preference-items/application/timezone/timezone.tsx index c3ee8f2ea1..987f378bd9 100644 --- a/packages/core/src/features/preferences/renderer/preference-items/application/timezone/timezone.tsx +++ b/packages/core/src/features/preferences/renderer/preference-items/application/timezone/timezone.tsx @@ -5,15 +5,15 @@ import React from "react"; import { SubTitle } from "../../../../../../renderer/components/layout/sub-title"; import { withInjectables } from "@ogre-tools/injectable-react"; -import type { UserStore } from "../../../../../../common/user-store"; -import userStoreInjectable from "../../../../../../common/user-store/user-store.injectable"; import { Select } from "../../../../../../renderer/components/select"; import moment from "moment-timezone"; import { observer } from "mobx-react"; -import currentTimezoneInjectable from "../../../../../../common/user-store/current-timezone.injectable"; +import type { UserPreferencesState } from "../../../../../user-preferences/common/state.injectable"; +import currentTimezoneInjectable from "../../../../../../common/vars/current-timezone.injectable"; +import userPreferencesStateInjectable from "../../../../../user-preferences/common/state.injectable"; interface Dependencies { - userStore: UserStore; + state: UserPreferencesState; currentTimezone: string; } @@ -23,9 +23,8 @@ const timezoneOptions = moment.tz.names() label: timezone.replace("_", " "), })); - const NonInjectedTimezone = observer(({ - userStore, + state, currentTimezone, }: Dependencies) => (
@@ -33,17 +32,16 @@ const NonInjectedTimezone = observer(({ )); -export const EditorFontFamily = withInjectables( - NonInjectedEditorFontFamily, - - { - getProps: (di) => ({ - userStore: di.inject(userStoreInjectable), - }), - }, -); +export const EditorFontFamily = withInjectables(NonInjectedEditorFontFamily, { + getProps: (di) => ({ + state: di.inject(userPreferencesStateInjectable), + }), +}); diff --git a/packages/core/src/features/preferences/renderer/preference-items/editor/editor-font-size/editor-font-size.tsx b/packages/core/src/features/preferences/renderer/preference-items/editor/editor-font-size/editor-font-size.tsx index 7fe83ec6aa..df144df2b7 100644 --- a/packages/core/src/features/preferences/renderer/preference-items/editor/editor-font-size/editor-font-size.tsx +++ b/packages/core/src/features/preferences/renderer/preference-items/editor/editor-font-size/editor-font-size.tsx @@ -5,16 +5,16 @@ import React from "react"; import { SubTitle } from "../../../../../../renderer/components/layout/sub-title"; import { withInjectables } from "@ogre-tools/injectable-react"; -import type { UserStore } from "../../../../../../common/user-store"; -import userStoreInjectable from "../../../../../../common/user-store/user-store.injectable"; import { Input, InputValidators } from "../../../../../../renderer/components/input"; import { observer } from "mobx-react"; +import type { UserPreferencesState } from "../../../../../user-preferences/common/state.injectable"; +import userPreferencesStateInjectable from "../../../../../user-preferences/common/state.injectable"; interface Dependencies { - userStore: UserStore; + state: UserPreferencesState; } -const NonInjectedEditorFontSize = observer(({ userStore: { editorConfiguration }}: Dependencies) => ( +const NonInjectedEditorFontSize = observer(({ state: { editorConfiguration }}: Dependencies) => (
)); -export const EditorFontSize = withInjectables( - NonInjectedEditorFontSize, - - { - getProps: (di) => ({ - userStore: di.inject(userStoreInjectable), - }), - }, -); +export const EditorFontSize = withInjectables(NonInjectedEditorFontSize, { + getProps: (di) => ({ + state: di.inject(userPreferencesStateInjectable), + }), +}); diff --git a/packages/core/src/features/preferences/renderer/preference-items/editor/line-numbers/line-numbers.tsx b/packages/core/src/features/preferences/renderer/preference-items/editor/line-numbers/line-numbers.tsx index 10cc508baa..bfd73f7415 100644 --- a/packages/core/src/features/preferences/renderer/preference-items/editor/line-numbers/line-numbers.tsx +++ b/packages/core/src/features/preferences/renderer/preference-items/editor/line-numbers/line-numbers.tsx @@ -5,15 +5,15 @@ import React from "react"; import { SubTitle } from "../../../../../../renderer/components/layout/sub-title"; import { withInjectables } from "@ogre-tools/injectable-react"; -import type { UserStore } from "../../../../../../common/user-store"; -import userStoreInjectable from "../../../../../../common/user-store/user-store.injectable"; import { Select } from "../../../../../../renderer/components/select"; -import { defaultEditorConfig } from "../../../../../../common/user-store/preferences-helpers"; import { capitalize } from "lodash/fp"; import { observer } from "mobx-react"; +import type { UserPreferencesState } from "../../../../../user-preferences/common/state.injectable"; +import { defaultEditorConfig } from "../../../../../user-preferences/common/preferences-helpers"; +import userPreferencesStateInjectable from "../../../../../user-preferences/common/state.injectable"; interface Dependencies { - userStore: UserStore; + state: UserPreferencesState; } const lineNumberOptions = ([ @@ -26,7 +26,7 @@ const lineNumberOptions = ([ label: capitalize(lineNumbers), })); -const NonInjectedLineNumbers = observer(({ userStore: { editorConfiguration }}: Dependencies) => ( +const NonInjectedLineNumbers = observer(({ state: { editorConfiguration }}: Dependencies) => (
)); -export const TabSize = withInjectables( - NonInjectedTabSize, - - { - getProps: (di) => ({ - userStore: di.inject(userStoreInjectable), - }), - }, -); +export const TabSize = withInjectables(NonInjectedTabSize, { + getProps: (di) => ({ + state: di.inject(userPreferencesStateInjectable), + }), +}); diff --git a/packages/core/src/features/preferences/renderer/preference-items/kubernetes/kubeconfig-sync/kubeconfig-sync.tsx b/packages/core/src/features/preferences/renderer/preference-items/kubernetes/kubeconfig-sync/kubeconfig-sync.tsx index 77642f1082..510fbd0115 100644 --- a/packages/core/src/features/preferences/renderer/preference-items/kubernetes/kubeconfig-sync/kubeconfig-sync.tsx +++ b/packages/core/src/features/preferences/renderer/preference-items/kubernetes/kubeconfig-sync/kubeconfig-sync.tsx @@ -7,13 +7,11 @@ import { computed, makeObservable, observable, reaction } from "mobx"; import { disposeOnUnmount, observer } from "mobx-react"; import React from "react"; import { Notice } from "../../../../../../renderer/components/+extensions/notice"; -import type { UserStore } from "../../../../../../common/user-store"; import { iter, tuple } from "@k8slens/utilities"; import { SubTitle } from "../../../../../../renderer/components/layout/sub-title"; import { PathPicker } from "../../../../../../renderer/components/path-picker/path-picker"; import { Spinner } from "../../../../../../renderer/components/spinner"; import { RemovableItem } from "../../../removable-item/removable-item"; -import userStoreInjectable from "../../../../../../common/user-store/user-store.injectable"; import isWindowsInjectable from "../../../../../../common/vars/is-windows.injectable"; import loggerInjectable from "../../../../../../common/logger.injectable"; import type { Logger } from "../../../../../../common/logger"; @@ -21,13 +19,15 @@ import type { DiscoverAllKubeconfigSyncKinds } from "./discover-all-sync-kinds.i import type { DiscoverKubeconfigSyncKind, SyncKind } from "./discover-sync-kind.injectable"; import discoverKubeconfigSyncKindInjectable from "./discover-sync-kind.injectable"; import discoverAllKubeconfigSyncKindsInjectable from "./discover-all-sync-kinds.injectable"; +import type { UserPreferencesState } from "../../../../../user-preferences/common/state.injectable"; +import userPreferencesStateInjectable from "../../../../../user-preferences/common/state.injectable"; interface Entry extends SyncKind { filePath: string; } interface Dependencies { - userStore: UserStore; + state: UserPreferencesState; isWindows: boolean; logger: Logger; discoverAllKubeconfigSyncKinds: DiscoverAllKubeconfigSyncKinds; @@ -47,7 +47,7 @@ class NonInjectedKubeconfigSync extends React.Component { async componentDidMount() { const mapEntries = await Promise.all( iter.map( - this.props.userStore.syncKubeconfigEntries, + this.props.state.syncKubeconfigEntries, ([filePath]) => this.props.discoverKubeconfigSyncKind(filePath), ), ); @@ -59,7 +59,7 @@ class NonInjectedKubeconfigSync extends React.Component { reaction( () => Array.from(this.syncs.entries(), ([filePath, kind]) => tuple.from(filePath, kind)), syncs => { - this.props.userStore.syncKubeconfigEntries.replace(syncs); + this.props.state.syncKubeconfigEntries.replace(syncs); }, ), ]); @@ -177,7 +177,7 @@ class NonInjectedKubeconfigSync extends React.Component { export const KubeconfigSync = withInjectables(NonInjectedKubeconfigSync, { getProps: (di) => ({ - userStore: di.inject(userStoreInjectable), + state: di.inject(userPreferencesStateInjectable), isWindows: di.inject(isWindowsInjectable), logger: di.inject(loggerInjectable), discoverAllKubeconfigSyncKinds: di.inject(discoverAllKubeconfigSyncKindsInjectable), diff --git a/packages/core/src/features/preferences/renderer/preference-items/kubernetes/kubectl/kubectl-binary-download/kubectl-binary-download.tsx b/packages/core/src/features/preferences/renderer/preference-items/kubernetes/kubectl/kubectl-binary-download/kubectl-binary-download.tsx index de096191fd..35d0ebff62 100644 --- a/packages/core/src/features/preferences/renderer/preference-items/kubernetes/kubectl/kubectl-binary-download/kubectl-binary-download.tsx +++ b/packages/core/src/features/preferences/renderer/preference-items/kubernetes/kubectl/kubectl-binary-download/kubectl-binary-download.tsx @@ -5,34 +5,29 @@ import React from "react"; import { SubTitle } from "../../../../../../../renderer/components/layout/sub-title"; import { withInjectables } from "@ogre-tools/injectable-react"; -import type { UserStore } from "../../../../../../../common/user-store"; -import userStoreInjectable from "../../../../../../../common/user-store/user-store.injectable"; import { observer } from "mobx-react"; import { Switch } from "../../../../../../../renderer/components/switch"; +import type { UserPreferencesState } from "../../../../../../user-preferences/common/state.injectable"; +import userPreferencesStateInjectable from "../../../../../../user-preferences/common/state.injectable"; interface Dependencies { - userStore: UserStore; + state: UserPreferencesState; } -const NonInjectedKubectlBinaryDownload = observer(({ userStore }: Dependencies) => ( +const NonInjectedKubectlBinaryDownload = observer(({ state }: Dependencies) => (
userStore.downloadKubectlBinaries = !userStore.downloadKubectlBinaries} + checked={state.downloadKubectlBinaries} + onChange={() => state.downloadKubectlBinaries = !state.downloadKubectlBinaries} > Download kubectl binaries matching the Kubernetes cluster version
- )); -export const KubectlBinaryDownload = withInjectables( - NonInjectedKubectlBinaryDownload, - - { - getProps: (di) => ({ - userStore: di.inject(userStoreInjectable), - }), - }, -); +export const KubectlBinaryDownload = withInjectables(NonInjectedKubectlBinaryDownload, { + getProps: (di) => ({ + state: di.inject(userPreferencesStateInjectable), + }), +}); diff --git a/packages/core/src/features/preferences/renderer/preference-items/kubernetes/kubectl/kubectl-directory-for-binaries/kubectl-directory-for-binaries.tsx b/packages/core/src/features/preferences/renderer/preference-items/kubernetes/kubectl/kubectl-directory-for-binaries/kubectl-directory-for-binaries.tsx index 497ec68f8e..7e4bc8a2ab 100644 --- a/packages/core/src/features/preferences/renderer/preference-items/kubernetes/kubectl/kubectl-directory-for-binaries/kubectl-directory-for-binaries.tsx +++ b/packages/core/src/features/preferences/renderer/preference-items/kubernetes/kubectl/kubectl-directory-for-binaries/kubectl-directory-for-binaries.tsx @@ -5,24 +5,24 @@ import React, { useState } from "react"; import { SubTitle } from "../../../../../../../renderer/components/layout/sub-title"; import { withInjectables } from "@ogre-tools/injectable-react"; -import type { UserStore } from "../../../../../../../common/user-store"; -import userStoreInjectable from "../../../../../../../common/user-store/user-store.injectable"; import { observer } from "mobx-react"; import { Input, InputValidators } from "../../../../../../../renderer/components/input"; import directoryForBinariesInjectable from "../../../../../../../common/app-paths/directory-for-binaries/directory-for-binaries.injectable"; +import type { UserPreferencesState } from "../../../../../../user-preferences/common/state.injectable"; +import userPreferencesStateInjectable from "../../../../../../user-preferences/common/state.injectable"; interface Dependencies { - userStore: UserStore; + state: UserPreferencesState; defaultPathForGeneralBinaries: string; } const NonInjectedKubectlDirectoryForBinaries = observer( - ({ userStore, defaultPathForGeneralBinaries }: Dependencies) => { - const [downloadPath, setDownloadPath] = useState(userStore.downloadBinariesPath || ""); + ({ state, defaultPathForGeneralBinaries }: Dependencies) => { + const [downloadPath, setDownloadPath] = useState(state.downloadBinariesPath || ""); const pathValidator = downloadPath ? InputValidators.isPath : undefined; const save = () => { - userStore.downloadBinariesPath = downloadPath; + state.downloadBinariesPath = downloadPath; }; return ( @@ -35,7 +35,7 @@ const NonInjectedKubectlDirectoryForBinaries = observer( validators={pathValidator} onChange={setDownloadPath} onBlur={save} - disabled={!userStore.downloadKubectlBinaries} + disabled={!state.downloadKubectlBinaries} />
The directory to download binaries into.
@@ -43,13 +43,9 @@ const NonInjectedKubectlDirectoryForBinaries = observer( }, ); -export const KubectlDirectoryForBinaries = withInjectables( - NonInjectedKubectlDirectoryForBinaries, - - { - getProps: (di) => ({ - defaultPathForGeneralBinaries: di.inject(directoryForBinariesInjectable), - userStore: di.inject(userStoreInjectable), - }), - }, -); +export const KubectlDirectoryForBinaries = withInjectables(NonInjectedKubectlDirectoryForBinaries, { + getProps: (di) => ({ + defaultPathForGeneralBinaries: di.inject(directoryForBinariesInjectable), + state: di.inject(userPreferencesStateInjectable), + }), +}); diff --git a/packages/core/src/features/preferences/renderer/preference-items/kubernetes/kubectl/kubectl-download-mirror/kubectl-download-mirror.tsx b/packages/core/src/features/preferences/renderer/preference-items/kubernetes/kubectl/kubectl-download-mirror/kubectl-download-mirror.tsx index e880ff3308..7f01d8a933 100644 --- a/packages/core/src/features/preferences/renderer/preference-items/kubernetes/kubectl/kubectl-download-mirror/kubectl-download-mirror.tsx +++ b/packages/core/src/features/preferences/renderer/preference-items/kubernetes/kubectl/kubectl-download-mirror/kubectl-download-mirror.tsx @@ -5,14 +5,14 @@ import React from "react"; import { SubTitle } from "../../../../../../../renderer/components/layout/sub-title"; import { withInjectables } from "@ogre-tools/injectable-react"; -import type { UserStore } from "../../../../../../../common/user-store"; -import userStoreInjectable from "../../../../../../../common/user-store/user-store.injectable"; import { observer } from "mobx-react"; import { Select } from "../../../../../../../renderer/components/select"; -import { defaultPackageMirror, packageMirrors } from "../../../../../../../common/user-store/preferences-helpers"; +import { defaultPackageMirror, packageMirrors } from "../../../../../../user-preferences/common/preferences-helpers"; +import type { UserPreferencesState } from "../../../../../../user-preferences/common/state.injectable"; +import userPreferencesStateInjectable from "../../../../../../user-preferences/common/state.injectable"; interface Dependencies { - userStore: UserStore; + state: UserPreferencesState; } const downloadMirrorOptions = Array.from(packageMirrors, ([name, mirror]) => ({ @@ -24,27 +24,23 @@ const downloadMirrorOptions = Array.from(packageMirrors, ([name, mirror]) => ({ })); -const NonInjectedKubectlDownloadMirror = observer(({ userStore }: Dependencies) => ( +const NonInjectedKubectlDownloadMirror = observer(({ state }: Dependencies) => (
-
- ); - }, + return ( +
+ + +
+ ); +}, ); -export const KubectlPathToBinary = withInjectables( - NonInjectedKubectlPathToBinary, - - { - getProps: (di) => ({ - defaultPathForKubectlBinaries: di.inject(directoryForKubectlBinariesInjectable), - userStore: di.inject(userStoreInjectable), - }), - }, -); +export const KubectlPathToBinary = withInjectables(NonInjectedKubectlPathToBinary, { + getProps: (di) => ({ + defaultPathForKubectlBinaries: di.inject(directoryForKubectlBinariesInjectable), + state: di.inject(userPreferencesStateInjectable), + }), +}); diff --git a/packages/core/src/features/preferences/renderer/preference-items/proxy/allow-untrusted-certificates/allow-untrusted-certificates.tsx b/packages/core/src/features/preferences/renderer/preference-items/proxy/allow-untrusted-certificates/allow-untrusted-certificates.tsx index bb1abd9a57..eea78f6cda 100644 --- a/packages/core/src/features/preferences/renderer/preference-items/proxy/allow-untrusted-certificates/allow-untrusted-certificates.tsx +++ b/packages/core/src/features/preferences/renderer/preference-items/proxy/allow-untrusted-certificates/allow-untrusted-certificates.tsx @@ -5,23 +5,21 @@ import React from "react"; import { SubTitle } from "../../../../../../renderer/components/layout/sub-title"; import { withInjectables } from "@ogre-tools/injectable-react"; -import type { UserStore } from "../../../../../../common/user-store"; -import userStoreInjectable from "../../../../../../common/user-store/user-store.injectable"; import { observer } from "mobx-react"; import { Switch } from "../../../../../../renderer/components/switch"; +import type { UserPreferencesState } from "../../../../../user-preferences/common/state.injectable"; +import userPreferencesStateInjectable from "../../../../../user-preferences/common/state.injectable"; interface Dependencies { - userStore: UserStore; + state: UserPreferencesState; } -const NonInjectedAllowUntrustedCertificates = observer(({ userStore }: Dependencies) => ( +const NonInjectedAllowUntrustedCertificates = observer(({ state }: Dependencies) => (
- (userStore.allowUntrustedCAs = !userStore.allowUntrustedCAs) - } + checked={state.allowUntrustedCAs} + onChange={() => state.allowUntrustedCAs = !state.allowUntrustedCAs} > Allow untrusted Certificate Authorities @@ -33,12 +31,8 @@ const NonInjectedAllowUntrustedCertificates = observer(({ userStore }: Dependenc
)); -export const AllowUntrustedCertificates = withInjectables( - NonInjectedAllowUntrustedCertificates, - - { - getProps: (di) => ({ - userStore: di.inject(userStoreInjectable), - }), - }, -); +export const AllowUntrustedCertificates = withInjectables(NonInjectedAllowUntrustedCertificates, { + getProps: (di) => ({ + state: di.inject(userPreferencesStateInjectable), + }), +}); diff --git a/packages/core/src/features/preferences/renderer/preference-items/proxy/http-proxy-url/http-proxy-url.tsx b/packages/core/src/features/preferences/renderer/preference-items/proxy/http-proxy-url/http-proxy-url.tsx index 7d55fd2b3b..9cc8e38d4b 100644 --- a/packages/core/src/features/preferences/renderer/preference-items/proxy/http-proxy-url/http-proxy-url.tsx +++ b/packages/core/src/features/preferences/renderer/preference-items/proxy/http-proxy-url/http-proxy-url.tsx @@ -5,43 +5,39 @@ import React from "react"; import { SubTitle } from "../../../../../../renderer/components/layout/sub-title"; import { withInjectables } from "@ogre-tools/injectable-react"; -import type { UserStore } from "../../../../../../common/user-store"; -import userStoreInjectable from "../../../../../../common/user-store/user-store.injectable"; import { observer } from "mobx-react"; import { Input } from "../../../../../../renderer/components/input"; +import type { UserPreferencesState } from "../../../../../user-preferences/common/state.injectable"; +import userPreferencesStateInjectable from "../../../../../user-preferences/common/state.injectable"; interface Dependencies { - userStore: UserStore; + state: UserPreferencesState; } -const NonInjectedHttpProxyUrl = observer( - ({ userStore }: Dependencies) => { - const [proxy, setProxy] = React.useState(userStore.httpsProxy || ""); +const NonInjectedHttpProxyUrl = observer(({ + state, +}: Dependencies) => { + const [proxy, setProxy] = React.useState(state.httpsProxy || ""); - return ( -
- - setProxy(v)} - onBlur={() => (userStore.httpsProxy = proxy)} - /> - - Proxy is used only for non-cluster communication. - -
- ); - }, -); + return ( +
+ + setProxy(v)} + onBlur={() => (state.httpsProxy = proxy)} + /> + + Proxy is used only for non-cluster communication. + +
+ ); +}); -export const HttpProxyUrl = withInjectables( - NonInjectedHttpProxyUrl, - - { - getProps: (di) => ({ - userStore: di.inject(userStoreInjectable), - }), - }, -); +export const HttpProxyUrl = withInjectables(NonInjectedHttpProxyUrl, { + getProps: (di) => ({ + state: di.inject(userPreferencesStateInjectable), + }), +}); diff --git a/packages/core/src/features/preferences/renderer/preference-items/telemetry/automatic-error-reporting/automatic-error-reporting.tsx b/packages/core/src/features/preferences/renderer/preference-items/telemetry/automatic-error-reporting/automatic-error-reporting.tsx index 7e23c0af35..167189fffc 100644 --- a/packages/core/src/features/preferences/renderer/preference-items/telemetry/automatic-error-reporting/automatic-error-reporting.tsx +++ b/packages/core/src/features/preferences/renderer/preference-items/telemetry/automatic-error-reporting/automatic-error-reporting.tsx @@ -5,16 +5,16 @@ import React from "react"; import { SubTitle } from "../../../../../../renderer/components/layout/sub-title"; import { withInjectables } from "@ogre-tools/injectable-react"; -import type { UserStore } from "../../../../../../common/user-store"; -import userStoreInjectable from "../../../../../../common/user-store/user-store.injectable"; import { observer } from "mobx-react"; import { Checkbox } from "../../../../../../renderer/components/checkbox"; +import type { UserPreferencesState } from "../../../../../user-preferences/common/state.injectable"; +import userPreferencesStateInjectable from "../../../../../user-preferences/common/state.injectable"; interface Dependencies { - userStore: UserStore; + state: UserPreferencesState; } -const NonInjectedAutomaticErrorReporting = observer(({ userStore }: Dependencies) => ( +const NonInjectedAutomaticErrorReporting = observer(({ state }: Dependencies) => (
(userStore.allowErrorReporting = value)} + value={state.allowErrorReporting} + onChange={(value) => (state.allowErrorReporting = value)} />
@@ -36,12 +36,8 @@ const NonInjectedAutomaticErrorReporting = observer(({ userStore }: Dependencies
)); -export const AutomaticErrorReporting = withInjectables( - NonInjectedAutomaticErrorReporting, - - { - getProps: (di) => ({ - userStore: di.inject(userStoreInjectable), - }), - }, -); +export const AutomaticErrorReporting = withInjectables(NonInjectedAutomaticErrorReporting, { + getProps: (di) => ({ + state: di.inject(userPreferencesStateInjectable), + }), +}); diff --git a/packages/core/src/features/preferences/renderer/preference-items/terminal/copy-paste-from-terminal/copy-paste-from-terminal.tsx b/packages/core/src/features/preferences/renderer/preference-items/terminal/copy-paste-from-terminal/copy-paste-from-terminal.tsx index 6fd65ccbbd..8e1f6701d8 100644 --- a/packages/core/src/features/preferences/renderer/preference-items/terminal/copy-paste-from-terminal/copy-paste-from-terminal.tsx +++ b/packages/core/src/features/preferences/renderer/preference-items/terminal/copy-paste-from-terminal/copy-paste-from-terminal.tsx @@ -5,38 +5,31 @@ import React from "react"; import { SubTitle } from "../../../../../../renderer/components/layout/sub-title"; import { withInjectables } from "@ogre-tools/injectable-react"; -import type { UserStore } from "../../../../../../common/user-store"; -import userStoreInjectable from "../../../../../../common/user-store/user-store.injectable"; import { observer } from "mobx-react"; import { Switch } from "../../../../../../renderer/components/switch"; +import type { UserPreferencesState } from "../../../../../user-preferences/common/state.injectable"; +import userPreferencesStateInjectable from "../../../../../user-preferences/common/state.injectable"; interface Dependencies { - userStore: UserStore; + state: UserPreferencesState; } -const NonInjectedCopyPasteFromTerminal = observer( - ({ userStore }: Dependencies) => { +const NonInjectedCopyPasteFromTerminal = observer(({ + state, +}: Dependencies) => ( +
+ + state.terminalCopyOnSelect = !state.terminalCopyOnSelect} + > + Copy on select and paste on right-click + +
+)); - return ( -
- - userStore.terminalCopyOnSelect = !userStore.terminalCopyOnSelect} - > - Copy on select and paste on right-click - -
- ); - }, -); - -export const CopyPasteFromTerminal = withInjectables( - NonInjectedCopyPasteFromTerminal, - - { - getProps: (di) => ({ - userStore: di.inject(userStoreInjectable), - }), - }, -); +export const CopyPasteFromTerminal = withInjectables(NonInjectedCopyPasteFromTerminal, { + getProps: (di) => ({ + state: di.inject(userPreferencesStateInjectable), + }), +}); diff --git a/packages/core/src/features/preferences/renderer/preference-items/terminal/terminal-font-family/terminal-font-options.injectable.tsx b/packages/core/src/features/preferences/renderer/preference-items/terminal/terminal-font-family/terminal-font-options.injectable.tsx index 3d6a0d2ae0..ceedb3fe02 100644 --- a/packages/core/src/features/preferences/renderer/preference-items/terminal/terminal-font-family/terminal-font-options.injectable.tsx +++ b/packages/core/src/features/preferences/renderer/preference-items/terminal/terminal-font-family/terminal-font-options.injectable.tsx @@ -7,10 +7,10 @@ import type { IComputedValue } from "mobx"; import { action, computed } from "mobx"; import React from "react"; import type { SingleValue } from "react-select"; -import userStoreInjectable from "../../../../../../common/user-store/user-store.injectable"; import { defaultTerminalFontFamily } from "../../../../../../common/vars"; import type { SelectOption } from "../../../../../../renderer/components/select"; import { terminalFontInjectionToken } from "../../../../../terminal/renderer/fonts/token"; +import userPreferencesStateInjectable from "../../../../../user-preferences/common/state.injectable"; export interface TerminalFontPreferencePresenter { readonly options: IComputedValue[]>; @@ -21,7 +21,7 @@ export interface TerminalFontPreferencePresenter { const terminalFontPreferencePresenterInjectable = getInjectable({ id: "terminal-font-preference-presenter", instantiate: (di): TerminalFontPreferencePresenter => { - const userStore = di.inject(userStoreInjectable); + const state = di.inject(userPreferencesStateInjectable); const terminalFonts = di.injectMany(terminalFontInjectionToken); return { @@ -30,18 +30,18 @@ const terminalFontPreferencePresenterInjectable = getInjectable({ {font.name} ), value: font.name, - isSelected: userStore.terminalConfig.fontFamily === font.name, + isSelected: state.terminalConfig.fontFamily === font.name, }))), - current: computed(() => userStore.terminalConfig.fontFamily), + current: computed(() => state.terminalConfig.fontFamily), onSelection: action(selection => { - userStore.terminalConfig.fontFamily = selection?.value ?? defaultTerminalFontFamily; + state.terminalConfig.fontFamily = selection?.value ?? defaultTerminalFontFamily; }), }; }, diff --git a/packages/core/src/features/preferences/renderer/preference-items/terminal/terminal-font-size/terminal-font-size.tsx b/packages/core/src/features/preferences/renderer/preference-items/terminal/terminal-font-size/terminal-font-size.tsx index bf11285509..75a6a35242 100644 --- a/packages/core/src/features/preferences/renderer/preference-items/terminal/terminal-font-size/terminal-font-size.tsx +++ b/packages/core/src/features/preferences/renderer/preference-items/terminal/terminal-font-size/terminal-font-size.tsx @@ -5,40 +5,32 @@ import React from "react"; import { SubTitle } from "../../../../../../renderer/components/layout/sub-title"; import { withInjectables } from "@ogre-tools/injectable-react"; -import type { UserStore } from "../../../../../../common/user-store"; -import userStoreInjectable from "../../../../../../common/user-store/user-store.injectable"; import { observer } from "mobx-react"; import { Input } from "../../../../../../renderer/components/input"; +import type { UserPreferencesState } from "../../../../../user-preferences/common/state.injectable"; +import userPreferencesStateInjectable from "../../../../../user-preferences/common/state.injectable"; interface Dependencies { - userStore: UserStore; + state: UserPreferencesState; } -const NonInjectedTerminalFontSize = observer( - ({ userStore }: Dependencies) => { +const NonInjectedTerminalFontSize = observer(({ + state, +}: Dependencies) => ( +
+ + state.terminalConfig.fontSize = Number(value)} /> +
+)); - return ( -
- - userStore.terminalConfig.fontSize = Number(value)} - /> -
- ); - }, -); - -export const TerminalFontSize = withInjectables( - NonInjectedTerminalFontSize, - - { - getProps: (di) => ({ - userStore: di.inject(userStoreInjectable), - }), - }, -); +export const TerminalFontSize = withInjectables(NonInjectedTerminalFontSize, { + getProps: (di) => ({ + state: di.inject(userPreferencesStateInjectable), + }), +}); diff --git a/packages/core/src/features/preferences/renderer/preference-items/terminal/terminal-shell-path/terminal-shell-path.tsx b/packages/core/src/features/preferences/renderer/preference-items/terminal/terminal-shell-path/terminal-shell-path.tsx index 5fe14ab9df..bb5deaefe4 100644 --- a/packages/core/src/features/preferences/renderer/preference-items/terminal/terminal-shell-path/terminal-shell-path.tsx +++ b/packages/core/src/features/preferences/renderer/preference-items/terminal/terminal-shell-path/terminal-shell-path.tsx @@ -5,42 +5,35 @@ import React from "react"; import { SubTitle } from "../../../../../../renderer/components/layout/sub-title"; import { withInjectables } from "@ogre-tools/injectable-react"; -import type { UserStore } from "../../../../../../common/user-store"; -import userStoreInjectable from "../../../../../../common/user-store/user-store.injectable"; import { observer } from "mobx-react"; import { Input } from "../../../../../../renderer/components/input"; import defaultShellInjectable from "./default-shell/default-shell.injectable"; +import type { UserPreferencesState } from "../../../../../user-preferences/common/state.injectable"; +import userPreferencesStateInjectable from "../../../../../user-preferences/common/state.injectable"; interface Dependencies { - userStore: UserStore; + state: UserPreferencesState; defaultShell: string; } -const NonInjectedTerminalShellPath = observer( - ({ userStore, defaultShell }: Dependencies) => { +const NonInjectedTerminalShellPath = observer(({ + state, + defaultShell, +}: Dependencies) => ( +
+ + state.shell = value} + /> +
+)); - return ( -
- - userStore.shell = value} - /> -
- - ); - }, -); - -export const TerminalShellPath = withInjectables( - NonInjectedTerminalShellPath, - - { - getProps: (di) => ({ - userStore: di.inject(userStoreInjectable), - defaultShell: di.inject(defaultShellInjectable), - }), - }, -); +export const TerminalShellPath = withInjectables(NonInjectedTerminalShellPath, { + getProps: (di) => ({ + state: di.inject(userPreferencesStateInjectable), + defaultShell: di.inject(defaultShellInjectable), + }), +}); diff --git a/packages/core/src/features/preferences/renderer/preference-items/terminal/terminal-theme/terminal-theme.tsx b/packages/core/src/features/preferences/renderer/preference-items/terminal/terminal-theme/terminal-theme.tsx index bea6b1279e..5819171c8c 100644 --- a/packages/core/src/features/preferences/renderer/preference-items/terminal/terminal-theme/terminal-theme.tsx +++ b/packages/core/src/features/preferences/renderer/preference-items/terminal/terminal-theme/terminal-theme.tsx @@ -5,26 +5,25 @@ import React from "react"; import { SubTitle } from "../../../../../../renderer/components/layout/sub-title"; import { withInjectables } from "@ogre-tools/injectable-react"; -import type { UserStore } from "../../../../../../common/user-store"; -import userStoreInjectable from "../../../../../../common/user-store/user-store.injectable"; import { observer } from "mobx-react"; import { Select } from "../../../../../../renderer/components/select"; import type { LensTheme } from "../../../../../../renderer/themes/lens-theme"; import { lensThemeDeclarationInjectionToken } from "../../../../../../renderer/themes/declaration"; +import type { UserPreferencesState } from "../../../../../user-preferences/common/state.injectable"; +import userPreferencesStateInjectable from "../../../../../user-preferences/common/state.injectable"; interface Dependencies { - userStore: UserStore; + state: UserPreferencesState; themes: LensTheme[]; } const NonInjectedTerminalTheme = observer(({ - userStore, + state, themes, }: Dependencies) => { - const themeOptions = [ { - value: "", // TODO: replace with a sentinal value that isn't string (and serialize it differently) + value: "", // TODO: replace with a sentinel value that isn't string (and serialize it differently) label: "Match Lens Theme", }, ...themes.map(theme => ({ @@ -40,8 +39,8 @@ const NonInjectedTerminalTheme = observer(({ id="terminal-theme-input" themeName="lens" options={themeOptions} - value={userStore.terminalTheme} - onChange={option => userStore.terminalTheme = option?.value ?? ""} + value={state.terminalTheme} + onChange={option => state.terminalTheme = option?.value ?? ""} />
); @@ -50,7 +49,7 @@ const NonInjectedTerminalTheme = observer(({ export const TerminalTheme = withInjectables(NonInjectedTerminalTheme, { getProps: (di) => ({ - userStore: di.inject(userStoreInjectable), + state: di.inject(userPreferencesStateInjectable), themes: di.injectMany(lensThemeDeclarationInjectionToken), }), }); diff --git a/packages/core/src/features/preferences/urls-of-legacy-extensions.test.tsx b/packages/core/src/features/preferences/urls-of-legacy-extensions.test.tsx index 1eba04c683..4b04e5500f 100644 --- a/packages/core/src/features/preferences/urls-of-legacy-extensions.test.tsx +++ b/packages/core/src/features/preferences/urls-of-legacy-extensions.test.tsx @@ -5,8 +5,8 @@ import type { RenderResult } from "@testing-library/react"; import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; -import type { Discover } from "../../renderer/components/test-utils/discovery-of-html-elements"; -import { discoverFor } from "../../renderer/components/test-utils/discovery-of-html-elements"; +import type { Discover } from "@k8slens/react-testing-library-discovery"; +import { discoverFor } from "@k8slens/react-testing-library-discovery"; import React from "react"; import type { Navigate } from "../../renderer/navigation/navigate.injectable"; import navigateInjectable from "../../renderer/navigation/navigate.injectable"; diff --git a/packages/core/src/features/shell-sync/main/setup-shell.injectable.ts b/packages/core/src/features/shell-sync/main/setup-shell.injectable.ts index 3a763b7426..f7a8b65974 100644 --- a/packages/core/src/features/shell-sync/main/setup-shell.injectable.ts +++ b/packages/core/src/features/shell-sync/main/setup-shell.injectable.ts @@ -8,9 +8,9 @@ import { onLoadOfApplicationInjectionToken } from "@k8slens/application"; import isSnapPackageInjectable from "../../../common/vars/is-snap-package.injectable"; import electronAppInjectable from "../../../main/electron-app/electron-app.injectable"; import computeShellEnvironmentInjectable from "./compute-shell-environment.injectable"; -import userShellSettingInjectable from "../../../common/user-store/shell-setting.injectable"; import emitShellSyncFailedInjectable from "./emit-failure.injectable"; import { unionPATHs } from "@k8slens/utilities"; +import userShellSettingInjectable from "../../user-preferences/common/shell-setting.injectable"; const setupShellInjectable = getInjectable({ id: "setup-shell", diff --git a/packages/core/src/features/status-bar/__snapshots__/status-bar-items-originating-from-extensions.test.tsx.snap b/packages/core/src/features/status-bar/__snapshots__/status-bar-items-originating-from-extensions.test.tsx.snap index f652074bf8..1b21dc2909 100644 --- a/packages/core/src/features/status-bar/__snapshots__/status-bar-items-originating-from-extensions.test.tsx.snap +++ b/packages/core/src/features/status-bar/__snapshots__/status-bar-items-originating-from-extensions.test.tsx.snap @@ -231,7 +231,7 @@ exports[`status-bar-items-originating-from-extensions when application starts wh class="HotbarSelector" > { - const userStore = di.inject(userStoreInjectable); + const userStore = di.inject(userPreferencesStateInjectable); return computed(() => userStore.httpsProxy); }, diff --git a/packages/core/src/features/user-preferences/common/is-table-column-hidden.injectable.ts b/packages/core/src/features/user-preferences/common/is-table-column-hidden.injectable.ts new file mode 100644 index 0000000000..f1e3d72318 --- /dev/null +++ b/packages/core/src/features/user-preferences/common/is-table-column-hidden.injectable.ts @@ -0,0 +1,38 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getInjectable } from "@ogre-tools/injectable"; +import userPreferencesStateInjectable from "./state.injectable"; + +/** + * Checks if a column (by ID) for a table (by ID) is configured to be hidden + * @param tableId The ID of the table to be checked against + * @param columnIds The list of IDs the check if one is hidden + * @returns true if at least one column under the table is set to hidden + */ +export type IsTableColumnHidden = (tableId: string, ...columnIds: (string | undefined)[]) => boolean; + +const isTableColumnHiddenInjectable = getInjectable({ + id: "is-table-column-hidden", + instantiate: (di): IsTableColumnHidden => { + const state = di.inject(userPreferencesStateInjectable); + + return (tableId, ...columnIds) => { + if (columnIds.length === 0) { + return false; + } + + const config = state.hiddenTableColumns.get(tableId); + + if (!config) { + return false; + } + + return columnIds.some(columnId => columnId && config.has(columnId)); + }; + }, +}); + +export default isTableColumnHiddenInjectable; diff --git a/packages/core/src/common/user-store/kubeconfig-syncs.injectable.ts b/packages/core/src/features/user-preferences/common/kubeconfig-syncs.injectable.ts similarity index 68% rename from packages/core/src/common/user-store/kubeconfig-syncs.injectable.ts rename to packages/core/src/features/user-preferences/common/kubeconfig-syncs.injectable.ts index 7327b9d8e4..db34b53c1f 100644 --- a/packages/core/src/common/user-store/kubeconfig-syncs.injectable.ts +++ b/packages/core/src/features/user-preferences/common/kubeconfig-syncs.injectable.ts @@ -3,11 +3,11 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import userStoreInjectable from "./user-store.injectable"; +import userPreferencesStateInjectable from "./state.injectable"; const kubeconfigSyncsInjectable = getInjectable({ id: "kubeconfig-syncs", - instantiate: (di) => di.inject(userStoreInjectable).syncKubeconfigEntries, + instantiate: (di) => di.inject(userPreferencesStateInjectable).syncKubeconfigEntries, }); export default kubeconfigSyncsInjectable; diff --git a/packages/core/src/common/user-store/lens-color-theme.injectable.ts b/packages/core/src/features/user-preferences/common/lens-color-theme.injectable.ts similarity index 78% rename from packages/core/src/common/user-store/lens-color-theme.injectable.ts rename to packages/core/src/features/user-preferences/common/lens-color-theme.injectable.ts index 5b48de1a37..1ed2930a37 100644 --- a/packages/core/src/common/user-store/lens-color-theme.injectable.ts +++ b/packages/core/src/features/user-preferences/common/lens-color-theme.injectable.ts @@ -4,7 +4,7 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import { computed } from "mobx"; -import userStoreInjectable from "./user-store.injectable"; +import userPreferencesStateInjectable from "./state.injectable"; export type LensColorThemePreference = { useSystemTheme: true; @@ -16,11 +16,11 @@ export type LensColorThemePreference = { const lensColorThemePreferenceInjectable = getInjectable({ id: "lens-color-theme-preference", instantiate: (di) => { - const userStore = di.inject(userStoreInjectable); + const state = di.inject(userPreferencesStateInjectable); return computed((): LensColorThemePreference => { // TODO: remove magic strings - if (userStore.colorTheme === "system") { + if (state.colorTheme === "system") { return { useSystemTheme: true, }; @@ -28,7 +28,7 @@ const lensColorThemePreferenceInjectable = getInjectable({ return { useSystemTheme: false, - lensThemeId: userStore.colorTheme, + lensThemeId: state.colorTheme, }; }); }, diff --git a/packages/core/src/features/user-preferences/common/migrations-token.ts b/packages/core/src/features/user-preferences/common/migrations-token.ts new file mode 100644 index 0000000000..0da426e3e6 --- /dev/null +++ b/packages/core/src/features/user-preferences/common/migrations-token.ts @@ -0,0 +1,11 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { getInjectionToken } from "@ogre-tools/injectable"; +import type { MigrationDeclaration } from "../../../common/persistent-storage/migrations.injectable"; + +export const userPreferencesMigrationInjectionToken = getInjectionToken({ + id: "user-preferences-migration-token", +}); diff --git a/packages/core/src/common/user-store/preference-descriptors.injectable.ts b/packages/core/src/features/user-preferences/common/preference-descriptors.injectable.ts similarity index 89% rename from packages/core/src/common/user-store/preference-descriptors.injectable.ts rename to packages/core/src/features/user-preferences/common/preference-descriptors.injectable.ts index 35815dbbea..afb84cb892 100644 --- a/packages/core/src/common/user-store/preference-descriptors.injectable.ts +++ b/packages/core/src/features/user-preferences/common/preference-descriptors.injectable.ts @@ -6,17 +6,17 @@ import { getInjectable } from "@ogre-tools/injectable"; import { merge } from "lodash"; import type { ObservableMap } from "mobx"; import { observable } from "mobx"; -import homeDirectoryPathInjectable from "../os/home-directory-path.injectable"; -import joinPathsInjectable from "../path/join-paths.injectable"; -import { defaultThemeId } from "../vars"; -import currentTimezoneInjectable from "./current-timezone.injectable"; +import homeDirectoryPathInjectable from "../../../common/os/home-directory-path.injectable"; +import joinPathsInjectable from "../../../common/path/join-paths.injectable"; +import { defaultThemeId } from "../../../common/vars"; +import currentTimezoneInjectable from "../../../common/vars/current-timezone.injectable"; import type { EditorConfiguration, ExtensionRegistry, KubeconfigSyncEntry, KubeconfigSyncValue, TerminalConfig } from "./preferences-helpers"; import { defaultExtensionRegistryUrlLocation, defaultEditorConfig, defaultTerminalConfig, defaultPackageMirror, getPreferenceDescriptor, packageMirrors } from "./preferences-helpers"; -export type PreferenceDescriptors = ReturnType; +export type PreferenceDescriptors = ReturnType; -const userStorePreferenceDescriptorsInjectable = getInjectable({ - id: "user-store-preference-descriptors", +const userPreferenceDescriptorsInjectable = getInjectable({ + id: "user-preference-descriptors", instantiate: (di) => { const currentTimezone = di.inject(currentTimezoneInjectable); const joinPaths = di.inject(joinPathsInjectable); @@ -140,4 +140,4 @@ const userStorePreferenceDescriptorsInjectable = getInjectable({ }, }); -export default userStorePreferenceDescriptorsInjectable; +export default userPreferenceDescriptorsInjectable; diff --git a/packages/core/src/common/user-store/preferences-helpers.ts b/packages/core/src/features/user-preferences/common/preferences-helpers.ts similarity index 94% rename from packages/core/src/common/user-store/preferences-helpers.ts rename to packages/core/src/features/user-preferences/common/preferences-helpers.ts index ecb9108bee..1a80dccd28 100644 --- a/packages/core/src/common/user-store/preferences-helpers.ts +++ b/packages/core/src/features/user-preferences/common/preferences-helpers.ts @@ -4,7 +4,7 @@ */ import type { editor } from "monaco-editor"; -import { defaultEditorFontFamily, defaultFontSize, defaultTerminalFontFamily } from "../vars"; +import { defaultFontSize, defaultTerminalFontFamily, defaultEditorFontFamily } from "../../../common/vars"; import type { PreferenceDescriptors } from "./preference-descriptors.injectable"; export interface KubeconfigSyncEntry extends KubeconfigSyncValue { @@ -95,5 +95,5 @@ export type UserStoreFlatModel = { }; export type UserPreferencesModel = { - [field in keyof PreferenceDescriptors]: PreferencesModelType; + [field in keyof PreferenceDescriptors]?: PreferencesModelType; } & { updateChannel: string }; diff --git a/packages/core/src/features/user-preferences/common/reset-theme.injectable.ts b/packages/core/src/features/user-preferences/common/reset-theme.injectable.ts new file mode 100644 index 0000000000..cdff170946 --- /dev/null +++ b/packages/core/src/features/user-preferences/common/reset-theme.injectable.ts @@ -0,0 +1,24 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { action } from "mobx"; +import userPreferenceDescriptorsInjectable from "./preference-descriptors.injectable"; +import userPreferencesStateInjectable from "./state.injectable"; + +export type ResetTheme = () => void; + +const resetThemeInjectable = getInjectable({ + id: "reset-theme", + instantiate: (di): ResetTheme => { + const state = di.inject(userPreferencesStateInjectable); + const preferenceDescriptors = di.inject(userPreferenceDescriptorsInjectable); + + return action(() => { + state.colorTheme = preferenceDescriptors.colorTheme.fromStore(undefined); + }); + }, +}); + +export default resetThemeInjectable; diff --git a/packages/core/src/common/user-store/shell-setting.injectable.ts b/packages/core/src/features/user-preferences/common/shell-setting.injectable.ts similarity index 63% rename from packages/core/src/common/user-store/shell-setting.injectable.ts rename to packages/core/src/features/user-preferences/common/shell-setting.injectable.ts index f93a4e5874..a4f7a42585 100644 --- a/packages/core/src/common/user-store/shell-setting.injectable.ts +++ b/packages/core/src/features/user-preferences/common/shell-setting.injectable.ts @@ -4,16 +4,16 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import { computed } from "mobx"; -import userInfoInjectable from "./user-info.injectable"; -import userStoreInjectable from "./user-store.injectable"; +import userInfoInjectable from "../../../common/vars/user-info.injectable"; +import userPreferencesStateInjectable from "./state.injectable"; const userShellSettingInjectable = getInjectable({ id: "user-shell-setting", instantiate: (di) => { - const userStore = di.inject(userStoreInjectable); + const state = di.inject(userPreferencesStateInjectable); const userInfo = di.inject(userInfoInjectable); - return computed(() => userStore.shell || userInfo.shell); + return computed(() => state.shell || userInfo.shell); }, }); diff --git a/packages/core/src/features/user-preferences/common/state.injectable.ts b/packages/core/src/features/user-preferences/common/state.injectable.ts new file mode 100644 index 0000000000..53f4ac0356 --- /dev/null +++ b/packages/core/src/features/user-preferences/common/state.injectable.ts @@ -0,0 +1,19 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { observable } from "mobx"; +import type { PreferenceDescriptors } from "./preference-descriptors.injectable"; +import type { StoreType } from "./preferences-helpers"; + +export type UserPreferencesState = { + -readonly [Field in keyof PreferenceDescriptors]: StoreType; +}; + +const userPreferencesStateInjectable = getInjectable({ + id: "user-preferences-state", + instantiate: () => observable.object({} as UserPreferencesState), +}); + +export default userPreferencesStateInjectable; diff --git a/packages/core/src/features/user-preferences/common/storage.injectable.ts b/packages/core/src/features/user-preferences/common/storage.injectable.ts new file mode 100644 index 0000000000..d736c28677 --- /dev/null +++ b/packages/core/src/features/user-preferences/common/storage.injectable.ts @@ -0,0 +1,88 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { action } from "mobx"; +import prefixedLoggerInjectable from "../../../common/logger/prefixed-logger.injectable"; +import createPersistentStorageInjectable from "../../../common/persistent-storage/create.injectable"; +import persistentStorageMigrationsInjectable from "../../../common/persistent-storage/migrations.injectable"; +import { userPreferencesMigrationInjectionToken } from "./migrations-token"; +import { toJS } from "../../../common/utils"; +import storeMigrationVersionInjectable from "../../../common/vars/store-migration-version.injectable"; +import selectedUpdateChannelInjectable from "../../application-update/common/selected-update-channel/selected-update-channel.injectable"; +import type { ReleaseChannel } from "../../application-update/common/update-channels"; +import userPreferencesStateInjectable from "./state.injectable"; +import userPreferenceDescriptorsInjectable from "./preference-descriptors.injectable"; +import type { UserPreferencesModel } from "./preferences-helpers"; + +export interface UserStoreModel { + preferences: UserPreferencesModel; +} + +const userPreferencesPersistentStorageInjectable = getInjectable({ + id: "user-preferences-persistent-storage", + instantiate: (di) => { + const createPersistentStorage = di.inject(createPersistentStorageInjectable); + const logger = di.inject(prefixedLoggerInjectable, "USER-PREFERENCES"); + const descriptors = di.inject(userPreferenceDescriptorsInjectable); + const selectedUpdateChannel = di.inject(selectedUpdateChannelInjectable); + const state = di.inject(userPreferencesStateInjectable); + + return createPersistentStorage({ + configName: "lens-user-store", + projectVersion: di.inject(storeMigrationVersionInjectable), + migrations: di.inject(persistentStorageMigrationsInjectable, userPreferencesMigrationInjectionToken), + fromStore: action(({ preferences = {}}) => { + logger.debug("fromStore()", { preferences }); + + state.allowErrorReporting = descriptors.allowErrorReporting.fromStore(preferences.allowErrorReporting); + state.allowUntrustedCAs = descriptors.allowUntrustedCAs.fromStore(preferences.allowUntrustedCAs); + state.colorTheme = descriptors.colorTheme.fromStore(preferences.colorTheme); + state.downloadBinariesPath = descriptors.downloadBinariesPath.fromStore(preferences.downloadBinariesPath); + state.downloadKubectlBinaries = descriptors.downloadKubectlBinaries.fromStore(preferences.downloadKubectlBinaries); + state.downloadMirror = descriptors.downloadMirror.fromStore(preferences.downloadMirror); + state.editorConfiguration = descriptors.editorConfiguration.fromStore(preferences.editorConfiguration); + state.extensionRegistryUrl = descriptors.extensionRegistryUrl.fromStore(preferences.extensionRegistryUrl); + state.hiddenTableColumns = descriptors.hiddenTableColumns.fromStore(preferences.hiddenTableColumns); + state.httpsProxy = descriptors.httpsProxy.fromStore(preferences.httpsProxy); + state.kubectlBinariesPath = descriptors.kubectlBinariesPath.fromStore(preferences.kubectlBinariesPath); + state.localeTimezone = descriptors.localeTimezone.fromStore(preferences.localeTimezone); + state.openAtLogin = descriptors.openAtLogin.fromStore(preferences.openAtLogin); + state.shell = descriptors.shell.fromStore(preferences.shell); + state.syncKubeconfigEntries = descriptors.syncKubeconfigEntries.fromStore(preferences.syncKubeconfigEntries); + state.terminalConfig = descriptors.terminalConfig.fromStore(preferences.terminalConfig); + state.terminalCopyOnSelect = descriptors.terminalCopyOnSelect.fromStore(preferences.terminalCopyOnSelect); + state.terminalTheme = descriptors.terminalTheme.fromStore(preferences.terminalTheme); + + // TODO: Switch to action-based saving instead saving stores by reaction + selectedUpdateChannel.setValue(preferences?.updateChannel as ReleaseChannel); + }), + toJSON: () => toJS({ + preferences: { + allowErrorReporting: descriptors.allowErrorReporting.toStore(state.allowErrorReporting), + allowUntrustedCAs: descriptors.allowUntrustedCAs.toStore(state.allowUntrustedCAs), + colorTheme: descriptors.colorTheme.toStore(state.colorTheme), + downloadBinariesPath: descriptors.downloadBinariesPath.toStore(state.downloadBinariesPath), + downloadKubectlBinaries: descriptors.downloadKubectlBinaries.toStore(state.downloadKubectlBinaries), + downloadMirror: descriptors.downloadMirror.toStore(state.downloadMirror), + editorConfiguration: descriptors.editorConfiguration.toStore(state.editorConfiguration), + extensionRegistryUrl: descriptors.extensionRegistryUrl.toStore(state.extensionRegistryUrl), + hiddenTableColumns: descriptors.hiddenTableColumns.toStore(state.hiddenTableColumns), + httpsProxy: descriptors.httpsProxy.toStore(state.httpsProxy), + kubectlBinariesPath: descriptors.kubectlBinariesPath.toStore(state.kubectlBinariesPath), + localeTimezone: descriptors.localeTimezone.toStore(state.localeTimezone), + openAtLogin: descriptors.openAtLogin.toStore(state.openAtLogin), + shell: descriptors.shell.toStore(state.shell), + syncKubeconfigEntries: descriptors.syncKubeconfigEntries.toStore(state.syncKubeconfigEntries), + terminalConfig: descriptors.terminalConfig.toStore(state.terminalConfig), + terminalCopyOnSelect: descriptors.terminalCopyOnSelect.toStore(state.terminalCopyOnSelect), + terminalTheme: descriptors.terminalTheme.toStore(state.terminalTheme), + updateChannel: selectedUpdateChannel.value.get().id, + }, + }), + }); + }, +}); + +export default userPreferencesPersistentStorageInjectable; diff --git a/packages/core/src/common/user-store/terminal-config.injectable.ts b/packages/core/src/features/user-preferences/common/terminal-config.injectable.ts similarity index 60% rename from packages/core/src/common/user-store/terminal-config.injectable.ts rename to packages/core/src/features/user-preferences/common/terminal-config.injectable.ts index 6f5be75eaf..cfa9cffb3c 100644 --- a/packages/core/src/common/user-store/terminal-config.injectable.ts +++ b/packages/core/src/features/user-preferences/common/terminal-config.injectable.ts @@ -3,16 +3,15 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { computed } from "mobx"; -import { toJS } from "../utils"; -import userStoreInjectable from "./user-store.injectable"; +import { computed, toJS } from "mobx"; +import userPreferencesStateInjectable from "./state.injectable"; const terminalConfigInjectable = getInjectable({ id: "terminal-config", instantiate: (di) => { - const store = di.inject(userStoreInjectable); + const state = di.inject(userPreferencesStateInjectable); - return computed(() => toJS(store.terminalConfig)); + return computed(() => toJS(state.terminalConfig)); }, }); diff --git a/packages/core/src/common/user-store/terminal-copy-on-select.injectable.ts b/packages/core/src/features/user-preferences/common/terminal-copy-on-select.injectable.ts similarity index 69% rename from packages/core/src/common/user-store/terminal-copy-on-select.injectable.ts rename to packages/core/src/features/user-preferences/common/terminal-copy-on-select.injectable.ts index 543a4f73b9..494b43039a 100644 --- a/packages/core/src/common/user-store/terminal-copy-on-select.injectable.ts +++ b/packages/core/src/features/user-preferences/common/terminal-copy-on-select.injectable.ts @@ -4,14 +4,14 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import { computed } from "mobx"; -import userStoreInjectable from "./user-store.injectable"; +import userPreferencesStateInjectable from "./state.injectable"; const terminalCopyOnSelectInjectable = getInjectable({ id: "terminal-copy-on-select", instantiate: (di) => { - const store = di.inject(userStoreInjectable); + const state = di.inject(userPreferencesStateInjectable); - return computed(() => store.terminalCopyOnSelect); + return computed(() => state.terminalCopyOnSelect); }, }); diff --git a/packages/core/src/common/user-store/terminal-theme.injectable.ts b/packages/core/src/features/user-preferences/common/terminal-theme.injectable.ts similarity index 79% rename from packages/core/src/common/user-store/terminal-theme.injectable.ts rename to packages/core/src/features/user-preferences/common/terminal-theme.injectable.ts index a0a00c3253..f9e5c6d6f5 100644 --- a/packages/core/src/common/user-store/terminal-theme.injectable.ts +++ b/packages/core/src/features/user-preferences/common/terminal-theme.injectable.ts @@ -4,7 +4,7 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import { computed } from "mobx"; -import userStoreInjectable from "./user-store.injectable"; +import userPreferencesStateInjectable from "./state.injectable"; export type TerminalThemePreference = { matchLensTheme: true; @@ -16,11 +16,11 @@ export type TerminalThemePreference = { const terminalThemePreferenceInjectable = getInjectable({ id: "terminal-theme-preference", instantiate: (di) => { - const userStore = di.inject(userStoreInjectable); + const state = di.inject(userPreferencesStateInjectable); return computed((): TerminalThemePreference => { // NOTE: remove use of magic strings - if (!userStore.terminalTheme) { + if (!state.terminalTheme) { return { matchLensTheme: true, }; @@ -28,7 +28,7 @@ const terminalThemePreferenceInjectable = getInjectable({ return { matchLensTheme: false, - themeId: userStore.terminalTheme, + themeId: state.terminalTheme, }; }); }, diff --git a/packages/core/src/features/user-preferences/common/toggle-table-column-visibility.injectable.ts b/packages/core/src/features/user-preferences/common/toggle-table-column-visibility.injectable.ts new file mode 100644 index 0000000000..94d1d07701 --- /dev/null +++ b/packages/core/src/features/user-preferences/common/toggle-table-column-visibility.injectable.ts @@ -0,0 +1,23 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getOrInsertSet, toggle } from "@k8slens/utilities"; +import { getInjectable } from "@ogre-tools/injectable"; +import { action } from "mobx"; +import userPreferencesStateInjectable from "./state.injectable"; + +export type ToggleTableColumnVisibility = (tableId: string, columnId: string) => void; + +const toggleTableColumnVisibilityInjectable = getInjectable({ + id: "toggle-table-column-visibility", + instantiate: (di): ToggleTableColumnVisibility => { + const state = di.inject(userPreferencesStateInjectable); + + return action((tableId, columnId) => { + toggle(getOrInsertSet(state.hiddenTableColumns, tableId), columnId); + }); + }, +}); + +export default toggleTableColumnVisibilityInjectable; diff --git a/packages/core/src/main/user-store/migrations/5.0.0-alpha.3.injectable.ts b/packages/core/src/features/user-preferences/main/5.0.0-alpha.3.injectable.ts similarity index 70% rename from packages/core/src/main/user-store/migrations/5.0.0-alpha.3.injectable.ts rename to packages/core/src/features/user-preferences/main/5.0.0-alpha.3.injectable.ts index 617f32ad5d..70127386cb 100644 --- a/packages/core/src/main/user-store/migrations/5.0.0-alpha.3.injectable.ts +++ b/packages/core/src/features/user-preferences/main/5.0.0-alpha.3.injectable.ts @@ -5,14 +5,14 @@ // Switch representation of hiddenTableColumns in store import { getInjectable } from "@ogre-tools/injectable"; -import { userStoreMigrationInjectionToken } from "../../../common/user-store/migrations-token"; +import { userPreferencesMigrationInjectionToken } from "../common/migrations-token"; interface PreV500Alpha3UserPreferencesModel { hiddenTableColumns?: Record; } -const v500Alpha3UserStoreMigrationInjectable = getInjectable({ - id: "v5.0.0-alpha.3-user-store-migration", +const v500Alpha3UserPreferencesStorageMigrationInjectable = getInjectable({ + id: "v5.0.0-alpha.3-preferences-storage-migration", instantiate: () => ({ version: "5.0.0-alpha.3", run(store) { @@ -29,8 +29,8 @@ const v500Alpha3UserStoreMigrationInjectable = getInjectable({ }); }, }), - injectionToken: userStoreMigrationInjectionToken, + injectionToken: userPreferencesMigrationInjectionToken, }); -export default v500Alpha3UserStoreMigrationInjectable; +export default v500Alpha3UserPreferencesStorageMigrationInjectable; diff --git a/packages/core/src/main/user-store/migrations/5.0.3-beta.1.injectable.ts b/packages/core/src/features/user-preferences/main/5.0.3-beta.1.injectable.ts similarity index 87% rename from packages/core/src/main/user-store/migrations/5.0.3-beta.1.injectable.ts rename to packages/core/src/features/user-preferences/main/5.0.3-beta.1.injectable.ts index 7739c67e66..d7baa496b0 100644 --- a/packages/core/src/main/user-store/migrations/5.0.3-beta.1.injectable.ts +++ b/packages/core/src/features/user-preferences/main/5.0.3-beta.1.injectable.ts @@ -3,8 +3,6 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { ClusterStoreModel } from "../../../common/cluster-store/cluster-store"; -import type { KubeconfigSyncEntry, UserPreferencesModel } from "../../../common/user-store"; import { isErrnoException } from "@k8slens/utilities"; import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; @@ -12,14 +10,16 @@ import joinPathsInjectable from "../../../common/path/join-paths.injectable"; import isLogicalChildPathInjectable from "../../../common/path/is-logical-child-path.injectable"; import getDirnameOfPathInjectable from "../../../common/path/get-dirname.injectable"; import { getInjectable } from "@ogre-tools/injectable"; -import { userStoreMigrationInjectionToken } from "../../../common/user-store/migrations-token"; +import { userPreferencesMigrationInjectionToken } from "../../../features/user-preferences/common/migrations-token"; import readJsonSyncInjectable from "../../../common/fs/read-json-sync.injectable"; import homeDirectoryPathInjectable from "../../../common/os/home-directory-path.injectable"; import loggerInjectable from "../../../common/logger.injectable"; import pathExistsSyncInjectable from "../../../common/fs/path-exists-sync.injectable"; +import type { ClusterStoreModel } from "../../../features/cluster/storage/common/storage.injectable"; +import type { UserPreferencesModel, KubeconfigSyncEntry } from "../common/preferences-helpers"; -const v503Beta1UserStoreMigrationInjectable = getInjectable({ - id: "v5.0.3-beta.1-user-store-migration", +const v503Beta1UserPreferencesStorageMigrationInjectable = getInjectable({ + id: "v5.0.3-beta.1-preferences-storage-migration", instantiate: (di) => { const userDataPath = di.inject(directoryForUserDataInjectable); const kubeConfigsPath = di.inject(directoryForKubeConfigsInjectable); @@ -85,7 +85,7 @@ const v503Beta1UserStoreMigrationInjectable = getInjectable({ }, }; }, - injectionToken: userStoreMigrationInjectionToken, + injectionToken: userPreferencesMigrationInjectionToken, }); -export default v503Beta1UserStoreMigrationInjectable; +export default v503Beta1UserPreferencesStorageMigrationInjectable; diff --git a/packages/core/src/common/user-store/file-name-migration.injectable.ts b/packages/core/src/features/user-preferences/main/file-name-migration.injectable.ts similarity index 64% rename from packages/core/src/common/user-store/file-name-migration.injectable.ts rename to packages/core/src/features/user-preferences/main/file-name-migration.injectable.ts index 31d5352056..e474e7f5c6 100644 --- a/packages/core/src/common/user-store/file-name-migration.injectable.ts +++ b/packages/core/src/features/user-preferences/main/file-name-migration.injectable.ts @@ -3,28 +3,29 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import fse from "fs-extra"; -import directoryForUserDataInjectable from "../app-paths/directory-for-user-data/directory-for-user-data.injectable"; +import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; import { isErrnoException } from "@k8slens/utilities"; import { getInjectable } from "@ogre-tools/injectable"; -import joinPathsInjectable from "../path/join-paths.injectable"; +import joinPathsInjectable from "../../../common/path/join-paths.injectable"; +import fsInjectable from "../../../common/fs/fs.injectable"; export type UserStoreFileNameMigration = () => Promise; -const userStoreFileNameMigrationInjectable = getInjectable({ - id: "user-store-file-name-migration", +const userPreferencesStorageFileNameMigrationInjectable = getInjectable({ + id: "preferences-storage-file-name-migration", instantiate: (di): UserStoreFileNameMigration => { const userDataPath = di.inject(directoryForUserDataInjectable); const joinPaths = di.inject(joinPathsInjectable); const configJsonPath = joinPaths(userDataPath, "config.json"); const lensUserStoreJsonPath = joinPaths(userDataPath, "lens-user-store.json"); + const { rename, rm } = di.inject(fsInjectable); return async () => { try { - await fse.move(configJsonPath, lensUserStoreJsonPath); + await rename(configJsonPath, lensUserStoreJsonPath); } catch (error) { if (error instanceof Error && error.message === "dest already exists.") { - await fse.remove(configJsonPath); + await rm(configJsonPath); } else if (isErrnoException(error) && error.code === "ENOENT" && error.path === configJsonPath) { // (No such file or directory) return; // file already moved @@ -35,7 +36,6 @@ const userStoreFileNameMigrationInjectable = getInjectable({ } }; }, - causesSideEffects: true, }); -export default userStoreFileNameMigrationInjectable; +export default userPreferencesStorageFileNameMigrationInjectable; diff --git a/packages/core/src/features/user-preferences/main/load-storage.injectable.ts b/packages/core/src/features/user-preferences/main/load-storage.injectable.ts new file mode 100644 index 0000000000..dac1c2aebb --- /dev/null +++ b/packages/core/src/features/user-preferences/main/load-storage.injectable.ts @@ -0,0 +1,26 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { beforeApplicationIsLoadingInjectionToken } from "@k8slens/application"; +import initDefaultUpdateChannelInjectable from "../../../main/vars/default-update-channel/init.injectable"; +import userPreferencesPersistentStorageInjectable from "../common/storage.injectable"; +import userPreferencesStorageFileNameMigrationInjectable from "./file-name-migration.injectable"; + +const loadUserPreferencesStorageInjectable = getInjectable({ + id: "load-user-preferences-storage", + instantiate: (di) => ({ + run: async () => { + const storage = di.inject(userPreferencesPersistentStorageInjectable); + const userStoreFileNameMigration = di.inject(userPreferencesStorageFileNameMigrationInjectable); + + await userStoreFileNameMigration(); + storage.loadAndStartSyncing(); + }, + runAfter: initDefaultUpdateChannelInjectable, + }), + injectionToken: beforeApplicationIsLoadingInjectionToken, +}); + +export default loadUserPreferencesStorageInjectable; diff --git a/packages/core/src/main/user-store/sync-open-at-login-with-os.injectable.ts b/packages/core/src/features/user-preferences/main/sync-open-at-login-with-os.injectable.ts similarity index 72% rename from packages/core/src/main/user-store/sync-open-at-login-with-os.injectable.ts rename to packages/core/src/features/user-preferences/main/sync-open-at-login-with-os.injectable.ts index a06d65163e..41f3dff55c 100644 --- a/packages/core/src/main/user-store/sync-open-at-login-with-os.injectable.ts +++ b/packages/core/src/features/user-preferences/main/sync-open-at-login-with-os.injectable.ts @@ -4,18 +4,18 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import { reaction } from "mobx"; -import userStoreInjectable from "../../common/user-store/user-store.injectable"; -import setLoginItemSettingsInjectable from "../electron-app/features/set-login-item-settings.injectable"; +import setLoginItemSettingsInjectable from "../../../main/electron-app/features/set-login-item-settings.injectable"; import { onLoadOfApplicationInjectionToken } from "@k8slens/application"; +import userPreferencesStateInjectable from "../common/state.injectable"; const setupSyncOpenAtLoginWithOsInjectable = getInjectable({ id: "setup-sync-open-at-login-with-os", instantiate: (di) => ({ run: () => { const setLoginItemSettings = di.inject(setLoginItemSettingsInjectable); - const userStore = di.inject(userStoreInjectable); + const state = di.inject(userPreferencesStateInjectable); - reaction(() => userStore.openAtLogin, openAtLogin => { + reaction(() => state.openAtLogin, openAtLogin => { setLoginItemSettings({ openAtLogin, openAsHidden: true, diff --git a/packages/core/src/features/user-preferences/renderer/load-storage.injectable.ts b/packages/core/src/features/user-preferences/renderer/load-storage.injectable.ts new file mode 100644 index 0000000000..43e10c1707 --- /dev/null +++ b/packages/core/src/features/user-preferences/renderer/load-storage.injectable.ts @@ -0,0 +1,23 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { beforeFrameStartsSecondInjectionToken } from "../../../renderer/before-frame-starts/tokens"; +import initDefaultUpdateChannelInjectable from "../../../renderer/vars/default-update-channel/init.injectable"; +import userPreferencesPersistentStorageInjectable from "../common/storage.injectable"; + +const loadUserPreferencesStorageInjectable = getInjectable({ + id: "load-user-preferences-storage", + instantiate: (di) => ({ + run: () => { + const storage = di.inject(userPreferencesPersistentStorageInjectable); + + return storage.loadAndStartSyncing(); + }, + runAfter: initDefaultUpdateChannelInjectable, + }), + injectionToken: beforeFrameStartsSecondInjectionToken, +}); + +export default loadUserPreferencesStorageInjectable; diff --git a/packages/core/src/features/weblinks/common/add.injectable.ts b/packages/core/src/features/weblinks/common/add.injectable.ts new file mode 100644 index 0000000000..75450d237d --- /dev/null +++ b/packages/core/src/features/weblinks/common/add.injectable.ts @@ -0,0 +1,41 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { action } from "mobx"; +import weblinksStateInjectable from "./state.injectable"; +import type { WeblinkData } from "./storage.injectable"; +import * as uuid from "uuid"; +import { getOrInsert } from "@k8slens/utilities"; + +export interface WeblinkCreateOptions { + id?: string; + name: string; + url: string; +} + +export type AddWeblink = (data: WeblinkCreateOptions) => WeblinkData; + +const addWeblinkInjectable = getInjectable({ + id: "add-weblink", + instantiate: (di): AddWeblink => { + const state = di.inject(weblinksStateInjectable); + + return action((data) => { + const { + id = uuid.v4(), + name, + url, + } = data; + + if (state.has(id)) { + throw new Error(`There already exists a weblink with id=${id}`); + } + + return getOrInsert(state, id, { id, name, url }); + }); + }, +}); + +export default addWeblinkInjectable; diff --git a/packages/core/src/common/weblinks-store/migration-token.ts b/packages/core/src/features/weblinks/common/migration-token.ts similarity index 77% rename from packages/core/src/common/weblinks-store/migration-token.ts rename to packages/core/src/features/weblinks/common/migration-token.ts index d1cea9334b..a0d8ce58fc 100644 --- a/packages/core/src/common/weblinks-store/migration-token.ts +++ b/packages/core/src/features/weblinks/common/migration-token.ts @@ -4,7 +4,7 @@ */ import { getInjectionToken } from "@ogre-tools/injectable"; -import type { MigrationDeclaration } from "../base-store/migrations.injectable"; +import type { MigrationDeclaration } from "../../../common/persistent-storage/migrations.injectable"; export const weblinkStoreMigrationInjectionToken = getInjectionToken({ id: "weblink-store-migration-token", diff --git a/packages/core/src/features/weblinks/common/remove.injectable.ts b/packages/core/src/features/weblinks/common/remove.injectable.ts new file mode 100644 index 0000000000..0928903561 --- /dev/null +++ b/packages/core/src/features/weblinks/common/remove.injectable.ts @@ -0,0 +1,20 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { action } from "mobx"; +import weblinksStateInjectable from "./state.injectable"; + +export type RemoveWeblink = (id: string) => void; + +const removeWeblinkInjectable = getInjectable({ + id: "remove-weblink", + instantiate: (di): RemoveWeblink => { + const state = di.inject(weblinksStateInjectable); + + return action((id) => state.delete(id)); + }, +}); + +export default removeWeblinkInjectable; diff --git a/packages/core/src/features/weblinks/common/state.injectable.ts b/packages/core/src/features/weblinks/common/state.injectable.ts new file mode 100644 index 0000000000..ab909050aa --- /dev/null +++ b/packages/core/src/features/weblinks/common/state.injectable.ts @@ -0,0 +1,14 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { observable } from "mobx"; +import type { WeblinkData } from "./storage.injectable"; + +const weblinksStateInjectable = getInjectable({ + id: "weblinks-state", + instantiate: () => observable.map(), +}); + +export default weblinksStateInjectable; diff --git a/packages/core/src/features/weblinks/common/storage.injectable.ts b/packages/core/src/features/weblinks/common/storage.injectable.ts new file mode 100644 index 0000000000..6b6c668b83 --- /dev/null +++ b/packages/core/src/features/weblinks/common/storage.injectable.ts @@ -0,0 +1,47 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { action, comparer, toJS } from "mobx"; +import createPersistentStorageInjectable from "../../../common/persistent-storage/create.injectable"; +import persistentStorageMigrationsInjectable from "../../../common/persistent-storage/migrations.injectable"; +import storeMigrationVersionInjectable from "../../../common/vars/store-migration-version.injectable"; +import { weblinkStoreMigrationInjectionToken } from "./migration-token"; +import weblinksStateInjectable from "./state.injectable"; + +export interface WeblinkData { + id: string; + name: string; + url: string; +} + +export interface WeblinkStoreModel { + weblinks: WeblinkData[]; +} + +const weblinksPersistentStorageInjectable = getInjectable({ + id: "weblinks-persistent-storage", + instantiate: (di) => { + const state = di.inject(weblinksStateInjectable); + const createPersistentStorage = di.inject(createPersistentStorageInjectable); + + return createPersistentStorage({ + configName: "lens-weblink-store", + accessPropertiesByDotNotation: false, // To make dots safe in cluster context names + syncOptions: { + equals: comparer.structural, + }, + projectVersion: di.inject(storeMigrationVersionInjectable), + migrations: di.inject(persistentStorageMigrationsInjectable, weblinkStoreMigrationInjectionToken), + fromStore: action(({ weblinks = [] }) => { + state.replace(weblinks.map(weblink => [weblink.id, weblink])); + }), + toJSON: () => ({ + weblinks: [...toJS(state).values()], + }), + }); + }, +}); + +export default weblinksPersistentStorageInjectable; diff --git a/packages/core/src/features/weblinks/common/weblinks.injectable.ts b/packages/core/src/features/weblinks/common/weblinks.injectable.ts new file mode 100644 index 0000000000..a4dca05caf --- /dev/null +++ b/packages/core/src/features/weblinks/common/weblinks.injectable.ts @@ -0,0 +1,18 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { computed } from "mobx"; +import weblinksStateInjectable from "./state.injectable"; + +const weblinksInjectable = getInjectable({ + id: "weblinks", + instantiate: (di) => { + const state = di.inject(weblinksStateInjectable); + + return computed(() => [...state.values()]); + }, +}); + +export default weblinksInjectable; diff --git a/packages/core/src/main/weblinks-store/migrations/5.1.4.injectable.ts b/packages/core/src/features/weblinks/main/5.1.4.injectable.ts similarity index 86% rename from packages/core/src/main/weblinks-store/migrations/5.1.4.injectable.ts rename to packages/core/src/features/weblinks/main/5.1.4.injectable.ts index 000937abba..db8eb1510b 100644 --- a/packages/core/src/main/weblinks-store/migrations/5.1.4.injectable.ts +++ b/packages/core/src/features/weblinks/main/5.1.4.injectable.ts @@ -4,10 +4,10 @@ */ import { docsUrl, forumsUrl } from "../../../common/vars"; -import type { WeblinkData } from "../../../common/weblinks-store/weblink-store"; import { getInjectable } from "@ogre-tools/injectable"; -import { weblinkStoreMigrationInjectionToken } from "../../../common/weblinks-store/migration-token"; -import * as links from "../links"; +import { weblinkStoreMigrationInjectionToken } from "../common/migration-token"; +import * as links from "./links"; +import type { WeblinkData } from "../common/storage.injectable"; const v514WeblinkStoreMigrationInjectable = getInjectable({ id: "v5.1.4-weblink-store-migration", diff --git a/packages/core/src/main/weblinks-store/migrations/5.4.5-beta.1.injectable.ts b/packages/core/src/features/weblinks/main/5.4.5-beta.1.injectable.ts similarity index 89% rename from packages/core/src/main/weblinks-store/migrations/5.4.5-beta.1.injectable.ts rename to packages/core/src/features/weblinks/main/5.4.5-beta.1.injectable.ts index fc67e4a2f5..01f252deb1 100644 --- a/packages/core/src/main/weblinks-store/migrations/5.4.5-beta.1.injectable.ts +++ b/packages/core/src/features/weblinks/main/5.4.5-beta.1.injectable.ts @@ -3,10 +3,10 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { WeblinkData } from "../../../common/weblinks-store/weblink-store"; -import * as links from "../links"; +import * as links from "../../../features/weblinks/main/links"; import { getInjectable } from "@ogre-tools/injectable"; -import { weblinkStoreMigrationInjectionToken } from "../../../common/weblinks-store/migration-token"; +import { weblinkStoreMigrationInjectionToken } from "../../../features/weblinks/common/migration-token"; +import type { WeblinkData } from "../common/storage.injectable"; const v545Beta1WeblinkStoreMigrationInjectable = getInjectable({ id: "v5.4.5-beta.1-weblink-store-migration", diff --git a/packages/core/src/main/weblinks-store/migrations/currentVersion.injectable.ts b/packages/core/src/features/weblinks/main/currentVersion.injectable.ts similarity index 87% rename from packages/core/src/main/weblinks-store/migrations/currentVersion.injectable.ts rename to packages/core/src/features/weblinks/main/currentVersion.injectable.ts index 4b873d78c4..dfb2946164 100644 --- a/packages/core/src/main/weblinks-store/migrations/currentVersion.injectable.ts +++ b/packages/core/src/features/weblinks/main/currentVersion.injectable.ts @@ -4,11 +4,11 @@ */ import { docsUrl, forumsUrl } from "../../../common/vars"; -import type { WeblinkData } from "../../../common/weblinks-store/weblink-store"; import { getInjectable } from "@ogre-tools/injectable"; -import { weblinkStoreMigrationInjectionToken } from "../../../common/weblinks-store/migration-token"; -import { lensDocumentationWeblinkId, lensForumsWeblinkId } from "../links"; +import { weblinkStoreMigrationInjectionToken } from "../../../features/weblinks/common/migration-token"; +import { lensDocumentationWeblinkId, lensForumsWeblinkId } from "../../../features/weblinks/main/links"; import { applicationInformationToken } from "@k8slens/application"; +import type { WeblinkData } from "../common/storage.injectable"; const currentVersionWeblinkStoreMigrationInjectable = getInjectable({ id: "current-version-weblink-store-migration", diff --git a/packages/core/src/main/weblinks-store/links.ts b/packages/core/src/features/weblinks/main/links.ts similarity index 100% rename from packages/core/src/main/weblinks-store/links.ts rename to packages/core/src/features/weblinks/main/links.ts diff --git a/packages/core/src/main/start-main-application/runnables/setup-syncing-of-weblinks.injectable.ts b/packages/core/src/features/weblinks/main/load-storage.injectable.ts similarity index 55% rename from packages/core/src/main/start-main-application/runnables/setup-syncing-of-weblinks.injectable.ts rename to packages/core/src/features/weblinks/main/load-storage.injectable.ts index 09a630db37..e309381bbd 100644 --- a/packages/core/src/main/start-main-application/runnables/setup-syncing-of-weblinks.injectable.ts +++ b/packages/core/src/features/weblinks/main/load-storage.injectable.ts @@ -2,22 +2,20 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ -import { getInjectable } from "@ogre-tools/injectable"; import { onLoadOfApplicationInjectionToken } from "@k8slens/application"; -import syncWeblinksInjectable from "../../catalog-sources/sync-weblinks.injectable"; - -const setupSyncingOfWeblinksInjectable = getInjectable({ - id: "setup-syncing-of-weblinks", +import { getInjectable } from "@ogre-tools/injectable"; +import weblinksPersistentStorageInjectable from "../common/storage.injectable"; +const loadWeblinkStorageInjectable = getInjectable({ + id: "load-weblink-storage", instantiate: (di) => ({ run: () => { - const syncWeblinks = di.inject(syncWeblinksInjectable); + const storage = di.inject(weblinksPersistentStorageInjectable); - syncWeblinks(); + storage.loadAndStartSyncing(); }, }), - injectionToken: onLoadOfApplicationInjectionToken, }); -export default setupSyncingOfWeblinksInjectable; +export default loadWeblinkStorageInjectable; diff --git a/packages/core/src/features/weblinks/main/setup-syncing-of-weblinks.injectable.ts b/packages/core/src/features/weblinks/main/setup-syncing-of-weblinks.injectable.ts new file mode 100644 index 0000000000..1f6b440904 --- /dev/null +++ b/packages/core/src/features/weblinks/main/setup-syncing-of-weblinks.injectable.ts @@ -0,0 +1,34 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { onLoadOfApplicationInjectionToken } from "@k8slens/application"; +import weblinkVerificationStartableStoppableInjectable from "./weblink-verification.injectable"; +import catalogEntityRegistryInjectable from "../../../main/catalog/entity-registry.injectable"; +import weblinkVerificationsInjectable from "./weblink-verifications.injectable"; +import { computed } from "mobx"; +import { iter } from "@k8slens/utilities"; + +const setupSyncingOfWeblinksInjectable = getInjectable({ + id: "setup-syncing-of-weblinks", + + instantiate: (di) => ({ + run: () => { + const weblinkVerificationStartableStoppable = di.inject(weblinkVerificationStartableStoppableInjectable); + const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable); + const weblinkVerifications = di.inject(weblinkVerificationsInjectable); + + weblinkVerificationStartableStoppable.start(); + catalogEntityRegistry.addComputedSource("weblinks", computed(() => ( + iter.chain(weblinkVerifications.values()) + .map(([weblink]) => weblink) + .toArray() + ))); + }, + }), + + injectionToken: onLoadOfApplicationInjectionToken, +}); + +export default setupSyncingOfWeblinksInjectable; diff --git a/packages/core/src/features/weblinks/main/stop-validating-weblinks.injectable.ts b/packages/core/src/features/weblinks/main/stop-validating-weblinks.injectable.ts new file mode 100644 index 0000000000..221a3ee0dd --- /dev/null +++ b/packages/core/src/features/weblinks/main/stop-validating-weblinks.injectable.ts @@ -0,0 +1,23 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { beforeQuitOfBackEndInjectionToken } from "../../../main/start-main-application/runnable-tokens/phases"; +import weblinkVerificationStartableStoppableInjectable from "./weblink-verification.injectable"; + +const stopValidatingWeblinksInjectable = getInjectable({ + id: "stop-validating-weblinks", + instantiate: (di) => ({ + run: () => { + const weblinkVerificationStartableStoppable = di.inject(weblinkVerificationStartableStoppableInjectable); + + weblinkVerificationStartableStoppable.stop(); + + return undefined; + }, + }), + injectionToken: beforeQuitOfBackEndInjectionToken, +}); + +export default stopValidatingWeblinksInjectable; diff --git a/packages/core/src/common/user-store/file-name-migration.global-override-for-injectable.ts b/packages/core/src/features/weblinks/main/validate-weblink.global-override-for-injectable.ts similarity index 53% rename from packages/core/src/common/user-store/file-name-migration.global-override-for-injectable.ts rename to packages/core/src/features/weblinks/main/validate-weblink.global-override-for-injectable.ts index 0cd1383cc6..8ec03bebaf 100644 --- a/packages/core/src/common/user-store/file-name-migration.global-override-for-injectable.ts +++ b/packages/core/src/features/weblinks/main/validate-weblink.global-override-for-injectable.ts @@ -4,6 +4,6 @@ */ import { getGlobalOverride } from "@k8slens/test-utils"; -import userStoreFileNameMigrationInjectable from "./file-name-migration.injectable"; +import validateWeblinkInjectable from "./validate-weblink.injectable"; -export default getGlobalOverride(userStoreFileNameMigrationInjectable, () => async () => {}); +export default getGlobalOverride(validateWeblinkInjectable, () => async () => "available"); diff --git a/packages/core/src/features/weblinks/main/validate-weblink.injectable.ts b/packages/core/src/features/weblinks/main/validate-weblink.injectable.ts new file mode 100644 index 0000000000..c38b483a95 --- /dev/null +++ b/packages/core/src/features/weblinks/main/validate-weblink.injectable.ts @@ -0,0 +1,42 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { chainSignal } from "@k8slens/utilities"; +import { getInjectable } from "@ogre-tools/injectable"; +import fetchInjectable from "../../../common/fetch/fetch.injectable"; +import { withTimeout } from "../../../common/fetch/timeout-controller"; + +export type ValidateWeblink = (url: string, signal: AbortSignal) => Promise<"available" | "unavailable">; + +const validateWeblinkInjectable = getInjectable({ + id: "validate-weblink", + instantiate: (di): ValidateWeblink => { + const fetch = di.inject(fetchInjectable); + + return async (url, signal) => { + const timeout = withTimeout(20_000); + + chainSignal(timeout, signal); + + try { + const res = await fetch(url, { + signal: timeout.signal, + }); + + if (res.status >= 200 && res.status < 500) { + return "available"; + } + } catch { + // ignore + } finally { + timeout.abort(); + } + + return "unavailable"; + }; + }, + causesSideEffects: true, +}); + +export default validateWeblinkInjectable; diff --git a/packages/core/src/features/weblinks/main/weblink-verification.injectable.ts b/packages/core/src/features/weblinks/main/weblink-verification.injectable.ts new file mode 100644 index 0000000000..991575de46 --- /dev/null +++ b/packages/core/src/features/weblinks/main/weblink-verification.injectable.ts @@ -0,0 +1,105 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getStartableStoppable } from "@k8slens/startable-stoppable"; +import type { Disposer } from "@k8slens/utilities"; +import { delay, disposer } from "@k8slens/utilities"; +import { getInjectable } from "@ogre-tools/injectable"; +import { random } from "lodash"; +import { reaction, runInAction } from "mobx"; +import { WebLink } from "../../../common/catalog-entities"; +import weblinksInjectable from "../common/weblinks.injectable"; +import validateWeblinkInjectable from "./validate-weblink.injectable"; +import weblinkVerificationsInjectable from "./weblink-verifications.injectable"; + +const sixtyMinutes = 60 * 60 * 1000; + +const weblinkVerificationStartableStoppableInjectable = getInjectable({ + id: "weblink-verification-startable-stoppable", + instantiate: (di) => { + const weblinkVerifications = di.inject(weblinkVerificationsInjectable); + const validateWeblink = di.inject(validateWeblinkInjectable); + const weblinks = di.inject(weblinksInjectable); + + const startPeriodicallyCheckingWebLink = (link: WebLink): Disposer => { + const controller = new AbortController(); + const dispose = disposer(() => controller.abort()); + + void (async () => { + for (;;) { + const newStatus = await validateWeblink(link.spec.url, controller.signal); + + runInAction(() => { + link.status.phase = newStatus; + }); + + const nextCheckAfter = sixtyMinutes + random(0, 5 * 60 * 1000, false); + + await delay(nextCheckAfter, controller.signal); + + if (controller.signal.aborted) { + return; + } + } + })(); + + return dispose; + }; + + return getStartableStoppable("weblink-verification", () => disposer( + reaction( + () => weblinks.get(), + (links) => { + const seenWeblinks = new Set(); + + for (const weblinkData of links) { + seenWeblinks.add(weblinkData.id); + + if (!weblinkVerifications.has(weblinkData.id)) { + const link = new WebLink({ + metadata: { + uid: weblinkData.id, + name: weblinkData.name, + source: "local", + labels: {}, + }, + spec: { + url: weblinkData.url, + }, + status: { + phase: "available", + active: true, + }, + }); + + weblinkVerifications.set(weblinkData.id, [ + link, + startPeriodicallyCheckingWebLink(link), + ]); + } + } + + // Stop checking and remove weblinks that are no longer in the store + for (const [weblinkId, [, disposer]] of weblinkVerifications) { + if (!seenWeblinks.has(weblinkId)) { + disposer(); + weblinkVerifications.delete(weblinkId); + } + } + }, + { + fireImmediately: true, + }, + ), + () => { + // Stop the validations + for (const [, [, disposer]] of weblinkVerifications) { + disposer(); + } + }, + )); + }, +}); + +export default weblinkVerificationStartableStoppableInjectable; diff --git a/packages/core/src/features/weblinks/main/weblink-verifications.injectable.ts b/packages/core/src/features/weblinks/main/weblink-verifications.injectable.ts new file mode 100644 index 0000000000..6e3f7d8f0f --- /dev/null +++ b/packages/core/src/features/weblinks/main/weblink-verifications.injectable.ts @@ -0,0 +1,15 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import type { Disposer } from "@k8slens/utilities"; +import { getInjectable } from "@ogre-tools/injectable"; +import { observable } from "mobx"; +import type { WebLink } from "../../../common/catalog-entities"; + +const weblinkVerificationsInjectable = getInjectable({ + id: "weblink-verifications", + instantiate: () => observable.map(), +}); + +export default weblinkVerificationsInjectable; diff --git a/packages/core/src/features/weblinks/renderer/load-storage.injectable.ts b/packages/core/src/features/weblinks/renderer/load-storage.injectable.ts new file mode 100644 index 0000000000..3cfacc3fa3 --- /dev/null +++ b/packages/core/src/features/weblinks/renderer/load-storage.injectable.ts @@ -0,0 +1,21 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { beforeFrameStartsSecondInjectionToken } from "../../../renderer/before-frame-starts/tokens"; +import weblinksPersistentStorageInjectable from "../common/storage.injectable"; + +const loadWeblinkStorageInjectable = getInjectable({ + id: "load-weblink-storage", + instantiate: (di) => ({ + run: () => { + const storage = di.inject(weblinksPersistentStorageInjectable); + + storage.loadAndStartSyncing(); + }, + }), + injectionToken: beforeFrameStartsSecondInjectionToken, +}); + +export default loadWeblinkStorageInjectable; diff --git a/packages/core/src/features/welcome/__snapshots__/navigation-using-application-menu.test.ts.snap b/packages/core/src/features/welcome/__snapshots__/navigation-using-application-menu.test.ts.snap index 4016a617c3..2ff50d8b1a 100644 --- a/packages/core/src/features/welcome/__snapshots__/navigation-using-application-menu.test.ts.snap +++ b/packages/core/src/features/welcome/__snapshots__/navigation-using-application-menu.test.ts.snap @@ -230,7 +230,7 @@ exports[`welcome - navigation using application menu renders 1`] = ` class="HotbarSelector" > { let cluster: Cluster; @@ -26,7 +28,7 @@ describe("create clusters", () => { beforeEach(() => { const di = getDiForUnitTesting(); - const clusterServerUrl = "https://192.168.64.3:8443"; + const writeJsonSync = di.inject(writeJsonSyncInjectable); di.override(directoryForUserDataInjectable, () => "some-directory-for-user-data"); di.override(directoryForTempInjectable, () => "some-directory-for-temp"); @@ -42,27 +44,45 @@ describe("create clusters", () => { setupPrometheus: jest.fn(), })); + writeJsonSync("/minikube-config.yml", { + apiVersion: "v1", + clusters: [{ + name: "minikube", + cluster: { + server: "https://192.168.64.3:8443", + }, + }], + "current-context": "minikube", + contexts: [{ + context: { + cluster: "minikube", + user: "minikube", + }, + name: "minikube", + }], + users: [{ + name: "minikube", + }], + kind: "Config", + preferences: {}, + }); + di.override(kubeconfigManagerInjectable, () => ({ ensurePath: async () => "/some-proxy-kubeconfig-file", } as Partial as KubeconfigManager)); jest.spyOn(Kubectl.prototype, "ensureKubectl").mockReturnValue(Promise.resolve(true)); - cluster = new Cluster({ + const addCluster = di.inject(addClusterInjectable); + + cluster = addCluster({ id: "foo", contextName: "minikube", - kubeConfigPath: "minikube-config.yml", - }, { - clusterServerUrl, + kubeConfigPath: "/minikube-config.yml", }); - clusterConnection = di.inject(clusterConnectionInjectable, cluster); }); - it("should be able to create a cluster from a cluster model and apiURL should be decoded", () => { - expect(cluster.apiUrl.get()).toBe("https://192.168.64.3:8443"); - }); - it("reconnect should not throw if contextHandler is missing", () => { expect(() => clusterConnection.reconnect()).not.toThrowError(); }); diff --git a/packages/core/src/main/__test__/kube-auth-proxy.test.ts b/packages/core/src/main/__test__/kube-auth-proxy.test.ts index dc4878f806..35de680e77 100644 --- a/packages/core/src/main/__test__/kube-auth-proxy.test.ts +++ b/packages/core/src/main/__test__/kube-auth-proxy.test.ts @@ -3,8 +3,6 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import waitUntilPortIsUsedInjectable from "../kube-auth-proxy/wait-until-port-is-used/wait-until-port-is-used.injectable"; -import { Cluster } from "../../common/cluster/cluster"; import type { ChildProcess } from "child_process"; import { Kubectl } from "../kubectl/kubectl"; import type { DeepMockProxy } from "jest-mock-extended"; @@ -12,7 +10,7 @@ import { mockDeep, mock } from "jest-mock-extended"; import type { Readable } from "stream"; import { EventEmitter } from "stream"; import { getDiForUnitTesting } from "../getDiForUnitTesting"; -import type { CreateKubeAuthProxy, KubeAuthProxy } from "../kube-auth-proxy/create-kube-auth-proxy.injectable"; +import type { KubeAuthProxy } from "../kube-auth-proxy/create-kube-auth-proxy.injectable"; import createKubeAuthProxyInjectable from "../kube-auth-proxy/create-kube-auth-proxy.injectable"; import spawnInjectable from "../child-process/spawn.injectable"; import directoryForUserDataInjectable from "../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; @@ -25,15 +23,17 @@ import writeJsonSyncInjectable from "../../common/fs/write-json-sync.injectable" import ensureDirInjectable from "../../common/fs/ensure-dir.injectable"; import type { GetBasenameOfPath } from "../../common/path/get-basename.injectable"; import getBasenameOfPathInjectable from "../../common/path/get-basename.injectable"; - -const clusterServerUrl = "https://192.168.64.3:8443"; +import type { Cluster } from "../../common/cluster/cluster"; +import waitUntilPortIsUsedInjectable from "../kube-auth-proxy/wait-until-port-is-used/wait-until-port-is-used.injectable"; +import addClusterInjectable from "../../features/cluster/storage/common/add.injectable"; describe("kube auth proxy tests", () => { - let createKubeAuthProxy: CreateKubeAuthProxy; let spawnMock: jest.Mock; let waitUntilPortIsUsedMock: jest.Mock; let broadcastMessageMock: jest.Mock; let getBasenameOfPath: GetBasenameOfPath; + let cluster: Cluster; + let kubeAuthProxy: KubeAuthProxy; beforeEach(async () => { const di = getDiForUnitTesting(); @@ -51,7 +51,7 @@ describe("kube auth proxy tests", () => { clusters: [{ name: "minikube", cluster: { - server: clusterServerUrl, + server: "https://192.168.64.3:8443", }, }], "current-context": "minikube", @@ -83,29 +83,25 @@ describe("kube auth proxy tests", () => { di.override(kubectlDownloadingNormalizedArchInjectable, () => "amd64"); di.override(normalizedPlatformInjectable, () => "darwin"); - createKubeAuthProxy = di.inject(createKubeAuthProxyInjectable); + const addCluster = di.inject(addClusterInjectable); + + cluster = addCluster({ + id: "foobar", + kubeConfigPath: "/minikube-config.yml", + contextName: "minikube", + }); + kubeAuthProxy = di.inject(createKubeAuthProxyInjectable, cluster)({}); }); it("calling exit multiple times shouldn't throw", async () => { - const cluster = new Cluster({ - id: "foobar", - kubeConfigPath: "minikube-config.yml", - contextName: "minikube", - }, { - clusterServerUrl, - }); - - const kap = createKubeAuthProxy(cluster, {}); - - kap.exit(); - kap.exit(); - kap.exit(); + kubeAuthProxy.exit(); + kubeAuthProxy.exit(); + kubeAuthProxy.exit(); }); describe("spawn tests", () => { let mockedCP: DeepMockProxy; let listeners: EventEmitter; - let proxy: KubeAuthProxy; beforeEach(async () => { mockedCP = mockDeep(); @@ -184,16 +180,7 @@ describe("kube auth proxy tests", () => { }); waitUntilPortIsUsedMock.mockReturnValueOnce(Promise.resolve()); - const cluster = new Cluster({ - id: "foobar", - kubeConfigPath: "minikube-config.yml", - contextName: "minikube", - }, { - clusterServerUrl, - }); - - proxy = createKubeAuthProxy(cluster, {}); - await proxy.run(); + await kubeAuthProxy.run(); }); it("should call spawn and broadcast errors", () => { diff --git a/packages/core/src/main/__test__/kubeconfig-manager.test.ts b/packages/core/src/main/__test__/kubeconfig-manager.test.ts index 1205ba4f8f..fb9b329b69 100644 --- a/packages/core/src/main/__test__/kubeconfig-manager.test.ts +++ b/packages/core/src/main/__test__/kubeconfig-manager.test.ts @@ -89,8 +89,6 @@ describe("kubeconfig manager tests", () => { id: "foo", contextName: "minikube", kubeConfigPath: "/minikube-config.yml", - }, { - clusterServerUrl, }); kubeConfManager = di.inject(kubeconfigManagerInjectable, clusterFake); diff --git a/packages/core/src/main/__test__/prometheus-handler.test.ts b/packages/core/src/main/__test__/prometheus-handler.test.ts index 74cab1ea74..df0e1b37c9 100644 --- a/packages/core/src/main/__test__/prometheus-handler.test.ts +++ b/packages/core/src/main/__test__/prometheus-handler.test.ts @@ -13,8 +13,8 @@ import { runInAction } from "mobx"; import prometheusHandlerInjectable from "../cluster/prometheus-handler/prometheus-handler.injectable"; import directoryForTempInjectable from "../../common/app-paths/directory-for-temp/directory-for-temp.injectable"; import lensProxyPortInjectable from "../lens-proxy/lens-proxy-port.injectable"; -import loadProxyKubeconfigInjectable from "../cluster/load-proxy-kubeconfig.injectable"; -import { KubeConfig } from "@kubernetes/client-node"; +import createKubeAuthProxyInjectable from "../kube-auth-proxy/create-kube-auth-proxy.injectable"; +import writeJsonFileInjectable from "../../common/fs/write-json-file.injectable"; enum ServiceResult { Success, @@ -47,37 +47,46 @@ describe("PrometheusHandler", () => { let di: DiContainer; let cluster: Cluster; - beforeEach(() => { + beforeEach(async () => { di = getDiForUnitTesting(); - di.override(loadProxyKubeconfigInjectable, (di, cluster) => async () => { - const res = new KubeConfig(); - - res.addCluster({ - name: "some-cluster-name", - server: cluster.apiUrl.get(), - skipTLSVerify: false, - }); - res.addContext({ - cluster: "some-cluster-name", - name: "some-context-name", - user: "some-user-name", - }); - res.addUser({ - name: "some-user-name", - }); - res.setCurrentContext("some-context-name"); - - return res; - }); + di.override(createKubeAuthProxyInjectable, () => () => ({ + apiPrefix: "/some-api-prefix", + exit: () => {}, + run: async () => {}, + port: 9191, + })); di.override(directoryForTempInjectable, () => "/some-temp-dir"); di.inject(lensProxyPortInjectable).set(12345); + const writeJsonFile = di.inject(writeJsonFileInjectable); + const kubeConfigPath = "/some/path-to-a-config"; + const contextName = "some-context-name"; + + await writeJsonFile(kubeConfigPath, { + apiVersion: "v1", + kind: "Config", + clusters: [{ + name: "some-cluster-name", + cluster: { + server: "https://localhost:8989", + }, + }], + users: [{ + name: "some-user-name", + }], + contexts: [{ + name: contextName, + context: { + user: "some-user-name", + cluster: "some-cluster-name", + }, + }], + }); + cluster = new Cluster({ - contextName: "some-context-name", + contextName, id: "some-cluster-id", - kubeConfigPath: "/some/path", - }, { - clusterServerUrl: "http://localhost:81", + kubeConfigPath, }); }); diff --git a/packages/core/src/main/base-store/disable-sync-in-ipc-listener.injectable.ts b/packages/core/src/main/base-store/disable-sync-in-ipc-listener.injectable.ts index 2e7af0c723..10c1528c57 100644 --- a/packages/core/src/main/base-store/disable-sync-in-ipc-listener.injectable.ts +++ b/packages/core/src/main/base-store/disable-sync-in-ipc-listener.injectable.ts @@ -3,12 +3,12 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldBaseStoreDisableSyncInIpcListenerInjectionToken } from "../../common/base-store/disable-sync"; +import { shouldPersistentStorageDisableSyncInIpcListenerInjectionToken } from "../../common/persistent-storage/disable-sync"; const shouldBaseStoreDisableSyncInIpcListenerInjectable = getInjectable({ id: "should-base-store-disable-sync-in-ipc-listener", instantiate: () => false, - injectionToken: shouldBaseStoreDisableSyncInIpcListenerInjectionToken, + injectionToken: shouldPersistentStorageDisableSyncInIpcListenerInjectionToken, }); export default shouldBaseStoreDisableSyncInIpcListenerInjectable; diff --git a/packages/core/src/main/base-store/ipc-channel-prefix.injectable.ts b/packages/core/src/main/base-store/ipc-channel-prefix.injectable.ts index 635f857fc4..a0a96ce3af 100644 --- a/packages/core/src/main/base-store/ipc-channel-prefix.injectable.ts +++ b/packages/core/src/main/base-store/ipc-channel-prefix.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { baseStoreIpcChannelPrefixesInjectionToken } from "../../common/base-store/channel-prefix"; +import { persistentStorageIpcChannelPrefixesInjectionToken } from "../../common/persistent-storage/channel-prefix"; const baseStoreIpcChannelPrefixInjectable = getInjectable({ id: "base-store-ipc-channel-prefix", @@ -11,7 +11,7 @@ const baseStoreIpcChannelPrefixInjectable = getInjectable({ local: "store-sync-main", remote: "store-sync-renderer", }), - injectionToken: baseStoreIpcChannelPrefixesInjectionToken, + injectionToken: persistentStorageIpcChannelPrefixesInjectionToken, }); export default baseStoreIpcChannelPrefixInjectable; diff --git a/packages/core/src/main/base-store/persist-state-to-config.injectable.ts b/packages/core/src/main/base-store/persist-state-to-config.injectable.ts index f6d28982ec..7869bbd6c5 100644 --- a/packages/core/src/main/base-store/persist-state-to-config.injectable.ts +++ b/packages/core/src/main/base-store/persist-state-to-config.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { persistStateToConfigInjectionToken } from "../../common/base-store/save-to-file"; +import { persistStateToConfigInjectionToken } from "../../common/persistent-storage/save-to-file"; import loggerInjectable from "../../common/logger.injectable"; const persistStateToConfigInjectable = getInjectable({ diff --git a/packages/core/src/main/catalog-sources/__test__/kubeconfig-sync.test.ts b/packages/core/src/main/catalog-sources/__test__/kubeconfig-sync.test.ts index 42ffbedf30..6cc651f7e5 100644 --- a/packages/core/src/main/catalog-sources/__test__/kubeconfig-sync.test.ts +++ b/packages/core/src/main/catalog-sources/__test__/kubeconfig-sync.test.ts @@ -17,9 +17,6 @@ import type { ConfigToModels } from "../kubeconfig-sync/config-to-models.injecta import configToModelsInjectable from "../kubeconfig-sync/config-to-models.injectable"; import kubeconfigSyncManagerInjectable from "../kubeconfig-sync/manager.injectable"; import type { KubeconfigSyncManager } from "../kubeconfig-sync/manager"; -import type { KubeconfigSyncValue } from "../../../common/user-store"; -import kubeconfigSyncsInjectable from "../../../common/user-store/kubeconfig-syncs.injectable"; -import getClusterByIdInjectable from "../../../common/cluster-store/get-by-id.injectable"; import type { DiContainer } from "@ogre-tools/injectable"; import type { AsyncFnMock } from "@async-fn/jest"; import type { Stat } from "../../../common/fs/stat.injectable"; @@ -36,12 +33,13 @@ import readJsonSyncInjectable from "../../../common/fs/read-json-sync.injectable import writeJsonSyncInjectable from "../../../common/fs/write-json-sync.injectable"; import type { KubeconfigManager } from "../../kubeconfig-manager/kubeconfig-manager"; import kubeconfigManagerInjectable from "../../kubeconfig-manager/kubeconfig-manager.injectable"; +import type { KubeconfigSyncValue } from "../../../features/user-preferences/common/preferences-helpers"; +import kubeconfigSyncsInjectable from "../../../features/user-preferences/common/kubeconfig-syncs.injectable"; describe("kubeconfig-sync.source tests", () => { let computeKubeconfigDiff: ComputeKubeconfigDiff; let configToModels: ConfigToModels; let kubeconfigSyncs: ObservableMap; - let clusters: Map; let di: DiContainer; beforeEach(async () => { @@ -58,9 +56,6 @@ describe("kubeconfig-sync.source tests", () => { ensurePath: async () => "/some-proxy-kubeconfig-file", } as Partial as KubeconfigManager)); - clusters = new Map(); - di.override(getClusterByIdInjectable, () => id => clusters.get(id)); - kubeconfigSyncs = observable.map(); di.override(kubeconfigSyncsInjectable, () => kubeconfigSyncs); @@ -102,8 +97,8 @@ describe("kubeconfig-sync.source tests", () => { const models = configToModels(config, "/bar"); expect(models.length).toBe(1); - expect(models[0][0].contextName).toBe("context-name"); - expect(models[0][0].kubeConfigPath).toBe("/bar"); + expect(models[0].contextName).toBe("context-name"); + expect(models[0].kubeConfigPath).toBe("/bar"); }); }); diff --git a/packages/core/src/main/catalog-sources/index.ts b/packages/core/src/main/catalog-sources/index.ts deleted file mode 100644 index 98c3f08536..0000000000 --- a/packages/core/src/main/catalog-sources/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -export { syncWeblinks } from "./weblinks"; diff --git a/packages/core/src/main/catalog-sources/kubeconfig-sync/compute-diff.injectable.ts b/packages/core/src/main/catalog-sources/kubeconfig-sync/compute-diff.injectable.ts index 3b81e82382..d1e8f0daab 100644 --- a/packages/core/src/main/catalog-sources/kubeconfig-sync/compute-diff.injectable.ts +++ b/packages/core/src/main/catalog-sources/kubeconfig-sync/compute-diff.injectable.ts @@ -9,7 +9,6 @@ import { action } from "mobx"; import { homedir } from "os"; import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable"; import type { CatalogEntity } from "../../../common/catalog"; -import getClusterByIdInjectable from "../../../common/cluster-store/get-by-id.injectable"; import { Cluster } from "../../../common/cluster/cluster"; import { loadConfigFromString } from "../../../common/kube-helpers"; import clustersThatAreBeingDeletedInjectable from "../../cluster/are-being-deleted.injectable"; @@ -17,6 +16,7 @@ import { catalogEntityFromCluster } from "../../cluster/manager"; import configToModelsInjectable from "./config-to-models.injectable"; import kubeconfigSyncLoggerInjectable from "./logger.injectable"; import clusterConnectionInjectable from "../../cluster/cluster-connection.injectable"; +import getClusterByIdInjectable from "../../../features/cluster/storage/common/get-by-id.injectable"; export type ComputeKubeconfigDiff = (contents: string, source: ObservableMap, filePath: string) => void; @@ -38,15 +38,15 @@ const computeKubeconfigDiffInjectable = getInjectable({ } const rawModels = configToModels(config, filePath); - const models = new Map(rawModels.map(([model, configData]) => [model.contextName, [model, configData] as const])); + const models = new Map(rawModels.map((model) => [model.contextName, model])); logger.debug(`File now has ${models.size} entries`, { filePath }); for (const [contextName, value] of source) { - const data = models.get(contextName); + const model = models.get(contextName); // remove and disconnect clusters that were removed from the config - if (!data) { + if (!model) { // remove from the deleting set, so that if a new context of the same name is added, it isn't marked as deleting clustersThatAreBeingDeleted.delete(value[0].id); @@ -63,21 +63,16 @@ const computeKubeconfigDiffInjectable = getInjectable({ // diff against that // or update the model and mark it as not needed to be added - value[0].updateModel(data[0]); + value[0].updateModel(model); models.delete(contextName); logger.debug(`Updated old cluster from sync`, { filePath, contextName }); } - for (const [contextName, [model, configData]] of models) { + for (const [contextName, model] of models) { // add new clusters to the source try { const clusterId = createHash("md5").update(`${filePath}:${contextName}`).digest("hex"); - const cluster = getClusterById(clusterId) ?? new Cluster({ ...model, id: clusterId }, configData); - - if (!cluster.apiUrl.get()) { - throw new Error("Cluster constructor failed, see above error"); - } - + const cluster = getClusterById(clusterId) ?? new Cluster({ ...model, id: clusterId }); const entity = catalogEntityFromCluster(cluster); if (!filePath.startsWith(directoryForKubeConfigs)) { diff --git a/packages/core/src/main/catalog-sources/kubeconfig-sync/config-to-models.injectable.ts b/packages/core/src/main/catalog-sources/kubeconfig-sync/config-to-models.injectable.ts index 8240d4e914..6baad196cf 100644 --- a/packages/core/src/main/catalog-sources/kubeconfig-sync/config-to-models.injectable.ts +++ b/packages/core/src/main/catalog-sources/kubeconfig-sync/config-to-models.injectable.ts @@ -4,11 +4,11 @@ */ import type { KubeConfig } from "@kubernetes/client-node"; import { getInjectable } from "@ogre-tools/injectable"; -import type { ClusterConfigData, UpdateClusterModel } from "../../../common/cluster-types"; +import type { UpdateClusterModel } from "../../../common/cluster-types"; import { splitConfig } from "../../../common/kube-helpers"; import kubeconfigSyncLoggerInjectable from "./logger.injectable"; -export type ConfigToModels = (rootConfig: KubeConfig, filePath: string) => [UpdateClusterModel, ClusterConfigData][]; +export type ConfigToModels = (rootConfig: KubeConfig, filePath: string) => UpdateClusterModel[]; const configToModelsInjectable = getInjectable({ id: "config-to-models", @@ -22,15 +22,10 @@ const configToModelsInjectable = getInjectable({ if (validationResult.error) { logger.debug(`context failed validation: ${validationResult.error}`, { context: config.currentContext, filePath }); } else { - validConfigs.push([ - { - kubeConfigPath: filePath, - contextName: config.currentContext, - }, - { - clusterServerUrl: validationResult.cluster.server, - }, - ]); + validConfigs.push({ + kubeConfigPath: filePath, + contextName: config.currentContext, + }); } } diff --git a/packages/core/src/main/catalog-sources/kubeconfig-sync/manager.injectable.ts b/packages/core/src/main/catalog-sources/kubeconfig-sync/manager.injectable.ts index 06265764c8..9160df83da 100644 --- a/packages/core/src/main/catalog-sources/kubeconfig-sync/manager.injectable.ts +++ b/packages/core/src/main/catalog-sources/kubeconfig-sync/manager.injectable.ts @@ -7,7 +7,7 @@ import directoryForKubeConfigsInjectable from "../../../common/app-paths/directo import { KubeconfigSyncManager } from "./manager"; import kubeconfigSyncLoggerInjectable from "./logger.injectable"; import watchKubeconfigFileChangesInjectable from "./watch-file-changes.injectable"; -import kubeconfigSyncsInjectable from "../../../common/user-store/kubeconfig-syncs.injectable"; +import kubeconfigSyncsInjectable from "../../../features/user-preferences/common/kubeconfig-syncs.injectable"; const kubeconfigSyncManagerInjectable = getInjectable({ id: "kubeconfig-sync-manager", diff --git a/packages/core/src/main/catalog-sources/kubeconfig-sync/manager.ts b/packages/core/src/main/catalog-sources/kubeconfig-sync/manager.ts index 77ef96b765..c7b53d8973 100644 --- a/packages/core/src/main/catalog-sources/kubeconfig-sync/manager.ts +++ b/packages/core/src/main/catalog-sources/kubeconfig-sync/manager.ts @@ -8,9 +8,9 @@ import { action, observable, computed, makeObservable, observe } from "mobx"; import type { CatalogEntity } from "../../../common/catalog"; import type { Disposer } from "@k8slens/utilities"; import { iter } from "@k8slens/utilities"; -import type { KubeconfigSyncValue } from "../../../common/user-store"; import type { Logger } from "../../../common/logger"; import type { WatchKubeconfigFileChanges } from "./watch-file-changes.injectable"; +import type { KubeconfigSyncValue } from "../../../features/user-preferences/common/preferences-helpers"; interface KubeconfigSyncManagerDependencies { readonly directoryForKubeConfigs: string; diff --git a/packages/core/src/main/catalog-sources/sync-weblinks.injectable.ts b/packages/core/src/main/catalog-sources/sync-weblinks.injectable.ts deleted file mode 100644 index f75de34a9d..0000000000 --- a/packages/core/src/main/catalog-sources/sync-weblinks.injectable.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import { syncWeblinks } from "./weblinks"; -import weblinkStoreInjectable from "../../common/weblinks-store/weblink-store.injectable"; -import catalogEntityRegistryInjectable from "../catalog/entity-registry.injectable"; - -const syncWeblinksInjectable = getInjectable({ - id: "sync-weblinks", - - instantiate: (di) => syncWeblinks({ - weblinkStore: di.inject(weblinkStoreInjectable), - catalogEntityRegistry: di.inject(catalogEntityRegistryInjectable), - }), -}); - -export default syncWeblinksInjectable; diff --git a/packages/core/src/main/catalog-sources/weblinks.ts b/packages/core/src/main/catalog-sources/weblinks.ts deleted file mode 100644 index 2ff2b645a1..0000000000 --- a/packages/core/src/main/catalog-sources/weblinks.ts +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { computed, observable, reaction } from "mobx"; -import type { WeblinkStore } from "../../common/weblinks-store/weblink-store"; -import { WebLink } from "../../common/catalog-entities"; -import type { CatalogEntityRegistry } from "../catalog"; -import got from "got"; -import type { Disposer } from "@k8slens/utilities"; -import { random } from "lodash"; - -async function validateLink(link: WebLink) { - try { - const response = await got.get(link.spec.url, { - throwHttpErrors: false, - timeout: 20_000, - }); - - if (response.statusCode >= 200 && response.statusCode < 500) { - link.status.phase = "available"; - } else { - link.status.phase = "unavailable"; - } - } catch { - link.status.phase = "unavailable"; - } -} - -interface Dependencies { - weblinkStore: WeblinkStore; - catalogEntityRegistry: CatalogEntityRegistry; -} - -export const syncWeblinks = ({ weblinkStore, catalogEntityRegistry }: Dependencies) => () => { - const webLinkEntities = observable.map(); - - function periodicallyCheckLink(link: WebLink): Disposer { - validateLink(link); - - let interval: NodeJS.Timeout; - const timeout = setTimeout(() => { - interval = setInterval(() => validateLink(link), 60 * 60 * 1000); // every 60 minutes - }, random(0, 5 * 60 * 1000, false)); // spread out over 5 minutes - - return () => { - clearTimeout(timeout); - clearInterval(interval); - }; - } - - reaction(() => weblinkStore.weblinks, (links) => { - const seenWeblinks = new Set(); - - for (const weblinkData of links) { - seenWeblinks.add(weblinkData.id); - - if (!webLinkEntities.has(weblinkData.id)) { - const link = new WebLink({ - metadata: { - uid: weblinkData.id, - name: weblinkData.name, - source: "local", - labels: {}, - }, - spec: { - url: weblinkData.url, - }, - status: { - phase: "available", - active: true, - }, - }); - - webLinkEntities.set(weblinkData.id, [ - link, - periodicallyCheckLink(link), - ]); - } - } - - // Stop checking and remove weblinks that are no longer in the store - for (const [weblinkId, [, disposer]] of webLinkEntities) { - if (!seenWeblinks.has(weblinkId)) { - disposer(); - webLinkEntities.delete(weblinkId); - } - } - }, { fireImmediately: true }); - - catalogEntityRegistry.addComputedSource("weblinks", computed(() => Array.from(webLinkEntities.values(), ([link]) => link))); -}; diff --git a/packages/core/src/main/cluster-detectors/cluster-distribution-detector.injectable.ts b/packages/core/src/main/cluster-detectors/cluster-distribution-detector.injectable.ts index 44f1c88102..673cb3cf8d 100644 --- a/packages/core/src/main/cluster-detectors/cluster-distribution-detector.injectable.ts +++ b/packages/core/src/main/cluster-detectors/cluster-distribution-detector.injectable.ts @@ -9,22 +9,24 @@ import { getInjectable } from "@ogre-tools/injectable"; import k8SRequestInjectable from "../k8s-request.injectable"; import type { Cluster } from "../../common/cluster/cluster"; import requestClusterVersionInjectable from "./request-cluster-version.injectable"; +import type { URL } from "url"; +import clusterApiUrlInjectable from "../../features/cluster/connections/main/api-url.injectable"; const isGKE = (version: string) => version.includes("gke"); const isEKS = (version: string) => version.includes("eks"); const isIKS = (version: string) => version.includes("IKS"); -const isAKS = (cluster: Cluster) => cluster.apiUrl.get().includes("azmk8s.io"); +const isAKS = (apiUrl: URL) => apiUrl.hostname.includes("azmk8s.io"); const isMirantis = (version: string) => version.includes("-mirantis-") || version.includes("-docker-"); -const isDigitalOcean = (cluster: Cluster) => cluster.apiUrl.get().endsWith("k8s.ondigitalocean.com"); -const isMinikube = (cluster: Cluster) => cluster.contextName.get().startsWith("minikube"); -const isMicrok8s = (cluster: Cluster) => cluster.contextName.get().startsWith("microk8s"); -const isKind = (cluster: Cluster) => cluster.contextName.get().startsWith("kubernetes-admin@kind-"); -const isDockerDesktop = (cluster: Cluster) => cluster.contextName.get() === "docker-desktop"; +const isDigitalOcean = (apiUrl: URL) => apiUrl.hostname.endsWith("k8s.ondigitalocean.com"); +const isMinikube = (contextName: string) => contextName.startsWith("minikube"); +const isMicrok8s = (contextName: string) => contextName.startsWith("microk8s"); +const isKind = (contextName: string) => contextName.startsWith("kubernetes-admin@kind-"); +const isDockerDesktop = (contextName: string) => contextName === "docker-desktop"; const isTke = (version: string) => version.includes("-tke."); const isCustom = (version: string) => version.includes("+"); const isVMWare = (version: string) => version.includes("+vmware"); const isRke = (version: string) => version.includes("-rancher"); -const isRancherDesktop = (cluster: Cluster) => cluster.contextName.get() === "rancher-desktop"; +const isRancherDesktop = (contextName: string) => contextName === "rancher-desktop"; const isK3s = (version: string) => version.includes("+k3s"); const isK0s = (version: string) => version.includes("-k0s") || version.includes("+k0s"); const isAlibaba = (version: string) => version.includes("-aliyun"); @@ -49,12 +51,14 @@ const clusterDistributionDetectorInjectable = getInjectable({ key: ClusterMetadataKey.DISTRIBUTION, detect: async (cluster) => { const version = await requestClusterVersion(cluster); + const apiUrl = await di.inject(clusterApiUrlInjectable, cluster)(); + const contextName = cluster.contextName.get(); if (isRke(version)) { return { value: "rke", accuracy: 80 }; } - if (isRancherDesktop(cluster)) { + if (isRancherDesktop(contextName)) { return { value: "rancher-desktop", accuracy: 80 }; } @@ -74,11 +78,11 @@ const clusterDistributionDetectorInjectable = getInjectable({ return { value: "iks", accuracy: 80 }; } - if (isAKS(cluster)) { + if (isAKS(apiUrl)) { return { value: "aks", accuracy: 80 }; } - if (isDigitalOcean(cluster)) { + if (isDigitalOcean(apiUrl)) { return { value: "digitalocean", accuracy: 90 }; } @@ -106,19 +110,19 @@ const clusterDistributionDetectorInjectable = getInjectable({ return { value: "tencent", accuracy: 90 }; } - if (isMinikube(cluster)) { + if (isMinikube(contextName)) { return { value: "minikube", accuracy: 80 }; } - if (isMicrok8s(cluster)) { + if (isMicrok8s(contextName)) { return { value: "microk8s", accuracy: 80 }; } - if (isKind(cluster)) { + if (isKind(contextName)) { return { value: "kind", accuracy: 70 }; } - if (isDockerDesktop(cluster)) { + if (isDockerDesktop(contextName)) { return { value: "docker-desktop", accuracy: 80 }; } diff --git a/packages/core/src/main/cluster-detectors/cluster-id-detector.injectable.ts b/packages/core/src/main/cluster-detectors/cluster-id-detector.injectable.ts index 6699986805..51b65f0d90 100644 --- a/packages/core/src/main/cluster-detectors/cluster-id-detector.injectable.ts +++ b/packages/core/src/main/cluster-detectors/cluster-id-detector.injectable.ts @@ -9,6 +9,7 @@ import { ClusterMetadataKey } from "../../common/cluster-types"; import { getInjectable } from "@ogre-tools/injectable"; import k8SRequestInjectable from "../k8s-request.injectable"; import type { Cluster } from "../../common/cluster/cluster"; +import clusterApiUrlInjectable from "../../features/cluster/connections/main/api-url.injectable"; const clusterIdDetectorFactoryInjectable = getInjectable({ id: "cluster-id-detector-factory", @@ -28,7 +29,7 @@ const clusterIdDetectorFactoryInjectable = getInjectable({ try { id = await getDefaultNamespaceId(cluster); } catch(_) { - id = cluster.apiUrl.get(); + id = (await di.inject(clusterApiUrlInjectable, cluster)()).toString(); } const value = createHash("sha256").update(id).digest("hex"); diff --git a/packages/core/src/main/cluster-detectors/detect-cluster-metadata.test.ts b/packages/core/src/main/cluster-detectors/detect-cluster-metadata.test.ts index 9582b386c1..833b468738 100644 --- a/packages/core/src/main/cluster-detectors/detect-cluster-metadata.test.ts +++ b/packages/core/src/main/cluster-detectors/detect-cluster-metadata.test.ts @@ -67,8 +67,6 @@ describe("detect-cluster-metadata", () => { id: "some-id", contextName: "some-context", kubeConfigPath: "minikube-config.yml", - }, { - clusterServerUrl: "foo", }); }); diff --git a/packages/core/src/main/cluster/kube-auth-proxy-server.injectable.ts b/packages/core/src/main/cluster/kube-auth-proxy-server.injectable.ts index ff70af3a7b..df14cf3f93 100644 --- a/packages/core/src/main/cluster/kube-auth-proxy-server.injectable.ts +++ b/packages/core/src/main/cluster/kube-auth-proxy-server.injectable.ts @@ -9,6 +9,7 @@ import type { Cluster } from "../../common/cluster/cluster"; import createKubeAuthProxyInjectable from "../kube-auth-proxy/create-kube-auth-proxy.injectable"; import kubeAuthProxyCertificateInjectable from "../kube-auth-proxy/kube-auth-proxy-certificate.injectable"; import type { KubeAuthProxy } from "../kube-auth-proxy/create-kube-auth-proxy.injectable"; +import clusterApiUrlInjectable from "../../features/cluster/connections/main/api-url.injectable"; export interface KubeAuthProxyServer { getApiTarget(isLongRunningRequest?: boolean): Promise; @@ -24,23 +25,23 @@ const thirtySecondsInMs = 30 * 1000; const kubeAuthProxyServerInjectable = getInjectable({ id: "kube-auth-proxy-server", instantiate: (di, cluster): KubeAuthProxyServer => { - const clusterUrl = new URL(cluster.apiUrl.get()); - - const createKubeAuthProxy = di.inject(createKubeAuthProxyInjectable); - const certificate = di.inject(kubeAuthProxyCertificateInjectable, clusterUrl.hostname); + const clusterApiUrl = di.inject(clusterApiUrlInjectable, cluster); + const createKubeAuthProxy = di.inject(createKubeAuthProxyInjectable, cluster); let kubeAuthProxy: KubeAuthProxy | undefined = undefined; let apiTarget: ServerOptions | undefined = undefined; const ensureServerHelper = async (): Promise => { if (!kubeAuthProxy) { - const proxyEnv = Object.assign({}, process.env); + const proxyEnv = { + ...process.env, + }; if (cluster.preferences.httpsProxy) { proxyEnv.HTTPS_PROXY = cluster.preferences.httpsProxy; } - kubeAuthProxy = createKubeAuthProxy(cluster, proxyEnv); + kubeAuthProxy = createKubeAuthProxy(proxyEnv); } await kubeAuthProxy.run(); @@ -49,31 +50,24 @@ const kubeAuthProxyServerInjectable = getInjectable({ }; const newApiTarget = async (timeout: number): Promise => { - const kubeAuthProxy = await ensureServerHelper(); - const headers: Record = {}; - - if (clusterUrl.hostname) { - headers.Host = clusterUrl.hostname; - - // fix current IPv6 inconsistency in url.Parse() and httpProxy. - // with url.Parse the IPv6 Hostname has no Square brackets but httpProxy needs the Square brackets to work. - if (headers.Host.includes(":")) { - headers.Host = `[${headers.Host}]`; - } - } + const { hostname } = await clusterApiUrl(); + const certificate = di.inject(kubeAuthProxyCertificateInjectable, hostname); + const { port, apiPrefix: path } = await ensureServerHelper(); return { target: { protocol: "https:", host: "127.0.0.1", - port: kubeAuthProxy.port, - path: kubeAuthProxy.apiPrefix, + port, + path, ca: certificate.cert, }, changeOrigin: true, timeout, secure: true, - headers, + headers: { + Host: hostname, + }, }; }; diff --git a/packages/core/src/main/cluster/manager.injectable.ts b/packages/core/src/main/cluster/manager.injectable.ts index e1567e51b6..21530dfe4d 100644 --- a/packages/core/src/main/cluster/manager.injectable.ts +++ b/packages/core/src/main/cluster/manager.injectable.ts @@ -3,8 +3,10 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import clusterStoreInjectable from "../../common/cluster-store/cluster-store.injectable"; import loggerInjectable from "../../common/logger.injectable"; +import addClusterInjectable from "../../features/cluster/storage/common/add.injectable"; +import clustersInjectable from "../../features/cluster/storage/common/clusters.injectable"; +import getClusterByIdInjectable from "../../features/cluster/storage/common/get-by-id.injectable"; import catalogEntityRegistryInjectable from "../catalog/entity-registry.injectable"; import clustersThatAreBeingDeletedInjectable from "./are-being-deleted.injectable"; import clusterConnectionInjectable from "./cluster-connection.injectable"; @@ -17,11 +19,13 @@ const clusterManagerInjectable = getInjectable({ id: "cluster-manager", instantiate: (di) => new ClusterManager({ - store: di.inject(clusterStoreInjectable), catalogEntityRegistry: di.inject(catalogEntityRegistryInjectable), clustersThatAreBeingDeleted: di.inject(clustersThatAreBeingDeletedInjectable), visibleCluster: di.inject(visibleClusterInjectable), logger: di.inject(loggerInjectable), + addCluster: di.inject(addClusterInjectable), + clusters: di.inject(clustersInjectable), + getClusterById: di.inject(getClusterByIdInjectable), updateEntityMetadata: di.inject(updateEntityMetadataInjectable), updateEntitySpec: di.inject(updateEntitySpecInjectable), getClusterConnection: (cluster) => di.inject(clusterConnectionInjectable, cluster), diff --git a/packages/core/src/main/cluster/manager.ts b/packages/core/src/main/cluster/manager.ts index bc270b79f1..800f981372 100644 --- a/packages/core/src/main/cluster/manager.ts +++ b/packages/core/src/main/cluster/manager.ts @@ -4,34 +4,36 @@ */ import "../../common/ipc/cluster"; -import type { IObservableValue, ObservableSet } from "mobx"; +import type { IComputedValue, IObservableValue, ObservableSet } from "mobx"; import { action, makeObservable, observe, reaction, toJS } from "mobx"; import type { Cluster } from "../../common/cluster/cluster"; -import { isErrnoException } from "@k8slens/utilities"; import { isKubernetesCluster, KubernetesCluster, LensKubernetesClusterStatus } from "../../common/catalog-entities/kubernetes-cluster"; import { ipcMainOn } from "../../common/ipc"; import { once } from "lodash"; -import type { ClusterStore } from "../../common/cluster-store/cluster-store"; import type { ClusterId } from "../../common/cluster-types"; import type { CatalogEntityRegistry } from "../catalog"; import type { Logger } from "../../common/logger"; import type { UpdateEntityMetadata } from "./update-entity-metadata.injectable"; import type { UpdateEntitySpec } from "./update-entity-spec.injectable"; import type { ClusterConnection } from "./cluster-connection.injectable"; +import type { GetClusterById } from "../../features/cluster/storage/common/get-by-id.injectable"; +import type { AddCluster } from "../../features/cluster/storage/common/add.injectable"; const logPrefix = "[CLUSTER-MANAGER]:"; const lensSpecificClusterStatuses: Set = new Set(Object.values(LensKubernetesClusterStatus)); interface Dependencies { - readonly store: ClusterStore; readonly catalogEntityRegistry: CatalogEntityRegistry; readonly clustersThatAreBeingDeleted: ObservableSet; readonly visibleCluster: IObservableValue; readonly logger: Logger; + readonly clusters: IComputedValue; updateEntityMetadata: UpdateEntityMetadata; updateEntitySpec: UpdateEntitySpec; getClusterConnection: (cluster: Cluster) => ClusterConnection; + getClusterById: GetClusterById; + addCluster: AddCluster; } export class ClusterManager { @@ -42,15 +44,15 @@ export class ClusterManager { init = once(() => { // reacting to every cluster's state change and total amount of items reaction( - () => this.dependencies.store.clustersList.map(c => c.getState()), - () => this.updateCatalog(this.dependencies.store.clustersList), + () => this.dependencies.clusters.get().map(c => c.getState()), + () => this.updateCatalog(this.dependencies.clusters.get()), { fireImmediately: false }, ); // reacting to every cluster's preferences change and total amount of items reaction( - () => this.dependencies.store.clustersList.map(c => toJS(c.preferences)), - () => this.updateCatalog(this.dependencies.store.clustersList), + () => this.dependencies.clusters.get().map(c => toJS(c.preferences)), + () => this.updateCatalog(this.dependencies.clusters.get()), { fireImmediately: false }, ); @@ -152,29 +154,15 @@ export class ClusterManager { @action protected syncClustersFromCatalog(entities: KubernetesCluster[]) { for (const entity of entities) { - const cluster = this.dependencies.store.getById(entity.getId()); + const cluster = this.dependencies.getClusterById(entity.getId()); if (!cluster) { - const model = { + this.dependencies.addCluster({ id: entity.getId(), kubeConfigPath: entity.spec.kubeconfigPath, contextName: entity.spec.kubeconfigContext, accessibleNamespaces: entity.spec.accessibleNamespaces ?? [], - }; - - try { - /** - * Add the bare minimum of data to ClusterStore. And especially no - * preferences, as those might be configured by the entity's source - */ - this.dependencies.store.addCluster(model); - } catch (error) { - if (isErrnoException(error) && error.code === "ENOENT" && error.path === entity.spec.kubeconfigPath) { - this.dependencies.logger.warn(`${logPrefix} kubeconfig file disappeared`, model); - } else { - this.dependencies.logger.error(`${logPrefix} failed to add cluster: ${error}`, model); - } - } + }); } else { cluster.kubeConfigPath.set(entity.spec.kubeconfigPath); cluster.contextName.set(entity.spec.kubeconfigContext); @@ -208,7 +196,9 @@ export class ClusterManager { this.dependencies.logger.info(`${logPrefix} network is offline`); await Promise.allSettled( - this.dependencies.store.clustersList + this.dependencies + .clusters + .get() .filter(cluster => !cluster.disconnected.get()) .map(async (cluster) => { cluster.online.set(false); @@ -225,7 +215,9 @@ export class ClusterManager { this.dependencies.logger.info(`${logPrefix} network is online`); await Promise.allSettled( - this.dependencies.store.clustersList + this.dependencies + .clusters + .get() .filter(cluster => !cluster.disconnected.get()) .map((cluster) => ( this.dependencies @@ -236,7 +228,7 @@ export class ClusterManager { }; stop() { - for (const cluster of this.dependencies.store.clustersList) { + for (const cluster of this.dependencies.clusters.get()) { this.dependencies .getClusterConnection(cluster) .disconnect(); diff --git a/packages/core/src/main/cluster/store-migrations/3.6.0-beta.1.injectable.ts b/packages/core/src/main/cluster/store-migrations/3.6.0-beta.1.injectable.ts index 9b8bb83e2b..5f43359d8d 100644 --- a/packages/core/src/main/cluster/store-migrations/3.6.0-beta.1.injectable.ts +++ b/packages/core/src/main/cluster/store-migrations/3.6.0-beta.1.injectable.ts @@ -18,7 +18,7 @@ interface Pre360ClusterModel extends ClusterModel { } import { getInjectable } from "@ogre-tools/injectable"; -import { clusterStoreMigrationInjectionToken } from "../../../common/cluster-store/migration-token"; +import { clusterStoreMigrationInjectionToken } from "../../../features/cluster/storage/common/migration-token"; import readFileBufferSyncInjectable from "../../../common/fs/read-file-buffer-sync.injectable"; import loggerInjectable from "../../../common/logger.injectable"; import writeFileSyncInjectable from "../../../common/fs/write-file-sync.injectable"; diff --git a/packages/core/src/main/cluster/store-migrations/5.0.0-beta.10.injectable.ts b/packages/core/src/main/cluster/store-migrations/5.0.0-beta.10.injectable.ts index 026ded3d1b..3ba3629578 100644 --- a/packages/core/src/main/cluster/store-migrations/5.0.0-beta.10.injectable.ts +++ b/packages/core/src/main/cluster/store-migrations/5.0.0-beta.10.injectable.ts @@ -4,7 +4,7 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import { clusterStoreMigrationInjectionToken } from "../../../common/cluster-store/migration-token"; +import { clusterStoreMigrationInjectionToken } from "../../../features/cluster/storage/common/migration-token"; import type { ClusterModel } from "../../../common/cluster-types"; import readJsonSyncInjectable from "../../../common/fs/read-json-sync.injectable"; import joinPathsInjectable from "../../../common/path/join-paths.injectable"; diff --git a/packages/core/src/main/cluster/store-migrations/5.0.0-beta.13.injectable.ts b/packages/core/src/main/cluster/store-migrations/5.0.0-beta.13.injectable.ts index 9137c81fed..6c33cfa2c4 100644 --- a/packages/core/src/main/cluster/store-migrations/5.0.0-beta.13.injectable.ts +++ b/packages/core/src/main/cluster/store-migrations/5.0.0-beta.13.injectable.ts @@ -10,7 +10,7 @@ import { isDefined } from "@k8slens/utilities"; import joinPathsInjectable from "../../../common/path/join-paths.injectable"; import { getInjectable } from "@ogre-tools/injectable"; import loggerInjectable from "../../../common/logger.injectable"; -import { clusterStoreMigrationInjectionToken } from "../../../common/cluster-store/migration-token"; +import { clusterStoreMigrationInjectionToken } from "../../../features/cluster/storage/common/migration-token"; import { generateNewIdFor } from "../../../common/utils/generate-new-id-for"; interface Pre500ClusterModel extends ClusterModel { diff --git a/packages/core/src/main/cluster/store-migrations/snap.injectable.ts b/packages/core/src/main/cluster/store-migrations/snap.injectable.ts index d4efc37612..f20f0a4820 100644 --- a/packages/core/src/main/cluster/store-migrations/snap.injectable.ts +++ b/packages/core/src/main/cluster/store-migrations/snap.injectable.ts @@ -6,7 +6,7 @@ // Fix embedded kubeconfig paths under snap config import { getInjectable } from "@ogre-tools/injectable"; -import { clusterStoreMigrationInjectionToken } from "../../../common/cluster-store/migration-token"; +import { clusterStoreMigrationInjectionToken } from "../../../features/cluster/storage/common/migration-token"; import loggerInjectable from "../../../common/logger.injectable"; import isSnapPackageInjectable from "../../../common/vars/is-snap-package.injectable"; import type { ClusterModel } from "../../../common/cluster-types"; diff --git a/packages/core/src/main/cluster/update-entity-metadata.test.ts b/packages/core/src/main/cluster/update-entity-metadata.test.ts index 13683196bb..eca53c238e 100644 --- a/packages/core/src/main/cluster/update-entity-metadata.test.ts +++ b/packages/core/src/main/cluster/update-entity-metadata.test.ts @@ -34,8 +34,6 @@ describe("update-entity-metadata", () => { id: "some-id", contextName: "some-context", kubeConfigPath: "minikube-config.yml", - }, { - clusterServerUrl: "foo", }); detectedMetadata = { diff --git a/packages/core/src/main/cluster/update-entity-spec.test.ts b/packages/core/src/main/cluster/update-entity-spec.test.ts index a21e1fae46..fa2c14bb5a 100644 --- a/packages/core/src/main/cluster/update-entity-spec.test.ts +++ b/packages/core/src/main/cluster/update-entity-spec.test.ts @@ -31,8 +31,6 @@ describe("update-entity-spec", () => { id: "some-id", contextName: "some-context", kubeConfigPath: "minikube-config.yml", - }, { - clusterServerUrl: "foo", }); entity = new KubernetesCluster({ diff --git a/packages/core/src/main/create-cluster/allowed-resources.injectable.ts b/packages/core/src/main/create-cluster/allowed-resources.injectable.ts index 09beae9fd2..7b520fd29a 100644 --- a/packages/core/src/main/create-cluster/allowed-resources.injectable.ts +++ b/packages/core/src/main/create-cluster/allowed-resources.injectable.ts @@ -4,7 +4,7 @@ */ import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; import { computed } from "mobx"; -import { shouldShowResourceInjectionToken } from "../../common/cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import type { KubeApiResourceDescriptor } from "../../common/rbac"; import { formatKubeApiResource } from "../../common/rbac"; diff --git a/packages/core/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable.ts b/packages/core/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable.ts index cdf120dad7..61e37642e1 100644 --- a/packages/core/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable.ts +++ b/packages/core/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable.ts @@ -5,12 +5,12 @@ import { getInjectable } from "@ogre-tools/injectable"; import { setupIpcMainHandlers } from "./setup-ipc-main-handlers"; import loggerInjectable from "../../../../common/logger.injectable"; -import clusterStoreInjectable from "../../../../common/cluster-store/cluster-store.injectable"; import { onLoadOfApplicationInjectionToken } from "@k8slens/application"; import applicationMenuItemCompositeInjectable from "../../../../features/application-menu/main/application-menu-item-composite.injectable"; -import getClusterByIdInjectable from "../../../../common/cluster-store/get-by-id.injectable"; import pushCatalogToRendererInjectable from "../../../catalog-sync-to-renderer/push-catalog-to-renderer.injectable"; import clusterFramesInjectable from "../../../../common/cluster-frames.injectable"; +import getClusterByIdInjectable from "../../../../features/cluster/storage/common/get-by-id.injectable"; +import clustersInjectable from "../../../../features/cluster/storage/common/clusters.injectable"; const setupIpcMainHandlersInjectable = getInjectable({ id: "setup-ipc-main-handlers", @@ -24,7 +24,7 @@ const setupIpcMainHandlersInjectable = getInjectable({ setupIpcMainHandlers({ applicationMenuItemComposite: di.inject(applicationMenuItemCompositeInjectable), pushCatalogToRenderer: di.inject(pushCatalogToRendererInjectable), - clusterStore: di.inject(clusterStoreInjectable), + clusters: di.inject(clustersInjectable), getClusterById: di.inject(getClusterByIdInjectable), clusterFrames: di.inject(clusterFramesInjectable), }); diff --git a/packages/core/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.ts b/packages/core/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.ts index d5b85c440a..6fdd498910 100644 --- a/packages/core/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.ts +++ b/packages/core/src/main/electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.ts @@ -7,7 +7,6 @@ import { BrowserWindow, Menu } from "electron"; import type { ClusterFrameInfo } from "../../../../common/cluster-frames.injectable"; import { clusterSetFrameIdHandler, clusterStates } from "../../../../common/ipc/cluster"; import type { ClusterId } from "../../../../common/cluster-types"; -import type { ClusterStore } from "../../../../common/cluster-store/cluster-store"; import { broadcastMainChannel, broadcastMessage, ipcMainHandle, ipcMainOn } from "../../../../common/ipc"; import type { IComputedValue, ObservableMap } from "mobx"; import { windowActionHandleChannel, windowLocationChangedChannel, windowOpenAppMenuAsContextMenuChannel } from "../../../../common/ipc/window"; @@ -16,21 +15,23 @@ import type { ApplicationMenuItemTypes } from "../../../../features/application- import type { Composite } from "../../../../common/utils/composite/get-composite/get-composite"; import { getApplicationMenuTemplate } from "../../../../features/application-menu/main/populate-application-menu.injectable"; import type { MenuItemRoot } from "../../../../features/application-menu/main/application-menu-item-composite.injectable"; -import type { GetClusterById } from "../../../../common/cluster-store/get-by-id.injectable"; +import type { GetClusterById } from "../../../../features/cluster/storage/common/get-by-id.injectable"; +import type { Cluster } from "../../../../common/cluster/cluster"; + interface Dependencies { applicationMenuItemComposite: IComputedValue>; - clusterStore: ClusterStore; getClusterById: GetClusterById; pushCatalogToRenderer: () => void; clusterFrames: ObservableMap; + clusters: IComputedValue; } export const setupIpcMainHandlers = ({ applicationMenuItemComposite, - clusterStore, getClusterById, pushCatalogToRenderer, clusterFrames, + clusters, }: Dependencies) => { ipcMainHandle(clusterSetFrameIdHandler, (event: IpcMainInvokeEvent, clusterId: ClusterId) => { const cluster = getClusterById(clusterId); @@ -60,7 +61,7 @@ export const setupIpcMainHandlers = ({ }); ipcMainHandle(clusterStates, () => ( - clusterStore.clustersList.map(cluster => ({ + clusters.get().map(cluster => ({ id: cluster.id, state: cluster.getState(), })) diff --git a/packages/core/src/main/electron-app/runnables/setup-runnables-before-closing-of-application.injectable.ts b/packages/core/src/main/electron-app/runnables/setup-runnables-before-closing-of-application.injectable.ts index 0ba1b68137..9843d2c223 100644 --- a/packages/core/src/main/electron-app/runnables/setup-runnables-before-closing-of-application.injectable.ts +++ b/packages/core/src/main/electron-app/runnables/setup-runnables-before-closing-of-application.injectable.ts @@ -8,7 +8,7 @@ import { beforeQuitOfFrontEndInjectionToken, beforeQuitOfBackEndInjectionToken } import electronAppInjectable from "../electron-app.injectable"; import isIntegrationTestingInjectable from "../../../common/vars/is-integration-testing.injectable"; import autoUpdaterInjectable from "../features/auto-updater.injectable"; -import { runManySyncFor } from "@k8slens/run-many"; +import { runManySyncFor, runManyFor } from "@k8slens/run-many"; const setupRunnablesBeforeClosingOfApplicationInjectable = getInjectable({ id: "setup-closing-of-application", @@ -16,8 +16,9 @@ const setupRunnablesBeforeClosingOfApplicationInjectable = getInjectable({ instantiate: (di) => ({ run: () => { const runManySync = runManySyncFor(di); + const runMany = runManyFor(di); const runRunnablesBeforeQuitOfFrontEnd = runManySync(beforeQuitOfFrontEndInjectionToken); - const runRunnablesBeforeQuitOfBackEnd = runManySync(beforeQuitOfBackEndInjectionToken); + const runRunnablesBeforeQuitOfBackEnd = runMany(beforeQuitOfBackEndInjectionToken); const app = di.inject(electronAppInjectable); const isIntegrationTesting = di.inject(isIntegrationTestingInjectable); const autoUpdater = di.inject(autoUpdaterInjectable); @@ -27,17 +28,43 @@ const setupRunnablesBeforeClosingOfApplicationInjectable = getInjectable({ isAutoUpdating = true; }); - app.on("will-quit", (event) => { + app.on("will-quit", () => { runRunnablesBeforeQuitOfFrontEnd(); - const shouldQuitBackEnd = isIntegrationTesting || isAutoUpdating; + let isAsyncQuitting = false; - if (shouldQuitBackEnd) { - runRunnablesBeforeQuitOfBackEnd(); - } else { - // IMPORTANT: This cannot be destructured as it would break binding of "this" for the Electron event + const doAsyncQuit = (event: Electron.Event, exitCode = 0) => { + if (isAsyncQuitting) { + return; + } + + isAsyncQuitting = true; + + void (async () => { + try { + await runRunnablesBeforeQuitOfBackEnd(); + } catch (error) { + console.error("A beforeQuitOfBackEnd failed!!!!", error); + exitCode = 1; + } + + app.exit(exitCode); + })(); + }; + + app.on("will-quit", (event) => { + runRunnablesBeforeQuitOfFrontEnd(); event.preventDefault(); - } + + if (isIntegrationTesting || isAutoUpdating) { + doAsyncQuit(event); + } + }); + + app.on("quit", (event, exitCode) => { + event.preventDefault(); + doAsyncQuit(event, exitCode); + }); }); return undefined; diff --git a/packages/core/src/main/extension-loader/create-extension-instance.injectable.ts b/packages/core/src/main/extension-loader/create-extension-instance.injectable.ts index 62417149d4..cd20ab7ede 100644 --- a/packages/core/src/main/extension-loader/create-extension-instance.injectable.ts +++ b/packages/core/src/main/extension-loader/create-extension-instance.injectable.ts @@ -6,7 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import type { Writable } from "type-fest"; import loggerInjectable from "../../common/logger.injectable"; import { createExtensionInstanceInjectionToken } from "../../extensions/extension-loader/create-extension-instance.token"; -import fileSystemProvisionerStoreInjectable from "../../extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store.injectable"; +import ensureHashedDirectoryForExtensionInjectable from "../../extensions/extension-loader/file-system-provisioner-store/ensure-hashed-directory-for-extension.injectable"; import { lensExtensionDependencies } from "../../extensions/lens-extension"; import type { LensMainExtensionDependencies } from "../../extensions/lens-extension-set-dependencies"; import type { LensMainExtension } from "../../extensions/lens-main-extension"; @@ -17,7 +17,7 @@ const createExtensionInstanceInjectable = getInjectable({ id: "create-extension-instance", instantiate: (di) => { const deps: LensMainExtensionDependencies = { - fileSystemProvisionerStore: di.inject(fileSystemProvisionerStoreInjectable), + ensureHashedDirectoryForExtension: di.inject(ensureHashedDirectoryForExtensionInjectable), entityRegistry: di.inject(catalogEntityRegistryInjectable), navigate: di.inject(navigateForExtensionInjectable), logger: di.inject(loggerInjectable), diff --git a/packages/core/src/main/getDiForUnitTesting.ts b/packages/core/src/main/getDiForUnitTesting.ts index b374eec1ca..83b3aa6d82 100644 --- a/packages/core/src/main/getDiForUnitTesting.ts +++ b/packages/core/src/main/getDiForUnitTesting.ts @@ -10,7 +10,7 @@ import spawnInjectable from "./child-process/spawn.injectable"; import initializeExtensionsInjectable from "./start-main-application/runnables/initialize-extensions.injectable"; import setupIpcMainHandlersInjectable from "./electron-app/runnables/setup-ipc-main-handlers/setup-ipc-main-handlers.injectable"; import setupLensProxyInjectable from "./start-main-application/runnables/setup-lens-proxy.injectable"; -import setupSyncingOfWeblinksInjectable from "./start-main-application/runnables/setup-syncing-of-weblinks.injectable"; +import setupSyncingOfWeblinksInjectable from "../features/weblinks/main/setup-syncing-of-weblinks.injectable"; import setupDeepLinkingInjectable from "./electron-app/runnables/setup-deep-linking.injectable"; import setupMainWindowVisibilityAfterActivationInjectable from "./electron-app/runnables/setup-main-window-visibility-after-activation.injectable"; import setupDeviceShutdownInjectable from "./electron-app/runnables/setup-device-shutdown.injectable"; diff --git a/packages/core/src/main/helm/exec-helm/exec-env.injectable.ts b/packages/core/src/main/helm/exec-helm/exec-env.injectable.ts index 3a72d0c17c..29864427a4 100644 --- a/packages/core/src/main/helm/exec-helm/exec-env.injectable.ts +++ b/packages/core/src/main/helm/exec-helm/exec-env.injectable.ts @@ -4,7 +4,7 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import { computed } from "mobx"; -import httpsProxyConfigurationInjectable from "../../../common/user-store/https-proxy.injectable"; +import httpsProxyConfigurationInjectable from "../../../features/user-preferences/common/https-proxy.injectable"; const execHelmEnvInjectable = getInjectable({ id: "exec-helm-env", diff --git a/packages/core/src/main/hotbar-store/migrations/5.0.0-alpha.0.injectable.ts b/packages/core/src/main/hotbar-store/migrations/5.0.0-alpha.0.injectable.ts deleted file mode 100644 index 9143167b47..0000000000 --- a/packages/core/src/main/hotbar-store/migrations/5.0.0-alpha.0.injectable.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -// Cleans up a store that had the state related data stored -import { getEmptyHotbar } from "../../../common/hotbars/types"; -import catalogCatalogEntityInjectable from "../../../common/catalog-entities/general-catalog-entities/implementations/catalog-catalog-entity.injectable"; -import { getInjectable } from "@ogre-tools/injectable"; -import { hotbarStoreMigrationInjectionToken } from "../../../common/hotbars/migrations-token"; - -const v500Alpha0HotbarStoreMigrationInjectable = getInjectable({ - id: "v5.0.0-alpha.0-hotbar-store-migration", - instantiate: (di) => { - const catalogCatalogEntity = di.inject(catalogCatalogEntityInjectable); - - return { - version: "5.0.0-alpha.0", - run(store) { - const hotbar = getEmptyHotbar("default"); - - const { metadata: { uid, name, source }} = catalogCatalogEntity; - - hotbar.items[0] = { entity: { uid, name, source }}; - - store.set("hotbars", [hotbar]); - }, - }; - }, - injectionToken: hotbarStoreMigrationInjectionToken, -}); - -export default v500Alpha0HotbarStoreMigrationInjectable; - diff --git a/packages/core/src/main/hotbar-store/migrations/5.0.0-beta.10.injectable.ts b/packages/core/src/main/hotbar-store/migrations/5.0.0-beta.10.injectable.ts deleted file mode 100644 index b327085aa6..0000000000 --- a/packages/core/src/main/hotbar-store/migrations/5.0.0-beta.10.injectable.ts +++ /dev/null @@ -1,182 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import * as uuid from "uuid"; -import type { Hotbar, HotbarItem } from "../../../common/hotbars/types"; -import { defaultHotbarCells, getEmptyHotbar } from "../../../common/hotbars/types"; -import { getLegacyGlobalDiForExtensionApi } from "../../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; -import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import catalogCatalogEntityInjectable from "../../../common/catalog-entities/general-catalog-entities/implementations/catalog-catalog-entity.injectable"; -import { isDefined, isErrnoException } from "@k8slens/utilities"; -import joinPathsInjectable from "../../../common/path/join-paths.injectable"; -import { getInjectable } from "@ogre-tools/injectable"; -import { hotbarStoreMigrationInjectionToken } from "../../../common/hotbars/migrations-token"; -import readJsonSyncInjectable from "../../../common/fs/read-json-sync.injectable"; -import loggerInjectable from "../../../common/logger.injectable"; -import { generateNewIdFor } from "../../../common/utils/generate-new-id-for"; -import type { ClusterModel } from "../../../common/cluster-types"; - -interface Pre500WorkspaceStoreModel { - workspaces: { - id: string; - name: string; - }[]; -} - -interface PartialHotbar { - id: string; - name: string; - items: (null | HotbarItem)[]; -} - -interface Pre500ClusterModel extends ClusterModel { - workspace?: string; - workspaces?: string[]; -} - -interface Pre500ClusterStoreModel { - clusters?: Pre500ClusterModel[]; -} - -const v500Beta10HotbarStoreMigrationInjectable = getInjectable({ - id: "v5.0.0-beta.10-hotbar-store-migration", - instantiate: (di) => { - const userDataPath = di.inject(directoryForUserDataInjectable); - const joinPaths = di.inject(joinPathsInjectable); - const readJsonSync = di.inject(readJsonSyncInjectable); - const catalogCatalogEntity = di.inject(catalogCatalogEntityInjectable); - const logger = di.inject(loggerInjectable); - - return { - version: "5.0.0-beta.10", - run(store) { - const rawHotbars = store.get("hotbars"); - const hotbars: Hotbar[] = Array.isArray(rawHotbars) ? rawHotbars.filter(h => h && typeof h === "object") : []; - - - // Hotbars might be empty, if some of the previous migrations weren't run - if (hotbars.length === 0) { - const hotbar = getEmptyHotbar("default"); - const { metadata: { uid, name, source }} = catalogCatalogEntity; - - hotbar.items[0] = { entity: { uid, name, source }}; - - hotbars.push(hotbar); - } - - try { - const workspaceStoreData: Pre500WorkspaceStoreModel = readJsonSync(joinPaths(userDataPath, "lens-workspace-store.json")); - const { clusters = [] }: Pre500ClusterStoreModel = readJsonSync(joinPaths(userDataPath, "lens-cluster-store.json")); - const workspaceHotbars = new Map(); // mapping from WorkspaceId to HotBar - - for (const { id, name } of workspaceStoreData.workspaces) { - logger.info(`Creating new hotbar for ${name}`); - workspaceHotbars.set(id, { - id: uuid.v4(), // don't use the old IDs as they aren't necessarily UUIDs - items: [], - name: `Workspace: ${name}`, - }); - } - - { - // grab the default named hotbar or the first. - const defaultHotbarIndex = Math.max(0, hotbars.findIndex(hotbar => hotbar.name === "default")); - const [{ name, id, items }] = hotbars.splice(defaultHotbarIndex, 1); - - workspaceHotbars.set("default", { - name, - id, - items: items.filter(isDefined), - }); - } - - for (const cluster of clusters) { - const uid = generateNewIdFor(cluster); - - for (const workspaceId of cluster.workspaces ?? [cluster.workspace].filter(isDefined)) { - const workspaceHotbar = workspaceHotbars.get(workspaceId); - - if (!workspaceHotbar) { - logger.info(`Cluster ${uid} has unknown workspace ID, skipping`); - continue; - } - - logger.info(`Adding cluster ${uid} to ${workspaceHotbar.name}`); - - if (workspaceHotbar?.items.length < defaultHotbarCells) { - workspaceHotbar.items.push({ - entity: { - uid: generateNewIdFor(cluster), - name: cluster.preferences?.clusterName || cluster.contextName, - }, - }); - } - } - } - - for (const hotbar of workspaceHotbars.values()) { - if (hotbar.items.length === 0) { - logger.info(`Skipping ${hotbar.name} due to it being empty`); - continue; - } - - while (hotbar.items.length < defaultHotbarCells) { - hotbar.items.push(null); - } - - hotbars.push(hotbar as Hotbar); - } - - /** - * Finally, make sure that the catalog entity hotbar item is in place. - * Just in case something else removed it. - * - * if every hotbar has elements that all not the `catalog-entity` item - */ - if (hotbars.every(hotbar => hotbar.items.every(item => item?.entity?.uid !== "catalog-entity"))) { - // note, we will add a new whole hotbar here called "default" if that was previously removed - const di = getLegacyGlobalDiForExtensionApi(); - const catalogCatalogEntity = di.inject(catalogCatalogEntityInjectable); - - const defaultHotbar = hotbars.find(hotbar => hotbar.name === "default"); - const { metadata: { uid, name, source }} = catalogCatalogEntity; - - if (defaultHotbar) { - const freeIndex = defaultHotbar.items.findIndex(i => i === null); - - if (freeIndex === -1) { - // making a new hotbar is less destructive if the first hotbar - // called "default" is full than overriding a hotbar item - const hotbar = getEmptyHotbar("initial"); - - hotbar.items[0] = { entity: { uid, name, source }}; - hotbars.unshift(hotbar); - } else { - defaultHotbar.items[freeIndex] = { entity: { uid, name, source }}; - } - } else { - const hotbar = getEmptyHotbar("default"); - - hotbar.items[0] = { entity: { uid, name, source }}; - hotbars.unshift(hotbar); - } - } - - } catch (error) { - // ignore files being missing - if (isErrnoException(error) && error.code !== "ENOENT") { - throw error; - } - } - - store.set("hotbars", hotbars); - }, - }; - }, - injectionToken: hotbarStoreMigrationInjectionToken, -}); - -export default v500Beta10HotbarStoreMigrationInjectable; - diff --git a/packages/core/src/main/hotbar-store/migrations/5.0.0-beta.5.injectable.ts b/packages/core/src/main/hotbar-store/migrations/5.0.0-beta.5.injectable.ts deleted file mode 100644 index eef10f7033..0000000000 --- a/packages/core/src/main/hotbar-store/migrations/5.0.0-beta.5.injectable.ts +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { Hotbar } from "../../../common/hotbars/types"; -import catalogEntityRegistryInjectable from "../../catalog/entity-registry.injectable"; -import { getInjectable } from "@ogre-tools/injectable"; -import { hotbarStoreMigrationInjectionToken } from "../../../common/hotbars/migrations-token"; - -const v500Beta5HotbarStoreMigrationInjectable = getInjectable({ - id: "v500-beta5-hotbar-store-migration", - instantiate: (di) => { - const catalogEntityRegistry = di.inject(catalogEntityRegistryInjectable); - - return { - version: "5.0.0-beta.5", - run(store) { - const rawHotbars = store.get("hotbars"); - const hotbars: Hotbar[] = Array.isArray(rawHotbars) ? rawHotbars : []; - - for (const hotbar of hotbars) { - for (let i = 0; i < hotbar.items.length; i += 1) { - const item = hotbar.items[i]; - - if (!item) { - continue; - } - - const entity = catalogEntityRegistry.findById(item.entity.uid); - - if (!entity) { - // Clear disabled item - hotbar.items[i] = null; - } else { - // Save additional data - item.entity = { - ...item.entity, - name: entity.metadata.name, - source: entity.metadata.source, - }; - } - } - } - - store.set("hotbars", hotbars); - }, - }; - }, - injectionToken: hotbarStoreMigrationInjectionToken, -}); - -export default v500Beta5HotbarStoreMigrationInjectable; - diff --git a/packages/core/src/main/kube-auth-proxy/create-kube-auth-proxy.injectable.ts b/packages/core/src/main/kube-auth-proxy/create-kube-auth-proxy.injectable.ts index fd4acf0d7a..df7108d8f1 100644 --- a/packages/core/src/main/kube-auth-proxy/create-kube-auth-proxy.injectable.ts +++ b/packages/core/src/main/kube-auth-proxy/create-kube-auth-proxy.injectable.ts @@ -2,18 +2,22 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ -import { getInjectable } from "@ogre-tools/injectable"; -import type { KubeAuthProxyDependencies } from "./kube-auth-proxy"; -import { KubeAuthProxyImpl } from "./kube-auth-proxy"; +import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; import type { Cluster } from "../../common/cluster/cluster"; import spawnInjectable from "../child-process/spawn.injectable"; -import kubeAuthProxyCertificateInjectable from "./kube-auth-proxy-certificate.injectable"; import loggerInjectable from "../../common/logger.injectable"; import waitUntilPortIsUsedInjectable from "./wait-until-port-is-used/wait-until-port-is-used.injectable"; import lensK8sProxyPathInjectable from "./lens-k8s-proxy-path.injectable"; import getPortFromStreamInjectable from "../utils/get-port-from-stream.injectable"; import getDirnameOfPathInjectable from "../../common/path/get-dirname.injectable"; +import randomBytesInjectable from "../../common/utils/random-bytes.injectable"; +import type { ChildProcess } from "child_process"; +import { observable, when } from "mobx"; +import assert from "assert"; +import clusterApiUrlInjectable from "../../features/cluster/connections/main/api-url.injectable"; +import kubeAuthProxyCertificateInjectable from "./kube-auth-proxy-certificate.injectable"; import broadcastConnectionUpdateInjectable from "../cluster/broadcast-connection-update.injectable"; +import { TypedRegEx } from "typed-regex"; export interface KubeAuthProxy { readonly apiPrefix: string; @@ -22,31 +26,159 @@ export interface KubeAuthProxy { exit: () => void; } -export type CreateKubeAuthProxy = (cluster: Cluster, env: NodeJS.ProcessEnv) => KubeAuthProxy; +export type CreateKubeAuthProxy = (env: NodeJS.ProcessEnv) => KubeAuthProxy; + +const startingServeMatcher = "starting to serve on (?
.+)"; +const startingServeRegex = Object.assign(TypedRegEx(startingServeMatcher, "i"), { + rawMatcher: startingServeMatcher, +}); const createKubeAuthProxyInjectable = getInjectable({ id: "create-kube-auth-proxy", - instantiate: (di): CreateKubeAuthProxy => { - const dependencies: Omit = { - proxyBinPath: di.inject(lensK8sProxyPathInjectable), - spawn: di.inject(spawnInjectable), - logger: di.inject(loggerInjectable), - waitUntilPortIsUsed: di.inject(waitUntilPortIsUsedInjectable), - getPortFromStream: di.inject(getPortFromStreamInjectable), - dirname: di.inject(getDirnameOfPathInjectable), - }; + instantiate: (di, cluster): CreateKubeAuthProxy => { + const lensK8sProxyPath = di.inject(lensK8sProxyPathInjectable); + const spawn = di.inject(spawnInjectable); + const logger = di.inject(loggerInjectable); + const waitUntilPortIsUsed = di.inject(waitUntilPortIsUsedInjectable); + const getPortFromStream = di.inject(getPortFromStreamInjectable); + const getDirnameOfPath = di.inject(getDirnameOfPathInjectable); + const randomBytes = di.inject(randomBytesInjectable); + const clusterApiUrl = di.inject(clusterApiUrlInjectable, cluster); + const broadcastConnectionUpdate = di.inject(broadcastConnectionUpdateInjectable, cluster); - return (cluster, env) => { - const clusterUrl = new URL(cluster.apiUrl.get()); + return (env) => { + let port: number | undefined; + let proxyProcess: ChildProcess | undefined; + const ready = observable.box(false); + const apiPrefix = `/${randomBytes(8).toString("hex")}`; - return new KubeAuthProxyImpl({ - ...dependencies, - proxyCert: di.inject(kubeAuthProxyCertificateInjectable, clusterUrl.hostname), - broadcastConnectionUpdate: di.inject(broadcastConnectionUpdateInjectable, cluster), - }, cluster, env); + const exit = () => { + ready.set(false); + + if (proxyProcess) { + logger.debug("[KUBE-AUTH]: stopping local proxy", cluster.getMeta()); + proxyProcess.removeAllListeners(); + proxyProcess.stderr?.removeAllListeners(); + proxyProcess.stdout?.removeAllListeners(); + proxyProcess.kill(); + proxyProcess = undefined; + } + }; + + const run = async (): Promise => { + if (proxyProcess) { + return when(() => ready.get()); + } + + const apiUrl = await clusterApiUrl(); + const certificate = di.inject(kubeAuthProxyCertificateInjectable, apiUrl.hostname); + + proxyProcess = spawn(lensK8sProxyPath, [], { + env: { + ...env, + KUBECONFIG: cluster.kubeConfigPath.get(), + KUBECONFIG_CONTEXT: cluster.contextName.get(), + API_PREFIX: apiPrefix, + PROXY_KEY: certificate.private, + PROXY_CERT: certificate.cert, + }, + cwd: getDirnameOfPath(cluster.kubeConfigPath.get()), + }); + proxyProcess.on("error", (error) => { + broadcastConnectionUpdate({ + level: "error", + message: error.message, + }); + exit(); + }); + + proxyProcess.on("exit", (code) => { + if (code) { + broadcastConnectionUpdate({ + level: "error", + message: `proxy exited with code: ${code}`, + }); + } else { + broadcastConnectionUpdate({ + level: "info", + message: "proxy exited successfully", + }); + } + exit(); + }); + + proxyProcess.on("disconnect", () => { + broadcastConnectionUpdate({ + level: "error", + message: "Proxy disconnected communications", + }); + exit(); + }); + + assert(proxyProcess.stderr); + assert(proxyProcess.stdout); + + proxyProcess.stderr.on("data", (data: Buffer) => { + if (data.includes("http: TLS handshake error")) { + return; + } + + broadcastConnectionUpdate({ + level: "error", + message: data.toString(), + }); + }); + + proxyProcess.stdout.on("data", (data: Buffer) => { + if (typeof port === "number") { + broadcastConnectionUpdate({ + level: "info", + message: data.toString(), + }); + } + }); + + port = await getPortFromStream(proxyProcess.stdout, { + lineRegex: startingServeRegex, + onFind: () => broadcastConnectionUpdate({ + level: "info", + message: "Authentication proxy started", + }), + }); + + logger.info(`[KUBE-AUTH-PROXY]: found port=${port}`); + + try { + await waitUntilPortIsUsed(port, 500, 10000); + ready.set(true); + } catch (error) { + logger.warn("[KUBE-AUTH-PROXY]: waitUntilUsed failed", error); + broadcastConnectionUpdate({ + level: "error", + message: "Proxy port failed to be used within time limit, restarting...", + }); + exit(); + + return run(); + } + }; + + return { + apiPrefix, + exit, + run, + get port() { + assert(port, "port has not yet been initialized"); + + return port; + }, + }; }; }, + lifecycle: lifecycleEnum.keyedSingleton({ + getInstanceKey: (di, cluster: Cluster) => cluster.id, + }), }); export default createKubeAuthProxyInjectable; diff --git a/packages/core/src/main/kube-auth-proxy/kube-auth-proxy.ts b/packages/core/src/main/kube-auth-proxy/kube-auth-proxy.ts deleted file mode 100644 index 160566c3a5..0000000000 --- a/packages/core/src/main/kube-auth-proxy/kube-auth-proxy.ts +++ /dev/null @@ -1,168 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { ChildProcess } from "child_process"; -import { randomBytes } from "crypto"; -import type { Cluster } from "../../common/cluster/cluster"; -import type { GetPortFromStream } from "../utils/get-port-from-stream.injectable"; -import { observable, when } from "mobx"; -import type { SelfSignedCert } from "selfsigned"; -import assert from "assert"; -import { TypedRegEx } from "typed-regex"; -import type { Spawn } from "../child-process/spawn.injectable"; -import type { Logger } from "../../common/logger"; -import type { WaitUntilPortIsUsed } from "./wait-until-port-is-used/wait-until-port-is-used.injectable"; -import type { GetDirnameOfPath } from "../../common/path/get-dirname.injectable"; -import type { BroadcastConnectionUpdate } from "../cluster/broadcast-connection-update.injectable"; -import type { KubeAuthProxy } from "./create-kube-auth-proxy.injectable"; - -const startingServeMatcher = "starting to serve on (?
.+)"; -const startingServeRegex = Object.assign(TypedRegEx(startingServeMatcher, "i"), { - rawMatcher: startingServeMatcher, -}); - -export interface KubeAuthProxyDependencies { - readonly proxyBinPath: string; - readonly proxyCert: SelfSignedCert; - readonly logger: Logger; - spawn: Spawn; - waitUntilPortIsUsed: WaitUntilPortIsUsed; - getPortFromStream: GetPortFromStream; - dirname: GetDirnameOfPath; - broadcastConnectionUpdate: BroadcastConnectionUpdate; -} - -export class KubeAuthProxyImpl implements KubeAuthProxy { - public readonly apiPrefix = `/${randomBytes(8).toString("hex")}`; - - public get port(): number { - const port = this._port; - - assert(port, "port has not yet been initialized"); - - return port; - } - - protected _port?: number; - protected proxyProcess?: ChildProcess; - protected readonly ready = observable.box(false); - - constructor( - private readonly dependencies: KubeAuthProxyDependencies, - protected readonly cluster: Cluster, - protected readonly env: NodeJS.ProcessEnv, - ) {} - - public async run(): Promise { - if (this.proxyProcess) { - return when(() => this.ready.get()); - } - - const proxyBin = this.dependencies.proxyBinPath; - const cert = this.dependencies.proxyCert; - - this.proxyProcess = this.dependencies.spawn(proxyBin, [], { - env: { - ...this.env, - KUBECONFIG: this.cluster.kubeConfigPath.get(), - KUBECONFIG_CONTEXT: this.cluster.contextName.get(), - API_PREFIX: this.apiPrefix, - PROXY_KEY: cert.private, - PROXY_CERT: cert.cert, - }, - cwd: this.dependencies.dirname(this.cluster.kubeConfigPath.get()), - }); - this.proxyProcess.on("error", (error) => { - this.dependencies.broadcastConnectionUpdate({ - level: "error", - message: error.message, - }); - this.exit(); - }); - - this.proxyProcess.on("exit", (code) => { - if (code) { - this.dependencies.broadcastConnectionUpdate({ - level: "error", - message: `proxy exited with code: ${code}`, - }); - } else { - this.dependencies.broadcastConnectionUpdate({ - level: "info", - message: "proxy exited successfully", - }); - } - this.exit(); - }); - - this.proxyProcess.on("disconnect", () => { - this.dependencies.broadcastConnectionUpdate({ - level: "error", - message: "Proxy disconnected communications", - }); - this.exit(); - }); - - assert(this.proxyProcess.stderr); - assert(this.proxyProcess.stdout); - - this.proxyProcess.stderr.on("data", (data: Buffer) => { - if (data.includes("http: TLS handshake error")) { - return; - } - - this.dependencies.broadcastConnectionUpdate({ - level: "error", - message: data.toString(), - }); - }); - - this.proxyProcess.stdout.on("data", (data: Buffer) => { - if (typeof this._port === "number") { - this.dependencies.broadcastConnectionUpdate({ - level: "info", - message: data.toString(), - }); - } - }); - - this._port = await this.dependencies.getPortFromStream(this.proxyProcess.stdout, { - lineRegex: startingServeRegex, - onFind: () => this.dependencies.broadcastConnectionUpdate({ - level: "info", - message: "Authentication proxy started", - }), - }); - - this.dependencies.logger.info(`[KUBE-AUTH-PROXY]: found port=${this._port}`); - - try { - await this.dependencies.waitUntilPortIsUsed(this.port, 500, 10000); - this.ready.set(true); - } catch (error) { - this.dependencies.logger.warn("[KUBE-AUTH-PROXY]: waitUntilUsed failed", error); - this.dependencies.broadcastConnectionUpdate({ - level: "error", - message: "Proxy port failed to be used within time limit, restarting...", - }); - this.exit(); - - return this.run(); - } - } - - public exit() { - this.ready.set(false); - - if (this.proxyProcess) { - this.dependencies.logger.debug("[KUBE-AUTH]: stopping local proxy", this.cluster.getMeta()); - this.proxyProcess.removeAllListeners(); - this.proxyProcess.stderr?.removeAllListeners(); - this.proxyProcess.stdout?.removeAllListeners(); - this.proxyProcess.kill(); - this.proxyProcess = undefined; - } - } -} diff --git a/packages/core/src/main/kubectl/apply-all-handler.injectable.ts b/packages/core/src/main/kubectl/apply-all-handler.injectable.ts index 00f58e5e3f..fb5f3a040f 100644 --- a/packages/core/src/main/kubectl/apply-all-handler.injectable.ts +++ b/packages/core/src/main/kubectl/apply-all-handler.injectable.ts @@ -3,8 +3,8 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import emitAppEventInjectable from "../../common/app-event-bus/emit-event.injectable"; -import getClusterByIdInjectable from "../../common/cluster-store/get-by-id.injectable"; import { kubectlApplyAllChannel } from "../../common/kube-helpers/channels"; +import getClusterByIdInjectable from "../../features/cluster/storage/common/get-by-id.injectable"; import resourceApplierInjectable from "../resource-applier/create-resource-applier.injectable"; import { getRequestChannelListenerInjectable } from "@k8slens/messaging"; @@ -15,14 +15,16 @@ const kubectlApplyAllChannelHandlerInjectable = getRequestChannelListenerInjecta const getClusterById = di.inject(getClusterByIdInjectable); const emitAppEvent = di.inject(emitAppEventInjectable); - return async ({ - clusterId, - extraArgs, - resources, - }) => { - emitAppEvent({ name: "cluster", action: "kubectl-apply-all" }); + return async (event) => { + const { + clusterId, + extraArgs, + resources, + } = event; const cluster = getClusterById(clusterId); + emitAppEvent({ name: "cluster", action: "kubectl-apply-all" }); + if (!cluster) { return { callWasSuccessful: false, diff --git a/packages/core/src/main/kubectl/create-kubectl.injectable.ts b/packages/core/src/main/kubectl/create-kubectl.injectable.ts index 5d7b19162c..84e5fee601 100644 --- a/packages/core/src/main/kubectl/create-kubectl.injectable.ts +++ b/packages/core/src/main/kubectl/create-kubectl.injectable.ts @@ -6,7 +6,6 @@ import { getInjectable } from "@ogre-tools/injectable"; import type { KubectlDependencies } from "./kubectl"; import { Kubectl } from "./kubectl"; import directoryForKubectlBinariesInjectable from "../../common/app-paths/directory-for-kubectl-binaries/directory-for-kubectl-binaries.injectable"; -import userStoreInjectable from "../../common/user-store/user-store.injectable"; import kubectlDownloadingNormalizedArchInjectable from "./normalized-arch.injectable"; import normalizedPlatformInjectable from "../../common/vars/normalized-platform.injectable"; import kubectlBinaryNameInjectable from "./binary-name.injectable"; @@ -20,6 +19,7 @@ import getBasenameOfPathInjectable from "../../common/path/get-basename.injectab import loggerInjectable from "../../common/logger.injectable"; import execFileInjectable from "../../common/fs/exec-file.injectable"; import unlinkInjectable from "../../common/fs/unlink.injectable"; +import userPreferencesStateInjectable from "../../features/user-preferences/common/state.injectable"; export type CreateKubectl = (version: string) => Kubectl; @@ -28,7 +28,7 @@ const createKubectlInjectable = getInjectable({ instantiate: (di): CreateKubectl => { const dependencies: KubectlDependencies = { - userStore: di.inject(userStoreInjectable), + state: di.inject(userPreferencesStateInjectable), directoryForKubectlBinaries: di.inject(directoryForKubectlBinariesInjectable), normalizedDownloadArch: di.inject(kubectlDownloadingNormalizedArchInjectable), normalizedDownloadPlatform: di.inject(normalizedPlatformInjectable), diff --git a/packages/core/src/main/kubectl/delete-all-handler.injectable.ts b/packages/core/src/main/kubectl/delete-all-handler.injectable.ts index 5d207be482..b7e21b4d8e 100644 --- a/packages/core/src/main/kubectl/delete-all-handler.injectable.ts +++ b/packages/core/src/main/kubectl/delete-all-handler.injectable.ts @@ -3,8 +3,8 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import emitAppEventInjectable from "../../common/app-event-bus/emit-event.injectable"; -import getClusterByIdInjectable from "../../common/cluster-store/get-by-id.injectable"; import { kubectlDeleteAllChannel } from "../../common/kube-helpers/channels"; +import getClusterByIdInjectable from "../../features/cluster/storage/common/get-by-id.injectable"; import resourceApplierInjectable from "../resource-applier/create-resource-applier.injectable"; import { getRequestChannelListenerInjectable } from "@k8slens/messaging"; @@ -15,15 +15,16 @@ const kubectlDeleteAllChannelHandlerInjectable = getRequestChannelListenerInject const emitAppEvent = di.inject(emitAppEventInjectable); const getClusterById = di.inject(getClusterByIdInjectable); - return async ({ - clusterId, - extraArgs, - resources, - }) => { - emitAppEvent({ name: "cluster", action: "kubectl-delete-all" }); - + return async (event) => { + const { + clusterId, + extraArgs, + resources, + } = event; const cluster = getClusterById(clusterId); + emitAppEvent({ name: "cluster", action: "kubectl-delete-all" }); + if (!cluster) { return { callWasSuccessful: false, diff --git a/packages/core/src/main/kubectl/kubectl.ts b/packages/core/src/main/kubectl/kubectl.ts index ca0fac16e2..631e08b833 100644 --- a/packages/core/src/main/kubectl/kubectl.ts +++ b/packages/core/src/main/kubectl/kubectl.ts @@ -7,7 +7,6 @@ import fs from "fs"; import { ensureDir, pathExists } from "fs-extra"; import * as lockFile from "proper-lockfile"; import { SemVer, coerce } from "semver"; -import { defaultPackageMirror, packageMirrors } from "../../common/user-store/preferences-helpers"; import got from "got/dist/source"; import { promisify } from "util"; import stream from "stream"; @@ -20,6 +19,7 @@ import type { Logger } from "../../common/logger"; import type { ExecFile } from "../../common/fs/exec-file.injectable"; import { hasTypedProperty, isObject, isString, json } from "@k8slens/utilities"; import type { Unlink } from "../../common/fs/unlink.injectable"; +import { packageMirrors, defaultPackageMirror } from "../../features/user-preferences/common/preferences-helpers"; const initScriptVersionString = "# lens-initscript v3"; @@ -30,7 +30,7 @@ export interface KubectlDependencies { readonly kubectlBinaryName: string; readonly bundledKubectlBinaryPath: string; readonly baseBundeledBinariesDirectory: string; - readonly userStore: { + readonly state: { readonly kubectlBinariesPath?: string; readonly downloadBinariesPath?: string; readonly downloadKubectlBinaries: boolean; @@ -91,12 +91,12 @@ export class Kubectl { } public getPathFromPreferences() { - return this.dependencies.userStore.kubectlBinariesPath || this.getBundledPath(); + return this.dependencies.state.kubectlBinariesPath || this.getBundledPath(); } protected getDownloadDir() { - if (this.dependencies.userStore.downloadBinariesPath) { - return this.dependencies.joinPaths(this.dependencies.userStore.downloadBinariesPath, "kubectl"); + if (this.dependencies.state.downloadBinariesPath) { + return this.dependencies.joinPaths(this.dependencies.state.downloadBinariesPath, "kubectl"); } return this.dependencies.directoryForKubectlBinaries; @@ -107,7 +107,7 @@ export class Kubectl { return this.getBundledPath(); } - if (this.dependencies.userStore.downloadKubectlBinaries === false) { + if (this.dependencies.state.downloadKubectlBinaries === false) { return this.getPathFromPreferences(); } @@ -231,7 +231,7 @@ export class Kubectl { } public async ensureKubectl(): Promise { - if (this.dependencies.userStore.downloadKubectlBinaries === false) { + if (this.dependencies.state.downloadKubectlBinaries === false) { return true; } @@ -303,7 +303,7 @@ export class Kubectl { protected async writeInitScripts() { const binariesDir = this.dependencies.baseBundeledBinariesDirectory; - const kubectlPath = this.dependencies.userStore.downloadKubectlBinaries + const kubectlPath = this.dependencies.state.downloadKubectlBinaries ? this.dirname : this.dependencies.getDirnameOfPath(this.getPathFromPreferences()); @@ -370,7 +370,7 @@ export class Kubectl { protected getDownloadMirror(): string { // MacOS packages are only available from default - const { url } = packageMirrors.get(this.dependencies.userStore.downloadMirror) + const { url } = packageMirrors.get(this.dependencies.state.downloadMirror) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion ?? packageMirrors.get(defaultPackageMirror)!; diff --git a/packages/core/src/main/lens-proxy/close-on-quit.injectable.ts b/packages/core/src/main/lens-proxy/close-on-quit.injectable.ts new file mode 100644 index 0000000000..2ff22d6cf6 --- /dev/null +++ b/packages/core/src/main/lens-proxy/close-on-quit.injectable.ts @@ -0,0 +1,21 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { beforeQuitOfBackEndInjectionToken } from "../start-main-application/runnable-tokens/phases"; +import lensProxyInjectable from "./lens-proxy.injectable"; + +const closeLensProxyOnQuitInjectable = getInjectable({ + id: "close-lens-proxy-on-quit", + instantiate: (di) => ({ + run: async () => { + const lensProxy = di.inject(lensProxyInjectable); + + await lensProxy.close(); + }, + }), + injectionToken: beforeQuitOfBackEndInjectionToken, +}); + +export default closeLensProxyOnQuitInjectable; diff --git a/packages/core/src/main/lens-proxy/get-cluster-for-request.injectable.ts b/packages/core/src/main/lens-proxy/get-cluster-for-request.injectable.ts index 7d529540d1..8be8d1d4e4 100644 --- a/packages/core/src/main/lens-proxy/get-cluster-for-request.injectable.ts +++ b/packages/core/src/main/lens-proxy/get-cluster-for-request.injectable.ts @@ -3,9 +3,9 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import getClusterByIdInjectable from "../../common/cluster-store/get-by-id.injectable"; import { getClusterIdFromHost } from "../../common/utils"; import { apiKubePrefix } from "../../common/vars"; +import getClusterByIdInjectable from "../../features/cluster/storage/common/get-by-id.injectable"; import type { GetClusterForRequest } from "./lens-proxy"; const getClusterForRequestInjectable = getInjectable({ diff --git a/packages/core/src/main/lens-proxy/lens-proxy.ts b/packages/core/src/main/lens-proxy/lens-proxy.ts index 629e6a0f12..ce5a54bdf1 100644 --- a/packages/core/src/main/lens-proxy/lens-proxy.ts +++ b/packages/core/src/main/lens-proxy/lens-proxy.ts @@ -48,7 +48,7 @@ export function isLongRunningRequest(reqUrl: string) { /** * This is the list of ports that chrome considers unsafe to allow HTTP - * conntections to. Because they are the standard ports for processes that are + * connections to. Because they are the standard ports for processes that are * too forgiving in the connection types they accept. * * If we get one of these ports, the easiest thing to do is to just try again. @@ -166,10 +166,17 @@ export class LensProxy { } close() { + if (this.closed) { + return; + } + + // mark as closed immediately + this.closed = true; this.dependencies.logger.info("[LENS-PROXY]: Closing server"); - this.proxyServer.close(); - this.closed = true; + return new Promise((resolve) => { + this.proxyServer.close(() => resolve()); + }); } protected configureProxy(proxy: httpProxy): httpProxy { diff --git a/packages/core/src/main/lens-proxy/proxy-functions/kube-api-upgrade-request.injectable.ts b/packages/core/src/main/lens-proxy/proxy-functions/kube-api-upgrade-request.injectable.ts index d6ddfca49a..a8f7b03b32 100644 --- a/packages/core/src/main/lens-proxy/proxy-functions/kube-api-upgrade-request.injectable.ts +++ b/packages/core/src/main/lens-proxy/proxy-functions/kube-api-upgrade-request.injectable.ts @@ -6,24 +6,24 @@ import { chunk } from "lodash"; import type { ConnectionOptions } from "tls"; import { connect } from "tls"; -import url, { URL } from "url"; +import url from "url"; import { apiKubePrefix } from "../../../common/vars"; import { getInjectable } from "@ogre-tools/injectable"; import type { LensProxyApiRequest } from "../lens-proxy"; import kubeAuthProxyServerInjectable from "../../cluster/kube-auth-proxy-server.injectable"; import kubeAuthProxyCertificateInjectable from "../../kube-auth-proxy/kube-auth-proxy-certificate.injectable"; +import clusterApiUrlInjectable from "../../../features/cluster/connections/main/api-url.injectable"; const skipRawHeaders = new Set(["Host", "Authorization"]); const kubeApiUpgradeRequestInjectable = getInjectable({ id: "kube-api-upgrade-request", instantiate: (di): LensProxyApiRequest => async ({ req, socket, head, cluster }) => { - const clusterUrl = new URL(cluster.apiUrl.get()); + const clusterApiUrl = await di.inject(clusterApiUrlInjectable, cluster)(); const kubeAuthProxyServer = di.inject(kubeAuthProxyServerInjectable, cluster); - const kubeAuthProxyCertificate = di.inject(kubeAuthProxyCertificateInjectable, clusterUrl.hostname); + const kubeAuthProxyCertificate = di.inject(kubeAuthProxyCertificateInjectable, clusterApiUrl.hostname); const proxyUrl = await kubeAuthProxyServer.ensureAuthProxyUrl() + req.url.replace(apiKubePrefix, ""); - const apiUrl = url.parse(cluster.apiUrl.get()); const pUrl = url.parse(proxyUrl); const connectOpts: ConnectionOptions = { port: pUrl.port ? parseInt(pUrl.port) : undefined, @@ -33,7 +33,7 @@ const kubeApiUpgradeRequestInjectable = getInjectable({ const proxySocket = connect(connectOpts, () => { proxySocket.write(`${req.method} ${pUrl.path} HTTP/1.1\r\n`); - proxySocket.write(`Host: ${apiUrl.host}\r\n`); + proxySocket.write(`Host: ${clusterApiUrl.host}\r\n`); for (const [key, value] of chunk(req.rawHeaders, 2)) { if (skipRawHeaders.has(key)) { diff --git a/packages/core/src/main/protocol-handler/__test__/router.test.ts b/packages/core/src/main/protocol-handler/__test__/router.test.ts index 5af2e588d8..e3eddb6b34 100644 --- a/packages/core/src/main/protocol-handler/__test__/router.test.ts +++ b/packages/core/src/main/protocol-handler/__test__/router.test.ts @@ -6,12 +6,10 @@ import * as uuid from "uuid"; import { ProtocolHandlerExtension, ProtocolHandlerInternal, ProtocolHandlerInvalid } from "../../../common/protocol-handler"; -import { delay, noop } from "@k8slens/utilities"; -import type { ExtensionsStore, IsEnabledExtensionDescriptor } from "../../../extensions/extensions-store/extensions-store"; +import { noop } from "@k8slens/utilities"; import type { LensProtocolRouterMain } from "../lens-protocol-router-main/lens-protocol-router-main"; import { getDiForUnitTesting } from "../../getDiForUnitTesting"; import lensProtocolRouterMainInjectable from "../lens-protocol-router-main/lens-protocol-router-main.injectable"; -import extensionsStoreInjectable from "../../../extensions/extensions-store/extensions-store.injectable"; import getConfigurationFileModelInjectable from "../../../common/get-configuration-file-model/get-configuration-file-model.injectable"; import { LensExtension } from "../../../extensions/lens-extension"; import type { ObservableMap } from "mobx"; @@ -23,6 +21,8 @@ import pathExistsInjectable from "../../../common/fs/path-exists.injectable"; import readJsonSyncInjectable from "../../../common/fs/read-json-sync.injectable"; import writeJsonSyncInjectable from "../../../common/fs/write-json-sync.injectable"; import type { LensExtensionId } from "@k8slens/legacy-extensions"; +import type { LensExtensionState } from "../../../features/extensions/enabled/common/state.injectable"; +import enabledExtensionsStateInjectable from "../../../features/extensions/enabled/common/state.injectable"; function throwIfDefined(val: any): void { if (val != null) { @@ -33,7 +33,7 @@ function throwIfDefined(val: any): void { describe("protocol router tests", () => { let extensionInstances: ObservableMap; let lpr: LensProtocolRouterMain; - let enabledExtensions: Set; + let enabledExtensions: ObservableMap; let broadcastMessageMock: jest.Mock; beforeEach(async () => { @@ -44,11 +44,7 @@ describe("protocol router tests", () => { di.override(readJsonSyncInjectable, () => () => { throw new Error("tried call readJsonSync without override"); }); di.override(writeJsonSyncInjectable, () => () => { throw new Error("tried call writeJsonSync without override"); }); - enabledExtensions = new Set(); - - di.override(extensionsStoreInjectable, () => ({ - isEnabled: ({ id, isBundled }: IsEnabledExtensionDescriptor) => isBundled || enabledExtensions.has(id), - } as unknown as ExtensionsStore)); + enabledExtensions = di.inject(enabledExtensionsStateInjectable); di.permitSideEffects(getConfigurationFileModelInjectable); @@ -95,7 +91,7 @@ describe("protocol router tests", () => { }); extensionInstances.set(extId, ext); - enabledExtensions.add(extId); + enabledExtensions.set(extId, { name: "@mirantis/minikube", enabled: true }); lpr.addInternalHandler("/", noop); @@ -105,14 +101,14 @@ describe("protocol router tests", () => { expect(throwIfDefined(error)).not.toThrow(); } + expect(broadcastMessageMock).toHaveBeenCalledWith(ProtocolHandlerInternal, "lens://app", "matched"); + try { expect(await lpr.route("lens://extension/@mirantis/minikube")).toBeUndefined(); } catch (error) { expect(throwIfDefined(error)).not.toThrow(); } - await delay(50); - expect(broadcastMessageMock).toHaveBeenCalledWith(ProtocolHandlerInternal, "lens://app", "matched"); expect(broadcastMessageMock).toHaveBeenCalledWith(ProtocolHandlerExtension, "lens://extension/@mirantis/minikube", "matched"); }); @@ -175,7 +171,7 @@ describe("protocol router tests", () => { }); extensionInstances.set(extId, ext); - enabledExtensions.add(extId); + enabledExtensions.set(extId, { name: "@foobar/icecream", enabled: true }); try { expect(await lpr.route("lens://extension/@foobar/icecream/page/foob")).toBeUndefined(); @@ -183,7 +179,6 @@ describe("protocol router tests", () => { expect(throwIfDefined(error)).not.toThrow(); } - await delay(50); expect(called).toBe("foob"); expect(broadcastMessageMock).toBeCalledWith(ProtocolHandlerExtension, "lens://extension/@foobar/icecream/page/foob", "matched"); }); @@ -214,7 +209,7 @@ describe("protocol router tests", () => { }); extensionInstances.set(extId, ext); - enabledExtensions.add(extId); + enabledExtensions.set(extId, { name: "@foobar/icecream", enabled: true }); } { @@ -240,19 +235,15 @@ describe("protocol router tests", () => { }); extensionInstances.set(extId, ext); - enabledExtensions.add(extId); + enabledExtensions.set(extId, { name: "icecream", enabled: true }); } - enabledExtensions.add("@foobar/icecream"); - enabledExtensions.add("icecream"); - try { expect(await lpr.route("lens://extension/icecream/page")).toBeUndefined(); } catch (error) { expect(throwIfDefined(error)).not.toThrow(); } - await delay(50); expect(called).toBe(1); expect(broadcastMessageMock).toBeCalledWith(ProtocolHandlerExtension, "lens://extension/icecream/page", "matched"); diff --git a/packages/core/src/main/protocol-handler/lens-protocol-router-main/lens-protocol-router-main.injectable.ts b/packages/core/src/main/protocol-handler/lens-protocol-router-main/lens-protocol-router-main.injectable.ts index bce91119c2..92fbdd8737 100644 --- a/packages/core/src/main/protocol-handler/lens-protocol-router-main/lens-protocol-router-main.injectable.ts +++ b/packages/core/src/main/protocol-handler/lens-protocol-router-main/lens-protocol-router-main.injectable.ts @@ -5,22 +5,21 @@ import { getInjectable } from "@ogre-tools/injectable"; import extensionLoaderInjectable from "../../../extensions/extension-loader/extension-loader.injectable"; import { LensProtocolRouterMain } from "./lens-protocol-router-main"; -import extensionsStoreInjectable from "../../../extensions/extensions-store/extensions-store.injectable"; import showApplicationWindowInjectable from "../../start-main-application/lens-window/show-application-window.injectable"; import broadcastMessageInjectable from "../../../common/ipc/broadcast-message.injectable"; import loggerInjectable from "../../../common/logger.injectable"; +import isExtensionEnabledInjectable from "../../../features/extensions/enabled/common/is-enabled.injectable"; const lensProtocolRouterMainInjectable = getInjectable({ id: "lens-protocol-router-main", - instantiate: (di) => - new LensProtocolRouterMain({ - extensionLoader: di.inject(extensionLoaderInjectable), - extensionsStore: di.inject(extensionsStoreInjectable), - showApplicationWindow: di.inject(showApplicationWindowInjectable), - broadcastMessage: di.inject(broadcastMessageInjectable), - logger: di.inject(loggerInjectable), - }), + instantiate: (di) => new LensProtocolRouterMain({ + extensionLoader: di.inject(extensionLoaderInjectable), + isExtensionEnabled: di.inject(isExtensionEnabledInjectable), + showApplicationWindow: di.inject(showApplicationWindowInjectable), + broadcastMessage: di.inject(broadcastMessageInjectable), + logger: di.inject(loggerInjectable), + }), }); export default lensProtocolRouterMainInjectable; diff --git a/packages/core/src/main/protocol-handler/lens-protocol-router-main/lens-protocol-router-main.ts b/packages/core/src/main/protocol-handler/lens-protocol-router-main/lens-protocol-router-main.ts index fb0ad71892..8185ca4e68 100644 --- a/packages/core/src/main/protocol-handler/lens-protocol-router-main/lens-protocol-router-main.ts +++ b/packages/core/src/main/protocol-handler/lens-protocol-router-main/lens-protocol-router-main.ts @@ -119,7 +119,13 @@ export class LensProtocolRouterMain extends proto.LensProtocolRouter { const rawUrl = url.toString(); // for sending to renderer const attempt = super._routeToInternal(url); - this.disposers.push(when(() => this.rendererLoaded, () => this.dependencies.broadcastMessage(proto.ProtocolHandlerInternal, rawUrl, attempt))); + const sendRoutingToRenderer = () => this.dependencies.broadcastMessage(proto.ProtocolHandlerInternal, rawUrl, attempt); + + if (this.rendererLoaded) { + sendRoutingToRenderer(); + } else { + this.disposers.push(when(() => this.rendererLoaded, sendRoutingToRenderer)); + } return attempt; } @@ -136,7 +142,13 @@ export class LensProtocolRouterMain extends proto.LensProtocolRouter { */ const attempt = await super._routeToExtension(new URLParse(url.toString(), true)); - this.disposers.push(when(() => this.rendererLoaded, () => this.dependencies.broadcastMessage(proto.ProtocolHandlerExtension, rawUrl, attempt))); + const sendRoutingToRenderer = () => this.dependencies.broadcastMessage(proto.ProtocolHandlerExtension, rawUrl, attempt); + + if (this.rendererLoaded) { + sendRoutingToRenderer(); + } else { + this.disposers.push(when(() => this.rendererLoaded, sendRoutingToRenderer)); + } return attempt; } diff --git a/packages/core/src/main/routes/kubeconfig-route/get-service-account-route.injectable.ts b/packages/core/src/main/routes/kubeconfig-route/get-service-account-route.injectable.ts index b6b77d5a40..267a3eafe7 100644 --- a/packages/core/src/main/routes/kubeconfig-route/get-service-account-route.injectable.ts +++ b/packages/core/src/main/routes/kubeconfig-route/get-service-account-route.injectable.ts @@ -5,12 +5,11 @@ import { apiPrefix } from "../../../common/vars"; import { getRouteInjectable } from "../../router/router.injectable"; -import type { Cluster } from "../../../common/cluster/cluster"; -import type { V1Secret } from "@kubernetes/client-node"; import { CoreV1Api } from "@kubernetes/client-node"; import { clusterRoute } from "../../router/route"; -import { dump } from "js-yaml"; +import * as yaml from "js-yaml"; import loadProxyKubeconfigInjectable from "../../cluster/load-proxy-kubeconfig.injectable"; +import clusterApiUrlInjectable from "../../../features/cluster/connections/main/api-url.injectable"; const getServiceAccountRouteInjectable = getRouteInjectable({ id: "get-service-account-route", @@ -25,73 +24,57 @@ const getServiceAccountRouteInjectable = getRouteInjectable({ const secretList = await client.listNamespacedSecret(params.namespace); const secret = secretList.body.items.find(secret => { - const { annotations } = secret.metadata ?? {}; + const { annotations = {}} = secret.metadata ?? {}; - return annotations?.["kubernetes.io/service-account.name"] === params.account; + return annotations["kubernetes.io/service-account.name"] === params.account; }); - if (!secret) { + if (!secret || !secret.data || !secret.metadata) { return { error: "No secret found", statusCode: 404, }; } - const kubeconfig = generateKubeConfig(params.account, secret, cluster); - - if (!kubeconfig) { - return { - error: "No secret found", - statusCode: 404, - }; - } + const { token, "ca.crt": caCrt } = secret.data; + const apiUrl = (await di.inject(clusterApiUrlInjectable, cluster)()).toString(); + const contextName = cluster.contextName.get(); return { - response: kubeconfig, + response: yaml.dump({ + apiVersion: "v1", + kind: "Config", + clusters: [ + { + name: contextName, + cluster: { + server: apiUrl, + "certificate-authority-data": caCrt, + }, + }, + ], + users: [ + { + name: params.account, + user: { + token: Buffer.from(token, "base64").toString("utf8"), + }, + }, + ], + contexts: [ + { + name: `${contextName}-${params.account}`, + context: { + user: params.account, + cluster: contextName, + namespace: secret.metadata.namespace, + }, + }, + ], + "current-context": contextName, + }), }; }), }); export default getServiceAccountRouteInjectable; - -function generateKubeConfig(username: string, secret: V1Secret, cluster: Cluster): string | undefined { - if (!secret.data || !secret.metadata) { - return undefined; - } - - const { token, "ca.crt": caCrt } = secret.data; - const tokenData = Buffer.from(token, "base64"); - - return dump({ - "apiVersion": "v1", - "kind": "Config", - "clusters": [ - { - "name": cluster.contextName.get(), - "cluster": { - "server": cluster.apiUrl.get(), - "certificate-authority-data": caCrt, - }, - }, - ], - "users": [ - { - "name": username, - "user": { - "token": tokenData.toString("utf8"), - }, - }, - ], - "contexts": [ - { - "name": cluster.contextName.get(), - "context": { - "user": username, - "cluster": cluster.contextName.get(), - "namespace": secret.metadata.namespace, - }, - }, - ], - "current-context": cluster.contextName.get(), - }); -} diff --git a/packages/core/src/main/shell-session/local-shell-session/local-shell-session.ts b/packages/core/src/main/shell-session/local-shell-session/local-shell-session.ts index 567fc1dca7..b066702266 100644 --- a/packages/core/src/main/shell-session/local-shell-session/local-shell-session.ts +++ b/packages/core/src/main/shell-session/local-shell-session/local-shell-session.ts @@ -3,17 +3,17 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { UserStore } from "../../../common/user-store"; import type { ShellSessionArgs, ShellSessionDependencies } from "../shell-session"; import { ShellSession } from "../shell-session"; import type { ModifyTerminalShellEnv } from "../shell-env-modifier/modify-terminal-shell-env.injectable"; import type { JoinPaths } from "../../../common/path/join-paths.injectable"; import type { GetDirnameOfPath } from "../../../common/path/get-dirname.injectable"; import type { GetBasenameOfPath } from "../../../common/path/get-basename.injectable"; +import type { UserPreferencesState } from "../../../features/user-preferences/common/state.injectable"; export interface LocalShellSessionDependencies extends ShellSessionDependencies { readonly directoryForBinaries: string; - readonly userStore: UserStore; + readonly state: UserPreferencesState; modifyTerminalShellEnv: ModifyTerminalShellEnv; joinPaths: JoinPaths; getDirnameOfPath: GetDirnameOfPath; @@ -50,8 +50,8 @@ export class LocalShellSession extends ShellSession { } protected async getShellArgs(shell: string): Promise { - const pathFromPreferences = this.dependencies.userStore.kubectlBinariesPath || this.kubectl.getBundledPath(); - const kubectlPathDir = this.dependencies.userStore.downloadKubectlBinaries + const pathFromPreferences = this.dependencies.state.kubectlBinariesPath || this.kubectl.getBundledPath(); + const kubectlPathDir = this.dependencies.state.downloadKubectlBinaries ? this.dependencies.directoryContainingKubectl : this.dependencies.getDirnameOfPath(pathFromPreferences); diff --git a/packages/core/src/main/shell-session/local-shell-session/open.injectable.ts b/packages/core/src/main/shell-session/local-shell-session/open.injectable.ts index 4d5d54984a..29cf79e63a 100644 --- a/packages/core/src/main/shell-session/local-shell-session/open.injectable.ts +++ b/packages/core/src/main/shell-session/local-shell-session/open.injectable.ts @@ -12,19 +12,21 @@ import isMacInjectable from "../../../common/vars/is-mac.injectable"; import type { Cluster } from "../../../common/cluster/cluster"; import isWindowsInjectable from "../../../common/vars/is-windows.injectable"; import loggerInjectable from "../../../common/logger.injectable"; -import userStoreInjectable from "../../../common/user-store/user-store.injectable"; import type WebSocket from "ws"; import getDirnameOfPathInjectable from "../../../common/path/get-dirname.injectable"; import joinPathsInjectable from "../../../common/path/join-paths.injectable"; import getBasenameOfPathInjectable from "../../../common/path/get-basename.injectable"; import computeShellEnvironmentInjectable from "../../../features/shell-sync/main/compute-shell-environment.injectable"; import spawnPtyInjectable from "../spawn-pty.injectable"; -import userShellSettingInjectable from "../../../common/user-store/shell-setting.injectable"; import appNameInjectable from "../../../common/vars/app-name.injectable"; import buildVersionInjectable from "../../vars/build-version/build-version.injectable"; import emitAppEventInjectable from "../../../common/app-event-bus/emit-event.injectable"; import statInjectable from "../../../common/fs/stat.injectable"; import kubeconfigManagerInjectable from "../../kubeconfig-manager/kubeconfig-manager.injectable"; +import userPreferencesStateInjectable from "../../../features/user-preferences/common/state.injectable"; +import userShellSettingInjectable from "../../../features/user-preferences/common/shell-setting.injectable"; +import shellSessionEnvsInjectable from "../shell-envs.injectable"; +import shellSessionProcessesInjectable from "../processes.injectable"; export interface OpenLocalShellSessionArgs { websocket: WebSocket; @@ -44,10 +46,12 @@ const openLocalShellSessionInjectable = getInjectable({ isMac: di.inject(isMacInjectable), isWindows: di.inject(isWindowsInjectable), logger: di.inject(loggerInjectable), - userStore: di.inject(userStoreInjectable), + state: di.inject(userPreferencesStateInjectable), userShellSetting: di.inject(userShellSettingInjectable), appName: di.inject(appNameInjectable), buildVersion: di.inject(buildVersionInjectable), + shellSessionEnvs: di.inject(shellSessionEnvsInjectable), + shellSessionProcesses: di.inject(shellSessionProcessesInjectable), modifyTerminalShellEnv: di.inject(modifyTerminalShellEnvInjectable), emitAppEvent: di.inject(emitAppEventInjectable), getDirnameOfPath: di.inject(getDirnameOfPathInjectable), diff --git a/packages/core/src/main/shell-session/local-shell-session/techincal.test.ts b/packages/core/src/main/shell-session/local-shell-session/techincal.test.ts index f37e690561..6d921ee5fe 100644 --- a/packages/core/src/main/shell-session/local-shell-session/techincal.test.ts +++ b/packages/core/src/main/shell-session/local-shell-session/techincal.test.ts @@ -103,8 +103,6 @@ describe("technical unit tests for local shell sessions", () => { contextName: "some-context-name", id: "some-cluster-id", kubeConfigPath: "/some-kube-config-path", - }, { - clusterServerUrl: "https://localhost:9999", }); await openLocalShellSession({ diff --git a/packages/core/src/main/shell-session/node-shell-session/open.injectable.ts b/packages/core/src/main/shell-session/node-shell-session/open.injectable.ts index 55ec26d362..bdff572b12 100644 --- a/packages/core/src/main/shell-session/node-shell-session/open.injectable.ts +++ b/packages/core/src/main/shell-session/node-shell-session/open.injectable.ts @@ -14,7 +14,6 @@ import loggerInjectable from "../../../common/logger.injectable"; import createKubeJsonApiForClusterInjectable from "../../../common/k8s-api/create-kube-json-api-for-cluster.injectable"; import computeShellEnvironmentInjectable from "../../../features/shell-sync/main/compute-shell-environment.injectable"; import spawnPtyInjectable from "../spawn-pty.injectable"; -import userShellSettingInjectable from "../../../common/user-store/shell-setting.injectable"; import appNameInjectable from "../../../common/vars/app-name.injectable"; import buildVersionInjectable from "../../vars/build-version/build-version.injectable"; import emitAppEventInjectable from "../../../common/app-event-bus/emit-event.injectable"; @@ -22,6 +21,9 @@ import statInjectable from "../../../common/fs/stat.injectable"; import createKubeApiInjectable from "../../../common/k8s-api/create-kube-api.injectable"; import loadProxyKubeconfigInjectable from "../../cluster/load-proxy-kubeconfig.injectable"; import kubeconfigManagerInjectable from "../../kubeconfig-manager/kubeconfig-manager.injectable"; +import userShellSettingInjectable from "../../../features/user-preferences/common/shell-setting.injectable"; +import shellSessionEnvsInjectable from "../shell-envs.injectable"; +import shellSessionProcessesInjectable from "../processes.injectable"; export interface NodeShellSessionArgs { websocket: WebSocket; @@ -43,6 +45,8 @@ const openNodeShellSessionInjectable = getInjectable({ userShellSetting: di.inject(userShellSettingInjectable), appName: di.inject(appNameInjectable), buildVersion: di.inject(buildVersionInjectable), + shellSessionEnvs: di.inject(shellSessionEnvsInjectable), + shellSessionProcesses: di.inject(shellSessionProcessesInjectable), createKubeJsonApiForCluster: di.inject(createKubeJsonApiForClusterInjectable), computeShellEnvironment: di.inject(computeShellEnvironmentInjectable), spawnPty: di.inject(spawnPtyInjectable), diff --git a/packages/core/src/main/shell-session/processes.injectable.ts b/packages/core/src/main/shell-session/processes.injectable.ts new file mode 100644 index 0000000000..ffe9934401 --- /dev/null +++ b/packages/core/src/main/shell-session/processes.injectable.ts @@ -0,0 +1,15 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import type { IPty } from "node-pty"; + +export type ShellSessionProcesses = Map; + +const shellSessionProcessesInjectable = getInjectable({ + id: "shell-session-processes", + instantiate: (): ShellSessionProcesses => new Map(), +}); + +export default shellSessionProcessesInjectable; diff --git a/packages/core/src/main/shell-session/shell-envs.injectable.ts b/packages/core/src/main/shell-session/shell-envs.injectable.ts new file mode 100644 index 0000000000..4b866c825b --- /dev/null +++ b/packages/core/src/main/shell-session/shell-envs.injectable.ts @@ -0,0 +1,14 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; + +export type ShellSessionEnvs = Map>; + +const shellSessionEnvsInjectable = getInjectable({ + id: "shell-session-envs", + instantiate: (): ShellSessionEnvs => new Map(), +}); + +export default shellSessionEnvsInjectable; diff --git a/packages/core/src/main/shell-session/shell-session.ts b/packages/core/src/main/shell-session/shell-session.ts index ae1434287a..90c8e80297 100644 --- a/packages/core/src/main/shell-session/shell-session.ts +++ b/packages/core/src/main/shell-session/shell-session.ts @@ -19,6 +19,8 @@ import type { InitializableState } from "../../common/initializable-state/create import type { EmitAppEvent } from "../../common/app-event-bus/emit-event.injectable"; import type { Stat } from "../../common/fs/stat.injectable"; import type { IComputedValue } from "mobx"; +import type { ShellSessionEnvs } from "./shell-envs.injectable"; +import type { ShellSessionProcesses } from "./processes.injectable"; export class ShellOpenError extends Error { constructor(message: string, options?: ErrorOptions) { @@ -113,6 +115,8 @@ export interface ShellSessionDependencies { readonly buildVersion: InitializableState; readonly proxyKubeconfigPath: string; readonly directoryContainingKubectl: string; + readonly shellSessionEnvs: ShellSessionEnvs; + readonly shellSessionProcesses: ShellSessionProcesses; computeShellEnvironment: ComputeShellEnvironment; spawnPty: SpawnPty; emitAppEvent: EmitAppEvent; @@ -129,25 +133,6 @@ export interface ShellSessionArgs { export abstract class ShellSession { abstract readonly ShellType: string; - private static readonly shellEnvs = new Map>(); - private static readonly processes = new Map(); - - /** - * Kill all remaining shell backing processes. Should be called when about to - * quit - */ - public static cleanup(): void { - for (const shellProcess of this.processes.values()) { - try { - process.kill(shellProcess.pid); - } catch { - // ignore error - } - } - - this.processes.clear(); - } - protected running = false; protected readonly terminalId: string; protected readonly kubectl: Kubectl; @@ -157,8 +142,8 @@ export abstract class ShellSession { protected abstract get cwd(): string | undefined; protected ensureShellProcess(shell: string, args: string[], env: Partial>, cwd: string): { shellProcess: pty.IPty; resume: boolean } { - const resume = ShellSession.processes.has(this.terminalId); - const shellProcess = getOrInsertWith(ShellSession.processes, this.terminalId, () => ( + const resume = this.dependencies.shellSessionProcesses.has(this.terminalId); + const shellProcess = getOrInsertWith(this.dependencies.shellSessionProcesses, this.terminalId, () => ( this.dependencies.spawnPty(shell, args, { rows: 30, cols: 80, @@ -304,7 +289,7 @@ export abstract class ShellSession { try { this.dependencies.logger.info(`[SHELL-SESSION]: Killing shell process (pid=${shellProcess.pid}) for ${this.terminalId}`); shellProcess.kill(); - ShellSession.processes.delete(this.terminalId); + this.dependencies.shellSessionProcesses.delete(this.terminalId); } catch (error) { this.dependencies.logger.warn(`[SHELL-SESSION]: failed to kill shell process (pid=${shellProcess.pid}) for ${this.terminalId}`, error); } @@ -321,15 +306,15 @@ export abstract class ShellSession { protected async getCachedShellEnv() { const { id: clusterId } = this.cluster; - let env = ShellSession.shellEnvs.get(clusterId); + let env = this.dependencies.shellSessionEnvs.get(clusterId); if (!env) { env = await this.getShellEnv(); - ShellSession.shellEnvs.set(clusterId, env); + this.dependencies.shellSessionEnvs.set(clusterId, env); } else { // refresh env in the background this.getShellEnv().then((shellEnv: any) => { - ShellSession.shellEnvs.set(clusterId, shellEnv); + this.dependencies.shellSessionEnvs.set(clusterId, shellEnv); }); } diff --git a/packages/core/src/main/start-main-application/lens-window/application-window/__test__/session-certificate-verifier.test.ts b/packages/core/src/main/start-main-application/lens-window/application-window/__test__/session-certificate-verifier.test.ts new file mode 100644 index 0000000000..a8db1947a3 --- /dev/null +++ b/packages/core/src/main/start-main-application/lens-window/application-window/__test__/session-certificate-verifier.test.ts @@ -0,0 +1,78 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import type { DiContainer } from "@ogre-tools/injectable"; +import setupLensProxyCertificateInjectable from "../../../../start-main-application/runnables/setup-lens-proxy-certificate.injectable"; +import lensProxyCertificateInjectable from "../../../../../common/certificate/lens-proxy-certificate.injectable"; +import { getDiForUnitTesting } from "../../../../getDiForUnitTesting"; +import sessionCertificateVerifierInjectable, { ChromiumNetError } from "../session-certificate-verifier.injectable"; + +const externalCertificate = `-----BEGIN CERTIFICATE----- +MIIFzzCCBLegAwIBAgIQByL1wEn7yGRLqHZvmBzvpTANBgkqhkiG9w0BAQsFADA8 +MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRwwGgYDVQQDExNBbWF6b24g +UlNBIDIwNDggTTAyMB4XDTIzMDIwOTAwMDAwMFoXDTIzMTAxNDIzNTk1OVowFjEU +MBIGA1UEAxMLazhzbGVucy5kZXYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQDNdPm5tKztUpgHgDHjktelNvRsaj4QHTzShUP5p2uGu+lNHhPByp3+fp8p +v6V4PhRyH006RcyvUkQlEOiprP0fF/L16Jlrlo13N7hspVS4drlxE0v4JcLxBKm8 +pwsv7bfeZ7g6SWKA/0wbSTk8AyL0rCgcpMUWyPloq3gInO1x7kazgCAgrB34CSdj +JyD1Y8Od8eH8C9qdRlTcV0rG8y2np8YbK1lF77CXjD2feGjiUAMUAtArGKCZOc33 +erdhvXgJQ1/SgWcEbbhEZ7j8cfH6y7hPPmU43epyePvY0SZ7x1PBt870W1LjG6lq +pfzqxVVxmT6Txiktnd/6cHCzfxjbAgMBAAGjggLxMIIC7TAfBgNVHSMEGDAWgBTA +MVLNWlDDgnx0cc7L6Zz5euuC4jAdBgNVHQ4EFgQUcC3Qdy61LUiE9hOvDJGYC/yt +fu0wJQYDVR0RBB4wHIILazhzbGVucy5kZXaCDSouazhzbGVucy5kZXYwDgYDVR0P +AQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjA7BgNVHR8E +NDAyMDCgLqAshipodHRwOi8vY3JsLnIybTAyLmFtYXpvbnRydXN0LmNvbS9yMm0w +Mi5jcmwwEwYDVR0gBAwwCjAIBgZngQwBAgEwdQYIKwYBBQUHAQEEaTBnMC0GCCsG +AQUFBzABhiFodHRwOi8vb2NzcC5yMm0wMi5hbWF6b250cnVzdC5jb20wNgYIKwYB +BQUHMAKGKmh0dHA6Ly9jcnQucjJtMDIuYW1hem9udHJ1c3QuY29tL3IybTAyLmNl +cjAMBgNVHRMBAf8EAjAAMIIBfAYKKwYBBAHWeQIEAgSCAWwEggFoAWYAdQDoPtDa +PvUGNTLnVyi8iWvJA9PL0RFr7Otp4Xd9bQa9bgAAAYY4etOIAAAEAwBGMEQCIGT/ +/BWgTcOFQdzEX2qKlArMTvMwXggEY+m4ervIFLFnAiAyuX0I9jbGBI1XBiQ2mjXT +FIGw3TMF5b4rrCwhkRBG/gB1ALNzdwfhhFD4Y4bWBancEQlKeS2xZwwLh9zwAw55 +NqWaAAABhjh6084AAAQDAEYwRAIgewezL8S3+qwozF4fNt+0FiV95luazD1yKb35 +ZeOqudACIC7eFoZsaySOOivbqIp+nr9PB3qD08C1VKoi/LmnDp+3AHYAtz77JN+c +Tbp18jnFulj0bF38Qs96nzXEnh0JgSXttJkAAAGGOHrTlgAABAMARzBFAiAmZyNU +1H54FbGdwwXVXPxNYVE3MUlHswkR56WvWkvJ0wIhAJELvOBDIsCJ5uxTam2Xaxe0 +nZ+YTVzXDoQAfHplV1N6MA0GCSqGSIb3DQEBCwUAA4IBAQAghl2vkfW4Gph6Ez/v +EA/INeDXSErm/o3zBv4uTS7kuINPAtTlDtVJW/usw++F5fmgjmyNVc94y35hFG9Q +8LTDgJWvxekmiJJ+FCAxbpkhqXjHhugXwoUvAKktpyFnw+1cliYeA01EevOhnN+n +ux6vjEyhhEZm/JV/TXWaNSmVprXRXwc1m5dQzEEqkXgIhhhSK7E/63L+Zm548cjp +LAp+pJnaHfg0a83QnPWyZeyob+GklQjEdx64i+7wAhhpUp1Ge2TnFfs6zQGv2Y7/ +mgyzhHkKlUwQb5pi0rgR4oqKhnItyXjWqN3Y3wefTJblIs2sxEtYEzBUwlQZ3YM/ +ycM4 +-----END CERTIFICATE-----`; + +describe("sessionCertificateVerifier", () => { + let di: DiContainer; + + beforeEach(() => { + di = getDiForUnitTesting(); + di.unoverride(lensProxyCertificateInjectable); + di.inject(setupLensProxyCertificateInjectable).run(); + }); + + it("marks lens proxy certificate as trusted", () => { + const sessionCertificateVerifier = di.inject(sessionCertificateVerifierInjectable); + const lensProxyCertificate = di.inject(lensProxyCertificateInjectable).get(); + const callback = jest.fn(); + + sessionCertificateVerifier({ + certificate: { data: lensProxyCertificate.cert }, + } as any, callback); + + expect(callback).toHaveBeenCalledWith(ChromiumNetError.SUCCESS); + }); + + it("passes verification to chromium on non lens proxy certificate", () => { + const sessionCertificateVerifier = di.inject(sessionCertificateVerifierInjectable); + const callback = jest.fn(); + + sessionCertificateVerifier({ + certificate: { data: externalCertificate }, + } as any, callback); + + expect(callback).toHaveBeenCalledWith(ChromiumNetError.RESULT_FROM_CHROMIUM); + }); +}); diff --git a/packages/core/src/main/start-main-application/lens-window/application-window/create-electron-window.injectable.ts b/packages/core/src/main/start-main-application/lens-window/application-window/create-electron-window.injectable.ts index b53f58c838..0693c71643 100644 --- a/packages/core/src/main/start-main-application/lens-window/application-window/create-electron-window.injectable.ts +++ b/packages/core/src/main/start-main-application/lens-window/application-window/create-electron-window.injectable.ts @@ -3,7 +3,6 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { timingSafeEqual, X509Certificate } from "crypto"; import loggerInjectable from "../../../../common/logger.injectable"; import applicationWindowStateInjectable from "./application-window-state.injectable"; import { BrowserWindow } from "electron"; @@ -14,8 +13,8 @@ import getAbsolutePathInjectable from "../../../../common/path/get-absolute-path import lensResourcesDirInjectable from "../../../../common/vars/lens-resources-dir.injectable"; import isLinuxInjectable from "../../../../common/vars/is-linux.injectable"; import pathExistsSyncInjectable from "../../../../common/fs/path-exists-sync.injectable"; -import lensProxyCertificateInjectable from "../../../../common/certificate/lens-proxy-certificate.injectable"; import { applicationInformationToken } from "@k8slens/application"; +import sessionCertificateVerifierInjectable from "./session-certificate-verifier.injectable"; export type ElectronWindowTitleBarStyle = "hiddenInset" | "hidden" | "default" | "customButtonsOnHover"; @@ -27,13 +26,6 @@ export interface UrlSource { } export type ContentSource = RequireExactlyOne; -// see https://www.electronjs.org/docs/latest/api/session#sessetcertificateverifyprocproc -enum ChromiumNetError { - SUCCESS = 0, - FAILURE = -2, - RESULT_FROM_CHROMIUM = -3, -} - export interface ElectronWindowConfiguration { id: string; title: string; @@ -64,8 +56,7 @@ const createElectronWindowInjectable = getInjectable({ const isLinux = di.inject(isLinuxInjectable); const applicationInformation = di.inject(applicationInformationToken); const pathExistsSync = di.inject(pathExistsSyncInjectable); - const lensProxyCertificate = di.inject(lensProxyCertificateInjectable).get(); - const lensProxyX509Cert = new X509Certificate(lensProxyCertificate.cert); + const sessionCertificateVerifier = di.inject(sessionCertificateVerifierInjectable); return (configuration) => { const applicationWindowState = di.inject( @@ -119,14 +110,7 @@ const createElectronWindowInjectable = getInjectable({ applicationWindowState.manage(browserWindow); - browserWindow.webContents.session.setCertificateVerifyProc((request, shouldBeTrusted) => { - const { certificate } = request; - const cert = new X509Certificate(certificate.data); - const shouldTrustCert = cert.raw.length === lensProxyX509Cert.raw.length - && timingSafeEqual(cert.raw, lensProxyX509Cert.raw); - - shouldBeTrusted(shouldTrustCert ? ChromiumNetError.SUCCESS : ChromiumNetError.RESULT_FROM_CHROMIUM); - }); + browserWindow.webContents.session.setCertificateVerifyProc(sessionCertificateVerifier); browserWindow .on("focus", () => { diff --git a/packages/core/src/main/start-main-application/lens-window/application-window/create-lens-window.injectable.ts b/packages/core/src/main/start-main-application/lens-window/application-window/create-lens-window.injectable.ts index e3854f6df6..3d6e636c8a 100644 --- a/packages/core/src/main/start-main-application/lens-window/application-window/create-lens-window.injectable.ts +++ b/packages/core/src/main/start-main-application/lens-window/application-window/create-lens-window.injectable.ts @@ -4,7 +4,7 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import type { ContentSource, ElectronWindowTitleBarStyle } from "./create-electron-window.injectable"; -import createElectronWindowForInjectable from "./create-electron-window.injectable"; +import createElectronWindowInjectable from "./create-electron-window.injectable"; import type { ClusterFrameInfo } from "../../../../common/cluster-frames.injectable"; export interface ElectronWindow { @@ -59,7 +59,7 @@ const createLensWindowInjectable = getInjectable({ id: "create-lens-window", instantiate: (di) => { - const createElectronWindow = di.inject(createElectronWindowForInjectable); + const createElectronWindow = di.inject(createElectronWindowInjectable); return (configuration: LensWindowConfiguration): LensWindow => { let browserWindow: ElectronWindow | undefined; diff --git a/packages/core/src/main/start-main-application/lens-window/application-window/session-certificate-verifier.injectable.ts b/packages/core/src/main/start-main-application/lens-window/application-window/session-certificate-verifier.injectable.ts new file mode 100644 index 0000000000..33ec0d1f09 --- /dev/null +++ b/packages/core/src/main/start-main-application/lens-window/application-window/session-certificate-verifier.injectable.ts @@ -0,0 +1,36 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { timingSafeEqual, X509Certificate } from "crypto"; +import type { Request } from "electron"; +import lensProxyCertificateInjectable from "../../../../common/certificate/lens-proxy-certificate.injectable"; + +// see https://www.electronjs.org/docs/latest/api/session#sessetcertificateverifyprocproc +export enum ChromiumNetError { + SUCCESS = 0, + FAILURE = -2, + RESULT_FROM_CHROMIUM = -3, +} + +export type CertificateVerificationCallback = (error: ChromiumNetError) => void; + +const sessionCertificateVerifierInjectable = getInjectable({ + id: "session-certificate-verifier", + instantiate: (di) => { + const lensProxyCertificate = di.inject(lensProxyCertificateInjectable).get(); + const lensProxyX509Cert = new X509Certificate(lensProxyCertificate.cert); + + return (request: Request, shouldBeTrusted: CertificateVerificationCallback) => { + const { certificate } = request; + const cert = new X509Certificate(certificate.data); + const shouldTrustCert = cert.raw.length === lensProxyX509Cert.raw.length + && timingSafeEqual(cert.raw, lensProxyX509Cert.raw); + + shouldBeTrusted(shouldTrustCert ? ChromiumNetError.SUCCESS : ChromiumNetError.RESULT_FROM_CHROMIUM); + }; + }, +}); + +export default sessionCertificateVerifierInjectable; diff --git a/packages/core/src/main/start-main-application/runnable-tokens/phases.ts b/packages/core/src/main/start-main-application/runnable-tokens/phases.ts index 5808062fb2..b5844436e1 100644 --- a/packages/core/src/main/start-main-application/runnable-tokens/phases.ts +++ b/packages/core/src/main/start-main-application/runnable-tokens/phases.ts @@ -10,7 +10,7 @@ export const beforeQuitOfFrontEndInjectionToken = getInjectionToken({ +export const beforeQuitOfBackEndInjectionToken = getInjectionToken({ id: "before-quit-of-back-end", }); diff --git a/packages/core/src/main/start-main-application/runnables/clean-up-shell-sessions.injectable.ts b/packages/core/src/main/start-main-application/runnables/clean-up-shell-sessions.injectable.ts index 2388f0fa73..fd428dd1fe 100644 --- a/packages/core/src/main/start-main-application/runnables/clean-up-shell-sessions.injectable.ts +++ b/packages/core/src/main/start-main-application/runnables/clean-up-shell-sessions.injectable.ts @@ -4,13 +4,31 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import { beforeQuitOfBackEndInjectionToken } from "../runnable-tokens/phases"; -import { ShellSession } from "../../shell-session/shell-session"; +import shellSessionProcessesInjectable from "../../shell-session/processes.injectable"; +import prefixedLoggerInjectable from "../../../common/logger/prefixed-logger.injectable"; const cleanUpShellSessionsInjectable = getInjectable({ id: "clean-up-shell-sessions", - instantiate: () => ({ - run: () => void ShellSession.cleanup(), + instantiate: (di) => ({ + run: () => { + const shellSessionProcesses = di.inject(shellSessionProcessesInjectable); + const logger = di.inject(prefixedLoggerInjectable, "SHELL-SESSIONS"); + + logger.info("Killing all remaining shell sessions"); + + for (const { pid } of shellSessionProcesses.values()) { + try { + process.kill(pid); + } catch { + // ignore error + } + } + + shellSessionProcesses.clear(); + + return undefined; + }, }), injectionToken: beforeQuitOfBackEndInjectionToken, diff --git a/packages/core/src/main/start-main-application/runnables/initialize-extensions.injectable.ts b/packages/core/src/main/start-main-application/runnables/initialize-extensions.injectable.ts index a986bea2ef..cf89992ade 100644 --- a/packages/core/src/main/start-main-application/runnables/initialize-extensions.injectable.ts +++ b/packages/core/src/main/start-main-application/runnables/initialize-extensions.injectable.ts @@ -8,6 +8,7 @@ import extensionDiscoveryInjectable from "../../../extensions/extension-discover import extensionLoaderInjectable from "../../../extensions/extension-loader/extension-loader.injectable"; import showErrorPopupInjectable from "../../electron-app/features/show-error-popup.injectable"; import { onLoadOfApplicationInjectionToken } from "@k8slens/application"; +import setupShellInjectable from "../../../features/shell-sync/main/setup-shell.injectable"; const initializeExtensionsInjectable = getInjectable({ id: "initialize-extensions", @@ -51,6 +52,7 @@ const initializeExtensionsInjectable = getInjectable({ console.trace(); } }, + runAfter: setupShellInjectable, }), causesSideEffects: true, diff --git a/packages/core/src/main/stop-services-and-exit-app.injectable.ts b/packages/core/src/main/stop-services-and-exit-app.injectable.ts index 402862b462..f9bd436818 100644 --- a/packages/core/src/main/stop-services-and-exit-app.injectable.ts +++ b/packages/core/src/main/stop-services-and-exit-app.injectable.ts @@ -8,6 +8,7 @@ import clusterManagerInjectable from "./cluster/manager.injectable"; import loggerInjectable from "../common/logger.injectable"; import closeAllWindowsInjectable from "./start-main-application/lens-window/hide-all-windows/close-all-windows.injectable"; import emitAppEventInjectable from "../common/app-event-bus/emit-event.injectable"; +import stopAllExtensionsInjectable from "../features/extensions/stopping/main/stop-all.injectable"; const stopServicesAndExitAppInjectable = getInjectable({ id: "stop-services-and-exit-app", @@ -18,11 +19,13 @@ const stopServicesAndExitAppInjectable = getInjectable({ const logger = di.inject(loggerInjectable); const closeAllWindows = di.inject(closeAllWindowsInjectable); const emitAppEvent = di.inject(emitAppEventInjectable); + const stopAllExtensions = di.inject(stopAllExtensionsInjectable); - return () => { + return async () => { emitAppEvent({ name: "service", action: "close" }); closeAllWindows(); clusterManager.stop(); + await stopAllExtensions(); logger.info("SERVICE:QUIT"); setTimeout(exitApp, 1000); }; diff --git a/packages/core/src/main/stores/init-user-store.injectable.ts b/packages/core/src/main/stores/init-user-store.injectable.ts deleted file mode 100644 index b7d1dac33d..0000000000 --- a/packages/core/src/main/stores/init-user-store.injectable.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import userStoreFileNameMigrationInjectable from "../../common/user-store/file-name-migration.injectable"; -import userStoreInjectable from "../../common/user-store/user-store.injectable"; -import { beforeApplicationIsLoadingInjectionToken } from "@k8slens/application"; -import initDefaultUpdateChannelInjectable from "../vars/default-update-channel/init.injectable"; - -const initUserStoreInjectable = getInjectable({ - id: "init-user-store", - instantiate: (di) => ({ - run: async () => { - const userStore = di.inject(userStoreInjectable); - const userStoreFileNameMigration = di.inject(userStoreFileNameMigrationInjectable); - - await userStoreFileNameMigration(); - userStore.load(); - }, - runAfter: initDefaultUpdateChannelInjectable, - }), - injectionToken: beforeApplicationIsLoadingInjectionToken, -}); - -export default initUserStoreInjectable; diff --git a/packages/core/src/main/utils/resolve-system-proxy/resolve-system-proxy-from-electron.injectable.ts b/packages/core/src/main/utils/resolve-system-proxy/resolve-system-proxy-from-electron.injectable.ts index 4ad2da39b8..1a111f7dd7 100644 --- a/packages/core/src/main/utils/resolve-system-proxy/resolve-system-proxy-from-electron.injectable.ts +++ b/packages/core/src/main/utils/resolve-system-proxy/resolve-system-proxy-from-electron.injectable.ts @@ -10,11 +10,12 @@ const resolveSystemProxyFromElectronInjectable = getInjectable({ id: "resolve-system-proxy-from-electron", instantiate: (di) => { - const helperWindow = di.inject(resolveSystemProxyWindowInjectable); const withErrorLoggingFor = di.inject(withErrorLoggingInjectable); const withErrorLogging = withErrorLoggingFor(() => "Error resolving proxy"); return withErrorLogging(async (url: string) => { + const helperWindow = await di.inject(resolveSystemProxyWindowInjectable); + return await helperWindow.webContents.session.resolveProxy(url); }); }, diff --git a/packages/core/src/main/utils/resolve-system-proxy/resolve-system-proxy-from-electron.test.ts b/packages/core/src/main/utils/resolve-system-proxy/resolve-system-proxy-from-electron.test.ts index a2e6d01627..9ee9c86459 100644 --- a/packages/core/src/main/utils/resolve-system-proxy/resolve-system-proxy-from-electron.test.ts +++ b/packages/core/src/main/utils/resolve-system-proxy/resolve-system-proxy-from-electron.test.ts @@ -32,7 +32,7 @@ describe("technical: resolve-system-proxy-from-electron", () => { di.override( resolveSystemProxyWindowInjectable, - () => ({ + async () => ({ webContents: { session: { resolveProxy: resolveSystemProxyMock, @@ -73,7 +73,7 @@ describe("technical: resolve-system-proxy-from-electron", () => { di.override( resolveSystemProxyWindowInjectable, - () => ({ + async () => ({ webContents: { session: { resolveProxy: () => { diff --git a/packages/core/src/main/utils/resolve-system-proxy/resolve-system-proxy-window.global-override-for-injectable.ts b/packages/core/src/main/utils/resolve-system-proxy/resolve-system-proxy-window.global-override-for-injectable.ts index 4bf1ada952..680101b998 100644 --- a/packages/core/src/main/utils/resolve-system-proxy/resolve-system-proxy-window.global-override-for-injectable.ts +++ b/packages/core/src/main/utils/resolve-system-proxy/resolve-system-proxy-window.global-override-for-injectable.ts @@ -8,8 +8,8 @@ import type { BrowserWindow, Session, WebContents } from "electron"; import resolveSystemProxyWindowInjectable from "./resolve-system-proxy-window.injectable"; export default getGlobalOverride( - resolveSystemProxyWindowInjectable, - () => ({ + resolveSystemProxyWindowInjectable, + async () => ({ webContents: { session: { resolveProxy: () => "DIRECT", diff --git a/packages/core/src/main/utils/resolve-system-proxy/resolve-system-proxy-window.injectable.ts b/packages/core/src/main/utils/resolve-system-proxy/resolve-system-proxy-window.injectable.ts index baa0da6c39..91536c8204 100644 --- a/packages/core/src/main/utils/resolve-system-proxy/resolve-system-proxy-window.injectable.ts +++ b/packages/core/src/main/utils/resolve-system-proxy/resolve-system-proxy-window.injectable.ts @@ -4,11 +4,19 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import { BrowserWindow } from "electron"; +import electronAppInjectable from "../../electron-app/electron-app.injectable"; const resolveSystemProxyWindowInjectable = getInjectable({ id: "resolve-system-proxy-window", - instantiate: () => { - const window = new BrowserWindow({ show: false }); + instantiate: async (di) => { + const app = di.inject(electronAppInjectable); + + await app.whenReady(); + + const window = new BrowserWindow({ + show: false, + paintWhenInitiallyHidden: false, + }); window.hide(); diff --git a/packages/core/src/renderer/api/catalog/entity/__tests__/metrics-enabled.test.ts b/packages/core/src/renderer/api/catalog/entity/__tests__/metrics-enabled.test.ts new file mode 100644 index 0000000000..c248133541 --- /dev/null +++ b/packages/core/src/renderer/api/catalog/entity/__tests__/metrics-enabled.test.ts @@ -0,0 +1,52 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ + +import { Cluster } from "../../../../../common/cluster/cluster"; +import { getDiForUnitTesting } from "../../../../getDiForUnitTesting"; +import enabledMetricsInjectable from "../metrics-enabled.injectable"; +import activeEntityInternalClusterInjectable from "../get-active-cluster-entity.injectable"; +import { observable } from "mobx"; +import { ClusterMetricsResourceType } from "../../../../../common/cluster-types"; +import type { DiContainer } from "@ogre-tools/injectable"; + +describe("metrics-enabled", () => { + let cluster: Cluster; + let di: DiContainer; + + beforeEach(() => { + di = getDiForUnitTesting(); + + cluster = new Cluster({ contextName: "irrelevant", id: "irrelevant", kubeConfigPath: "irrelevant" }); + const observableCluster = observable.box(cluster); + + di.override(activeEntityInternalClusterInjectable, () => observableCluster); + }); + + it("given cluster has no hidden metrics preferences, should be true for all resources", () => { + const resourceTypes = Object.values(ClusterMetricsResourceType); + + delete cluster.preferences.hiddenMetrics; + resourceTypes.forEach((resourceType) => { + expect(di.inject(enabledMetricsInjectable, resourceType).get()).toBe(true); + }); + }); + + it("given cluster has metrics preferences, but nothing is hidden, should be true for all resources", () => { + const resourceTypes = Object.values(ClusterMetricsResourceType); + + cluster.preferences.hiddenMetrics = []; + + resourceTypes.forEach((resourceType) => { + expect(di.inject(enabledMetricsInjectable, resourceType).get()).toBe(true); + }); + }); + + it("given cluster has metrics preferences, and some resource is hidden, should be false", () => { + cluster.preferences.hiddenMetrics = [ClusterMetricsResourceType.Pod]; + + expect(di.inject(enabledMetricsInjectable, ClusterMetricsResourceType.Pod).get()).toBe(false); + }); +}); + diff --git a/packages/core/src/renderer/api/catalog/entity/get-active-cluster-entity.injectable.ts b/packages/core/src/renderer/api/catalog/entity/get-active-cluster-entity.injectable.ts index b856c15ffd..5a8c691c82 100644 --- a/packages/core/src/renderer/api/catalog/entity/get-active-cluster-entity.injectable.ts +++ b/packages/core/src/renderer/api/catalog/entity/get-active-cluster-entity.injectable.ts @@ -4,16 +4,24 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import { computed } from "mobx"; -import clusterStoreInjectable from "../../../../common/cluster-store/cluster-store.injectable"; +import getClusterByIdInjectable from "../../../../features/cluster/storage/common/get-by-id.injectable"; import catalogEntityRegistryInjectable from "./registry.injectable"; const activeEntityInternalClusterInjectable = getInjectable({ id: "active-entity-internal-cluster", instantiate: (di) => { - const store = di.inject(clusterStoreInjectable); + const getClusterById = di.inject(getClusterByIdInjectable); const entityRegistry = di.inject(catalogEntityRegistryInjectable); - return computed(() => store.getById(entityRegistry.activeEntity?.getId())); + return computed(() => { + const entityId = entityRegistry.activeEntity?.getId(); + + if (entityId) { + return getClusterById(entityId); + } + + return undefined; + }); }, }); diff --git a/packages/core/src/renderer/api/catalog/entity/metrics-details-component-enabled.injectable.ts b/packages/core/src/renderer/api/catalog/entity/metrics-details-component-enabled.injectable.ts index e480eadc78..7b837291c8 100644 --- a/packages/core/src/renderer/api/catalog/entity/metrics-details-component-enabled.injectable.ts +++ b/packages/core/src/renderer/api/catalog/entity/metrics-details-component-enabled.injectable.ts @@ -21,7 +21,7 @@ const metricsDetailsComponentEnabledInjectable = getInjectable({ return false; } - return current.object.kind == kind && metricsEnabled.get(); + return metricsEnabled.get() && current.object.kind == kind; }); }, lifecycle: lifecycleEnum.keyedSingleton({ diff --git a/packages/core/src/renderer/api/catalog/entity/metrics-enabled.injectable.ts b/packages/core/src/renderer/api/catalog/entity/metrics-enabled.injectable.ts index 0954259d98..2b9148bd81 100644 --- a/packages/core/src/renderer/api/catalog/entity/metrics-enabled.injectable.ts +++ b/packages/core/src/renderer/api/catalog/entity/metrics-enabled.injectable.ts @@ -16,10 +16,10 @@ const enabledMetricsInjectable = getInjectable({ const cluster = activeEntityInternalCluster.get(); if (!cluster?.preferences.hiddenMetrics) { - return false; + return true; } - return cluster.preferences.hiddenMetrics.includes(kind); + return !cluster.preferences.hiddenMetrics.includes(kind); }); }, lifecycle: lifecycleEnum.keyedSingleton({ diff --git a/packages/core/src/renderer/base-store/disable-sync-in-ipc-listener.injectable.ts b/packages/core/src/renderer/base-store/disable-sync-in-ipc-listener.injectable.ts index 82670d8e1b..a1ae454992 100644 --- a/packages/core/src/renderer/base-store/disable-sync-in-ipc-listener.injectable.ts +++ b/packages/core/src/renderer/base-store/disable-sync-in-ipc-listener.injectable.ts @@ -3,12 +3,12 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { shouldBaseStoreDisableSyncInIpcListenerInjectionToken } from "../../common/base-store/disable-sync"; +import { shouldPersistentStorageDisableSyncInIpcListenerInjectionToken } from "../../common/persistent-storage/disable-sync"; const shouldBaseStoreDisableSyncInIpcListenerInjectable = getInjectable({ id: "should-base-store-disable-sync-in-ipc-listener", instantiate: () => true, - injectionToken: shouldBaseStoreDisableSyncInIpcListenerInjectionToken, + injectionToken: shouldPersistentStorageDisableSyncInIpcListenerInjectionToken, }); export default shouldBaseStoreDisableSyncInIpcListenerInjectable; diff --git a/packages/core/src/renderer/base-store/ipc-channel-prefix.injectable.ts b/packages/core/src/renderer/base-store/ipc-channel-prefix.injectable.ts index bb2d407720..8a27bc456e 100644 --- a/packages/core/src/renderer/base-store/ipc-channel-prefix.injectable.ts +++ b/packages/core/src/renderer/base-store/ipc-channel-prefix.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { baseStoreIpcChannelPrefixesInjectionToken } from "../../common/base-store/channel-prefix"; +import { persistentStorageIpcChannelPrefixesInjectionToken } from "../../common/persistent-storage/channel-prefix"; const baseStoreIpcChannelPrefixInjectable = getInjectable({ id: "base-store-ipc-channel-prefix", @@ -11,7 +11,7 @@ const baseStoreIpcChannelPrefixInjectable = getInjectable({ local: "store-sync-renderer", remote: "store-sync-main", }), - injectionToken: baseStoreIpcChannelPrefixesInjectionToken, + injectionToken: persistentStorageIpcChannelPrefixesInjectionToken, }); export default baseStoreIpcChannelPrefixInjectable; diff --git a/packages/core/src/renderer/base-store/persist-state-to-config.injectable.ts b/packages/core/src/renderer/base-store/persist-state-to-config.injectable.ts index 0cf594062a..78d87c7fb1 100644 --- a/packages/core/src/renderer/base-store/persist-state-to-config.injectable.ts +++ b/packages/core/src/renderer/base-store/persist-state-to-config.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { persistStateToConfigInjectionToken } from "../../common/base-store/save-to-file"; +import { persistStateToConfigInjectionToken } from "../../common/persistent-storage/save-to-file"; import { noop } from "@k8slens/utilities"; const persistStateToConfigInjectable = getInjectable({ diff --git a/packages/core/src/renderer/before-frame-starts/runnables/setup-kubernetes-cluster-context-menu-open.injectable.ts b/packages/core/src/renderer/before-frame-starts/runnables/setup-kubernetes-cluster-context-menu-open.injectable.ts index 0775b501b5..d175ce2899 100644 --- a/packages/core/src/renderer/before-frame-starts/runnables/setup-kubernetes-cluster-context-menu-open.injectable.ts +++ b/packages/core/src/renderer/before-frame-starts/runnables/setup-kubernetes-cluster-context-menu-open.injectable.ts @@ -4,9 +4,9 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import catalogCategoryRegistryInjectable from "../../../common/catalog/category-registry.injectable"; -import getClusterByIdInjectable from "../../../common/cluster-store/get-by-id.injectable"; import loadKubeconfigInjectable from "../../../common/cluster/load-kubeconfig.injectable"; import loggerInjectable from "../../../common/logger.injectable"; +import getClusterByIdInjectable from "../../../features/cluster/storage/common/get-by-id.injectable"; import openDeleteClusterDialogInjectable from "../../components/delete-cluster-dialog/open.injectable"; import { beforeFrameStartsSecondInjectionToken } from "../tokens"; diff --git a/packages/core/src/renderer/bootstrap.injectable.ts b/packages/core/src/renderer/bootstrap.injectable.ts index 7cc172e3f1..24c01deecc 100644 --- a/packages/core/src/renderer/bootstrap.injectable.ts +++ b/packages/core/src/renderer/bootstrap.injectable.ts @@ -3,9 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { - afterApplicationIsLoadedInjectionToken, -} from "@k8slens/application"; +import { onLoadOfApplicationInjectionToken } from "@k8slens/application"; import { bootstrap } from "./bootstrap"; import startFrameInjectable from "./start-frame/start-frame.injectable"; @@ -22,7 +20,7 @@ const bootstrapInjectable = getInjectable({ causesSideEffects: true, - injectionToken: afterApplicationIsLoadedInjectionToken, + injectionToken: onLoadOfApplicationInjectionToken, }); export default bootstrapInjectable; diff --git a/packages/core/src/renderer/bootstrap.tsx b/packages/core/src/renderer/bootstrap.tsx index a811f07d5c..fcd2dc7415 100644 --- a/packages/core/src/renderer/bootstrap.tsx +++ b/packages/core/src/renderer/bootstrap.tsx @@ -5,10 +5,7 @@ import "./components/app.scss"; -import React from "react"; -import { render, unmountComponentAtNode } from "react-dom"; -import { DefaultProps } from "./mui-base-theme"; -import { DiContextProvider } from "@ogre-tools/injectable-react"; +import { unmountComponentAtNode } from "react-dom"; import type { DiContainerForInjection, } from "@ogre-tools/injectable"; @@ -17,8 +14,6 @@ import extensionDiscoveryInjectable from "../extensions/extension-discovery/exte import extensionInstallationStateStoreInjectable from "../extensions/extension-installation-state-store/extension-installation-state-store.injectable"; import initRootFrameInjectable from "./frames/root-frame/init-root-frame.injectable"; import initClusterFrameInjectable from "./frames/cluster-frame/init-cluster-frame/init-cluster-frame.injectable"; -import { Router } from "react-router"; -import historyInjectable from "./navigation/history.injectable"; import assert from "assert"; export async function bootstrap(di: DiContainerForInjection) { @@ -30,16 +25,13 @@ export async function bootstrap(di: DiContainerForInjection) { await di.inject(extensionDiscoveryInjectable).init(); di.inject(extensionInstallationStateStoreInjectable).bindIpcListeners(); - let App; let initializeApp; // TODO: Introduce proper architectural boundaries between root and cluster iframes if (process.isMainFrame) { initializeApp = di.inject(initRootFrameInjectable); - App = (await import("./frames/root-frame/root-frame")).RootFrame; } else { initializeApp = di.inject(initClusterFrameInjectable); - App = (await import("./frames/cluster-frame/cluster-frame")).ClusterFrame; } try { @@ -50,15 +42,4 @@ export async function bootstrap(di: DiContainerForInjection) { isTopFrameView: process.isMainFrame, }); } - - const history = di.inject(historyInjectable); - - render( - - - {DefaultProps(App)} - - , - rootElem, - ); } diff --git a/packages/core/src/renderer/cluster-frame-context/hosted-cluster.injectable.ts b/packages/core/src/renderer/cluster-frame-context/hosted-cluster.injectable.ts index a466984952..934a660188 100644 --- a/packages/core/src/renderer/cluster-frame-context/hosted-cluster.injectable.ts +++ b/packages/core/src/renderer/cluster-frame-context/hosted-cluster.injectable.ts @@ -4,16 +4,20 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import hostedClusterIdInjectable from "./hosted-cluster-id.injectable"; -import clusterStoreInjectable from "../../common/cluster-store/cluster-store.injectable"; +import getClusterByIdInjectable from "../../features/cluster/storage/common/get-by-id.injectable"; const hostedClusterInjectable = getInjectable({ id: "hosted-cluster", instantiate: (di) => { const hostedClusterId = di.inject(hostedClusterIdInjectable); - const store = di.inject(clusterStoreInjectable); + const getClusterById = di.inject(getClusterByIdInjectable); - return store.getById(hostedClusterId); + if (!hostedClusterId) { + return undefined; + } + + return getClusterById(hostedClusterId); }, }); diff --git a/packages/core/src/renderer/cluster-frame-context/should-show-resource.injectable.ts b/packages/core/src/renderer/cluster-frame-context/should-show-resource.injectable.ts index 6258796a0b..39b6070c3b 100644 --- a/packages/core/src/renderer/cluster-frame-context/should-show-resource.injectable.ts +++ b/packages/core/src/renderer/cluster-frame-context/should-show-resource.injectable.ts @@ -5,7 +5,7 @@ import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable"; import { computed } from "mobx"; import hostedClusterInjectable from "./hosted-cluster.injectable"; -import { shouldShowResourceInjectionToken } from "../../common/cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import type { KubeApiResourceDescriptor } from "../../common/rbac"; import { formatKubeApiResource } from "../../common/rbac"; diff --git a/packages/core/src/renderer/components/+catalog/__tests__/custom-columns.test.ts b/packages/core/src/renderer/components/+catalog/__tests__/custom-columns.test.ts index 8b5ed60a36..381f9541ac 100644 --- a/packages/core/src/renderer/components/+catalog/__tests__/custom-columns.test.ts +++ b/packages/core/src/renderer/components/+catalog/__tests__/custom-columns.test.ts @@ -11,7 +11,6 @@ import { getDiForUnitTesting } from "../../../getDiForUnitTesting"; import type { AdditionalCategoryColumnRegistration, CategoryColumnRegistration } from "../custom-category-columns"; import type { CategoryColumns, GetCategoryColumnsParams } from "../columns/get.injectable"; import getCategoryColumnsInjectable from "../columns/get.injectable"; -import hotbarStoreInjectable from "../../../../common/hotbars/store.injectable"; import extensionInjectable from "../../../../extensions/extension-loader/extension/extension.injectable"; import currentlyInClusterFrameInjectable from "../../../routes/currently-in-cluster-frame.injectable"; @@ -46,7 +45,6 @@ describe("Custom Category Columns", () => { beforeEach(() => { di = getDiForUnitTesting(); - di.override(hotbarStoreInjectable, () => ({})); di.override(currentlyInClusterFrameInjectable, () => false); getCategoryColumns = di.inject(getCategoryColumnsInjectable); diff --git a/packages/core/src/renderer/components/+catalog/catalog.tsx b/packages/core/src/renderer/components/+catalog/catalog.tsx index 1d385d78c4..0d11d070ab 100644 --- a/packages/core/src/renderer/components/+catalog/catalog.tsx +++ b/packages/core/src/renderer/components/+catalog/catalog.tsx @@ -28,8 +28,6 @@ import type { VisitEntityContextMenu } from "../../../common/catalog/visit-entit import visitEntityContextMenuInjectable from "../../../common/catalog/visit-entity-context-menu.injectable"; import type { NavigateToCatalog } from "../../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable"; import navigateToCatalogInjectable from "../../../common/front-end-routing/routes/catalog/navigate-to-catalog.injectable"; -import type { HotbarStore } from "../../../common/hotbars/store"; -import hotbarStoreInjectable from "../../../common/hotbars/store.injectable"; import type { Logger } from "../../../common/logger"; import loggerInjectable from "../../../common/logger.injectable"; import type { NormalizeCatalogEntityContextMenu } from "../../catalog/normalize-menu-item.injectable"; @@ -51,6 +49,8 @@ import type { OnCatalogEntityListClick } from "./entity-details/on-catalog-click import onCatalogEntityListClickInjectable from "./entity-details/on-catalog-click.injectable"; import type { ShowEntityDetails } from "./entity-details/show.injectable"; import showEntityDetailsInjectable from "./entity-details/show.injectable"; +import type { Hotbar } from "../../../features/hotbar/storage/common/hotbar"; +import activeHotbarInjectable from "../../../features/hotbar/storage/common/active.injectable"; interface Dependencies { catalogPreviousActiveTabStorage: StorageLayer; @@ -65,13 +65,13 @@ interface Dependencies { kind: IComputedValue; }; navigateToCatalog: NavigateToCatalog; - hotbarStore: HotbarStore; catalogCategoryRegistry: CatalogCategoryRegistry; visitEntityContextMenu: VisitEntityContextMenu; navigate: Navigate; normalizeMenuItem: NormalizeCatalogEntityContextMenu; showErrorNotification: ShowNotification; logger: Logger; + activeHotbar: IComputedValue; } @observer @@ -156,11 +156,11 @@ class NonInjectedCatalog extends React.Component { } addToHotbar(entity: CatalogEntity): void { - this.props.hotbarStore.addToHotbar(entity); + this.props.activeHotbar.get()?.addEntity(entity); } removeFromHotbar(entity: CatalogEntity): void { - this.props.hotbarStore.removeFromHotbar(entity.getId()); + this.props.activeHotbar.get()?.removeEntity(entity.getId()); } onTabChange = action((tabId: string | null) => { @@ -323,7 +323,7 @@ export const Catalog = withInjectables(NonInjectedCatalog, { routeParameters: di.inject(catalogRouteParametersInjectable), navigateToCatalog: di.inject(navigateToCatalogInjectable), emitEvent: di.inject(emitAppEventInjectable), - hotbarStore: di.inject(hotbarStoreInjectable), + activeHotbar: di.inject(activeHotbarInjectable), catalogCategoryRegistry: di.inject(catalogCategoryRegistryInjectable), visitEntityContextMenu: di.inject(visitEntityContextMenuInjectable), navigate: di.inject(navigateInjectable), diff --git a/packages/core/src/renderer/components/+catalog/columns/named-category.injectable.ts b/packages/core/src/renderer/components/+catalog/columns/named-category.injectable.ts new file mode 100644 index 0000000000..433d263b79 --- /dev/null +++ b/packages/core/src/renderer/components/+catalog/columns/named-category.injectable.ts @@ -0,0 +1,27 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import styles from "../catalog.module.scss"; +import type { RegisteredAdditionalCategoryColumn } from "../custom-category-columns"; +import renderNamedCategoryColumnCellInjectable from "./render-named-category-column-cell.injectable"; + +const namedCategoryColumnInjectable = getInjectable({ + id: "name-category-column", + instantiate: (di): RegisteredAdditionalCategoryColumn => ({ + id: "name", + priority: 0, + renderCell: di.inject(renderNamedCategoryColumnCellInjectable), + titleProps: { + title: "Name", + className: styles.entityName, + id: "name", + sortBy: "name", + }, + searchFilter: (entity) => entity.getName(), + sortCallback: (entity) => `name=${entity.getName()}`, + }), +}); + +export default namedCategoryColumnInjectable; diff --git a/packages/core/src/renderer/components/+catalog/columns/named-category.injectable.tsx b/packages/core/src/renderer/components/+catalog/columns/named-category.injectable.tsx deleted file mode 100644 index 0f7544ffc1..0000000000 --- a/packages/core/src/renderer/components/+catalog/columns/named-category.injectable.tsx +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import styles from "../catalog.module.scss"; -import type { CatalogEntity } from "../../../../common/catalog"; -import { prevDefault } from "@k8slens/utilities"; -import { Avatar } from "../../avatar"; -import { Icon } from "../../icon"; -import React from "react"; -import type { RegisteredAdditionalCategoryColumn } from "../custom-category-columns"; -import hotbarStoreInjectable from "../../../../common/hotbars/store.injectable"; -import type { HotbarStore } from "../../../../common/hotbars/store"; - -const renderEntityName = (hotbarStore: HotbarStore) => (entity: CatalogEntity) => { - const isItemInHotbar = hotbarStore.isAddedToActive(entity); - const onClick = prevDefault( - isItemInHotbar - ? () => hotbarStore.removeFromHotbar(entity.getId()) - : () => hotbarStore.addToHotbar(entity), - ); - - return ( - <> - - {entity.spec.icon?.material && } - - {entity.getName()} - - - ); -}; - -const namedCategoryColumnInjectable = getInjectable({ - id: "name-category-column", - instantiate: (di): RegisteredAdditionalCategoryColumn => ({ - id: "name", - priority: 0, - renderCell: renderEntityName(di.inject(hotbarStoreInjectable)), - titleProps: { - title: "Name", - className: styles.entityName, - id: "name", - sortBy: "name", - }, - searchFilter: (entity) => entity.getName(), - sortCallback: (entity) => `name=${entity.getName()}`, - }), -}); - -export default namedCategoryColumnInjectable; diff --git a/packages/core/src/renderer/components/+catalog/columns/render-named-category-column-cell.injectable.tsx b/packages/core/src/renderer/components/+catalog/columns/render-named-category-column-cell.injectable.tsx new file mode 100644 index 0000000000..21fd683ffb --- /dev/null +++ b/packages/core/src/renderer/components/+catalog/columns/render-named-category-column-cell.injectable.tsx @@ -0,0 +1,59 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import styles from "../catalog.module.scss"; +import React from "react"; +import activeHotbarInjectable from "../../../../features/hotbar/storage/common/active.injectable"; +import { Avatar } from "../../avatar"; +import type { RegisteredAdditionalCategoryColumn } from "../custom-category-columns"; +import { Icon } from "../../icon"; +import { prevDefault } from "@k8slens/utilities"; + +const renderNamedCategoryColumnCellInjectable = getInjectable({ + id: "render-named-category-column-cell", + instantiate: (di): RegisteredAdditionalCategoryColumn["renderCell"] => { + const activeHotbar = di.inject(activeHotbarInjectable); + + return (entity) => { + const hotbar = activeHotbar.get(); + + if (!hotbar) { + return null; + } + + const isItemInHotbar = hotbar.hasEntity(entity.getId()); + const onClick = prevDefault(( + isItemInHotbar + ? () => hotbar.removeEntity(entity.getId()) + : () => hotbar.addEntity(entity) + )); + + return ( + <> + + {entity.spec.icon?.material && } + + {entity.getName()} + + + ); + }; + }, +}); + +export default renderNamedCategoryColumnCellInjectable; diff --git a/packages/core/src/renderer/components/+catalog/hotbar-toggle-menu-item.tsx b/packages/core/src/renderer/components/+catalog/hotbar-toggle-menu-item.tsx index bae8895ff7..bab9b79eb5 100644 --- a/packages/core/src/renderer/components/+catalog/hotbar-toggle-menu-item.tsx +++ b/packages/core/src/renderer/components/+catalog/hotbar-toggle-menu-item.tsx @@ -9,11 +9,12 @@ import { MenuItem } from "../menu"; import type { CatalogEntity } from "../../api/catalog-entity"; import { withInjectables } from "@ogre-tools/injectable-react"; -import hotbarStoreInjectable from "../../../common/hotbars/store.injectable"; -import type { HotbarStore } from "../../../common/hotbars/store"; +import type { IComputedValue } from "mobx"; +import type { Hotbar } from "../../../features/hotbar/storage/common/hotbar"; +import activeHotbarInjectable from "../../../features/hotbar/storage/common/active.injectable"; interface Dependencies { - hotbarStore: HotbarStore; + activeHotbar: IComputedValue; } interface HotbarToggleMenuItemProps { @@ -25,19 +26,19 @@ interface HotbarToggleMenuItemProps { function NonInjectedHotbarToggleMenuItem({ addContent, entity, - hotbarStore, + activeHotbar, removeContent, }: Dependencies & HotbarToggleMenuItemProps) { - const [itemInHotbar, setItemInHotbar] = useState(hotbarStore.isAddedToActive(entity)); + const [itemInHotbar, setItemInHotbar] = useState(activeHotbar.get()?.hasEntity(entity.getId()) ?? false); return ( { if (itemInHotbar) { - hotbarStore.removeFromHotbar(entity.getId()); + activeHotbar.get()?.removeEntity(entity.getId()); setItemInHotbar(false); } else { - hotbarStore.addToHotbar(entity); + activeHotbar.get()?.addEntity(entity); setItemInHotbar(true); } }} @@ -47,14 +48,10 @@ function NonInjectedHotbarToggleMenuItem({ ); } -export const HotbarToggleMenuItem = withInjectables( - NonInjectedHotbarToggleMenuItem, - - { - getProps: (di, props) => ({ - hotbarStore: di.inject(hotbarStoreInjectable), - ...props, - }), - }, -); +export const HotbarToggleMenuItem = withInjectables(NonInjectedHotbarToggleMenuItem, { + getProps: (di, props) => ({ + ...props, + activeHotbar: di.inject(activeHotbarInjectable), + }), +}); diff --git a/packages/core/src/renderer/components/+cluster/cluster-overview.tsx b/packages/core/src/renderer/components/+cluster/cluster-overview.tsx index 516a2f9114..75b3e8cd1e 100644 --- a/packages/core/src/renderer/components/+cluster/cluster-overview.tsx +++ b/packages/core/src/renderer/components/+cluster/cluster-overview.tsx @@ -98,11 +98,12 @@ class NonInjectedClusterOverview extends React.Component { render() { const { eventStore, nodeStore, clusterMetricsAreVisible } = this.props; const isLoaded = nodeStore.isLoaded && eventStore.isLoaded; + const isMetricsHidden = !clusterMetricsAreVisible.get(); return (
- {this.renderClusterOverview(isLoaded, clusterMetricsAreVisible.get())} + {this.renderClusterOverview(isLoaded, isMetricsHidden)}
); diff --git a/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/hpa-details.test.tsx b/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/hpa-details.test.tsx index aa2d2a3b32..3190be6edd 100644 --- a/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/hpa-details.test.tsx +++ b/packages/core/src/renderer/components/+config-horizontal-pod-autoscalers/hpa-details.test.tsx @@ -53,8 +53,6 @@ describe("", () => { contextName: "some-context-name", id: "some-cluster-id", kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", })); render = renderFor(di); diff --git a/packages/core/src/renderer/components/+config-secrets/__tests__/secret-details.test.tsx b/packages/core/src/renderer/components/+config-secrets/__tests__/secret-details.test.tsx index e110056ab0..795c204975 100644 --- a/packages/core/src/renderer/components/+config-secrets/__tests__/secret-details.test.tsx +++ b/packages/core/src/renderer/components/+config-secrets/__tests__/secret-details.test.tsx @@ -31,8 +31,6 @@ describe("SecretDetails tests", () => { contextName: "some-context-name", id: "some-cluster-id", kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", })); const secret = new Secret({ diff --git a/packages/core/src/renderer/components/+entity-settings/internal-kubernetes-cluster/general-settings.injectable.tsx b/packages/core/src/renderer/components/+entity-settings/internal-kubernetes-cluster/general-settings.injectable.tsx index 059f8c15eb..64d4028f24 100644 --- a/packages/core/src/renderer/components/+entity-settings/internal-kubernetes-cluster/general-settings.injectable.tsx +++ b/packages/core/src/renderer/components/+entity-settings/internal-kubernetes-cluster/general-settings.injectable.tsx @@ -6,8 +6,8 @@ import { getInjectable } from "@ogre-tools/injectable"; import { withInjectables } from "@ogre-tools/injectable-react"; import React from "react"; import type { KubernetesCluster } from "../../../../common/catalog-entities"; -import type { GetClusterById } from "../../../../common/cluster-store/get-by-id.injectable"; -import getClusterByIdInjectable from "../../../../common/cluster-store/get-by-id.injectable"; +import type { GetClusterById } from "../../../../features/cluster/storage/common/get-by-id.injectable"; +import getClusterByIdInjectable from "../../../../features/cluster/storage/common/get-by-id.injectable"; import { ClusterIconSetting } from "../../cluster-settings/icon-settings"; import { ClusterKubeconfig } from "../../cluster-settings/kubeconfig"; import { ClusterNameSetting } from "../../cluster-settings/name-setting"; diff --git a/packages/core/src/renderer/components/+entity-settings/internal-kubernetes-cluster/metrics-settings.injectable.tsx b/packages/core/src/renderer/components/+entity-settings/internal-kubernetes-cluster/metrics-settings.injectable.tsx index 1be5f7e78a..0a860300a3 100644 --- a/packages/core/src/renderer/components/+entity-settings/internal-kubernetes-cluster/metrics-settings.injectable.tsx +++ b/packages/core/src/renderer/components/+entity-settings/internal-kubernetes-cluster/metrics-settings.injectable.tsx @@ -5,8 +5,8 @@ import { getInjectable } from "@ogre-tools/injectable"; import { withInjectables } from "@ogre-tools/injectable-react"; import React from "react"; -import type { GetClusterById } from "../../../../common/cluster-store/get-by-id.injectable"; -import getClusterByIdInjectable from "../../../../common/cluster-store/get-by-id.injectable"; +import type { GetClusterById } from "../../../../features/cluster/storage/common/get-by-id.injectable"; +import getClusterByIdInjectable from "../../../../features/cluster/storage/common/get-by-id.injectable"; import { ClusterMetricsSetting } from "../../cluster-settings/metrics-setting"; import { ClusterPrometheusSetting } from "../../cluster-settings/prometheus-setting"; import { ShowMetricsSetting } from "../../cluster-settings/show-metrics"; diff --git a/packages/core/src/renderer/components/+entity-settings/internal-kubernetes-cluster/namespace-settings.injectable.tsx b/packages/core/src/renderer/components/+entity-settings/internal-kubernetes-cluster/namespace-settings.injectable.tsx index a7c86265b8..c84827f2a7 100644 --- a/packages/core/src/renderer/components/+entity-settings/internal-kubernetes-cluster/namespace-settings.injectable.tsx +++ b/packages/core/src/renderer/components/+entity-settings/internal-kubernetes-cluster/namespace-settings.injectable.tsx @@ -5,8 +5,8 @@ import { getInjectable } from "@ogre-tools/injectable"; import { withInjectables } from "@ogre-tools/injectable-react"; import React from "react"; -import type { GetClusterById } from "../../../../common/cluster-store/get-by-id.injectable"; -import getClusterByIdInjectable from "../../../../common/cluster-store/get-by-id.injectable"; +import type { GetClusterById } from "../../../../features/cluster/storage/common/get-by-id.injectable"; +import getClusterByIdInjectable from "../../../../features/cluster/storage/common/get-by-id.injectable"; import { ClusterAccessibleNamespaces } from "../../cluster-settings/accessible-namespaces"; import type { EntitySettingViewProps } from "../extension-registrator.injectable"; import { entitySettingInjectionToken } from "../token"; diff --git a/packages/core/src/renderer/components/+entity-settings/internal-kubernetes-cluster/node-shell-settings.injectable.tsx b/packages/core/src/renderer/components/+entity-settings/internal-kubernetes-cluster/node-shell-settings.injectable.tsx index ff899095d6..1a1424b5b3 100644 --- a/packages/core/src/renderer/components/+entity-settings/internal-kubernetes-cluster/node-shell-settings.injectable.tsx +++ b/packages/core/src/renderer/components/+entity-settings/internal-kubernetes-cluster/node-shell-settings.injectable.tsx @@ -5,8 +5,8 @@ import { getInjectable } from "@ogre-tools/injectable"; import { withInjectables } from "@ogre-tools/injectable-react"; import React from "react"; -import type { GetClusterById } from "../../../../common/cluster-store/get-by-id.injectable"; -import getClusterByIdInjectable from "../../../../common/cluster-store/get-by-id.injectable"; +import type { GetClusterById } from "../../../../features/cluster/storage/common/get-by-id.injectable"; +import getClusterByIdInjectable from "../../../../features/cluster/storage/common/get-by-id.injectable"; import { ClusterNodeShellSetting } from "../../cluster-settings/node-shell-setting"; import type { EntitySettingViewProps } from "../extension-registrator.injectable"; import { entitySettingInjectionToken } from "../token"; diff --git a/packages/core/src/renderer/components/+entity-settings/internal-kubernetes-cluster/proxy-settings.injectable.tsx b/packages/core/src/renderer/components/+entity-settings/internal-kubernetes-cluster/proxy-settings.injectable.tsx index 4479a04c2a..90b30ce7d2 100644 --- a/packages/core/src/renderer/components/+entity-settings/internal-kubernetes-cluster/proxy-settings.injectable.tsx +++ b/packages/core/src/renderer/components/+entity-settings/internal-kubernetes-cluster/proxy-settings.injectable.tsx @@ -5,8 +5,8 @@ import { getInjectable } from "@ogre-tools/injectable"; import { withInjectables } from "@ogre-tools/injectable-react"; import React from "react"; -import type { GetClusterById } from "../../../../common/cluster-store/get-by-id.injectable"; -import getClusterByIdInjectable from "../../../../common/cluster-store/get-by-id.injectable"; +import type { GetClusterById } from "../../../../features/cluster/storage/common/get-by-id.injectable"; +import getClusterByIdInjectable from "../../../../features/cluster/storage/common/get-by-id.injectable"; import { ClusterProxySetting } from "../../cluster-settings/proxy-setting"; import type { EntitySettingViewProps } from "../extension-registrator.injectable"; import { entitySettingInjectionToken } from "../token"; diff --git a/packages/core/src/renderer/components/+entity-settings/internal-kubernetes-cluster/terminal-settings.injectable.tsx b/packages/core/src/renderer/components/+entity-settings/internal-kubernetes-cluster/terminal-settings.injectable.tsx index e778c97728..6448c935bc 100644 --- a/packages/core/src/renderer/components/+entity-settings/internal-kubernetes-cluster/terminal-settings.injectable.tsx +++ b/packages/core/src/renderer/components/+entity-settings/internal-kubernetes-cluster/terminal-settings.injectable.tsx @@ -5,8 +5,8 @@ import { getInjectable } from "@ogre-tools/injectable"; import { withInjectables } from "@ogre-tools/injectable-react"; import React from "react"; -import type { GetClusterById } from "../../../../common/cluster-store/get-by-id.injectable"; -import getClusterByIdInjectable from "../../../../common/cluster-store/get-by-id.injectable"; +import type { GetClusterById } from "../../../../features/cluster/storage/common/get-by-id.injectable"; +import getClusterByIdInjectable from "../../../../features/cluster/storage/common/get-by-id.injectable"; import { ClusterLocalTerminalSetting } from "../../cluster-settings/local-terminal-settings"; import type { EntitySettingViewProps } from "../extension-registrator.injectable"; import { entitySettingInjectionToken } from "../token"; diff --git a/packages/core/src/renderer/components/+extensions/get-base-registry-url/get-base-registry-url.injectable.tsx b/packages/core/src/renderer/components/+extensions/get-base-registry-url/get-base-registry-url.injectable.tsx index 6a03466abd..2707f99a3b 100644 --- a/packages/core/src/renderer/components/+extensions/get-base-registry-url/get-base-registry-url.injectable.tsx +++ b/packages/core/src/renderer/components/+extensions/get-base-registry-url/get-base-registry-url.injectable.tsx @@ -7,15 +7,15 @@ import { getInjectable } from "@ogre-tools/injectable"; import React from "react"; import execFileInjectable from "../../../../common/fs/exec-file.injectable"; import loggerInjectable from "../../../../common/logger.injectable"; -import { defaultExtensionRegistryUrl } from "../../../../common/user-store/preferences-helpers"; -import userStoreInjectable from "../../../../common/user-store/user-store.injectable"; +import { defaultExtensionRegistryUrl } from "../../../../features/user-preferences/common/preferences-helpers"; +import userPreferencesStateInjectable from "../../../../features/user-preferences/common/state.injectable"; import showErrorNotificationInjectable from "../../notifications/show-error-notification.injectable"; const getBaseRegistryUrlInjectable = getInjectable({ id: "get-base-registry-url", instantiate: (di) => { - const { extensionRegistryUrl } = di.inject(userStoreInjectable); + const { extensionRegistryUrl } = di.inject(userPreferencesStateInjectable); const showErrorNotification = di.inject(showErrorNotificationInjectable); const logger = di.inject(loggerInjectable); const execFile = di.inject(execFileInjectable); diff --git a/packages/core/src/renderer/components/+helm-charts/__tests__/icon.test.tsx b/packages/core/src/renderer/components/+helm-charts/__tests__/icon.test.tsx new file mode 100644 index 0000000000..2b651fa1e3 --- /dev/null +++ b/packages/core/src/renderer/components/+helm-charts/__tests__/icon.test.tsx @@ -0,0 +1,64 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import React from "react"; +import { render, screen } from "@testing-library/react"; +import { HelmChartIcon } from "../icon"; + +const mainImageSrc = "https://example.com/main-picture.jpg"; +const mainPngImageSrc = "https://example.com/main-picture.png"; +const invalidImageSrc = "file://invalid-image-url.png"; +const svgImageSrc = "https://example.com/main-picture.svg"; + +describe("HelmChartIcon", () => { + it("renders the placeholder image by default", () => { + render(); + const imageContainer = screen.getByTestId("image-container"); + + expect(imageContainer.style.backgroundImage).toContain("data:image/svg+xml"); + }); + + it("renders img tag when image url is valid", () => { + render(); + const mainImage = screen.getByRole("img"); + + expect(mainImage).toBeInTheDocument(); + }); + + it("renders jpg image when its loaded", () => { + render(); + + const imageContainer = screen.getByTestId("image-container"); + const mainImage = screen.getByRole("img"); + + mainImage.dispatchEvent(new Event("load")); + expect(imageContainer.style.backgroundImage).toBe("url(https://example.com/main-picture.jpg)"); + }); + + it("renders png image when its loaded", () => { + render(); + + const imageContainer = screen.getByTestId("image-container"); + const mainImage = screen.getByRole("img"); + + mainImage.dispatchEvent(new Event("load")); + expect(imageContainer.style.backgroundImage).toBe("url(https://example.com/main-picture.png)"); + }); + + it("does not render invalid image url", () => { + render(); + + const mainImage = screen.queryByRole("img"); + + expect(mainImage).not.toBeInTheDocument(); + }); + + it("does not render svg image", () => { + render(); + + const mainImage = screen.queryByRole("img"); + + expect(mainImage).not.toBeInTheDocument(); + }); +}); diff --git a/packages/core/src/renderer/components/+helm-charts/helm-chart-details.scss b/packages/core/src/renderer/components/+helm-charts/helm-chart-details.scss index b82512dff4..80ddfb9f9c 100644 --- a/packages/core/src/renderer/components/+helm-charts/helm-chart-details.scss +++ b/packages/core/src/renderer/components/+helm-charts/helm-chart-details.scss @@ -5,17 +5,14 @@ .HelmChartDetails { .intro-logo { - margin-right: $margin * 2; - background: var(--helmLogoBackground); - border-radius: $radius; - max-width: 150px; - max-height: 100px; - padding: $padding; - box-sizing: content-box; - } - - div.intro-logo { + margin-right: calc(var(--margin) * 2); + background: var(--helmLogoBackground) center no-repeat; + border-radius: var(--border-radius); + padding: var(--padding); width: 100px; + height: 100px; + box-sizing: content-box; + background-size: contain; } .Select__option { diff --git a/packages/core/src/renderer/components/+helm-charts/helm-chart-details.tsx b/packages/core/src/renderer/components/+helm-charts/helm-chart-details.tsx index 2004a8704c..15add2c2ea 100644 --- a/packages/core/src/renderer/components/+helm-charts/helm-chart-details.tsx +++ b/packages/core/src/renderer/components/+helm-charts/helm-chart-details.tsx @@ -71,7 +71,7 @@ class NonInjectedHelmChartDetails extends Component
diff --git a/packages/core/src/renderer/components/+helm-charts/helm-charts.tsx b/packages/core/src/renderer/components/+helm-charts/helm-charts.tsx index a8367aac23..dff8025de5 100644 --- a/packages/core/src/renderer/components/+helm-charts/helm-charts.tsx +++ b/packages/core/src/renderer/components/+helm-charts/helm-charts.tsx @@ -121,7 +121,7 @@ class NonInjectedHelmCharts extends Component { ]} renderTableContents={chart => [
- +
, chart.getName(), chart.getDescription(), diff --git a/packages/core/src/renderer/components/+helm-charts/icon.module.css b/packages/core/src/renderer/components/+helm-charts/icon.module.css new file mode 100644 index 0000000000..a665648c83 --- /dev/null +++ b/packages/core/src/renderer/components/+helm-charts/icon.module.css @@ -0,0 +1,13 @@ +.chartIcon { + background-size: cover; + background-position: center; +} + +.imageNotLoaded { + width: 95%; + height: 95%; +} + +.img { + opacity: 0; +} \ No newline at end of file diff --git a/packages/core/src/renderer/components/+helm-charts/icon.tsx b/packages/core/src/renderer/components/+helm-charts/icon.tsx index 78d5be7ed3..12e3c8761b 100644 --- a/packages/core/src/renderer/components/+helm-charts/icon.tsx +++ b/packages/core/src/renderer/components/+helm-charts/icon.tsx @@ -3,47 +3,48 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ +import styles from "./icon.module.css"; + +import { cssNames } from "@k8slens/utilities"; import React, { useState } from "react"; -import type { HelmChart } from "../../../common/k8s-api/endpoints/helm-charts.api"; export interface HelmChartIconProps { className?: string; - chart: HelmChart; + imageUrl?: string; } export const HelmChartIcon = ({ - chart, + imageUrl = "", className, }: HelmChartIconProps) => { - const [failedToLoad, setFailedToLoad] = useState(false); - const icon = chart.getIcon(); + const [isImageLoaded, setIsImageLoaded] = useState(false); + const backgroundImage = `url(${imageUrl})`; + const placeholderImageUrl = "data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgNzIyLjggNzAyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogICAgICAgICAgPGcgZmlsbD0iIzhlOTI5NyI+CiAgICAgICAgICAgIDxwYXRoIGQ9Im0zMTggMjk5LjVjMi4xIDEuNiA0LjggMi41IDcuNiAyLjUgNi45IDAgMTIuNi01LjUgMTIuOS0xMi4zbC4zLS4yIDQuMy03Ni43Yy01LjIuNi0xMC40IDEuNS0xNS42IDIuNy0yOC41IDYuNS01My4yIDIwLjUtNzIuNiAzOS41bDYyLjkgNDQuNnoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTMwOS41IDQxMS45Yy0xLjQtNS45LTYuNi05LjktMTIuNC0xMC0uOCAwLTEuNy4xLTIuNS4ybC0uMS0uMi03NS41IDEyLjhjMTEuNyAzMi4yIDMzLjQgNTguNSA2MC44IDc2LjFsMjkuMi03MC43LS4yLS4zYzEuMS0yLjQgMS40LTUuMi43LTcuOXoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTI4NC40IDM1Ny41YzIuNS0uNyA0LjktMi4yIDYuNy00LjQgNC4zLTUuNCAzLjYtMTMuMi0xLjYtMTcuOGwuMS0uMy01Ny40LTUxLjRjLTE3IDI3LjgtMjUuMSA2MS4xLTIxLjQgOTUuM2w3My42LTIxLjJ6Ii8+CiAgICAgICAgICAgIDxwYXRoIGQ9Im0zNDAuMiAzODAgMjEuMiAxMC4yIDIxLjEtMTAuMSA1LjMtMjIuOS0xNC42LTE4LjJoLTIzLjZsLTE0LjYgMTguMnoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTM4NC4yIDI4OS40Yy4xIDIuNiAxIDUuMiAyLjggNy41IDQuMyA1LjQgMTIuMSA2LjQgMTcuNyAyLjRsLjIuMSA2Mi41LTQ0LjNjLTIzLjYtMjMuMS01NC40LTM4LjItODcuNi00Mi4yeiIvPgogICAgICAgICAgICA8cGF0aCBkPSJtNDkwLjMgMjgzLjctNTcuMSA1MS4xdi4yYy0yIDEuNy0zLjUgNC4xLTQuMSA2LjgtMS41IDYuOCAyLjUgMTMuNSA5LjIgMTUuM2wuMS4zIDc0IDIxLjNjMS42LTE2IC42LTMyLjUtMy4yLTQ5LTMuOS0xNi44LTEwLjQtMzIuMi0xOC45LTQ2eiIvPgogICAgICAgICAgICA8cGF0aCBkPSJtMzcyLjggNDM5LjZjLTEuMi0yLjMtMy4yLTQuMy01LjgtNS41LTItLjktNC0xLjQtNi0xLjMtNC41LjItOC43IDIuNi0xMC45IDYuOGgtLjFsLTM3LjEgNjcuMWMyNS43IDguOCA1NC4xIDEwLjcgODIuNSA0LjIgNS4xLTEuMiAxMC0yLjUgMTQuOS00LjJsLTM3LjMtNjcuMXoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTcxMS43IDQyNS02MC40LTI2Mi4yYy0zLjItMTMuNy0xMi41LTI1LjMtMjUuMy0zMS40bC0yNDQuNC0xMTYuOGMtNy4xLTMuNC0xNC44LTQuOS0yMi43LTQuNS02LjIuMy0xMi4zIDEuOS0xNy45IDQuNWwtMjQ0LjMgMTE2LjdjLTEyLjggNi4xLTIyLjEgMTcuNy0yNS4zIDMxLjRsLTYwLjIgMjYyLjNjLTIuOCAxMi4yLS41IDI1IDYuMyAzNS41LjggMS4zIDEuNyAyLjUgMi43IDMuN2wxNjkuMSAyMTAuM2M4LjkgMTEgMjIuMyAxNy40IDM2LjUgMTcuNGwyNzEuMi0uMWMxNC4yIDAgMjcuNy02LjQgMzYuNS0xNy40bDE2OS4xLTIxMC4zYzguOS0xMC45IDEyLjItMjUuNCA5LjEtMzkuMXptLTkzLTMuMmMtMS44IDcuOC0xMC4yIDEyLjYtMTguOSAxMC43LS4xIDAtLjIgMC0uMiAwLS4xIDAtLjItLjEtLjMtLjEtMS4yLS4zLTIuNy0uNS0zLjgtLjgtNS0xLjMtOC42LTMuMy0xMy4xLTUuMS05LjctMy41LTE3LjctNi40LTI1LjUtNy41LTQtLjMtNiAxLjYtOC4yIDMtMS4xLS4yLTQuNC0uOC02LjItMS4xLTE0IDQ0LTQzLjkgODIuMi04NC4zIDEwNi4xLjcgMS43IDEuOSA1LjMgMi40IDUuOS0uOSAyLjUtMi4zIDQuOC0xLjEgOC42IDIuOCA3LjQgNy40IDE0LjYgMTMgMjMuMiAyLjcgNCA1LjQgNy4xIDcuOCAxMS43LjYgMS4xIDEuMyAyLjggMS45IDMuOSAzLjggOCAxIDE3LjMtNi4yIDIwLjgtNy4zIDMuNS0xNi4zLS4yLTIwLjItOC4zLS42LTEuMS0xLjMtMi43LTEuOC0zLjgtMi4xLTQuNy0yLjgtOC44LTQuMi0xMy40LTMuMy05LjctNi0xNy44LTEwLTI0LjYtMi4yLTMuMy01LTMuNy03LjUtNC41LS41LS44LTIuMi00LTMuMS01LjYtOC4xIDMuMS0xNi40IDUuNi0yNS4xIDcuNi0zNy45IDguNi03NS45IDUuMS0xMDkuOS03LjlsLTMuMyA2Yy0yLjUuNy00LjggMS4zLTYuMyAzLjEtNS4zIDYuNC03LjUgMTYuNi0xMS4zIDI2LjMtMS41IDQuNi0yLjEgOC43LTQuMiAxMy40LS41IDEuMS0xLjMgMi42LTEuOCAzLjctMy45IDguMS0xMi45IDExLjctMjAuMiA4LjItNy4yLTMuNS0xMC0xMi43LTYuMi0yMC44LjYtMS4yIDEuMy0yLjggMS45LTMuOSAyLjQtNC42IDUuMi03LjcgNy44LTExLjcgNS41LTguNyAxMC40LTE2LjQgMTMuMi0yMy44LjctMi40LS4zLTUuOC0xLjMtOC4zbDIuNy02LjRjLTM4LjktMjMuMS02OS43LTU5LjgtODQuMy0xMDUuM2wtNi40IDEuMWMtMS43LTEtNS4xLTMuMi04LjQtMy03LjggMS4xLTE1LjggNC0yNS41IDcuNS00LjUgMS43LTguMSAzLjctMTMuMSA1LTEuMS4zLTIuNi42LTMuOC44LS4xIDAtLjIuMS0uMy4xcy0uMiAwLS4yIDBjLTguNyAxLjktMTcuMS0yLjktMTguOS0xMC43czMuOC0xNS43IDEyLjQtMTcuOGMuMSAwIC4yIDAgLjItLjFoLjFjMS4yLS4zIDIuOC0uNyAzLjktLjkgNS4xLTEgOS4yLS43IDE0LTEuMSAxMC4yLTEuMSAxOC43LTEuOSAyNi4yLTQuMyAyLjQtMSA0LjctNC4zIDYuMy02LjNsNi4xLTEuOGMtNi45LTQ3LjUgNC44LTk0LjIgMjkuOC0xMzEuOWwtNC43LTQuMmMtLjMtMS44LS43LTYtMi45LTguNC01LjgtNS40LTEzLTkuOS0yMS44LTE1LjMtNC4yLTIuNC04LTQtMTIuMS03LjEtLjktLjctMi4xLTEuNy0zLTIuNC0uMS0uMS0uMS0uMS0uMi0uMi03LTUuNi04LjYtMTUuMi0zLjYtMjEuNiAyLjgtMy42IDcuMi01LjMgMTEuNy01LjIgMy41LjEgNy4xIDEuNCAxMC4yIDMuOCAxIC44IDIuNCAxLjggMy4yIDIuNiAzLjkgMy40IDYuMyA2LjcgOS42IDEwLjIgNy4yIDcuMyAxMy4yIDEzLjQgMTkuNyAxNy44IDMuNCAyIDYuMSAxLjIgOC43LjguOC42IDMuNyAyLjYgNS4zIDMuOCAyNC45LTI2LjQgNTcuNi00NiA5NS42LTU0LjYgOC44LTIgMTcuNy0zLjMgMjYuNC00LjFsLjMtNi4yYzEuOS0xLjkgNC4xLTQuNiA0LjgtNy42LjYtNy45LS40LTE2LjMtMS42LTI2LjUtLjctNC44LTEuOC04LjctMi0xMy45IDAtMS4xIDAtMi41IDAtMy44IDAtLjEgMC0uMyAwLS40IDAtOSA2LjUtMTYuMiAxNC42LTE2LjJzMTQuNiA3LjMgMTQuNiAxNi4yYzAgMS4zLjEgMyAwIDQuMi0uMiA1LjItMS4zIDkuMS0yIDEzLjktMS4yIDEwLjItMi4zIDE4LjctMS43IDI2LjUuNiAzLjkgMi45IDUuNSA0LjggNy4zIDAgMS4xLjIgNC42LjMgNi41IDQ2LjUgNC4xIDg5LjcgMjUuNCAxMjEuNCA1OC43bDUuNi00YzEuOS4xIDYgLjcgOC45LTEgNi41LTQuNCAxMi41LTEwLjUgMTkuNy0xNy44IDMuMy0zLjUgNS43LTYuOCA5LjctMTAuMi45LS44IDIuMy0xLjggMy4yLTIuNiA3LTUuNiAxNi44LTUgMjEuOCAxLjNzMy40IDE2LTMuNiAyMS42Yy0xIC44LTIuMyAxLjktMy4yIDIuNi00LjIgMy4xLTggNC43LTEyLjIgNy4xLTguNyA1LjQtMTYgOS45LTIxLjggMTUuMy0yLjcgMi45LTIuNSA1LjctMi44IDguMy0uOC43LTMuNyAzLjMtNS4yIDQuNyAxMi42IDE4LjggMjIuMSA0MC4xIDI3LjQgNjMuMyA1LjMgMjMuMSA2LjEgNDYuMSAzLjEgNjguM2w1LjkgMS43YzEuMSAxLjUgMy4yIDUuMiA2LjMgNi4zIDcuNSAyLjQgMTYgMy4yIDI2LjIgNC4zIDQuOC40IDguOS4yIDE0IDEuMSAxLjIuMiAzIC43IDQuMiAxIDguOSAyLjQgMTQuNCAxMC40IDEyLjYgMTguMnoiLz4KICAgICAgICAgICAgPHBhdGggZD0ibTQyOCA0MDEuN2MtMS0uMi0yLS4zLTMtLjItMS43LjEtMy4zLjUtNC45IDEuMy02LjIgMy05IDEwLjQtNi4yIDE2LjdsLS4xLjEgMjkuNiA3MS40YzI4LjUtMTguMiA0OS44LTQ1LjMgNjEtNzYuNmwtNzYuMi0xMi45eiIvPgogICAgICAgICAgPC9nPgogICAgICAgIDwvc3ZnPg=="; - if (!icon || failedToLoad) { - return ( -
- - - - - - - - - - - - - -
- ); - } + const handleImageLoad = () => { + setIsImageLoaded(true); + }; + + const isValidImage = () => { + return /^https?:\/\/.*(? evt.currentTarget.classList.add("visible")} - onError={() => setFailedToLoad(true)} - /> +
+ {isValidImage() && ( + + )} +
); }; diff --git a/packages/core/src/renderer/components/+namespaces/namespace-select-filter.test.tsx b/packages/core/src/renderer/components/+namespaces/namespace-select-filter.test.tsx index b2369689e7..a067224811 100644 --- a/packages/core/src/renderer/components/+namespaces/namespace-select-filter.test.tsx +++ b/packages/core/src/renderer/components/+namespaces/namespace-select-filter.test.tsx @@ -62,8 +62,6 @@ describe("", () => { contextName: "some-context-name", id: "some-cluster-id", kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", })); namespaceStore = di.inject(namespaceStoreInjectable); diff --git a/packages/core/src/renderer/components/+namespaces/namespace-store.test.ts b/packages/core/src/renderer/components/+namespaces/namespace-store.test.ts index c4b26e9b8a..f274746b0f 100644 --- a/packages/core/src/renderer/components/+namespaces/namespace-store.test.ts +++ b/packages/core/src/renderer/components/+namespaces/namespace-store.test.ts @@ -115,8 +115,6 @@ describe("NamespaceStore", () => { contextName: "some-context-name", id: "some-cluster-id", kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", })); namespaceStore = di.inject(namespaceStoreInjectable); diff --git a/packages/core/src/renderer/components/+nodes/nodes.scss b/packages/core/src/renderer/components/+nodes/nodes.scss index 74adf66e3d..99cab9e3a0 100644 --- a/packages/core/src/renderer/components/+nodes/nodes.scss +++ b/packages/core/src/renderer/components/+nodes/nodes.scss @@ -16,6 +16,10 @@ @include table-cell-warning; } + .LineProgress { + width: 100%; + } + &.cpu { flex: 1.0; align-self: center; diff --git a/packages/core/src/renderer/components/+user-management/+cluster-role-bindings/__tests__/dialog.test.tsx b/packages/core/src/renderer/components/+user-management/+cluster-role-bindings/__tests__/dialog.test.tsx index 04cf657606..3af6b8c8ff 100644 --- a/packages/core/src/renderer/components/+user-management/+cluster-role-bindings/__tests__/dialog.test.tsx +++ b/packages/core/src/renderer/components/+user-management/+cluster-role-bindings/__tests__/dialog.test.tsx @@ -40,8 +40,6 @@ describe("ClusterRoleBindingDialog tests", () => { contextName: "some-context-name", id: "some-cluster-id", kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", })); render = renderFor(di); diff --git a/packages/core/src/renderer/components/+user-management/+role-bindings/__tests__/dialog.test.tsx b/packages/core/src/renderer/components/+user-management/+role-bindings/__tests__/dialog.test.tsx index 96f7bb5b68..16e2b8e5d1 100644 --- a/packages/core/src/renderer/components/+user-management/+role-bindings/__tests__/dialog.test.tsx +++ b/packages/core/src/renderer/components/+user-management/+role-bindings/__tests__/dialog.test.tsx @@ -36,8 +36,6 @@ describe("RoleBindingDialog tests", () => { contextName: "some-context-name", id: "some-cluster-id", kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", })); render = renderFor(di); diff --git a/packages/core/src/renderer/components/+workloads-daemonsets/store.ts b/packages/core/src/renderer/components/+workloads-daemonsets/store.ts index 1345468b62..0a5e0b8f65 100644 --- a/packages/core/src/renderer/components/+workloads-daemonsets/store.ts +++ b/packages/core/src/renderer/components/+workloads-daemonsets/store.ts @@ -18,6 +18,9 @@ export class DaemonSetStore extends KubeObjectStore { super(dependencies, api, opts); } + /** + * @deprecated Switch to using `getPodsByOwnerId` directly + */ getChildPods(daemonSet: DaemonSet): Pod[] { return this.dependencies.getPodsByOwnerId(daemonSet.getId()); } diff --git a/packages/core/src/renderer/components/+workloads-jobs/store.ts b/packages/core/src/renderer/components/+workloads-jobs/store.ts index 2f6f671aad..d55153c6fa 100644 --- a/packages/core/src/renderer/components/+workloads-jobs/store.ts +++ b/packages/core/src/renderer/components/+workloads-jobs/store.ts @@ -19,6 +19,9 @@ export class JobStore extends KubeObjectStore { super(dependencies, api, opts); } + /** + * @deprecated Switch to using `getPodsByOwnerId` directly + */ getChildPods(job: Job): Pod[] { return this.dependencies.getPodsByOwnerId(job.getId()); } diff --git a/packages/core/src/renderer/components/+workloads-overview/workloads/workloads.injectable.ts b/packages/core/src/renderer/components/+workloads-overview/workloads/workloads.injectable.ts index d6b23b582f..a90e3d63d9 100644 --- a/packages/core/src/renderer/components/+workloads-overview/workloads/workloads.injectable.ts +++ b/packages/core/src/renderer/components/+workloads-overview/workloads/workloads.injectable.ts @@ -4,7 +4,7 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import { computed } from "mobx"; -import { shouldShowResourceInjectionToken } from "../../../../common/cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { byOrderNumber } from "../../../../common/utils/composable-responsibilities/orderable/orderable"; import { workloadInjectionToken } from "./workload-injection-token"; diff --git a/packages/core/src/renderer/components/+workloads-pods/__snapshots__/secret-key.test.tsx.snap b/packages/core/src/renderer/components/+workloads-pods/__snapshots__/secret-key.test.tsx.snap new file mode 100644 index 0000000000..3d1c8368ad --- /dev/null +++ b/packages/core/src/renderer/components/+workloads-pods/__snapshots__/secret-key.test.tsx.snap @@ -0,0 +1,92 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SecretKey technical tests renders 1`] = ` + +
+ secret(some-secret-name)[some-key] +   + + + visibility + + +
+ Show +
+
+ +`; + +exports[`SecretKey technical tests when the show secret button is clicked renders 1`] = ` + +
+ secret(some-secret-name)[some-key] +   + + + visibility + + +
+ Show +
+
+ +`; + +exports[`SecretKey technical tests when the show secret button is clicked when the secret fails to load with a primitive renders 1`] = ` + +
+ Error: some-other-error +
+ +`; + +exports[`SecretKey technical tests when the show secret button is clicked when the secret fails to load with an error renders 1`] = ` + +
+ Error: some-error +
+ +`; + +exports[`SecretKey technical tests when the show secret button is clicked when the secret fails to load with an object renders 1`] = ` + +
+ Error: {"message":"some-error"} +
+ +`; + +exports[`SecretKey technical tests when the show secret button is clicked when the secret loads with base64 encoded data renders 1`] = ` + +
+ some-data-for-some-key +
+ +`; + +exports[`SecretKey technical tests when the show secret button is clicked when the secret loads with non base64 encoded data renders 1`] = ` + +
+ some-data-for-some-key +
+ +`; diff --git a/packages/core/src/renderer/components/+workloads-pods/__tests__/__snapshots__/pod-container-env.test.tsx.snap b/packages/core/src/renderer/components/+workloads-pods/__tests__/__snapshots__/pod-container-env.test.tsx.snap index 1183ce4309..1a243d6fa8 100644 --- a/packages/core/src/renderer/components/+workloads-pods/__tests__/__snapshots__/pod-container-env.test.tsx.snap +++ b/packages/core/src/renderer/components/+workloads-pods/__tests__/__snapshots__/pod-container-env.test.tsx.snap @@ -158,10 +158,11 @@ exports[` renders envFrom when given a secretRef 1`] = ` bar : - secretKeyRef(my-secret.bar) + secret(my-secret)[bar]   renders envFrom when given a secretRef 1`] = ` visibility -
+
Show
diff --git a/packages/core/src/renderer/components/+workloads-pods/pod-container-env.tsx b/packages/core/src/renderer/components/+workloads-pods/pod-container-env.tsx index 977c945f18..00d13fa833 100644 --- a/packages/core/src/renderer/components/+workloads-pods/pod-container-env.tsx +++ b/packages/core/src/renderer/components/+workloads-pods/pod-container-env.tsx @@ -5,19 +5,19 @@ import "./pod-container-env.scss"; -import React, { useEffect, useState } from "react"; +import React, { useEffect } from "react"; import { observer } from "mobx-react"; -import type { Container, EnvVarKeySelector, Secret } from "../../../common/k8s-api/endpoints"; +import type { Container } from "../../../common/k8s-api/endpoints"; import { DrawerItem } from "../drawer"; import { autorun } from "mobx"; -import { Icon } from "../icon"; -import { base64, cssNames, object } from "@k8slens/utilities"; +import { object } from "@k8slens/utilities"; import _ from "lodash"; import { withInjectables } from "@ogre-tools/injectable-react"; import type { ConfigMapStore } from "../+config-maps/store"; import type { SecretStore } from "../+config-secrets/store"; import configMapStoreInjectable from "../+config-maps/store.injectable"; import secretStoreInjectable from "../+config-secrets/store.injectable"; +import { SecretKey } from "./secret-key"; export interface ContainerEnvironmentProps { container: Container; @@ -74,9 +74,11 @@ const NonInjectedContainerEnvironment = observer((props: Dependencies & Containe } else if (secretKeyRef?.name) { secretValue = ( ); } else if (configMapKeyRef?.name) { @@ -151,7 +153,6 @@ const NonInjectedContainerEnvironment = observer((props: Dependencies & Containe key, }} namespace={namespace} - secretStore={secretStore} />
)); @@ -172,52 +173,3 @@ export const ContainerEnvironment = withInjectables { - const { - reference: { name, key }, - namespace, - secretStore, - } = props; - - const [loading, setLoading] = useState(false); - const [secret, setSecret] = useState(); - - if (!name) { - return null; - } - - const showKey = async (evt: React.MouseEvent) => { - evt.preventDefault(); - setLoading(true); - const secret = await secretStore.load({ name, namespace }); - - setLoading(false); - setSecret(secret); - }; - - const value = secret?.data?.[key]; - - if (value) { - return <>{base64.decode(value)}; - } - - return ( - <> - {`secretKeyRef(${name}.${key})`} -   - - - ); -}; diff --git a/packages/core/src/renderer/components/+workloads-pods/pod-details-container.tsx b/packages/core/src/renderer/components/+workloads-pods/pod-details-container.tsx index 00db1af71b..d066497780 100644 --- a/packages/core/src/renderer/components/+workloads-pods/pod-details-container.tsx +++ b/packages/core/src/renderer/components/+workloads-pods/pod-details-container.tsx @@ -97,7 +97,7 @@ class NonInjectedPodDetailsContainer extends React.Component c.name == name); - const isMetricHidden = containerMetricsVisible.get(); + const isMetricVisible = containerMetricsVisible.get(); return (
@@ -105,7 +105,7 @@ class NonInjectedPodDetailsContainer extends React.Component {name}
- {(!isMetricHidden && !isInitContainer && metrics) && ( + {(isMetricVisible && !isInitContainer && metrics) && ( { + let loadSecretMock: AsyncFnMock; + let result: RenderResult; + + beforeEach(() => { + const di = getDiForUnitTesting(); + const render = renderFor(di); + + loadSecretMock = asyncFn(); + di.override(secretStoreInjectable, () => ({ + load: loadSecretMock, + } as Partial as SecretStore)); + + result = render(( + + )); + }); + + it("renders", () => { + expect(result.baseElement).toMatchSnapshot(); + }); + + it("should not try to load secret", () => { + expect(loadSecretMock).not.toBeCalled(); + }); + + it("should show the 'show secret' button", () => { + expect(result.queryByTestId("show-secret-button-for-some-namespace/some-secret-name:some-key")).toBeInTheDocument(); + }); + + describe("when the show secret button is clicked", () => { + beforeEach(() => { + result + .getByTestId("show-secret-button-for-some-namespace/some-secret-name:some-key") + .click(); + }); + + it("renders", () => { + expect(result.baseElement).toMatchSnapshot(); + }); + + it("should try to load secret", () => { + expect(loadSecretMock).toBeCalledWith({ + name: "some-secret-name", + namespace: "some-namespace", + }); + }); + + it("should mark icon as disabled", () => { + expect(result.queryByTestId("show-secret-button-for-some-namespace/some-secret-name:some-key")).toHaveClass("disabled"); + }); + + describe("when the secret loads with base64 encoded data", () => { + beforeEach(async () => { + await act(async () => { + await loadSecretMock.resolve(new Secret({ + apiVersion: Secret.apiBase, + kind: Secret.kind, + metadata: { + name: "some-secret-name", + namespace: "some-namespace", + resourceVersion: "some-resource-version", + selfLink: "some-self-link", + uid: "some-uid", + }, + type: SecretType.Opaque, + data: { + "some-key": base64.encode("some-data-for-some-key"), + }, + })); + }); + }); + + it("renders", () => { + expect(result.baseElement).toMatchSnapshot(); + }); + + it("should not show the 'show secret' button", () => { + expect(result.queryByTestId("show-secret-button-for-some-namespace/some-secret-name:some-key")).not.toBeInTheDocument(); + }); + + it("should show the decoded secret data", () => { + expect(result.queryByText("some-data-for-some-key")).toBeInTheDocument(); + }); + }); + + describe("when the secret loads with non base64 encoded data", () => { + beforeEach(async () => { + await act(async () => { + await loadSecretMock.resolve(new Secret({ + apiVersion: Secret.apiBase, + kind: Secret.kind, + metadata: { + name: "some-secret-name", + namespace: "some-namespace", + resourceVersion: "some-resource-version", + selfLink: "some-self-link", + uid: "some-uid", + }, + type: SecretType.Opaque, + data: { + "some-key": "some-data-for-some-key", + }, + })); + }); + }); + + it("renders", () => { + expect(result.baseElement).toMatchSnapshot(); + }); + + it("should not show the 'show secret' button", () => { + expect(result.queryByTestId("show-secret-button-for-some-namespace/some-secret-name:some-key")).not.toBeInTheDocument(); + }); + + it("should show the non decoded secret data", () => { + expect(result.queryByText("some-data-for-some-key")).toBeInTheDocument(); + }); + }); + + describe("when the secret fails to load with an error", () => { + beforeEach(async () => { + await act(async () => { + await loadSecretMock.reject(new Error("some-error")); + }); + }); + + it("renders", () => { + expect(result.baseElement).toMatchSnapshot(); + }); + + it("should not show the 'show secret' button", () => { + expect(result.queryByTestId("show-secret-button-for-some-namespace/some-secret-name:some-key")).not.toBeInTheDocument(); + }); + + it("should show the loading error", () => { + expect(result.queryByText("Error: some-error")).toBeInTheDocument(); + }); + }); + + describe("when the secret fails to load with an object", () => { + beforeEach(async () => { + await act(async () => { + await loadSecretMock.reject({ message: "some-error" }); + }); + }); + + it("renders", () => { + expect(result.baseElement).toMatchSnapshot(); + }); + + it("should not show the 'show secret' button", () => { + expect(result.queryByTestId("show-secret-button-for-some-namespace/some-secret-name:some-key")).not.toBeInTheDocument(); + }); + + it("should show the loading error as JSON", () => { + expect(result.queryByText(`Error: {"message":"some-error"}`)).toBeInTheDocument(); + }); + }); + + describe("when the secret fails to load with a primitive", () => { + beforeEach(async () => { + await act(async () => { + await loadSecretMock.reject("some-other-error"); + }); + }); + + it("renders", () => { + expect(result.baseElement).toMatchSnapshot(); + }); + + it("should not show the 'show secret' button", () => { + expect(result.queryByTestId("show-secret-button-for-some-namespace/some-secret-name:some-key")).not.toBeInTheDocument(); + }); + + it("should show the loading error as JSON", () => { + expect(result.queryByText("Error: some-other-error")).toBeInTheDocument(); + }); + }); + }); +}); diff --git a/packages/core/src/renderer/components/+workloads-pods/secret-key.tsx b/packages/core/src/renderer/components/+workloads-pods/secret-key.tsx new file mode 100644 index 0000000000..ada6e9c3a2 --- /dev/null +++ b/packages/core/src/renderer/components/+workloads-pods/secret-key.tsx @@ -0,0 +1,85 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import React, { useState } from "react"; +import type { EnvVarKeySelector } from "../../../common/k8s-api/endpoints"; +import { Icon } from "../icon"; +import { base64, cssNames, isObject } from "@k8slens/utilities"; +import type { SecretStore } from "../+config-secrets/store"; +import { withInjectables } from "@ogre-tools/injectable-react"; +import secretStoreInjectable from "../+config-secrets/store.injectable"; +import type { SetRequired } from "type-fest"; + +export interface SecretKeyProps { + reference: SetRequired; + namespace: string; +} + +interface Dependencies { + secretStore: SecretStore; +} + +const NonInjectedSecretKey = (props: SecretKeyProps & Dependencies) => { + const { + reference: { name, key }, namespace, secretStore, + } = props; + + const [loading, setLoading] = useState(false); + const [secretData, setSecretData] = useState(); + + if (!name) { + return null; + } + + const showKey = async (evt: React.MouseEvent) => { + evt.preventDefault(); + setLoading(true); + + try { + const secret = await secretStore.load({ name, namespace }); + + try { + setSecretData(base64.decode(secret.data[key] ?? "")); + } catch { + setSecretData(secret.data[key]); + } + } catch (error) { + if (error instanceof Error) { + setSecretData(`${error}`); + } else if (isObject(error)) { + setSecretData(`Error: ${JSON.stringify(error)}`); + } else { + setSecretData(`Error: ${error}`); + } + } finally { + setLoading(false); + } + }; + + if (secretData) { + return <>{secretData}; + } + + return ( + <> + {`secret(${name})[${key}]`} +   + + + ); +}; + +export const SecretKey = withInjectables(NonInjectedSecretKey, { + getProps: (di, props) => ({ + ...props, + secretStore: di.inject(secretStoreInjectable), + }), +}); diff --git a/packages/core/src/renderer/components/+workloads-replicasets/store.ts b/packages/core/src/renderer/components/+workloads-replicasets/store.ts index 1a6ac96610..e0873e9386 100644 --- a/packages/core/src/renderer/components/+workloads-replicasets/store.ts +++ b/packages/core/src/renderer/components/+workloads-replicasets/store.ts @@ -18,6 +18,9 @@ export class ReplicaSetStore extends KubeObjectStore super(dependencies, api, opts); } + /** + * @deprecated Switch to using `getPodsByOwnerId` directly + */ getChildPods(replicaSet: ReplicaSet) { return this.dependencies.getPodsByOwnerId(replicaSet.getId()); } diff --git a/packages/core/src/renderer/components/+workloads-statefulsets/store.ts b/packages/core/src/renderer/components/+workloads-statefulsets/store.ts index bdedcdcaf7..28a581ed4a 100644 --- a/packages/core/src/renderer/components/+workloads-statefulsets/store.ts +++ b/packages/core/src/renderer/components/+workloads-statefulsets/store.ts @@ -18,6 +18,9 @@ export class StatefulSetStore extends KubeObjectStore { contextName: "some-context-name", id: "some-cluster-id", kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", })); cronJobStore = di.inject(cronJobStoreInjectable); diff --git a/packages/core/src/renderer/components/__tests__/daemonset.store.test.ts b/packages/core/src/renderer/components/__tests__/daemonset.store.test.ts index bfd4861f48..72ab9e20b1 100644 --- a/packages/core/src/renderer/components/__tests__/daemonset.store.test.ts +++ b/packages/core/src/renderer/components/__tests__/daemonset.store.test.ts @@ -148,8 +148,6 @@ describe("DaemonSet Store tests", () => { contextName: "some-context-name", id: "some-cluster-id", kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", })); const podStore = di.inject(podStoreInjectable); diff --git a/packages/core/src/renderer/components/__tests__/deployments.store.test.ts b/packages/core/src/renderer/components/__tests__/deployments.store.test.ts index 23987ccd37..f1a8de27a0 100644 --- a/packages/core/src/renderer/components/__tests__/deployments.store.test.ts +++ b/packages/core/src/renderer/components/__tests__/deployments.store.test.ts @@ -220,8 +220,6 @@ describe("Deployment Store tests", () => { contextName: "some-context-name", id: "some-cluster-id", kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", })); const podStore = di.inject(podStoreInjectable); diff --git a/packages/core/src/renderer/components/__tests__/job.store.test.ts b/packages/core/src/renderer/components/__tests__/job.store.test.ts index 73a5cade8f..6f08fc5e7a 100644 --- a/packages/core/src/renderer/components/__tests__/job.store.test.ts +++ b/packages/core/src/renderer/components/__tests__/job.store.test.ts @@ -185,8 +185,6 @@ describe("Job Store tests", () => { contextName: "some-context-name", id: "some-cluster-id", kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", })); jobStore = di.inject(jobStoreInjectable); diff --git a/packages/core/src/renderer/components/__tests__/pods.store.test.ts b/packages/core/src/renderer/components/__tests__/pods.store.test.ts index 03a057458d..79d4f0d4de 100644 --- a/packages/core/src/renderer/components/__tests__/pods.store.test.ts +++ b/packages/core/src/renderer/components/__tests__/pods.store.test.ts @@ -131,8 +131,6 @@ describe("Pod Store tests", () => { contextName: "some-context-name", id: "some-cluster-id", kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", })); podStore = di.inject(podStoreInjectable); diff --git a/packages/core/src/renderer/components/__tests__/replicaset.store.test.ts b/packages/core/src/renderer/components/__tests__/replicaset.store.test.ts index aea690a507..18a0cb813e 100644 --- a/packages/core/src/renderer/components/__tests__/replicaset.store.test.ts +++ b/packages/core/src/renderer/components/__tests__/replicaset.store.test.ts @@ -148,8 +148,6 @@ describe("ReplicaSet Store tests", () => { contextName: "some-context-name", id: "some-cluster-id", kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", })); const podStore = di.inject(podStoreInjectable); diff --git a/packages/core/src/renderer/components/__tests__/statefulset.store.test.ts b/packages/core/src/renderer/components/__tests__/statefulset.store.test.ts index 9e7ada1aa1..de6acf220a 100644 --- a/packages/core/src/renderer/components/__tests__/statefulset.store.test.ts +++ b/packages/core/src/renderer/components/__tests__/statefulset.store.test.ts @@ -148,8 +148,6 @@ describe("StatefulSet Store tests", () => { contextName: "some-context-name", id: "some-cluster-id", kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", })); statefulSetStore = di.inject(statefulSetStoreInjectable); diff --git a/packages/core/src/renderer/components/avatar/avatar.tsx b/packages/core/src/renderer/components/avatar/avatar.tsx index 84892b081c..991b7e761b 100644 --- a/packages/core/src/renderer/components/avatar/avatar.tsx +++ b/packages/core/src/renderer/components/avatar/avatar.tsx @@ -8,9 +8,9 @@ import styles from "./avatar.module.scss"; import type { ImgHTMLAttributes, MouseEventHandler } from "react"; import React from "react"; import randomColor from "randomcolor"; -import GraphemeSplitter from "grapheme-splitter"; import type { SingleOrMany } from "@k8slens/utilities"; -import { cssNames, isDefined, iter } from "@k8slens/utilities"; +import { cssNames } from "@k8slens/utilities"; +import { computeDefaultShortName } from "../../../common/catalog/helpers"; export interface AvatarProps { title: string; @@ -28,40 +28,6 @@ export interface AvatarProps { "data-testid"?: string; } -function getNameParts(name: string): string[] { - const byWhitespace = name.split(/\s+/); - - if (byWhitespace.length > 1) { - return byWhitespace; - } - - const byDashes = name.split(/[-_]+/); - - if (byDashes.length > 1) { - return byDashes; - } - - return name.split(/@+/); -} - -function getLabelFromTitle(title: string) { - if (!title) { - return "??"; - } - - const [rawFirst, rawSecond, rawThird] = getNameParts(title); - const splitter = new GraphemeSplitter(); - const first = splitter.iterateGraphemes(rawFirst); - const second = rawSecond ? splitter.iterateGraphemes(rawSecond): first; - const third = rawThird ? splitter.iterateGraphemes(rawThird) : iter.newEmpty(); - - return [ - ...iter.take(first, 1), - ...iter.take(second, 1), - ...iter.take(third, 1), - ].filter(isDefined).join(""); -} - export const Avatar = ({ title, variant = "rounded", @@ -104,6 +70,6 @@ export const Avatar = ({ alt={title} /> ) - : children || getLabelFromTitle(title)} + : children || computeDefaultShortName(title)}
); diff --git a/packages/core/src/renderer/components/catalog-entities/weblink-add-command.tsx b/packages/core/src/renderer/components/catalog-entities/weblink-add-command.tsx index 0209ff93fa..221af86f38 100644 --- a/packages/core/src/renderer/components/catalog-entities/weblink-add-command.tsx +++ b/packages/core/src/renderer/components/catalog-entities/weblink-add-command.tsx @@ -7,15 +7,15 @@ import React from "react"; import { observer } from "mobx-react"; import { Input } from "../input"; import { isUrl } from "../input/input_validators"; -import type { WeblinkStore } from "../../../common/weblinks-store/weblink-store"; import { computed, makeObservable, observable } from "mobx"; import { withInjectables } from "@ogre-tools/injectable-react"; import commandOverlayInjectable from "../command-palette/command-overlay.injectable"; -import weblinkStoreInjectable from "../../../common/weblinks-store/weblink-store.injectable"; +import type { AddWeblink } from "../../../features/weblinks/common/add.injectable"; +import addWeblinkInjectable from "../../../features/weblinks/common/add.injectable"; interface Dependencies { closeCommandOverlay: () => void; - weblinkStore: WeblinkStore; + addWeblink: AddWeblink; } @@ -42,7 +42,7 @@ class NonInjectedWeblinkAddCommand extends React.Component { } onSubmit(name: string) { - this.props.weblinkStore.add({ + this.props.addWeblink({ name: name || this.url, url: this.url, }); @@ -97,6 +97,6 @@ export const WeblinkAddCommand = withInjectables(NonInjectedWeblin getProps: (di, props) => ({ ...props, closeCommandOverlay: di.inject(commandOverlayInjectable).close, - weblinkStore: di.inject(weblinkStoreInjectable), + addWeblink: di.inject(addWeblinkInjectable), }), }); diff --git a/packages/core/src/renderer/components/cluster-manager/cluster-frame-handler.injectable.ts b/packages/core/src/renderer/components/cluster-manager/cluster-frame-handler.injectable.ts index 3f40455527..079bf22b2d 100644 --- a/packages/core/src/renderer/components/cluster-manager/cluster-frame-handler.injectable.ts +++ b/packages/core/src/renderer/components/cluster-manager/cluster-frame-handler.injectable.ts @@ -3,8 +3,8 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import getClusterByIdInjectable from "../../../common/cluster-store/get-by-id.injectable"; import loggerInjectable from "../../../common/logger.injectable"; +import getClusterByIdInjectable from "../../../features/cluster/storage/common/get-by-id.injectable"; import { ClusterFrameHandler } from "./cluster-frame-handler"; import emitClusterVisibilityInjectable from "./emit-cluster-visibility.injectable"; diff --git a/packages/core/src/renderer/components/cluster-manager/cluster-frame-handler.ts b/packages/core/src/renderer/components/cluster-manager/cluster-frame-handler.ts index 7420fa680a..eca0e28e11 100644 --- a/packages/core/src/renderer/components/cluster-manager/cluster-frame-handler.ts +++ b/packages/core/src/renderer/components/cluster-manager/cluster-frame-handler.ts @@ -9,8 +9,8 @@ import type { Disposer } from "@k8slens/utilities"; import { onceDefined } from "@k8slens/utilities"; import assert from "assert"; import type { Logger } from "../../../common/logger"; -import type { GetClusterById } from "../../../common/cluster-store/get-by-id.injectable"; import { getClusterFrameUrl } from "../../../common/utils"; +import type { GetClusterById } from "../../../features/cluster/storage/common/get-by-id.injectable"; export interface LensView { isLoaded: boolean; diff --git a/packages/core/src/renderer/components/cluster-manager/cluster-view.tsx b/packages/core/src/renderer/components/cluster-manager/cluster-view.tsx index 6749fdd9d3..1e5062b313 100644 --- a/packages/core/src/renderer/components/cluster-manager/cluster-view.tsx +++ b/packages/core/src/renderer/components/cluster-manager/cluster-view.tsx @@ -18,10 +18,10 @@ import clusterViewRouteParametersInjectable from "./cluster-view-route-parameter import clusterFrameHandlerInjectable from "./cluster-frame-handler.injectable"; import type { CatalogEntityRegistry } from "../../api/catalog/entity/registry"; import catalogEntityRegistryInjectable from "../../api/catalog/entity/registry.injectable"; -import type { GetClusterById } from "../../../common/cluster-store/get-by-id.injectable"; -import getClusterByIdInjectable from "../../../common/cluster-store/get-by-id.injectable"; import type { RequestClusterActivation } from "../../../features/cluster/activation/common/request-token"; import requestClusterActivationInjectable from "../../../features/cluster/activation/renderer/request-activation.injectable"; +import type { GetClusterById } from "../../../features/cluster/storage/common/get-by-id.injectable"; +import getClusterByIdInjectable from "../../../features/cluster/storage/common/get-by-id.injectable"; interface Dependencies { clusterId: IComputedValue; diff --git a/packages/core/src/renderer/components/cluster-settings/__tests__/cluster-local-terminal-settings.test.tsx b/packages/core/src/renderer/components/cluster-settings/__tests__/cluster-local-terminal-settings.test.tsx index ae807bab0a..7abaf8ac15 100644 --- a/packages/core/src/renderer/components/cluster-settings/__tests__/cluster-local-terminal-settings.test.tsx +++ b/packages/core/src/renderer/components/cluster-settings/__tests__/cluster-local-terminal-settings.test.tsx @@ -55,8 +55,6 @@ describe("ClusterLocalTerminalSettings", () => { terminalCWD: "/foobar", defaultNamespace: "kube-system", }, - }, { - clusterServerUrl: "https://localhost:12345", }); const dom = render(); @@ -76,8 +74,6 @@ describe("ClusterLocalTerminalSettings", () => { preferences: { terminalCWD: "/foobar", }, - }, { - clusterServerUrl: "https://localhost:12345", }); const dom = render(); @@ -98,8 +94,6 @@ describe("ClusterLocalTerminalSettings", () => { preferences: { terminalCWD: "/foobar", }, - }, { - clusterServerUrl: "https://localhost:12345", }); const dom = render(); @@ -129,8 +123,6 @@ describe("ClusterLocalTerminalSettings", () => { contextName: "some-context-name", id: "some-cluster-id", kubeConfigPath: "/some/path", - }, { - clusterServerUrl: "https://localhost:12345", }); const dom = render(); @@ -161,8 +153,6 @@ describe("ClusterLocalTerminalSettings", () => { contextName: "some-context-name", id: "some-cluster-id", kubeConfigPath: "/some/path", - }, { - clusterServerUrl: "https://localhost:12345", }); const dom = render(); diff --git a/packages/core/src/renderer/components/cluster-settings/__tests__/icon-settings.test.tsx b/packages/core/src/renderer/components/cluster-settings/__tests__/icon-settings.test.tsx index 31bf671854..4c88f34503 100644 --- a/packages/core/src/renderer/components/cluster-settings/__tests__/icon-settings.test.tsx +++ b/packages/core/src/renderer/components/cluster-settings/__tests__/icon-settings.test.tsx @@ -17,32 +17,6 @@ import { clusterIconSettingsComponentInjectionToken, clusterIconSettingsMenuInje import { runInAction } from "mobx"; import { getInjectable, type DiContainer } from "@ogre-tools/injectable"; -const cluster = new Cluster({ - contextName: "some-context", - id: "some-id", - kubeConfigPath: "/some/path/to/kubeconfig", - preferences: { - clusterName: "some-cluster-name", - }, -}, { - clusterServerUrl: "https://localhost:9999", -}); - -const clusterEntity = new KubernetesCluster({ - metadata: { - labels: {}, - name: "some-kubernetes-cluster", - uid: "some-entity-id", - }, - spec: { - kubeconfigContext: "some-context", - kubeconfigPath: "/some/path/to/kubeconfig", - }, - status: { - phase: "connecting", - }, -}); - const newMenuItem = getInjectable({ id: "cluster-icon-settings-menu-test-item", @@ -67,7 +41,7 @@ function CustomSettingsComponent(props: ClusterIconSettingComponentProps) {
); -} +} const newSettingsReactComponent = getInjectable({ id: "cluster-icon-settings-react-component", @@ -86,8 +60,31 @@ describe("Icon settings", () => { beforeEach(() => { di = getDiForUnitTesting(); - + const render = renderFor(di); + const cluster = new Cluster({ + contextName: "some-context", + id: "some-id", + kubeConfigPath: "/some/path/to/kubeconfig", + preferences: { + clusterName: "some-cluster-name", + }, + }); + + const clusterEntity = new KubernetesCluster({ + metadata: { + labels: {}, + name: "some-kubernetes-cluster", + uid: "some-entity-id", + }, + spec: { + kubeconfigContext: "some-context", + kubeconfigPath: "/some/path/to/kubeconfig", + }, + status: { + phase: "connecting", + }, + }); rendered = render( , diff --git a/packages/core/src/renderer/components/delete-cluster-dialog/view.tsx b/packages/core/src/renderer/components/delete-cluster-dialog/view.tsx index b1548354fd..69666efede 100644 --- a/packages/core/src/renderer/components/delete-cluster-dialog/view.tsx +++ b/packages/core/src/renderer/components/delete-cluster-dialog/view.tsx @@ -15,9 +15,7 @@ import { Dialog } from "../dialog"; import { Icon } from "../icon"; import { Select } from "../select"; import { Checkbox } from "../checkbox"; -import type { HotbarStore } from "../../../common/hotbars/store"; import { withInjectables } from "@ogre-tools/injectable-react"; -import hotbarStoreInjectable from "../../../common/hotbars/store.injectable"; import type { DeleteClusterDialogState } from "./state.injectable"; import deleteClusterDialogStateInjectable from "./state.injectable"; import type { RequestSetClusterAsDeleting } from "../../../features/cluster/delete-dialog/renderer/request-set-as-deleting.injectable"; @@ -32,16 +30,18 @@ import showErrorNotificationInjectable from "../notifications/show-error-notific import { isCurrentContext } from "./is-current-context"; import type { IsInLocalKubeconfig } from "./is-in-local-kubeconfig.injectable"; import isInLocalKubeconfigInjectable from "./is-in-local-kubeconfig.injectable"; +import type { RemoveEntityFromAllHotbars } from "../../../features/hotbar/storage/common/remove-entity-from-all.injectable"; +import removeEntityFromAllHotbarsInjectable from "../../../features/hotbar/storage/common/remove-entity-from-all.injectable"; interface Dependencies { state: IObservableValue; - hotbarStore: HotbarStore; requestSetClusterAsDeleting: RequestSetClusterAsDeleting; requestDeleteCluster: RequestDeleteCluster; requestClearClusterAsDeleting: RequestClearClusterAsDeleting; showErrorNotification: ShowNotification; saveKubeconfig: SaveKubeconfig; isInLocalKubeconfig: IsInLocalKubeconfig; + removeEntityFromAllHotbars: RemoveEntityFromAllHotbars; } @observer @@ -65,7 +65,7 @@ class NonInjectedDeleteClusterDialog extends React.Component { try { await this.props.saveKubeconfig(config, cluster.kubeConfigPath.get()); - this.props.hotbarStore.removeAllHotbarItems(cluster.id); + this.props.removeEntityFromAllHotbars(cluster.id); await this.props.requestDeleteCluster(cluster.id); } catch(error) { this.props.showErrorNotification(`Cannot remove cluster, failed to process config file. ${error}`); @@ -267,7 +267,6 @@ class NonInjectedDeleteClusterDialog extends React.Component { export const DeleteClusterDialog = withInjectables(NonInjectedDeleteClusterDialog, { getProps: (di) => ({ - hotbarStore: di.inject(hotbarStoreInjectable), state: di.inject(deleteClusterDialogStateInjectable), requestSetClusterAsDeleting: di.inject(requestSetClusterAsDeletingInjectable), requestClearClusterAsDeleting: di.inject(requestClearClusterAsDeletingInjectable), @@ -275,5 +274,6 @@ export const DeleteClusterDialog = withInjectables(NonInjectedDele saveKubeconfig: di.inject(saveKubeconfigInjectable), showErrorNotification: di.inject(showErrorNotificationInjectable), isInLocalKubeconfig: di.inject(isInLocalKubeconfigInjectable), + removeEntityFromAllHotbars: di.inject(removeEntityFromAllHotbarsInjectable), }), }); diff --git a/packages/core/src/renderer/components/dock/logs/download-all-logs.injectable.ts b/packages/core/src/renderer/components/dock/logs/download-all-logs.injectable.ts index 089652af0e..45e9700564 100644 --- a/packages/core/src/renderer/components/dock/logs/download-all-logs.injectable.ts +++ b/packages/core/src/renderer/components/dock/logs/download-all-logs.injectable.ts @@ -25,7 +25,7 @@ const downloadAllLogsInjectable = getInjectable({ }); if (logs) { - openSaveFileDialog(`${params.name}.log`, logs, "text/plain"); + openSaveFileDialog(`${query.container}.log`, logs, "text/plain"); } else { showErrorNotification("No logs to download"); } diff --git a/packages/core/src/renderer/components/dock/logs/list.tsx b/packages/core/src/renderer/components/dock/logs/list.tsx index 6b3eded365..d0e437e720 100644 --- a/packages/core/src/renderer/components/dock/logs/list.tsx +++ b/packages/core/src/renderer/components/dock/logs/list.tsx @@ -15,7 +15,6 @@ import { disposeOnUnmount, observer } from "mobx-react"; import moment from "moment-timezone"; import type { Align, ListOnScrollProps } from "react-window"; import { SearchStore } from "../../../search-store/search-store"; -import type { UserStore } from "../../../../common/user-store"; import { array, cssNames } from "@k8slens/utilities"; import type { VirtualListRef } from "../../virtual-list"; import { VirtualList } from "../../virtual-list"; @@ -23,8 +22,9 @@ import { ToBottom } from "./to-bottom"; import type { LogTabViewModel } from "../logs/logs-view-model"; import { Spinner } from "../../spinner"; import { withInjectables } from "@ogre-tools/injectable-react"; -import userStoreInjectable from "../../../../common/user-store/user-store.injectable"; import autoBindReact from "auto-bind/react"; +import type { UserPreferencesState } from "../../../../features/user-preferences/common/state.injectable"; +import userPreferencesStateInjectable from "../../../../features/user-preferences/common/state.injectable"; export interface LogListProps { model: LogTabViewModel; @@ -37,7 +37,7 @@ export interface LogListRef { } interface Dependencies { - userStore: UserStore; + state: UserPreferencesState; } @observer @@ -129,7 +129,7 @@ class NonForwardedLogList extends React.Component (`${logTimestamp && moment.tz(logTimestamp, this.props.userStore.localeTimezone).format()}${log}`)); + .map(([logTimestamp, log]) => (`${logTimestamp && moment.tz(logTimestamp, this.props.state.localeTimezone).format()}${log}`)); } /** @@ -283,7 +283,7 @@ class NonForwardedLogList extends React.Component }>(NonForwardedLogList, { getProps: (di, props) => ({ ...props, - userStore: di.inject(userStoreInjectable), + state: di.inject(userPreferencesStateInjectable), }), }); diff --git a/packages/core/src/renderer/components/dock/logs/logs-view-model.ts b/packages/core/src/renderer/components/dock/logs/logs-view-model.ts index a54c0f64f6..fbf79a4010 100644 --- a/packages/core/src/renderer/components/dock/logs/logs-view-model.ts +++ b/packages/core/src/renderer/components/dock/logs/logs-view-model.ts @@ -101,7 +101,7 @@ export class LogTabViewModel { if (pod && tabData) { const params = { name: pod.getName(), namespace: pod.getNs() }; - const query = { timestamps: tabData.showTimestamps, previous: tabData.showPrevious }; + const query = { timestamps: tabData.showTimestamps, previous: tabData.showPrevious, container: tabData.selectedContainer }; return this.dependencies.downloadAllLogs(params, query); } diff --git a/packages/core/src/renderer/components/dock/terminal/create-terminal.injectable.ts b/packages/core/src/renderer/components/dock/terminal/create-terminal.injectable.ts index 2ff82e2c17..88e7514855 100644 --- a/packages/core/src/renderer/components/dock/terminal/create-terminal.injectable.ts +++ b/packages/core/src/renderer/components/dock/terminal/create-terminal.injectable.ts @@ -8,13 +8,12 @@ import { Terminal } from "./terminal"; import type { TabId } from "../dock/store"; import type { TerminalApi } from "../../../api/terminal-api"; import terminalSpawningPoolInjectable from "./terminal-spawning-pool.injectable"; -import terminalConfigInjectable from "../../../../common/user-store/terminal-config.injectable"; -import terminalCopyOnSelectInjectable - from "../../../../common/user-store/terminal-copy-on-select.injectable"; import isMacInjectable from "../../../../common/vars/is-mac.injectable"; import openLinkInBrowserInjectable from "../../../../common/utils/open-link-in-browser.injectable"; import xtermColorThemeInjectable from "../../../themes/terminal-colors.injectable"; import loggerInjectable from "../../../../common/logger.injectable"; +import terminalConfigInjectable from "../../../../features/user-preferences/common/terminal-config.injectable"; +import terminalCopyOnSelectInjectable from "../../../../features/user-preferences/common/terminal-copy-on-select.injectable"; export type CreateTerminal = (tabId: TabId, api: TerminalApi) => Terminal; diff --git a/packages/core/src/renderer/components/dock/terminal/terminal.ts b/packages/core/src/renderer/components/dock/terminal/terminal.ts index 9e307aa7dc..a0c3a5384a 100644 --- a/packages/core/src/renderer/components/dock/terminal/terminal.ts +++ b/packages/core/src/renderer/components/dock/terminal/terminal.ts @@ -14,11 +14,11 @@ import { disposer } from "@k8slens/utilities"; import { once } from "lodash"; import { clipboard } from "electron"; import type { Logger } from "../../../../common/logger"; -import type { TerminalConfig } from "../../../../common/user-store/preferences-helpers"; import assert from "assert"; import { TerminalChannels } from "../../../../common/terminal/channels"; import { LinkProvider } from "xterm-link-provider"; import type { OpenLinkInBrowser } from "../../../../common/utils/open-link-in-browser.injectable"; +import type { TerminalConfig } from "../../../../features/user-preferences/common/preferences-helpers"; export interface TerminalDependencies { readonly spawningPool: HTMLElement; diff --git a/packages/core/src/renderer/components/hotbar/__tests__/__snapshots__/hotbar-remove-command.test.tsx.snap b/packages/core/src/renderer/components/hotbar/__tests__/__snapshots__/hotbar-remove-command.test.tsx.snap new file mode 100644 index 0000000000..89eacf6e48 --- /dev/null +++ b/packages/core/src/renderer/components/hotbar/__tests__/__snapshots__/hotbar-remove-command.test.tsx.snap @@ -0,0 +1,97 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders w/o errors 1`] = ` +
+
+ + + option , selected. + + + 2 results available. Use Up and Down to choose options, press Enter to select the currently focused option, press Escape to exit the menu, press Tab to select the option and exit the menu. + + + +
+
+
+ Remove hotbar +
+
+ +
+
+
+
+
+
+
+ 1: default +
+
+ 2: non-default +
+
+
+
+
+`; diff --git a/packages/core/src/renderer/components/hotbar/__tests__/hotbar-remove-command.test.tsx b/packages/core/src/renderer/components/hotbar/__tests__/hotbar-remove-command.test.tsx index bb370562f3..98b007206c 100644 --- a/packages/core/src/renderer/components/hotbar/__tests__/hotbar-remove-command.test.tsx +++ b/packages/core/src/renderer/components/hotbar/__tests__/hotbar-remove-command.test.tsx @@ -5,75 +5,56 @@ import "@testing-library/jest-dom/extend-expect"; import { HotbarRemoveCommand } from "../hotbar-remove-command"; +import type { RenderResult } from "@testing-library/react"; import { fireEvent } from "@testing-library/react"; import React from "react"; -import type { DiContainer } from "@ogre-tools/injectable"; import { getDiForUnitTesting } from "../../../getDiForUnitTesting"; -import type { DiRender } from "../../test-utils/renderFor"; import { renderFor } from "../../test-utils/renderFor"; -import hotbarStoreInjectable from "../../../../common/hotbars/store.injectable"; import { ConfirmDialog } from "../../confirm-dialog"; import directoryForUserDataInjectable from "../../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; -import type { HotbarStore } from "../../../../common/hotbars/store"; -import storesAndApisCanBeCreatedInjectable from "../../../stores-apis-can-be-created.injectable"; - -const mockHotbars: Partial> = { - "1": { - id: "1", - name: "Default", - items: [], - }, -}; +import hotbarsStateInjectable from "../../../../features/hotbar/storage/common/state.injectable"; +import type { CreateHotbar } from "../../../../features/hotbar/storage/common/create-hotbar.injectable"; +import createHotbarInjectable from "../../../../features/hotbar/storage/common/create-hotbar.injectable"; +import type { IComputedValue } from "mobx"; +import type { Hotbar } from "../../../../features/hotbar/storage/common/hotbar"; +import hotbarsInjectable from "../../../../features/hotbar/storage/common/hotbars.injectable"; describe("", () => { - let di: DiContainer; - let render: DiRender; + let result: RenderResult; + let createHotbar: CreateHotbar; + let hotbars: IComputedValue; beforeEach(() => { - di = getDiForUnitTesting(); + const di = getDiForUnitTesting(); + const render = renderFor(di); - di.override(storesAndApisCanBeCreatedInjectable, () => true); di.override(directoryForUserDataInjectable, () => "/some-directory-for-user-data"); - render = renderFor(di); - }); + createHotbar = di.inject(createHotbarInjectable); + hotbars = di.inject(hotbarsInjectable); - it("renders w/o errors", () => { - di.override(hotbarStoreInjectable, () => ({ - hotbars: [mockHotbars["1"]], - getById: (id: string) => mockHotbars[id], - remove: () => { - }, - hotbarIndex: () => 0, - getDisplayLabel: () => "1: Default", - }) as unknown as HotbarStore); + const hotbarsState = di.inject(hotbarsStateInjectable); + const defaultHotbar = createHotbar({ name: "default" }); + const nonDefaultHotbar = createHotbar({ name: "non-default" }); - const { container } = render(); + hotbarsState.set(defaultHotbar.id, defaultHotbar); + hotbarsState.set(nonDefaultHotbar.id, nonDefaultHotbar); - expect(container).toBeInstanceOf(HTMLElement); - }); - - it("calls remove if you click on the entry", () => { - const removeMock = jest.fn(); - - di.override(hotbarStoreInjectable, () => ({ - hotbars: [mockHotbars["1"]], - getById: (id: string) => mockHotbars[id], - remove: removeMock, - hotbarIndex: () => 0, - getDisplayLabel: () => "1: Default", - }) as unknown as HotbarStore); - - const { getByText } = render( + result = render(( <> - , - ); + + )); + }); - fireEvent.click(getByText("1: Default")); - fireEvent.click(getByText("Remove Hotbar")); + it("renders w/o errors", () => { + expect(result.container).toMatchSnapshot(); + }); - expect(removeMock).toHaveBeenCalled(); + it("calls remove if you click on the entry", () => { + fireEvent.click(result.getByText("1: default")); + fireEvent.click(result.getByText("Remove Hotbar")); + expect(hotbars.get().length).toBe(1); }); }); diff --git a/packages/core/src/renderer/components/hotbar/hotbar-add-command.tsx b/packages/core/src/renderer/components/hotbar/hotbar-add-command.tsx index 3fd261b213..cb97f638e9 100644 --- a/packages/core/src/renderer/components/hotbar/hotbar-add-command.tsx +++ b/packages/core/src/renderer/components/hotbar/hotbar-add-command.tsx @@ -7,15 +7,15 @@ import React from "react"; import { observer } from "mobx-react"; import type { InputValidator } from "../input"; import { Input } from "../input"; -import type { CreateHotbarData, CreateHotbarOptions } from "../../../common/hotbars/types"; import { withInjectables } from "@ogre-tools/injectable-react"; import commandOverlayInjectable from "../command-palette/command-overlay.injectable"; import uniqueHotbarNameInjectable from "../input/validators/unique-hotbar-name.injectable"; -import addHotbarInjectable from "../../../common/hotbars/add-hotbar.injectable"; +import type { AddHotbar } from "../../../features/hotbar/storage/common/add.injectable"; +import addHotbarInjectable from "../../../features/hotbar/storage/common/add.injectable"; interface Dependencies { closeCommandOverlay: () => void; - addHotbar: (data: CreateHotbarData, opts: CreateHotbarOptions) => void; + addHotbar: AddHotbar; uniqueHotbarName: InputValidator; } diff --git a/packages/core/src/renderer/components/hotbar/hotbar-menu.tsx b/packages/core/src/renderer/components/hotbar/hotbar-menu.tsx index e83848cdfd..b9ba43b711 100644 --- a/packages/core/src/renderer/components/hotbar/hotbar-menu.tsx +++ b/packages/core/src/renderer/components/hotbar/hotbar-menu.tsx @@ -5,7 +5,7 @@ import "./hotbar-menu.scss"; -import React from "react"; +import React, { useState } from "react"; import { observer } from "mobx-react"; import { HotbarEntityIcon } from "./hotbar-entity-icon"; import type { IClassName } from "@k8slens/utilities"; @@ -16,57 +16,46 @@ import { DragDropContext, Draggable, Droppable, type DropResult } from "react-be import { HotbarSelector } from "./hotbar-selector"; import { HotbarCell } from "./hotbar-cell"; import { HotbarIcon } from "./hotbar-icon"; -import type { HotbarItem } from "../../../common/hotbars/types"; -import { defaultHotbarCells } from "../../../common/hotbars/types"; -import { action, makeObservable, observable } from "mobx"; -import hotbarStoreInjectable from "../../../common/hotbars/store.injectable"; +import type { HotbarItem } from "../../../features/hotbar/storage/common/types"; +import { defaultHotbarCells } from "../../../features/hotbar/storage/common/types"; +import type { IComputedValue } from "mobx"; import { withInjectables } from "@ogre-tools/injectable-react"; -import type { HotbarStore } from "../../../common/hotbars/store"; import catalogEntityRegistryInjectable from "../../api/catalog/entity/registry.injectable"; +import type { Hotbar } from "../../../features/hotbar/storage/common/hotbar"; +import activeHotbarInjectable from "../../../features/hotbar/storage/common/active.injectable"; export interface HotbarMenuProps { className?: IClassName; } interface Dependencies { - hotbarStore: HotbarStore; + activeHotbar: IComputedValue; entityRegistry: CatalogEntityRegistry; } -@observer -class NonInjectedHotbarMenu extends React.Component { - @observable draggingOver = false; +const NonInjectedHotbarMenu = observer((props: Dependencies & HotbarMenuProps) => { + const { + activeHotbar, + entityRegistry, + className, + } = props; - constructor(props: Dependencies & HotbarMenuProps) { - super(props); - makeObservable(this); - } + const [draggingOver, setDraggingOver] = useState(false); + const hotbar = activeHotbar.get(); - get hotbar() { - return this.props.hotbarStore.getActive(); - } - - getEntity(item: HotbarItem | null) { - const hotbar = this.props.hotbarStore.getActive(); - - if (!hotbar || !item) { - return null; + const getEntity = (item: HotbarItem | null) => { + if (!item) { + return undefined; } - return this.props.entityRegistry.getById(item.entity.uid) ?? null; - } + return entityRegistry.getById(item.entity.uid); + }; + const onDragStart = () => setDraggingOver(true); + const onDragEnd = (result: DropResult) => { + setDraggingOver(false); - @action - onDragStart() { - this.draggingOver = true; - } - - @action - onDragEnd(result: DropResult) { const { source, destination } = result; - this.draggingOver = false; - if (!destination) { // Dropped outside of the list return; } @@ -74,135 +63,115 @@ class NonInjectedHotbarMenu extends React.Component { - const hotbar = this.props.hotbarStore; - - hotbar.removeFromHotbar(uid); + hotbar?.restack(from, to); }; - - addItem = (entity: CatalogEntity, index = -1) => { - const hotbar = this.props.hotbarStore; - - hotbar.addToHotbar(entity, index); + const removeItem = (entityId: string) => { + hotbar?.removeEntity(entityId); }; - - getMoveAwayDirection(entityId: string | undefined | null, cellIndex: number) { - if (!entityId) { + const addItem = (entity: CatalogEntity) => { + hotbar?.addEntity(entity); + }; + const getMoveAwayDirection = (entityId: string | undefined | null, cellIndex: number) => { + if (!entityId || !hotbar) { return "animateDown"; } - const draggableItemIndex = this.hotbar.items.findIndex(item => item?.entity.uid == entityId); + const draggableItemIndex = hotbar.items.findIndex(item => item?.entity.uid == entityId); return draggableItemIndex > cellIndex ? "animateDown" : "animateUp"; - } + }; - renderGrid() { - return this.hotbar.items.map((item, index) => { - const entity = this.getEntity(item); - - return ( - - {(provided, snapshot) => ( - - {item && ( - - {(provided, snapshot) => { - const style = { - zIndex: defaultHotbarCells - index, - position: "absolute", - ...provided.draggableProps.style, - } as React.CSSProperties; - - return ( -
- {entity ? ( - this.props.entityRegistry.onRun(entity)} - className={cssNames({ isDragging: snapshot.isDragging })} - remove={this.removeItem} - add={this.addItem} - size={40} - /> - ) : ( - this.removeItem(item.entity.uid), - }, - ]} - disabled - size={40} - /> - )} -
- ); - }} -
- )} - {provided.placeholder} -
- )} -
- ); - }); - } - - render() { - const { className, hotbarStore } = this.props; - const hotbar = hotbarStore.getActive(); + const renderGrid = () => hotbar?.items.map((item, index) => { + const entity = getEntity(item); return ( -
-
- this.onDragStart()} - onDragEnd={(result) => this.onDragEnd(result)}> - {this.renderGrid()} - -
- -
+ + {(provided, snapshot) => ( + + {item && ( + + {(provided, snapshot) => ( +
+ {entity ? ( + entityRegistry.onRun(entity)} + className={cssNames({ isDragging: snapshot.isDragging })} + remove={removeItem} + add={addItem} + size={40} /> + ) : ( + removeItem(item.entity.uid), + }, + ]} + disabled + size={40} /> + )} +
+ )} +
+ )} + {provided.placeholder} +
+ )} +
); - } -} + }); + + return ( +
+
+ onDragStart()} + onDragEnd={(result) => onDragEnd(result)}> + {renderGrid()} + +
+ +
+ ); +}); export const HotbarMenu = withInjectables(NonInjectedHotbarMenu, { getProps: (di, props) => ({ ...props, - hotbarStore: di.inject(hotbarStoreInjectable), entityRegistry: di.inject(catalogEntityRegistryInjectable), + activeHotbar: di.inject(activeHotbarInjectable), }), }); diff --git a/packages/core/src/renderer/components/hotbar/hotbar-remove-command.tsx b/packages/core/src/renderer/components/hotbar/hotbar-remove-command.tsx index 0199b7f739..4f2b1a4e17 100644 --- a/packages/core/src/renderer/components/hotbar/hotbar-remove-command.tsx +++ b/packages/core/src/renderer/components/hotbar/hotbar-remove-command.tsx @@ -6,23 +6,32 @@ import React from "react"; import { observer } from "mobx-react"; import { Select } from "../select"; -import hotbarStoreInjectable from "../../../common/hotbars/store.injectable"; import { withInjectables } from "@ogre-tools/injectable-react"; import commandOverlayInjectable from "../command-palette/command-overlay.injectable"; -import type { HotbarStore } from "../../../common/hotbars/store"; import type { OpenConfirmDialog } from "../confirm-dialog/open.injectable"; import openConfirmDialogInjectable from "../confirm-dialog/open.injectable"; +import type { IComputedValue } from "mobx"; +import type { Hotbar } from "../../../features/hotbar/storage/common/hotbar"; +import type { ComputeHotbarDisplayLabel } from "../../../features/hotbar/storage/common/compute-display-label.injectable"; +import computeHotbarDisplayLabelInjectable from "../../../features/hotbar/storage/common/compute-display-label.injectable"; +import hotbarsInjectable from "../../../features/hotbar/storage/common/hotbars.injectable"; +import type { RemoveHotbar } from "../../../features/hotbar/storage/common/remove.injectable"; +import removeHotbarInjectable from "../../../features/hotbar/storage/common/remove.injectable"; interface Dependencies { closeCommandOverlay: () => void; openConfirmDialog: OpenConfirmDialog; - hotbarStore: HotbarStore; + hotbars: IComputedValue; + computeHotbarDisplayLabel: ComputeHotbarDisplayLabel; + removeHotbar: RemoveHotbar; } const NonInjectedHotbarRemoveCommand = observer(({ closeCommandOverlay, - hotbarStore, openConfirmDialog, + hotbars, + computeHotbarDisplayLabel, + removeHotbar, }: Dependencies) => ( ); } } else { - hotbarStore.setActiveHotbar(option.value); + setAsActiveHotbar(option.value); commandOverlay.close(); } }} components={{ DropdownIndicator: null, IndicatorSeparator: null }} menuIsOpen={true} options={[ - ...hotbarStore.hotbars.map(hotbar => ({ + ...hotbars.get().map(hotbar => ({ value: hotbar, - label: hotbarStore.getDisplayLabel(hotbar), + label: computeHotbarDisplayLabel(hotbar), })), { value: hotbarAddAction, label: "Add hotbar ...", }, - ...ignoreIf(hotbarStore.hotbars.length > 1, [ + ...ignoreIf(hotbars.get().length > 1, [ { value: hotbarRemoveAction, label: "Remove hotbar ...", @@ -85,8 +94,10 @@ const NonInjectedHotbarSwitchCommand = observer(({ export const HotbarSwitchCommand = withInjectables(NonInjectedHotbarSwitchCommand, { getProps: (di, props) => ({ - hotbarStore: di.inject(hotbarStoreInjectable), - commandOverlay: di.inject(commandOverlayInjectable), ...props, + commandOverlay: di.inject(commandOverlayInjectable), + computeHotbarDisplayLabel: di.inject(computeHotbarDisplayLabelInjectable), + hotbars: di.inject(hotbarsInjectable), + setAsActiveHotbar: di.inject(setAsActiveHotbarInjectable), }), }); diff --git a/packages/core/src/renderer/components/input/validators/unique-hotbar-name.injectable.ts b/packages/core/src/renderer/components/input/validators/unique-hotbar-name.injectable.ts index e4d68e9f29..48c1fb6fc1 100644 --- a/packages/core/src/renderer/components/input/validators/unique-hotbar-name.injectable.ts +++ b/packages/core/src/renderer/components/input/validators/unique-hotbar-name.injectable.ts @@ -4,19 +4,19 @@ */ import { getInjectable } from "@ogre-tools/injectable"; -import hotbarStoreInjectable from "../../../../common/hotbars/store.injectable"; +import findHotbarByNameInjectable from "../../../../features/hotbar/storage/common/find-by-name.injectable"; import { inputValidator } from "../input_validators"; const uniqueHotbarNameInjectable = getInjectable({ id: "unique-hotbar-name", instantiate: di => { - const store = di.inject(hotbarStoreInjectable); + const findHotbarByName = di.inject(findHotbarByNameInjectable); return inputValidator({ condition: ({ required }) => required, message: () => "Hotbar with this name already exists", - validate: value => !store.findByName(value), + validate: value => !findHotbarByName(value), }); }, }); diff --git a/packages/core/src/renderer/components/item-object-list/content.tsx b/packages/core/src/renderer/components/item-object-list/content.tsx index 17b864ed6e..2b11a97570 100644 --- a/packages/core/src/renderer/components/item-object-list/content.tsx +++ b/packages/core/src/renderer/components/item-object-list/content.tsx @@ -25,15 +25,17 @@ import type { LensTheme } from "../../themes/lens-theme"; import { MenuActions } from "../menu/menu-actions"; import { MenuItem } from "../menu"; import { Checkbox } from "../checkbox"; -import type { UserStore } from "../../../common/user-store"; import type { ItemListStore } from "./list-layout"; import { withInjectables } from "@ogre-tools/injectable-react"; -import userStoreInjectable from "../../../common/user-store/user-store.injectable"; import pageFiltersStoreInjectable from "./page-filters/store.injectable"; import type { OpenConfirmDialog } from "../confirm-dialog/open.injectable"; import openConfirmDialogInjectable from "../confirm-dialog/open.injectable"; import activeThemeInjectable from "../../themes/active.injectable"; import autoBindReact from "auto-bind/react"; +import type { ToggleTableColumnVisibility } from "../../../features/user-preferences/common/toggle-table-column-visibility.injectable"; +import toggleTableColumnVisibilityInjectable from "../../../features/user-preferences/common/toggle-table-column-visibility.injectable"; +import type { IsTableColumnHidden } from "../../../features/user-preferences/common/is-table-column-hidden.injectable"; +import isTableColumnHiddenInjectable from "../../../features/user-preferences/common/is-table-column-hidden.injectable"; export interface ItemListLayoutContentProps { getFilters: () => Filter[]; @@ -74,9 +76,10 @@ export interface ItemListLayoutContentProps; - userStore: UserStore; pageFiltersStore: PageFiltersStore; openConfirmDialog: OpenConfirmDialog; + toggleTableColumnVisibility: ToggleTableColumnVisibility; + isTableColumnHidden: IsTableColumnHidden; } @observer @@ -342,7 +345,7 @@ class NonInjectedItemListLayoutContent< showColumn({ id: columnId, showWithColumn }: TableCellProps): boolean { const { tableId, isConfigurable } = this.props; - return !isConfigurable || !tableId || !this.props.userStore.isTableColumnHidden(tableId, columnId, showWithColumn); + return !isConfigurable || !tableId || !this.props.isTableColumnHidden(tableId, columnId, showWithColumn); } renderColumnVisibilityMenu(tableId: string) { @@ -365,7 +368,7 @@ class NonInjectedItemListLayoutContent< `} value={this.showColumn(cellProps)} - onChange={() => this.props.userStore.toggleTableColumnVisibility(tableId, cellProps.id)} + onChange={() => this.props.toggleTableColumnVisibility(tableId, cellProps.id)} /> )) @@ -379,8 +382,9 @@ export const ItemListLayoutContent = withInjectables ({ ...props, activeTheme: di.inject(activeThemeInjectable), - userStore: di.inject(userStoreInjectable), pageFiltersStore: di.inject(pageFiltersStoreInjectable), openConfirmDialog: di.inject(openConfirmDialogInjectable), + toggleTableColumnVisibility: di.inject(toggleTableColumnVisibilityInjectable), + isTableColumnHidden: di.inject(isTableColumnHiddenInjectable), }), }) as (props: ItemListLayoutContentProps) => React.ReactElement; diff --git a/packages/core/src/renderer/components/kube-object-list-layout/kube-object-list-layout.test.tsx b/packages/core/src/renderer/components/kube-object-list-layout/kube-object-list-layout.test.tsx index 74f31ff3dd..691000c887 100644 --- a/packages/core/src/renderer/components/kube-object-list-layout/kube-object-list-layout.test.tsx +++ b/packages/core/src/renderer/components/kube-object-list-layout/kube-object-list-layout.test.tsx @@ -38,8 +38,6 @@ describe("kube-object-list-layout", () => { contextName: "some-context-name", id: "some-cluster-id", kubeConfigPath: "/some-path-to-a-kubeconfig", - }, { - clusterServerUrl: "https://localhost:8080", })); render = renderFor(di); diff --git a/packages/core/src/renderer/components/layout/__tests__/__snapshots__/sidebar-cluster.test.tsx.snap b/packages/core/src/renderer/components/layout/__tests__/__snapshots__/sidebar-cluster.test.tsx.snap new file mode 100644 index 0000000000..26aab5675c --- /dev/null +++ b/packages/core/src/renderer/components/layout/__tests__/__snapshots__/sidebar-cluster.test.tsx.snap @@ -0,0 +1,36 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[` renders w/o errors 1`] = ` +
+ +
+`; diff --git a/packages/core/src/renderer/components/layout/__tests__/sidebar-cluster.test.tsx b/packages/core/src/renderer/components/layout/__tests__/sidebar-cluster.test.tsx index f9d447dd5e..69bfe5458c 100644 --- a/packages/core/src/renderer/components/layout/__tests__/sidebar-cluster.test.tsx +++ b/packages/core/src/renderer/components/layout/__tests__/sidebar-cluster.test.tsx @@ -5,56 +5,47 @@ import React from "react"; import "@testing-library/jest-dom/extend-expect"; +import type { RenderResult } from "@testing-library/react"; import { fireEvent } from "@testing-library/react"; import { SidebarCluster } from "../sidebar-cluster"; import { KubernetesCluster } from "../../../../common/catalog-entities"; import { getDiForUnitTesting } from "../../../getDiForUnitTesting"; -import type { DiRender } from "../../test-utils/renderFor"; import { renderFor } from "../../test-utils/renderFor"; -import hotbarStoreInjectable from "../../../../common/hotbars/store.injectable"; -import type { HotbarStore } from "../../../../common/hotbars/store"; - -const clusterEntity = new KubernetesCluster({ - metadata: { - uid: "test-uid", - name: "test-cluster", - source: "local", - labels: {}, - }, - spec: { - kubeconfigPath: "", - kubeconfigContext: "", - }, - status: { - phase: "connected", - }, -}); describe("", () => { - let render: DiRender; + let result: RenderResult; beforeEach(() => { const di = getDiForUnitTesting(); + const render = renderFor(di); - di.override(hotbarStoreInjectable, () => ({ - isAddedToActive: () => {}, - }) as unknown as HotbarStore); + const clusterEntity = new KubernetesCluster({ + metadata: { + uid: "test-uid", + name: "test-cluster", + source: "local", + labels: {}, + }, + spec: { + kubeconfigPath: "", + kubeconfigContext: "", + }, + status: { + phase: "connected", + }, + }); - render = renderFor(di); + result = render(); }); it("renders w/o errors", () => { - const { container } = render(); - - expect(container).toBeInstanceOf(HTMLElement); + expect(result.container).toMatchSnapshot(); }); it("renders cluster avatar and name", () => { - const { getByText, getAllByText } = render(); + expect(result.getByText("tc")).toBeInTheDocument(); - expect(getByText("tc")).toBeInTheDocument(); - - const v = getAllByText("test-cluster"); + const v = result.getAllByText("test-cluster"); expect(v.length).toBeGreaterThan(0); @@ -64,11 +55,8 @@ describe("", () => { }); it("renders cluster menu", () => { - const { getByTestId, getByText } = render(); - const link = getByTestId("sidebar-cluster-dropdown"); - - fireEvent.click(link); - expect(getByText("Add to Hotbar")).toBeInTheDocument(); + fireEvent.click(result.getByTestId("sidebar-cluster-dropdown")); + expect(result.getByText("Add to Hotbar")).toBeInTheDocument(); }); }); diff --git a/packages/core/src/renderer/components/layout/sidebar-cluster.tsx b/packages/core/src/renderer/components/layout/sidebar-cluster.tsx index ba5ae7e387..7ae6ee9405 100644 --- a/packages/core/src/renderer/components/layout/sidebar-cluster.tsx +++ b/packages/core/src/renderer/components/layout/sidebar-cluster.tsx @@ -14,8 +14,6 @@ import { Icon } from "../icon"; import { Menu, MenuItem } from "../menu"; import { Tooltip } from "../tooltip"; import { withInjectables } from "@ogre-tools/injectable-react"; -import hotbarStoreInjectable from "../../../common/hotbars/store.injectable"; -import type { HotbarStore } from "../../../common/hotbars/store"; import { observer } from "mobx-react"; import type { VisitEntityContextMenu } from "../../../common/catalog/visit-entity-context-menu.injectable"; import visitEntityContextMenuInjectable from "../../../common/catalog/visit-entity-context-menu.injectable"; @@ -23,6 +21,8 @@ import type { Navigate } from "../../navigation/navigate.injectable"; import type { NormalizeCatalogEntityContextMenu } from "../../catalog/normalize-menu-item.injectable"; import navigateInjectable from "../../navigation/navigate.injectable"; import normalizeCatalogEntityContextMenuInjectable from "../../catalog/normalize-menu-item.injectable"; +import type { ActiveHotbarModel } from "../../../features/hotbar/storage/common/toggling.injectable"; +import activeHotbarInjectable from "../../../features/hotbar/storage/common/toggling.injectable"; export interface SidebarClusterProps { clusterEntity: CatalogEntity | null | undefined; @@ -31,16 +31,16 @@ export interface SidebarClusterProps { interface Dependencies { navigate: Navigate; normalizeMenuItem: NormalizeCatalogEntityContextMenu; - hotbarStore: HotbarStore; visitEntityContextMenu: VisitEntityContextMenu; + entityInActiveHotbar: ActiveHotbarModel; } const NonInjectedSidebarCluster = observer(({ clusterEntity, - hotbarStore, visitEntityContextMenu: onContextMenuOpen, navigate, normalizeMenuItem, + entityInActiveHotbar, }: Dependencies & SidebarClusterProps) => { const [menuItems] = useState(observable.array()); const [opened, setOpened] = useState(false ); @@ -61,13 +61,10 @@ const NonInjectedSidebarCluster = observer(({ } const onMenuOpen = () => { - const isAddedToActive = hotbarStore.isAddedToActive(clusterEntity); - const title = isAddedToActive + const title = entityInActiveHotbar.hasEntity(clusterEntity.getId()) ? "Remove from Hotbar" : "Add to Hotbar"; - const onClick = isAddedToActive - ? () => hotbarStore.removeFromHotbar(clusterEntity.getId()) - : () => hotbarStore.addToHotbar(clusterEntity); + const onClick = () => entityInActiveHotbar.toggleEntity(clusterEntity); menuItems.replace([{ title, onClick }]); onContextMenuOpen(clusterEntity, { @@ -148,9 +145,9 @@ const NonInjectedSidebarCluster = observer(({ export const SidebarCluster = withInjectables(NonInjectedSidebarCluster, { getProps: (di, props) => ({ ...props, - hotbarStore: di.inject(hotbarStoreInjectable), visitEntityContextMenu: di.inject(visitEntityContextMenuInjectable), navigate: di.inject(navigateInjectable), normalizeMenuItem: di.inject(normalizeCatalogEntityContextMenuInjectable), + entityInActiveHotbar: di.inject(activeHotbarInjectable), }), }); diff --git a/packages/core/src/renderer/components/locale-date/locale-date.tsx b/packages/core/src/renderer/components/locale-date/locale-date.tsx index 28b5f6b05a..4ec558e617 100644 --- a/packages/core/src/renderer/components/locale-date/locale-date.tsx +++ b/packages/core/src/renderer/components/locale-date/locale-date.tsx @@ -6,27 +6,27 @@ import React from "react"; import { observer } from "mobx-react"; import moment from "moment-timezone"; -import type { UserStore } from "../../../common/user-store"; import { withInjectables } from "@ogre-tools/injectable-react"; -import userStoreInjectable from "../../../common/user-store/user-store.injectable"; +import type { UserPreferencesState } from "../../../features/user-preferences/common/state.injectable"; +import userPreferencesStateInjectable from "../../../features/user-preferences/common/state.injectable"; export interface LocaleDateProps { date: string; } interface Dependencies { - userStore: UserStore; + state: UserPreferencesState; } -const NonInjectedLocaleDate = observer(({ date, userStore }: LocaleDateProps & Dependencies) => ( +const NonInjectedLocaleDate = observer(({ date, state }: LocaleDateProps & Dependencies) => ( <> - {`${moment.tz(date, userStore.localeTimezone).format()}`} + {`${moment.tz(date, state.localeTimezone).format()}`} )); export const LocaleDate = withInjectables(NonInjectedLocaleDate, { getProps: (di, props) => ({ ...props, - userStore: di.inject(userStoreInjectable), + state: di.inject(userPreferencesStateInjectable), }), }); diff --git a/packages/core/src/renderer/components/monaco-editor/monaco-editor.tsx b/packages/core/src/renderer/components/monaco-editor/monaco-editor.tsx index d41ab4157b..7e36639c41 100644 --- a/packages/core/src/renderer/components/monaco-editor/monaco-editor.tsx +++ b/packages/core/src/renderer/components/monaco-editor/monaco-editor.tsx @@ -13,15 +13,15 @@ import type { MonacoTheme } from "./monaco-themes"; import { type MonacoValidator, monacoValidators } from "./monaco-validators"; import { debounce, merge } from "lodash"; import { cssNames, disposer } from "@k8slens/utilities"; -import type { UserStore } from "../../../common/user-store"; import type { LensTheme } from "../../themes/lens-theme"; import { withInjectables } from "@ogre-tools/injectable-react"; -import userStoreInjectable from "../../../common/user-store/user-store.injectable"; import activeThemeInjectable from "../../themes/active.injectable"; import getEditorHeightFromLinesCountInjectable from "./get-editor-height-from-lines-number.injectable"; import type { Logger } from "../../../common/logger"; import loggerInjectable from "../../../common/logger.injectable"; import autoBindReact from "auto-bind/react"; +import type { UserPreferencesState } from "../../../features/user-preferences/common/state.injectable"; +import userPreferencesStateInjectable from "../../../features/user-preferences/common/state.injectable"; export type MonacoEditorId = string; @@ -45,7 +45,7 @@ export interface MonacoEditorProps { } interface Dependencies { - userStore: UserStore; + state: UserPreferencesState; activeTheme: IComputedValue; getEditorHeightFromLinesCount: (linesCount: number) => number; logger: Logger; @@ -116,7 +116,7 @@ class NonInjectedMonacoEditor extends React.Component( - React.forwardRef((props, ref) => ), - { - getProps: (di, props) => ({ - ...props, - userStore: di.inject(userStoreInjectable), - activeTheme: di.inject(activeThemeInjectable), - getEditorHeightFromLinesCount: di.inject(getEditorHeightFromLinesCountInjectable), - logger: di.inject(loggerInjectable), - }), - }, -); +const ForwardedRefMonacoEditor = React.forwardRef(( + (props, ref) => +)); + +export const MonacoEditor = withInjectables(ForwardedRefMonacoEditor, { + getProps: (di, props) => ({ + ...props, + state: di.inject(userPreferencesStateInjectable), + activeTheme: di.inject(activeThemeInjectable), + getEditorHeightFromLinesCount: di.inject(getEditorHeightFromLinesCountInjectable), + logger: di.inject(loggerInjectable), + }), +}); diff --git a/packages/core/src/renderer/components/test-utils/discovery-of-html-elements.ts b/packages/core/src/renderer/components/test-utils/discovery-of-html-elements.ts deleted file mode 100644 index 472199fc60..0000000000 --- a/packages/core/src/renderer/components/test-utils/discovery-of-html-elements.ts +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import type { RenderResult } from "@testing-library/react"; - -type DiscoverySourceTypes = RenderResult | Element; - -export type QuerySingleElement = ( - attributeName: string, - attributeValue?: string -) => { discovered: Element | null } & Discover; - -export type GetSingleElement = ( - attributeName: string, - attributeValue?: string -) => { discovered: Element } & Discover; - -export type QueryAllElements = (attributeName: string) => { - discovered: Element[]; - attributeValues: (string | null)[]; -}; - -export interface Discover { - querySingleElement: QuerySingleElement; - queryAllElements: QueryAllElements; - getSingleElement: GetSingleElement; -} - -export const discoverFor = (getSource: () => DiscoverySourceTypes): Discover => ({ - querySingleElement: querySingleElement(getSource), - queryAllElements: queryAllElements(getSource), - getSingleElement: getSingleElement(getSource), -}); - -export const querySingleElement = - (getSource: () => DiscoverySourceTypes): QuerySingleElement => - (attributeName, attributeValue) => { - const source = getSource(); - - const dataAttribute = `data-${attributeName}-test`; - - const selector = attributeValue - ? `[${dataAttribute}="${attributeValue}"]` - : `[${dataAttribute}]`; - - const discovered = getBaseElement(source).querySelector(selector); - - const nestedDiscover = discoverFor(() => { - if (!discovered) { - throw new Error("Tried to do nested discover using source that does not exist"); - } - - return discovered; - }); - - return { - discovered, - - ...nestedDiscover, - }; - }; - -export const queryAllElements = - (getSource: () => DiscoverySourceTypes): QueryAllElements => - (attributeName) => { - const source = getSource(); - - const dataAttribute = `data-${attributeName}-test`; - - const results = [ - ...getBaseElement(source).querySelectorAll(`[${dataAttribute}]`), - ]; - - return { - discovered: results, - - attributeValues: results.map((result) => - result.getAttribute(dataAttribute), - ), - }; - }; - -export const getSingleElement = - (getSource: () => DiscoverySourceTypes): GetSingleElement => - (attributeName, attributeValue) => { - const dataAttribute = `data-${attributeName}-test`; - - const { discovered, ...nestedDiscover } = querySingleElement(getSource)( - attributeName, - attributeValue, - ); - - if (!discovered) { - const validValues = - queryAllElements(getSource)(attributeName).attributeValues; - - if (attributeValue) { - throw new Error( - `Couldn't find HTML-element with attribute "${dataAttribute}" with value "${attributeValue}". Present values are:\n\n"${validValues.join( - '",\n"', - )}"`, - ); - } - - throw new Error( - `Couldn't find HTML-element with attribute "${dataAttribute}"`, - ); - } - - return { discovered, ...nestedDiscover }; - }; - -const getBaseElement = (source: DiscoverySourceTypes) => - "baseElement" in source ? source.baseElement : source; diff --git a/packages/core/src/renderer/components/test-utils/get-application-builder.tsx b/packages/core/src/renderer/components/test-utils/get-application-builder.tsx index 731c5ba8c7..48dd102bf8 100644 --- a/packages/core/src/renderer/components/test-utils/get-application-builder.tsx +++ b/packages/core/src/renderer/components/test-utils/get-application-builder.tsx @@ -59,7 +59,7 @@ import { Namespace } from "../../../common/k8s-api/endpoints"; import { getOverrideFsWithFakes } from "../../../test-utils/override-fs-with-fakes"; import applicationMenuItemCompositeInjectable from "../../../features/application-menu/main/application-menu-item-composite.injectable"; import { getCompositePaths } from "../../../common/utils/composite/get-composite-paths/get-composite-paths"; -import { discoverFor } from "./discovery-of-html-elements"; +import { discoverFor } from "@k8slens/react-testing-library-discovery"; import { findComposite } from "../../../common/utils/composite/find-composite/find-composite"; import shouldStartHiddenInjectable from "../../../main/electron-app/features/should-start-hidden.injectable"; import fsInjectable from "../../../common/fs/fs.injectable"; @@ -528,8 +528,6 @@ export const getApplicationBuilder = () => { id: "some-cluster-id", contextName: "some-context-name", kubeConfigPath: "/some-path-to-kube-config", - }, { - clusterServerUrl: "https://localhost:12345", }); windowDi.override(activeKubernetesClusterInjectable, () => diff --git a/packages/core/src/renderer/components/test-utils/get-extension-fake.ts b/packages/core/src/renderer/components/test-utils/get-extension-fake.ts index 675b7e93e2..9da4adb019 100644 --- a/packages/core/src/renderer/components/test-utils/get-extension-fake.ts +++ b/packages/core/src/renderer/components/test-utils/get-extension-fake.ts @@ -3,7 +3,6 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import type { Writable } from "type-fest"; -import fileSystemProvisionerStoreInjectable from "../../../extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store.injectable"; import { lensExtensionDependencies } from "../../../extensions/lens-extension"; import { LensMainExtension } from "../../../extensions/lens-main-extension"; import navigateForExtensionInjectable from "../../../main/start-main-application/lens-window/navigate-for-extension.injectable"; @@ -16,6 +15,7 @@ import catalogEntityRegistryForMainInjectable from "../../../main/catalog/entity import catalogEntityRegistryForRendererInjectable from "../../api/catalog/entity/registry.injectable"; import type { DiContainer } from "@ogre-tools/injectable"; import loggerInjectable from "../../../common/logger.injectable"; +import ensureHashedDirectoryForExtensionInjectable from "../../../extensions/extension-loader/file-system-provisioner-store/ensure-hashed-directory-for-extension.injectable"; export class TestExtensionMain extends LensMainExtension {} export class TestExtensionRenderer extends LensRendererExtension {} @@ -47,7 +47,7 @@ export const getMainExtensionFakeWith = (di: DiContainer) => ({ id, name, mainOp Object.assign(instance, mainOptions); (instance as Writable)[lensExtensionDependencies] = { - fileSystemProvisionerStore: di.inject(fileSystemProvisionerStoreInjectable), + ensureHashedDirectoryForExtension: di.inject(ensureHashedDirectoryForExtensionInjectable), entityRegistry: di.inject(catalogEntityRegistryForMainInjectable), navigate: di.inject(navigateForExtensionInjectable), logger: di.inject(loggerInjectable), @@ -78,7 +78,7 @@ export const getRendererExtensionFakeWith = (di: DiContainer) => ({ id, name, re (instance as Writable)[lensExtensionDependencies] = { categoryRegistry: di.inject(catalogCategoryRegistryInjectable), entityRegistry: di.inject(catalogEntityRegistryForRendererInjectable), - fileSystemProvisionerStore: di.inject(fileSystemProvisionerStoreInjectable), + ensureHashedDirectoryForExtension: di.inject(ensureHashedDirectoryForExtensionInjectable), getExtensionPageParameters: di.inject(getExtensionPageParametersInjectable), navigateToRoute: di.inject(navigateToRouteInjectable), routes: di.inject(routesInjectable), diff --git a/packages/core/src/renderer/extension-loader/create-extension-instance.injectable.ts b/packages/core/src/renderer/extension-loader/create-extension-instance.injectable.ts index 0f92440338..ebc424641a 100644 --- a/packages/core/src/renderer/extension-loader/create-extension-instance.injectable.ts +++ b/packages/core/src/renderer/extension-loader/create-extension-instance.injectable.ts @@ -7,7 +7,7 @@ import type { Writable } from "type-fest"; import catalogCategoryRegistryInjectable from "../../common/catalog/category-registry.injectable"; import loggerInjectable from "../../common/logger.injectable"; import { createExtensionInstanceInjectionToken } from "../../extensions/extension-loader/create-extension-instance.token"; -import fileSystemProvisionerStoreInjectable from "../../extensions/extension-loader/file-system-provisioner-store/file-system-provisioner-store.injectable"; +import ensureHashedDirectoryForExtensionInjectable from "../../extensions/extension-loader/file-system-provisioner-store/ensure-hashed-directory-for-extension.injectable"; import { lensExtensionDependencies } from "../../extensions/lens-extension"; import type { LensRendererExtensionDependencies } from "../../extensions/lens-extension-set-dependencies"; import type { LensRendererExtension } from "../../extensions/lens-renderer-extension"; @@ -22,7 +22,7 @@ const createExtensionInstanceInjectable = getInjectable({ const deps: LensRendererExtensionDependencies = { categoryRegistry: di.inject(catalogCategoryRegistryInjectable), entityRegistry: di.inject(catalogEntityRegistryInjectable), - fileSystemProvisionerStore: di.inject(fileSystemProvisionerStoreInjectable), + ensureHashedDirectoryForExtension: di.inject(ensureHashedDirectoryForExtensionInjectable), getExtensionPageParameters: di.inject(getExtensionPageParametersInjectable), navigateToRoute: di.inject(navigateToRouteInjectable), routes: di.inject(routesInjectable), diff --git a/packages/core/src/renderer/frames/cluster-frame/cluster-frame.test.tsx b/packages/core/src/renderer/frames/cluster-frame/cluster-frame.test.tsx index 4ad315ab98..e3ce80e13a 100644 --- a/packages/core/src/renderer/frames/cluster-frame/cluster-frame.test.tsx +++ b/packages/core/src/renderer/frames/cluster-frame/cluster-frame.test.tsx @@ -48,16 +48,11 @@ describe("", () => { testUsingFakeTime("2000-01-01 12:00:00am"); - cluster = new Cluster( - { - contextName: "my-cluster", - id: "123456", - kubeConfigPath: "/irrelavent", - }, - { - clusterServerUrl: "https://localhost", - }, - ); + cluster = new Cluster({ + contextName: "my-cluster", + id: "123456", + kubeConfigPath: "/irrelavent", + }); di.override(hostedClusterInjectable, () => cluster); di.override(hostedClusterIdInjectable, () => cluster.id); diff --git a/packages/core/src/renderer/frames/frame-application-root.injectable.ts b/packages/core/src/renderer/frames/frame-application-root.injectable.ts new file mode 100644 index 0000000000..afae5a5fb3 --- /dev/null +++ b/packages/core/src/renderer/frames/frame-application-root.injectable.ts @@ -0,0 +1,31 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { + reactApplicationChildrenInjectionToken, +} from "@k8slens/react-application"; +import { computed } from "mobx"; + +const frameApplicationRootInjectable = getInjectable({ + id: "frame-application-root", + + instantiate: () => { + const Frame = process.isMainFrame + ? require("./root-frame/root-frame").RootFrame + : require("./cluster-frame/cluster-frame").ClusterFrame; + + return { + id: "frame-application-root", + Component: Frame, + enabled: computed(() => true), + }; + }, + + causesSideEffects: true, + + injectionToken: reactApplicationChildrenInjectionToken, +}); + +export default frameApplicationRootInjectable; diff --git a/packages/core/src/renderer/frames/routing-react-application-hoc.injectable.tsx b/packages/core/src/renderer/frames/routing-react-application-hoc.injectable.tsx new file mode 100644 index 0000000000..6853b0eaed --- /dev/null +++ b/packages/core/src/renderer/frames/routing-react-application-hoc.injectable.tsx @@ -0,0 +1,31 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import { Router } from "react-router"; +import historyInjectable from "../navigation/history.injectable"; +import React from "react"; + +import { + reactApplicationHigherOrderComponentInjectionToken, +} from "@k8slens/react-application"; + +const routingReactApplicationHocInjectable = getInjectable({ + id: "routing-react-application-hoc", + + instantiate: (di) => { + const history = di.inject(historyInjectable); + + return ({ children }) => + ( + + {children} + + ); + }, + + injectionToken: reactApplicationHigherOrderComponentInjectionToken, +}); + +export default routingReactApplicationHocInjectable; diff --git a/packages/core/src/renderer/frames/theme-provider-react-application-hoc.injectable.tsx b/packages/core/src/renderer/frames/theme-provider-react-application-hoc.injectable.tsx new file mode 100644 index 0000000000..45f94c6404 --- /dev/null +++ b/packages/core/src/renderer/frames/theme-provider-react-application-hoc.injectable.tsx @@ -0,0 +1,22 @@ +/** + * Copyright (c) OpenLens Authors. All rights reserved. + * Licensed under MIT License. See LICENSE in root directory for more information. + */ +import { getInjectable } from "@ogre-tools/injectable"; +import React from "react"; +import { reactApplicationHigherOrderComponentInjectionToken } from "@k8slens/react-application"; +import { ThemeProvider } from "@material-ui/core"; +import { defaultMuiBaseTheme } from "../mui-base-theme"; + +const themeProviderReactApplicationHocInjectable = getInjectable({ + id: "theme-provider-react-application-hoc", + + instantiate: + () => + ({ children }) => + {children}, + + injectionToken: reactApplicationHigherOrderComponentInjectionToken, +}); + +export default themeProviderReactApplicationHocInjectable; diff --git a/packages/core/src/renderer/initializers/add-sync-entries.injectable.tsx b/packages/core/src/renderer/initializers/add-sync-entries.injectable.tsx index 2f9f3ec74e..5d42d0e496 100644 --- a/packages/core/src/renderer/initializers/add-sync-entries.injectable.tsx +++ b/packages/core/src/renderer/initializers/add-sync-entries.injectable.tsx @@ -3,24 +3,24 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import userStoreInjectable from "../../common/user-store/user-store.injectable"; import React from "react"; import navigateToKubernetesPreferencesInjectable from "../../features/preferences/common/navigate-to-kubernetes-preferences.injectable"; import { runInAction } from "mobx"; import showSuccessNotificationInjectable from "../components/notifications/show-success-notification.injectable"; +import userPreferencesStateInjectable from "../../features/user-preferences/common/state.injectable"; const addSyncEntriesInjectable = getInjectable({ id: "add-sync-entries", instantiate: (di) => { - const userStore = di.inject(userStoreInjectable); + const state = di.inject(userPreferencesStateInjectable); const navigateToKubernetesPreferences = di.inject(navigateToKubernetesPreferencesInjectable); const showSuccessNotification = di.inject(showSuccessNotificationInjectable); return async (paths: string[]) => { runInAction(() => { for (const path of paths) { - userStore.syncKubeconfigEntries.set(path, {}); + state.syncKubeconfigEntries.set(path, {}); } }); diff --git a/packages/core/src/renderer/initializers/workload-events.tsx b/packages/core/src/renderer/initializers/workload-events.tsx index 73251382c8..945746cb2f 100644 --- a/packages/core/src/renderer/initializers/workload-events.tsx +++ b/packages/core/src/renderer/initializers/workload-events.tsx @@ -7,7 +7,7 @@ import { withInjectables } from "@ogre-tools/injectable-react"; import type { IComputedValue } from "mobx"; import { observer } from "mobx-react"; import React from "react"; -import { shouldShowResourceInjectionToken } from "../../common/cluster-store/allowed-resources-injection-token"; +import { shouldShowResourceInjectionToken } from "../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { Events } from "../components/+events/events"; export interface WorkloadEventsProps {} diff --git a/packages/core/src/renderer/ipc/list-namespaces-forbidden-handler.injectable.tsx b/packages/core/src/renderer/ipc/list-namespaces-forbidden-handler.injectable.tsx index 4acf8182b1..f8c850e7f1 100644 --- a/packages/core/src/renderer/ipc/list-namespaces-forbidden-handler.injectable.tsx +++ b/packages/core/src/renderer/ipc/list-namespaces-forbidden-handler.injectable.tsx @@ -10,8 +10,8 @@ import type { IpcRendererEvent } from "electron"; import React from "react"; import notificationsStoreInjectable from "../components/notifications/notifications-store.injectable"; import { getMillisecondsFromUnixEpoch } from "../../common/utils/date/get-current-date-time"; -import getClusterByIdInjectable from "../../common/cluster-store/get-by-id.injectable"; import showSuccessNotificationInjectable from "../components/notifications/show-success-notification.injectable"; +import getClusterByIdInjectable from "../../features/cluster/storage/common/get-by-id.injectable"; const intervalBetweenNotifications = 1000 * 60; // 60s diff --git a/packages/core/src/renderer/ipc/register-ipc-listeners.injectable.ts b/packages/core/src/renderer/ipc/register-ipc-listeners.injectable.ts index 0c2d407d59..0a80abb9e6 100644 --- a/packages/core/src/renderer/ipc/register-ipc-listeners.injectable.ts +++ b/packages/core/src/renderer/ipc/register-ipc-listeners.injectable.ts @@ -3,7 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { getInjectable } from "@ogre-tools/injectable"; -import { defaultHotbarCells } from "../../common/hotbars/types"; +import { defaultHotbarCells } from "../../features/hotbar/storage/common/types"; import { clusterListNamespaceForbiddenChannel } from "../../common/ipc/cluster"; import { hotbarTooManyItemsChannel } from "../../common/ipc/hotbar"; import showErrorNotificationInjectable from "../components/notifications/show-error-notification.injectable"; diff --git a/packages/core/src/renderer/mui-base-theme.tsx b/packages/core/src/renderer/mui-base-theme.tsx index c32374cf58..7238462172 100644 --- a/packages/core/src/renderer/mui-base-theme.tsx +++ b/packages/core/src/renderer/mui-base-theme.tsx @@ -6,7 +6,7 @@ import React from "react"; import { createTheme, ThemeProvider } from "@material-ui/core"; -const defaultTheme = createTheme({ +export const defaultMuiBaseTheme = createTheme({ props: { MuiIconButton: { color: "inherit", @@ -32,7 +32,7 @@ const defaultTheme = createTheme({ export function DefaultProps(App: React.ComponentType | React.FunctionComponent) { return ( - + ); diff --git a/packages/core/src/renderer/protocol-handler/bind-protocol-add-route-handlers/bind-protocol-add-route-handlers.injectable.ts b/packages/core/src/renderer/protocol-handler/bind-protocol-add-route-handlers/bind-protocol-add-route-handlers.injectable.ts index 1d378df375..c4497dfaeb 100644 --- a/packages/core/src/renderer/protocol-handler/bind-protocol-add-route-handlers/bind-protocol-add-route-handlers.injectable.ts +++ b/packages/core/src/renderer/protocol-handler/bind-protocol-add-route-handlers/bind-protocol-add-route-handlers.injectable.ts @@ -12,11 +12,9 @@ import navigateToExtensionsInjectable from "../../../common/front-end-routing/ro import navigateToEntitySettingsInjectable from "../../../common/front-end-routing/routes/entity-settings/navigate-to-entity-settings.injectable"; import navigateToClusterViewInjectable from "../../../common/front-end-routing/routes/cluster-view/navigate-to-cluster-view.injectable"; import catalogEntityRegistryInjectable from "../../api/catalog/entity/registry.injectable"; - -// TODO: Importing from features is not OK. Make protocol-router to comply with Open Closed Principle to allow moving implementation under a feature import navigateToPreferencesInjectable from "../../../features/preferences/common/navigate-to-preferences.injectable"; -import getClusterByIdInjectable from "../../../common/cluster-store/get-by-id.injectable"; import showShortInfoNotificationInjectable from "../../components/notifications/show-short-info.injectable"; +import getClusterByIdInjectable from "../../../features/cluster/storage/common/get-by-id.injectable"; const bindProtocolAddRouteHandlersInjectable = getInjectable({ id: "bind-protocol-add-route-handlers", diff --git a/packages/core/src/renderer/protocol-handler/bind-protocol-add-route-handlers/bind-protocol-add-route-handlers.tsx b/packages/core/src/renderer/protocol-handler/bind-protocol-add-route-handlers/bind-protocol-add-route-handlers.tsx index 4bcf207340..bf80d6c4d4 100644 --- a/packages/core/src/renderer/protocol-handler/bind-protocol-add-route-handlers/bind-protocol-add-route-handlers.tsx +++ b/packages/core/src/renderer/protocol-handler/bind-protocol-add-route-handlers/bind-protocol-add-route-handlers.tsx @@ -17,7 +17,7 @@ import type { NavigateToEntitySettings } from "../../../common/front-end-routing import type { NavigateToClusterView } from "../../../common/front-end-routing/routes/cluster-view/navigate-to-cluster-view.injectable"; import assert from "assert"; import type { AttemptInstallByInfo } from "../../components/+extensions/attempt-install-by-info.injectable"; -import type { GetClusterById } from "../../../common/cluster-store/get-by-id.injectable"; +import type { GetClusterById } from "../../../features/cluster/storage/common/get-by-id.injectable"; interface Dependencies { attemptInstallByInfo: AttemptInstallByInfo; diff --git a/packages/core/src/renderer/protocol-handler/lens-protocol-router-renderer/lens-protocol-router-renderer.injectable.ts b/packages/core/src/renderer/protocol-handler/lens-protocol-router-renderer/lens-protocol-router-renderer.injectable.ts index 35938b32fd..aa9525b31a 100644 --- a/packages/core/src/renderer/protocol-handler/lens-protocol-router-renderer/lens-protocol-router-renderer.injectable.ts +++ b/packages/core/src/renderer/protocol-handler/lens-protocol-router-renderer/lens-protocol-router-renderer.injectable.ts @@ -5,17 +5,17 @@ import { getInjectable } from "@ogre-tools/injectable"; import extensionLoaderInjectable from "../../../extensions/extension-loader/extension-loader.injectable"; import { LensProtocolRouterRenderer } from "./lens-protocol-router-renderer"; -import extensionsStoreInjectable from "../../../extensions/extensions-store/extensions-store.injectable"; import loggerInjectable from "../../../common/logger.injectable"; import showErrorNotificationInjectable from "../../components/notifications/show-error-notification.injectable"; import showShortInfoNotificationInjectable from "../../components/notifications/show-short-info.injectable"; +import isExtensionEnabledInjectable from "../../../features/extensions/enabled/common/is-enabled.injectable"; const lensProtocolRouterRendererInjectable = getInjectable({ id: "lens-protocol-router-renderer", instantiate: (di) => new LensProtocolRouterRenderer({ extensionLoader: di.inject(extensionLoaderInjectable), - extensionsStore: di.inject(extensionsStoreInjectable), + isExtensionEnabled: di.inject(isExtensionEnabledInjectable), logger: di.inject(loggerInjectable), showErrorNotification: di.inject(showErrorNotificationInjectable), showShortInfoNotification: di.inject(showShortInfoNotificationInjectable), diff --git a/packages/core/src/renderer/start-frame/start-frame.injectable.ts b/packages/core/src/renderer/start-frame/start-frame.injectable.ts index b4bddc9c30..668419191c 100644 --- a/packages/core/src/renderer/start-frame/start-frame.injectable.ts +++ b/packages/core/src/renderer/start-frame/start-frame.injectable.ts @@ -6,9 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import { runManyFor } from "@k8slens/run-many"; import * as tokens from "../before-frame-starts/tokens"; import currentlyInClusterFrameInjectable from "../routes/currently-in-cluster-frame.injectable"; -import { - afterApplicationIsLoadedInjectionToken, -} from "@k8slens/application"; +import { onLoadOfApplicationInjectionToken } from "@k8slens/application"; const startFrameInjectable = getInjectable({ id: "start-frame", @@ -44,7 +42,7 @@ const startFrameInjectable = getInjectable({ }; }, - injectionToken: afterApplicationIsLoadedInjectionToken, + injectionToken: onLoadOfApplicationInjectionToken, }); export default startFrameInjectable; diff --git a/packages/core/src/renderer/stores/init-user-store.injectable.ts b/packages/core/src/renderer/stores/init-user-store.injectable.ts deleted file mode 100644 index af192d9b2e..0000000000 --- a/packages/core/src/renderer/stores/init-user-store.injectable.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ -import { getInjectable } from "@ogre-tools/injectable"; -import userStoreInjectable from "../../common/user-store/user-store.injectable"; -import { beforeFrameStartsSecondInjectionToken } from "../before-frame-starts/tokens"; -import initDefaultUpdateChannelInjectable from "../vars/default-update-channel/init.injectable"; - -const initUserStoreInjectable = getInjectable({ - id: "init-user-store", - instantiate: (di) => ({ - run: () => { - const userStore = di.inject(userStoreInjectable); - - return userStore.load(); - }, - runAfter: initDefaultUpdateChannelInjectable, - }), - injectionToken: beforeFrameStartsSecondInjectionToken, -}); - -export default initUserStoreInjectable; diff --git a/packages/core/src/renderer/themes/active.injectable.ts b/packages/core/src/renderer/themes/active.injectable.ts index e22aee237b..324bbf8117 100644 --- a/packages/core/src/renderer/themes/active.injectable.ts +++ b/packages/core/src/renderer/themes/active.injectable.ts @@ -5,7 +5,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import assert from "assert"; import { computed } from "mobx"; -import lensColorThemePreferenceInjectable from "../../common/user-store/lens-color-theme.injectable"; +import lensColorThemePreferenceInjectable from "../../features/user-preferences/common/lens-color-theme.injectable"; import { lensThemeDeclarationInjectionToken } from "./declaration"; import defaultLensThemeInjectable from "./default-theme.injectable"; import systemThemeConfigurationInjectable from "./system-theme.injectable"; diff --git a/packages/core/src/renderer/themes/apply-lens-theme.injectable.ts b/packages/core/src/renderer/themes/apply-lens-theme.injectable.ts index 94afd5c531..977c5eb77c 100644 --- a/packages/core/src/renderer/themes/apply-lens-theme.injectable.ts +++ b/packages/core/src/renderer/themes/apply-lens-theme.injectable.ts @@ -4,9 +4,9 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import loggerInjectable from "../../common/logger.injectable"; -import userStoreInjectable from "../../common/user-store/user-store.injectable"; import { object } from "@k8slens/utilities"; import type { LensTheme } from "./lens-theme"; +import resetThemeInjectable from "../../features/user-preferences/common/reset-theme.injectable"; export type ApplyLensTheme = (theme: LensTheme) => void; @@ -14,7 +14,7 @@ const applyLensThemeInjectable = getInjectable({ id: "apply-lens-theme", instantiate: (di): ApplyLensTheme => { const logger = di.inject(loggerInjectable); - const userStore = di.inject(userStoreInjectable); + const resetTheme = di.inject(resetThemeInjectable); return (theme) => { try { @@ -28,7 +28,7 @@ const applyLensThemeInjectable = getInjectable({ document.body.classList.toggle("theme-light", theme.type === "light"); } catch (error) { logger.error("[THEME]: Failed to apply active theme", error); - userStore.resetTheme(); + resetTheme(); } }; }, diff --git a/packages/core/src/renderer/themes/setup-apply-active-theme.injectable.ts b/packages/core/src/renderer/themes/setup-apply-active-theme.injectable.ts index c71dd5baa7..f658093f8e 100644 --- a/packages/core/src/renderer/themes/setup-apply-active-theme.injectable.ts +++ b/packages/core/src/renderer/themes/setup-apply-active-theme.injectable.ts @@ -6,7 +6,7 @@ import { getInjectable } from "@ogre-tools/injectable"; import { reaction } from "mobx"; import initializeSystemThemeTypeInjectable from "../../features/theme/system-type/renderer/initialize.injectable"; import { beforeFrameStartsSecondInjectionToken } from "../before-frame-starts/tokens"; -import initUserStoreInjectable from "../stores/init-user-store.injectable"; +import initUserStoreInjectable from "../../features/user-preferences/renderer/load-storage.injectable"; import activeThemeInjectable from "./active.injectable"; import applyLensThemeInjectable from "./apply-lens-theme.injectable"; diff --git a/packages/core/src/renderer/themes/terminal-colors.injectable.ts b/packages/core/src/renderer/themes/terminal-colors.injectable.ts index c7010b08b6..7b83d7b16c 100644 --- a/packages/core/src/renderer/themes/terminal-colors.injectable.ts +++ b/packages/core/src/renderer/themes/terminal-colors.injectable.ts @@ -4,7 +4,7 @@ */ import { getInjectable } from "@ogre-tools/injectable"; import { computed } from "mobx"; -import terminalThemePreferenceInjectable from "../../common/user-store/terminal-theme.injectable"; +import terminalThemePreferenceInjectable from "../../features/user-preferences/common/terminal-theme.injectable"; import activeThemeInjectable from "./active.injectable"; import lensThemesInjectable from "./themes.injectable"; diff --git a/packages/core/src/test-utils/override-fs-with-fakes.ts b/packages/core/src/test-utils/override-fs-with-fakes.ts index 87f8dd583a..5eaeb90ba7 100644 --- a/packages/core/src/test-utils/override-fs-with-fakes.ts +++ b/packages/core/src/test-utils/override-fs-with-fakes.ts @@ -63,6 +63,7 @@ export const getOverrideFsWithFakes = () => { createReadStream: root.createReadStream as any, stat: root.promises.stat as any, unlink: root.promises.unlink, + rename: root.promises.rename, })); }; }; diff --git a/packages/core/src/test-utils/use-fake-time.ts b/packages/core/src/test-utils/use-fake-time.ts index e455984861..11d5fe0918 100644 --- a/packages/core/src/test-utils/use-fake-time.ts +++ b/packages/core/src/test-utils/use-fake-time.ts @@ -19,7 +19,11 @@ export const advanceFakeTime = (milliseconds: number) => { export const testUsingFakeTime = (dateTime = "2015-10-21T07:28:00Z") => { usingFakeTime = true; - jest.useFakeTimers(); + jest.useFakeTimers({ + doNotFake: [ + "nextTick", + ], + }); jest.setSystemTime(new Date(dateTime)); }; diff --git a/packages/ensure-binaries/package.json b/packages/ensure-binaries/package.json index af8c7b12f3..c7d7cb5256 100644 --- a/packages/ensure-binaries/package.json +++ b/packages/ensure-binaries/package.json @@ -7,6 +7,9 @@ "scripts": { "clean": "rimraf dist/", "build": "swc ./src/index.ts -d ./dist/", + "postbuild": "run-script-os", + "postbuild:windows": "", + "postbuild:nix": "chmod u+x ./dist/index.js", "prepare:dev": "npm run build", "prepare": "npm run build" }, @@ -32,12 +35,13 @@ }, "devDependencies": { "@swc/cli": "^0.1.61", - "@swc/core": "^1.3.37", + "@swc/core": "^1.3.44", "@types/cli-progress": "^3.11.0", "@types/gunzip-maybe": "^1.4.0", "@types/node": "^16.18.11", "@types/semver": "^7.3.13", "@types/tar-stream": "^2.2.2", - "rimraf": "^4.1.2" + "rimraf": "^4.4.1", + "run-script-os": "^1.1.6" } } diff --git a/packages/extension-api/package.json b/packages/extension-api/package.json index a1b17c96e9..ab1156a70b 100644 --- a/packages/extension-api/package.json +++ b/packages/extension-api/package.json @@ -30,12 +30,12 @@ }, "devDependencies": { "@types/node": "^16.18.6", - "@types/webpack": "^5.28.0", + "@types/webpack": "^5.28.1", "@types/webpack-env": "^1.18.0", "@types/webpack-node-externals": "2.5.3", "css-loader": "^6.7.2", "node-loader": "^2.0.0", - "rimraf": "^4.1.2", + "rimraf": "^4.4.1", "style-loader": "^3.3.1", "ts-loader": "^9.4.2", "ts-node": "^10.9.1", @@ -44,7 +44,7 @@ "typedoc-plugin-markdown": "^3.13.6", "typescript": "^4.9.5", "typescript-plugin-css-modules": "^4.1.1", - "webpack": "^5.75.0", + "webpack": "^5.77.0", "webpack-cli": "^5.0.1" } } diff --git a/packages/generate-tray-icons/package.json b/packages/generate-tray-icons/package.json index f659fabcf1..04d58cc449 100644 --- a/packages/generate-tray-icons/package.json +++ b/packages/generate-tray-icons/package.json @@ -6,6 +6,9 @@ "scripts": { "clean": "rimraf dist/", "build": "swc ./src/index.ts -d ./dist/", + "postbuild": "run-script-os", + "postbuild:windows": "", + "postbuild:nix": "chmod u+x ./dist/index.js", "prepare:dev": "npm run build", "prepare": "npm run build" }, @@ -23,16 +26,17 @@ }, "devDependencies": { "@swc/cli": "^0.1.61", - "@swc/core": "^1.3.37", + "@swc/core": "^1.3.44", "@types/jsdom": "^20.0.1", "@types/node": "^18.11.18", "@types/sharp": "^0.31.1", - "rimraf": "^4.1.2" + "rimraf": "^4.4.1", + "run-script-os": "^1.1.6" }, "dependencies": { "arg": "^5.0.2", "jsdom": "^21.1.0", - "rimraf": "^4.1.2", + "rimraf": "^4.4.1", "sharp": "^0.31.3" } } diff --git a/packages/infrastructure/jest/package.json b/packages/infrastructure/jest/package.json index 6f75802b77..ffac66dd81 100644 --- a/packages/infrastructure/jest/package.json +++ b/packages/infrastructure/jest/package.json @@ -20,7 +20,7 @@ "license": "MIT", "homepage": "https://github.com/lensapp/lens", "dependencies": { - "@swc/core": "^1.3.38", + "@swc/core": "^1.3.44", "@swc/jest": "^0.2.23", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", diff --git a/packages/infrastructure/webpack/package.json b/packages/infrastructure/webpack/package.json index 3faa94494b..6b59746e9e 100644 --- a/packages/infrastructure/webpack/package.json +++ b/packages/infrastructure/webpack/package.json @@ -30,7 +30,7 @@ "sass-loader": "^13.2.0", "style-loader": "^3.3.1", "ts-loader": "^9.4.1", - "webpack": "^5.76.0", + "webpack": "^5.77.0", "webpack-cli": "^4.10.0", "webpack-node-externals": "^3.0.0" } diff --git a/packages/infrastructure/webpack/src/react-config.js b/packages/infrastructure/webpack/src/react-config.js index 3b25515208..201a3c9a6b 100644 --- a/packages/infrastructure/webpack/src/react-config.js +++ b/packages/infrastructure/webpack/src/react-config.js @@ -1,7 +1,7 @@ const path = require("path"); const getReactConfig = require("./get-react-config"); -module.exports = getReactConfig({ +module.exports = getReactConfig()({ entrypointFilePath: "./index.ts", outputDirectory: path.resolve(process.cwd(), "dist"), }); diff --git a/packages/legacy-extension-example/package.json b/packages/legacy-extension-example/package.json index e175eeb1f1..d37249fdfe 100644 --- a/packages/legacy-extension-example/package.json +++ b/packages/legacy-extension-example/package.json @@ -32,7 +32,6 @@ "scripts": { "clean": "rimraf dist/", "build": "webpack --config webpack.ts", - "dev": "webpack --mode=development --watch --config webpack.ts", "lint": "lens-lint", "lint:fix": "lens-lint --fix" }, @@ -40,7 +39,7 @@ "@k8slens/extensions": "^6.5.0-alpha.3", "@types/node": "^16.18.16", "typescript": "^4.9.5", - "webpack": "^5.76.1", + "webpack": "^5.77.0", "webpack-cli": "^5.0.1" } } diff --git a/packages/node-fetch/package.json b/packages/node-fetch/package.json index df4f9f579a..0634ee5448 100644 --- a/packages/node-fetch/package.json +++ b/packages/node-fetch/package.json @@ -23,14 +23,14 @@ }, "dependencies": { "node-fetch": "^3.3.0", - "rimraf": "^4.1.2" + "rimraf": "^4.4.1" }, "devDependencies": { - "rimraf": "^4.1.2", + "rimraf": "^4.4.1", "ts-loader": "^9.4.2", "ts-node": "^10.9.1", "typescript": "^4.9.5", - "webpack": "^5.75.0", + "webpack": "^5.77.0", "webpack-cli": "^5.0.1" } } diff --git a/packages/open-lens/package.json b/packages/open-lens/package.json index 577c1a2a6b..4060884d92 100644 --- a/packages/open-lens/package.json +++ b/packages/open-lens/package.json @@ -28,8 +28,7 @@ "build:dir": "npm run compile && electron-builder --dir", "compile": "cross-env NODE_ENV=production webpack --config webpack/webpack.ts --progress", "postcompile": "npm run build:tray-icons && npm run download:binaries", - "predev": "rimraf static/build/ && npm run build:tray-icons && npm run download:binaries", - "dev": "concurrently -i -k \"npm run dev-run -C\" npm:dev:*", + "start": "concurrently -i -k \"npm run dev-run -C\" npm:dev:*", "dev-run": "nodemon --watch ./static/build/main.js --exec \"electron --remote-debugging-port=9223 --inspect .\"", "dev:main": "cross-env NODE_ENV=development webpack --config webpack/main.ts --progress --watch", "dev:renderer": "cross-env NODE_ENV=development ts-node ./webpack/dev-server.ts", @@ -70,15 +69,6 @@ "{workspaceRoot}/static/build/" ] }, - "dev": { - "dependsOn": [ - "^prepare:dev" - ], - "outputs": [ - "{workspaceRoot}/binaries/", - "{workspaceRoot}/static/build/" - ] - }, "build:app": { "dependsOn": [ "build" @@ -200,15 +190,16 @@ "@k8slens/core": "^6.5.0-alpha.3", "@k8slens/ensure-binaries": "^6.5.0-alpha.1", "@k8slens/feature-core": "^6.5.0-alpha.1", - "@k8slens/generate-tray-icons": "^6.5.0-alpha.1", + "@k8slens/keyboard-shortcuts": "^1.0.0-alpha.0", "@k8slens/legacy-extension-example": "^1.0.0-alpha.1", "@k8slens/legacy-extensions": "^1.0.0-alpha.1", "@k8slens/messaging": "^1.0.0-alpha.1", "@k8slens/messaging-for-main": "^1.0.0-alpha.1", "@k8slens/messaging-for-renderer": "^1.0.0-alpha.1", + "@k8slens/node-fetch": "^6.5.0-alpha.1", + "@k8slens/react-application": "^1.0.0-alpha.0", "@k8slens/run-many": "^1.0.0-alpha.1", "@k8slens/startable-stoppable": "^1.0.0-alpha.1", - "@k8slens/test-utils": "^1.0.0-alpha.1", "@k8slens/utilities": "^1.0.0-alpha.1", "@ogre-tools/fp": "^15.1.2", "@ogre-tools/injectable": "^15.1.2", @@ -216,14 +207,15 @@ "@ogre-tools/injectable-extension-for-mobx": "^15.1.2", "@ogre-tools/injectable-react": "^15.1.2", "mobx": "^6.8.0", - "rimraf": "^4.1.2" + "rimraf": "^4.4.1" }, "devDependencies": { "@electron/rebuild": "^3.2.10", - "@k8slens/node-fetch": "^6.5.0-alpha.1", + "@k8slens/generate-tray-icons": "^6.5.0-alpha.1", + "@k8slens/test-utils": "^1.0.0-alpha.1", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.10", "@swc/cli": "^0.1.61", - "@swc/core": "^1.3.37", + "@swc/core": "^1.3.44", "@swc/jest": "^0.2.24", "@types/byline": "^4.2.33", "@types/chart.js": "^2.9.36", @@ -241,7 +233,7 @@ "@types/tcp-port-used": "^1.0.1", "@types/url-parse": "^1.4.8", "@types/uuid": "^8.3.4", - "@types/webpack": "^5.28.0", + "@types/webpack": "^5.28.1", "@types/webpack-dev-server": "^4.7.2", "@types/webpack-env": "^1.18.0", "@types/webpack-node-externals": "2.5.3", @@ -257,7 +249,7 @@ "esbuild-loader": "^2.20.0", "fork-ts-checker-webpack-plugin": "^7.3.0", "html-webpack-plugin": "^5.5.0", - "jest": "^28.1.3", + "jest": "^29.5.0", "jest-environment-jsdom": "^28.1.3", "jsonfile": "^6.1.0", "mini-css-extract-plugin": "^2.7.1", @@ -265,11 +257,11 @@ "monaco-editor-webpack-plugin": "^5.0.0", "node-loader": "^2.0.0", "nodemon": "^2.0.20", - "playwright": "^1.30.0", + "playwright": "^1.32.1", "react-refresh": "^0.14.0", "react-refresh-typescript": "^2.0.7", "react-select": "^5.7.0", - "rimraf": "^4.1.2", + "rimraf": "^4.4.1", "run-script-os": "^1.1.6", "style-loader": "^3.3.1", "tailwindcss": "^3.2.4", @@ -279,9 +271,9 @@ "typed-emitter": "^1.4.0", "typescript": "^4.9.5", "typescript-plugin-css-modules": "^4.1.1", - "webpack": "^5.75.0", + "webpack": "^5.77.0", "webpack-cli": "^4.9.2", - "webpack-dev-server": "^4.11.1", + "webpack-dev-server": "^4.13.2", "webpack-node-externals": "^3.0.0", "xterm-addon-fit": "^0.5.0" } diff --git a/packages/open-lens/src/renderer/index.ts b/packages/open-lens/src/renderer/index.ts index 207fc2265c..d14e817347 100644 --- a/packages/open-lens/src/renderer/index.ts +++ b/packages/open-lens/src/renderer/index.ts @@ -15,6 +15,8 @@ import { createContainer } from "@ogre-tools/injectable"; import { registerMobX } from "@ogre-tools/injectable-extension-for-mobx"; import { registerInjectableReact } from "@ogre-tools/injectable-react"; import { messagingFeatureForRenderer } from "@k8slens/messaging-for-renderer"; +import { keyboardShortcutsFeature } from "@k8slens/keyboard-shortcuts"; +import { reactApplicationFeature } from "@k8slens/react-application"; const environment = "renderer"; @@ -24,7 +26,14 @@ runInAction(() => { registerMobX(di); registerInjectableReact(di); registerLensCore(di, environment); - registerFeature(di, applicationFeature, messagingFeatureForRenderer); + + registerFeature( + di, + applicationFeature, + messagingFeatureForRenderer, + keyboardShortcutsFeature, + reactApplicationFeature + ); autoRegister({ di, diff --git a/packages/release-tool/package.json b/packages/release-tool/package.json index b98aa84153..22b3180c3d 100644 --- a/packages/release-tool/package.json +++ b/packages/release-tool/package.json @@ -7,7 +7,10 @@ "private": true, "scripts": { "clean": "rimraf dist/", - "build": "swc ./src/index.ts -d ./dist" + "build": "swc ./src/index.ts -d ./dist", + "postbuild": "run-script-os", + "postbuild:windows": "", + "postbuild:nix": "chmod u+x ./dist/index.js" }, "bin": { "create-release-pr": "./dist/index.js" @@ -15,11 +18,12 @@ "type": "module", "devDependencies": { "@swc/cli": "^0.1.61", - "@swc/core": "^1.3.37", + "@swc/core": "^1.3.44", "@types/inquirer": "^9.0.3", "@types/node": "^16.18.11", "@types/semver": "^7.3.13", - "rimraf": "^4.1.2" + "rimraf": "^4.4.1", + "run-script-os": "^1.1.6" }, "dependencies": { "chalk": "^5.2.0", diff --git a/packages/semver/package.json b/packages/semver/package.json index 78ba4e3ae9..25ed955b4b 100644 --- a/packages/semver/package.json +++ b/packages/semver/package.json @@ -15,10 +15,10 @@ }, "devDependencies": { "@swc/cli": "^0.1.61", - "@swc/core": "^1.3.37", + "@swc/core": "^1.3.44", "@types/command-line-args": "^5.2.0", "@types/node": "^16.18.11", "@types/semver": "^7.3.13", - "rimraf": "^4.1.2" + "rimraf": "^4.4.1" } } diff --git a/packages/technical-features/application/agnostic/package.json b/packages/technical-features/application/agnostic/package.json index ab869a87c0..9874443c4f 100644 --- a/packages/technical-features/application/agnostic/package.json +++ b/packages/technical-features/application/agnostic/package.json @@ -24,8 +24,8 @@ "license": "MIT", "homepage": "https://github.com/lensapp/lens", "scripts": { + "clean": "rimraf dist/", "build": "webpack", - "dev": "webpack --mode=development --watch", "test:unit": "jest --coverage --runInBand", "lint": "lens-lint", "lint:fix": "lens-lint --fix" diff --git a/packages/technical-features/application/electron-main/package.json b/packages/technical-features/application/electron-main/package.json index 96d649a362..92817dc407 100644 --- a/packages/technical-features/application/electron-main/package.json +++ b/packages/technical-features/application/electron-main/package.json @@ -24,8 +24,8 @@ "license": "MIT", "homepage": "https://github.com/lensapp/lens", "scripts": { + "clean": "rimraf dist/", "build": "webpack", - "dev": "webpack --mode=development --watch", "test:unit": "jest --coverage --runInBand", "lint": "lens-lint", "lint:fix": "lens-lint --fix" diff --git a/packages/technical-features/application/legacy-extensions/package.json b/packages/technical-features/application/legacy-extensions/package.json index 6e92ca7c02..ee170be546 100644 --- a/packages/technical-features/application/legacy-extensions/package.json +++ b/packages/technical-features/application/legacy-extensions/package.json @@ -24,8 +24,8 @@ "license": "MIT", "homepage": "https://github.com/lensapp/lens", "scripts": { + "clean": "rimraf dist/", "build": "webpack", - "dev": "webpack --mode=development --watch", "lint": "lens-lint", "lint:fix": "lens-lint --fix" }, diff --git a/packages/technical-features/feature-core/package.json b/packages/technical-features/feature-core/package.json index cabb6e17a0..8d989c720c 100644 --- a/packages/technical-features/feature-core/package.json +++ b/packages/technical-features/feature-core/package.json @@ -24,8 +24,8 @@ "license": "MIT", "homepage": "https://github.com/lensapp/lens", "scripts": { + "clean": "rimraf dist/", "build": "webpack", - "dev": "webpack --mode=development --watch", "test:unit": "jest --coverage --runInBand", "lint": "lens-lint", "lint:fix": "lens-lint --fix" diff --git a/packages/technical-features/messaging/agnostic/package.json b/packages/technical-features/messaging/agnostic/package.json index 085304d4e0..ac4383c37a 100644 --- a/packages/technical-features/messaging/agnostic/package.json +++ b/packages/technical-features/messaging/agnostic/package.json @@ -26,8 +26,8 @@ "license": "MIT", "homepage": "https://github.com/lensapp/lens", "scripts": { + "clean": "rimraf dist/", "build": "webpack", - "dev": "webpack --mode=development --watch", "test:unit": "jest --coverage --runInBand", "lint:fix": "lens-lint --fix", "lint": "lens-lint" diff --git a/packages/technical-features/messaging/agnostic/src/features/actual/channel.no-coverage.ts b/packages/technical-features/messaging/agnostic/src/features/actual/channel.no-coverage.ts deleted file mode 100644 index 62a2ea1490..0000000000 --- a/packages/technical-features/messaging/agnostic/src/features/actual/channel.no-coverage.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface Channel { - id: string; - _messageTemplate?: MessageTemplate; - _returnTemplate?: ReturnTemplate; -} diff --git a/packages/technical-features/messaging/agnostic/src/features/actual/index.ts b/packages/technical-features/messaging/agnostic/src/features/actual/index.ts index e8209f26f0..f85542c954 100644 --- a/packages/technical-features/messaging/agnostic/src/features/actual/index.ts +++ b/packages/technical-features/messaging/agnostic/src/features/actual/index.ts @@ -6,8 +6,6 @@ export { getMessageChannel } from "./message/get-message-channel"; export { requestFromChannelInjectionToken } from "./request/request-from-channel-injection-token"; -export type { Channel } from "./channel.no-coverage"; - export { sendMessageToChannelInjectionToken } from "./message/message-to-channel-injection-token"; export type { SendMessageToChannel } from "./message/message-to-channel-injection-token"; diff --git a/packages/technical-features/messaging/agnostic/src/features/actual/listening-of-channels/listening-of-channels.injectable.ts b/packages/technical-features/messaging/agnostic/src/features/actual/listening-of-channels/listening-of-channels.injectable.ts index c28d6cbc5a..9040939f06 100644 --- a/packages/technical-features/messaging/agnostic/src/features/actual/listening-of-channels/listening-of-channels.injectable.ts +++ b/packages/technical-features/messaging/agnostic/src/features/actual/listening-of-channels/listening-of-channels.injectable.ts @@ -6,17 +6,24 @@ import { getStartableStoppable, StartableStoppable } from "@k8slens/startable-st import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx"; import { IComputedValue, reaction } from "mobx"; -import { messageChannelListenerInjectionToken } from "../message/message-channel-listener-injection-token"; -import { requestChannelListenerInjectionToken } from "../request/request-channel-listener-injection-token"; +import { + MessageChannel, + messageChannelListenerInjectionToken, +} from "../message/message-channel-listener-injection-token"; +import { + RequestChannel, + requestChannelListenerInjectionToken, +} from "../request/request-channel-listener-injection-token"; import { enlistRequestChannelListenerInjectionToken } from "../request/enlist-request-channel-listener-injection-token"; -import type { Channel } from "../channel.no-coverage"; export type ListeningOfChannels = StartableStoppable; export const listeningOfChannelsInjectionToken = getInjectionToken({ id: "listening-of-channels-injection-token", }); -const listening = }>( +const listening = < + T extends { id: string; channel: MessageChannel | RequestChannel }, +>( channelListeners: IComputedValue, enlistChannelListener: (listener: T) => () => void, getId: (listener: T) => string, diff --git a/packages/technical-features/messaging/agnostic/src/features/actual/message/enlist-message-channel-listener-injection-token.ts b/packages/technical-features/messaging/agnostic/src/features/actual/message/enlist-message-channel-listener-injection-token.ts index 9ec6f8b93a..1bb114ca09 100644 --- a/packages/technical-features/messaging/agnostic/src/features/actual/message/enlist-message-channel-listener-injection-token.ts +++ b/packages/technical-features/messaging/agnostic/src/features/actual/message/enlist-message-channel-listener-injection-token.ts @@ -1,3 +1,4 @@ +import type { Disposer } from "@k8slens/utilities"; import { getInjectionToken } from "@ogre-tools/injectable"; import type { @@ -5,9 +6,9 @@ import type { MessageChannelListener, } from "./message-channel-listener-injection-token"; -export type EnlistMessageChannelListener = ( - listener: MessageChannelListener>, -) => () => void; +export type EnlistMessageChannelListener = ( + listener: MessageChannelListener>, +) => Disposer; export const enlistMessageChannelListenerInjectionToken = getInjectionToken({ diff --git a/packages/technical-features/messaging/agnostic/src/features/actual/request/enlist-request-channel-listener-injection-token.ts b/packages/technical-features/messaging/agnostic/src/features/actual/request/enlist-request-channel-listener-injection-token.ts index cdb3ac97d5..7f2a04a78d 100644 --- a/packages/technical-features/messaging/agnostic/src/features/actual/request/enlist-request-channel-listener-injection-token.ts +++ b/packages/technical-features/messaging/agnostic/src/features/actual/request/enlist-request-channel-listener-injection-token.ts @@ -1,3 +1,4 @@ +import type { Disposer } from "@k8slens/utilities/index"; import { getInjectionToken } from "@ogre-tools/injectable"; import type { @@ -5,9 +6,9 @@ import type { RequestChannelListener, } from "./request-channel-listener-injection-token"; -export type EnlistRequestChannelListener = ( - listener: RequestChannelListener>, -) => () => void; +export type EnlistRequestChannelListener = ( + listener: RequestChannelListener>, +) => Disposer; export const enlistRequestChannelListenerInjectionToken = getInjectionToken({ diff --git a/packages/technical-features/messaging/computed-channel/index.ts b/packages/technical-features/messaging/computed-channel/index.ts index 4516e0b9a6..dd72ecf71b 100644 --- a/packages/technical-features/messaging/computed-channel/index.ts +++ b/packages/technical-features/messaging/computed-channel/index.ts @@ -6,7 +6,4 @@ export { export type { ChannelObserver, ComputedChannelFactory, - JsonifiableObject, - JsonifiableArray, - Jsonifiable, } from "./src/computed-channel/computed-channel.injectable"; diff --git a/packages/technical-features/messaging/computed-channel/package.json b/packages/technical-features/messaging/computed-channel/package.json index 81c4f80013..05763dba08 100644 --- a/packages/technical-features/messaging/computed-channel/package.json +++ b/packages/technical-features/messaging/computed-channel/package.json @@ -26,8 +26,8 @@ "license": "MIT", "homepage": "https://github.com/lensapp/lens", "scripts": { + "clean": "rimraf dist/", "build": "webpack", - "dev": "webpack --mode=development --watch", "test:unit": "jest --coverage --runInBand", "lint": "lens-lint", "lint:fix": "lens-lint --fix" diff --git a/packages/technical-features/messaging/computed-channel/src/computed-channel/computed-channel-administration-channel.injectable.ts b/packages/technical-features/messaging/computed-channel/src/computed-channel/computed-channel-administration-channel.injectable.ts index b0fdb3c59f..da8dd4110a 100644 --- a/packages/technical-features/messaging/computed-channel/src/computed-channel/computed-channel-administration-channel.injectable.ts +++ b/packages/technical-features/messaging/computed-channel/src/computed-channel/computed-channel-administration-channel.injectable.ts @@ -1,15 +1,9 @@ import { reaction } from "mobx"; - import { getMessageChannelListenerInjectable } from "@k8slens/messaging"; import { sendMessageToChannelInjectionToken } from "@k8slens/messaging"; -import type { JsonPrimitive } from "type-fest"; import { computedChannelObserverInjectionToken } from "./computed-channel.injectable"; import { getMessageChannel } from "@k8slens/messaging"; -export type JsonifiableObject = { [Key in string]?: Jsonifiable } | { toJSON: () => Jsonifiable }; -export type JsonifiableArray = readonly Jsonifiable[]; -export type Jsonifiable = JsonPrimitive | JsonifiableObject | JsonifiableArray; - export type ComputedChannelAdminMessage = { channelId: string; status: "became-observed" | "became-unobserved"; diff --git a/packages/technical-features/messaging/computed-channel/src/computed-channel/computed-channel.injectable.ts b/packages/technical-features/messaging/computed-channel/src/computed-channel/computed-channel.injectable.ts index 03f5a16aab..b652a0c795 100644 --- a/packages/technical-features/messaging/computed-channel/src/computed-channel/computed-channel.injectable.ts +++ b/packages/technical-features/messaging/computed-channel/src/computed-channel/computed-channel.injectable.ts @@ -1,7 +1,6 @@ import { getInjectable, getInjectionToken } from "@ogre-tools/injectable"; import { - _getGlobalState, computed, IComputedValue, observable, @@ -13,13 +12,8 @@ import { import type { MessageChannel } from "@k8slens/messaging"; import { getMessageChannelListenerInjectable } from "@k8slens/messaging"; import { sendMessageToChannelInjectionToken } from "@k8slens/messaging"; -import type { JsonPrimitive } from "type-fest"; import { computedChannelAdministrationChannel } from "./computed-channel-administration-channel.injectable"; -export type JsonifiableObject = { [Key in string]?: Jsonifiable } | { toJSON: () => Jsonifiable }; -export type JsonifiableArray = readonly Jsonifiable[]; -export type Jsonifiable = JsonPrimitive | JsonifiableObject | JsonifiableArray; - export type ComputedChannelFactory = ( channel: MessageChannel, pendingValue: T, @@ -29,14 +23,12 @@ export const computedChannelInjectionToken = getInjectionToken = { +export type ChannelObserver = { channel: MessageChannel; observer: IComputedValue; }; -export const computedChannelObserverInjectionToken = getInjectionToken< - ChannelObserver ->({ +export const computedChannelObserverInjectionToken = getInjectionToken>({ id: "computed-channel-observer", }); @@ -49,19 +41,7 @@ const computedChannelInjectable = getInjectable({ return ((channel, pendingValue) => { const observableValue = observable.box(pendingValue); - const computedValue = computed(() => { - const { trackingDerivation } = _getGlobalState(); - - const contextIsReactive = !!trackingDerivation; - - if (!contextIsReactive) { - throw new Error( - `Tried to access value of computed channel "${channel.id}" outside of reactive context. This is not possible, as the value is acquired asynchronously sometime *after* being observed. Not respecting that, the value could be stale.`, - ); - } - - return observableValue.get(); - }); + const computedValue = computed(() => observableValue.get()); const valueReceiverInjectable = getMessageChannelListenerInjectable({ id: `computed-channel-value-receiver-for-${channel.id}`, diff --git a/packages/technical-features/messaging/computed-channel/src/computed-channel/computed-channel.test.tsx b/packages/technical-features/messaging/computed-channel/src/computed-channel/computed-channel.test.tsx index 4736fe22d4..6bea2ca76d 100644 --- a/packages/technical-features/messaging/computed-channel/src/computed-channel/computed-channel.test.tsx +++ b/packages/technical-features/messaging/computed-channel/src/computed-channel/computed-channel.test.tsx @@ -292,14 +292,6 @@ const TestComponent = observer(({ someComputed }: { someComputed: IComputedValue }); }); - it("when accessing the computed value outside of reactive context, throws", () => { - expect(() => { - computedTestChannel.get(); - }).toThrow( - 'Tried to access value of computed channel "some-channel-id" outside of reactive context. This is not possible, as the value is acquired asynchronously sometime *after* being observed. Not respecting that, the value could be stale.', - ); - }); - it("no value gets listened in di-1 anymore", () => { expect(latestValueMessage).toBeUndefined(); }); @@ -381,14 +373,6 @@ const TestComponent = observer(({ someComputed }: { someComputed: IComputedValue }); }); }); - - it("when accessing the computed value outside of reactive context, throws", () => { - expect(() => { - computedTestChannel.get(); - }).toThrow( - 'Tried to access value of computed channel "some-channel-id" outside of reactive context. This is not possible, as the value is acquired asynchronously sometime *after* being observed. Not respecting that, the value could be stale.', - ); - }); }); it("given observation of unrelated computed channel is stopped, observation of other computed channel still works", async () => { @@ -509,14 +493,6 @@ const TestComponent = observer(({ someComputed }: { someComputed: IComputedValue }); }); - it("when accessing the computed value outside of reactive context, throws", () => { - expect(() => { - computedTestChannel.get(); - }).toThrow( - 'Tried to access value of computed channel "some-channel-id" outside of reactive context. This is not possible, as the value is acquired asynchronously sometime *after* being observed. Not respecting that, the value could be stale.', - ); - }); - it("given duplicate channel observer for the channel is registered, when the computed channel is observer, throws", () => { const duplicateChannelObserverInjectable = getInjectable({ id: "some-duplicate-channel-observer", diff --git a/packages/technical-features/messaging/electron/main/package.json b/packages/technical-features/messaging/electron/main/package.json index ff1a76a7d3..b1e17ef979 100644 --- a/packages/technical-features/messaging/electron/main/package.json +++ b/packages/technical-features/messaging/electron/main/package.json @@ -26,8 +26,8 @@ "license": "MIT", "homepage": "https://github.com/lensapp/lens", "scripts": { + "clean": "rimraf dist/", "build": "webpack", - "dev": "webpack --mode=development --watch", "test:unit": "jest --coverage --runInBand", "lint": "lens-lint", "lint:fix": "lens-lint --fix" diff --git a/packages/technical-features/messaging/electron/main/src/channel-listeners/enlist-message-channel-listener.injectable.ts b/packages/technical-features/messaging/electron/main/src/channel-listeners/enlist-message-channel-listener.injectable.ts index 5a33ed39d9..a8368c489b 100644 --- a/packages/technical-features/messaging/electron/main/src/channel-listeners/enlist-message-channel-listener.injectable.ts +++ b/packages/technical-features/messaging/electron/main/src/channel-listeners/enlist-message-channel-listener.injectable.ts @@ -10,7 +10,7 @@ const enlistMessageChannelListenerInjectable = getInjectable({ const ipcMain = di.inject(ipcMainInjectable); return ({ channel, handler }) => { - const nativeOnCallback = (nativeEvent: IpcMainEvent, message: unknown) => { + const nativeOnCallback = (nativeEvent: IpcMainEvent, message: any) => { handler(message, { frameId: nativeEvent.frameId, processId: nativeEvent.processId }); }; diff --git a/packages/technical-features/messaging/electron/renderer/package.json b/packages/technical-features/messaging/electron/renderer/package.json index 75fcb8e38f..f3864f7e4d 100644 --- a/packages/technical-features/messaging/electron/renderer/package.json +++ b/packages/technical-features/messaging/electron/renderer/package.json @@ -26,8 +26,8 @@ "license": "MIT", "homepage": "https://github.com/lensapp/lens", "scripts": { + "clean": "rimraf dist/", "build": "webpack", - "dev": "webpack --mode=development --watch", "test:unit": "jest --coverage --runInBand", "lint": "lens-lint", "lint:fix": "lens-lint --fix" diff --git a/packages/technical-features/messaging/electron/renderer/src/listening-of-messages/enlist-message-channel-listener.injectable.ts b/packages/technical-features/messaging/electron/renderer/src/listening-of-messages/enlist-message-channel-listener.injectable.ts index 6948e51073..fb1840f33f 100644 --- a/packages/technical-features/messaging/electron/renderer/src/listening-of-messages/enlist-message-channel-listener.injectable.ts +++ b/packages/technical-features/messaging/electron/renderer/src/listening-of-messages/enlist-message-channel-listener.injectable.ts @@ -10,7 +10,7 @@ const enlistMessageChannelListenerInjectable = getInjectable({ const ipcRenderer = di.inject(ipcRendererInjectable); return ({ channel, handler }) => { - const nativeCallback = (event: IpcRendererEvent, message: unknown) => { + const nativeCallback = (_: IpcRendererEvent, message: any) => { handler(message); }; diff --git a/packages/technical-features/messaging/message-bridge-fake/package.json b/packages/technical-features/messaging/message-bridge-fake/package.json index 6837504a0e..a48b2b5a33 100644 --- a/packages/technical-features/messaging/message-bridge-fake/package.json +++ b/packages/technical-features/messaging/message-bridge-fake/package.json @@ -26,8 +26,8 @@ "license": "MIT", "homepage": "https://github.com/lensapp/lens", "scripts": { + "clean": "rimraf dist/", "build": "webpack", - "dev": "webpack --mode=development --watch", "test:unit": "jest --coverage --runInBand", "lint:fix": "lens-lint --fix", "lint": "lens-lint" diff --git a/packages/technical-features/messaging/message-bridge-fake/src/get-message-bridge-fake/get-message-bridge-fake.ts b/packages/technical-features/messaging/message-bridge-fake/src/get-message-bridge-fake/get-message-bridge-fake.ts index ef8eed6461..8feb31dcd2 100644 --- a/packages/technical-features/messaging/message-bridge-fake/src/get-message-bridge-fake/get-message-bridge-fake.ts +++ b/packages/technical-features/messaging/message-bridge-fake/src/get-message-bridge-fake/get-message-bridge-fake.ts @@ -1,5 +1,11 @@ import type { DiContainer } from "@ogre-tools/injectable"; -import type { Channel, MessageChannelHandler, RequestChannelHandler } from "@k8slens/messaging"; +import type { + MessageChannel, + MessageChannelHandler, + MessageChannelListener, + RequestChannel, + RequestChannelHandler, +} from "@k8slens/messaging"; import { enlistMessageChannelListenerInjectionToken, @@ -20,6 +26,8 @@ export type MessageBridgeFake = { setAsync: (value: boolean) => void; }; +type MessageHandlers = Set>>; + const overrideMessaging = ({ di, messageListenersByDi, @@ -28,13 +36,13 @@ const overrideMessaging = ({ }: { di: DiContainer; - messageListenersByDi: Map>>>; + messageListenersByDi: Map>; messagePropagationBuffer: Set<{ resolve: () => Promise }>; getAsyncModeStatus: () => boolean; }) => { - const messageHandlersByChannel = new Map>>(); + const messageHandlersByChannel = new Map(); messageListenersByDi.set(di, messageHandlersByChannel); @@ -64,30 +72,36 @@ const overrideMessaging = ({ }); }); - di.override(enlistMessageChannelListenerInjectionToken, () => (listener) => { - if (!messageHandlersByChannel.has(listener.channel.id)) { - messageHandlersByChannel.set(listener.channel.id, new Set()); - } + di.override( + enlistMessageChannelListenerInjectionToken, + () => + (listener: MessageChannelListener>) => { + if (!messageHandlersByChannel.has(listener.channel.id)) { + messageHandlersByChannel.set(listener.channel.id, new Set()); + } - const handlerSet = messageHandlersByChannel.get(listener.channel.id); + const handlerSet = messageHandlersByChannel.get(listener.channel.id); - handlerSet?.add(listener.handler); + handlerSet?.add(listener.handler); - return () => { - handlerSet?.delete(listener.handler); - }; - }); + return () => { + handlerSet?.delete(listener.handler); + }; + }, + ); }; +type RequestHandlers = Set>>; + const overrideRequesting = ({ di, requestListenersByDi, }: { di: DiContainer; - requestListenersByDi: Map>>>; + requestListenersByDi: Map>; }) => { - const requestHandlersByChannel = new Map>>(); + const requestHandlersByChannel = new Map(); requestListenersByDi.set(di, requestHandlersByChannel); @@ -141,15 +155,8 @@ const overrideRequesting = ({ }; export const getMessageBridgeFake = (): MessageBridgeFake => { - const messageListenersByDi = new Map< - DiContainer, - Map>> - >(); - - const requestListenersByDi = new Map< - DiContainer, - Map>> - >(); + const messageListenersByDi = new Map>(); + const requestListenersByDi = new Map>(); const messagePropagationBuffer = new Set void>>(); diff --git a/packages/technical-features/react-application/.eslintrc.json b/packages/technical-features/react-application/.eslintrc.json new file mode 100644 index 0000000000..b15115cb69 --- /dev/null +++ b/packages/technical-features/react-application/.eslintrc.json @@ -0,0 +1,6 @@ +{ + "extends": "@k8slens/eslint-config/eslint", + "parserOptions": { + "project": "./tsconfig.json" + } +} diff --git a/packages/technical-features/react-application/.prettierrc b/packages/technical-features/react-application/.prettierrc new file mode 100644 index 0000000000..edd47b479e --- /dev/null +++ b/packages/technical-features/react-application/.prettierrc @@ -0,0 +1 @@ +"@k8slens/eslint-config/prettier" diff --git a/packages/technical-features/react-application/README.md b/packages/technical-features/react-application/README.md new file mode 100644 index 0000000000..2650d6f527 --- /dev/null +++ b/packages/technical-features/react-application/README.md @@ -0,0 +1,19 @@ +# @k8slens/react-application + +# Usage + +```bash +$ npm install @k8slens/react-application +``` + +```typescript +import { reactApplicationFeature } from "@k8slens/react-application"; +import { registerFeature } from "@k8slens/feature-core"; +import { createContainer } from "@ogre-tools/injectable"; + +const di = createContainer("some-container"); + +registerFeature(di, reactApplicationRootFeature); +``` + +## Extendability diff --git a/packages/technical-features/react-application/index.ts b/packages/technical-features/react-application/index.ts new file mode 100644 index 0000000000..e3f5236d54 --- /dev/null +++ b/packages/technical-features/react-application/index.ts @@ -0,0 +1,10 @@ +export { renderInjectionToken } from "./src/render-application/render.injectable"; +export type { Render } from "./src/render-application/render.injectable"; + +export { reactApplicationChildrenInjectionToken } from "./src/react-application/react-application-children-injection-token"; +export type { ReactApplicationChildren } from "./src/react-application/react-application-children-injection-token"; + +export { reactApplicationHigherOrderComponentInjectionToken } from "./src/react-application/react-application-higher-order-component-injection-token"; +export type { ReactApplicationHigherOrderComponent } from "./src/react-application/react-application-higher-order-component-injection-token"; + +export { reactApplicationFeature } from "./src/feature"; diff --git a/packages/technical-features/react-application/jest.config.js b/packages/technical-features/react-application/jest.config.js new file mode 100644 index 0000000000..38d54ab7b6 --- /dev/null +++ b/packages/technical-features/react-application/jest.config.js @@ -0,0 +1 @@ +module.exports = require("@k8slens/jest").monorepoPackageConfig(__dirname).configForReact; diff --git a/packages/technical-features/react-application/package.json b/packages/technical-features/react-application/package.json new file mode 100644 index 0000000000..45a2b3e1f5 --- /dev/null +++ b/packages/technical-features/react-application/package.json @@ -0,0 +1,52 @@ +{ + "name": "@k8slens/react-application", + "private": false, + "version": "1.0.0-alpha.0", + "description": "Package for React Application", + "type": "commonjs", + "files": [ + "dist" + ], + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/lensapp/lens.git" + }, + "main": "dist/index.js", + "types": "dist/index.d.ts", + "author": { + "name": "OpenLens Authors", + "email": "info@k8slens.dev" + }, + "license": "MIT", + "homepage": "https://github.com/lensapp/lens", + "scripts": { + "clean": "rimraf dist/", + "build": "webpack", + "test:unit": "jest --coverage --runInBand", + "lint": "lens-lint", + "lint:fix": "lens-lint --fix" + }, + "peerDependencies": { + "@k8slens/feature-core": "^6.5.0-alpha.0", + "@k8slens/application": "^6.5.0-alpha.2", + "@ogre-tools/fp": "^15.1.2", + "@ogre-tools/injectable": "^15.1.2", + "@ogre-tools/injectable-extension-for-auto-registration": "^15.1.2", + "@ogre-tools/injectable-extension-for-mobx": "^15.1.2", + "@ogre-tools/injectable-react": "^15.1.2", + "lodash": "^4.17.15", + "mobx": "^6.8.0", + "react": "^17.0.2", + "react-dom": "^17.0.2" + }, + "devDependencies": { + "@async-fn/jest": "^1.6.4", + "@k8slens/eslint-config": "6.5.0-alpha.1", + "@testing-library/react": "^12.1.5", + "@k8slens/react-testing-library-discovery": "*" + } +} diff --git a/packages/technical-features/react-application/src/__snapshots__/react-application.test.tsx.snap b/packages/technical-features/react-application/src/__snapshots__/react-application.test.tsx.snap new file mode 100644 index 0000000000..a029ece5f9 --- /dev/null +++ b/packages/technical-features/react-application/src/__snapshots__/react-application.test.tsx.snap @@ -0,0 +1,41 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`react-application renders 1`] = ` + +
+ +`; + +exports[`react-application when content is registered and enabled renders 1`] = ` + +
+
+ Some children +
+
+ +`; + +exports[`react-application when content is registered and enabled when content is disabled renders 1`] = ` + +
+ +`; + +exports[`react-application when content is registered and enabled when higher order component is registered renders 1`] = ` + +
+
+
+ Some children +
+
+
+ +`; diff --git a/packages/technical-features/react-application/src/feature.ts b/packages/technical-features/react-application/src/feature.ts new file mode 100644 index 0000000000..96860c90df --- /dev/null +++ b/packages/technical-features/react-application/src/feature.ts @@ -0,0 +1,17 @@ +import { getFeature } from "@k8slens/feature-core"; +import { autoRegister } from "@ogre-tools/injectable-extension-for-auto-registration"; +import { applicationFeature } from "@k8slens/application"; + +export const reactApplicationFeature = getFeature({ + id: "react-application", + + register: (di) => { + autoRegister({ + di, + targetModule: module, + getRequireContexts: () => [require.context("./", true, /\.injectable\.(ts|tsx)$/)], + }); + }, + + dependencies: [applicationFeature], +}); diff --git a/packages/technical-features/react-application/src/react-application.test.tsx b/packages/technical-features/react-application/src/react-application.test.tsx new file mode 100644 index 0000000000..12fcdb6bac --- /dev/null +++ b/packages/technical-features/react-application/src/react-application.test.tsx @@ -0,0 +1,137 @@ +import { registerFeature } from "@k8slens/feature-core"; +import { createContainer, DiContainer, getInjectable } from "@ogre-tools/injectable"; +import { registerMobX } from "@ogre-tools/injectable-extension-for-mobx"; +import { registerInjectableReact } from "@ogre-tools/injectable-react"; +import { reactApplicationFeature } from "./feature"; +import { runInAction, computed, observable, IObservableValue } from "mobx"; +import { startApplicationInjectionToken } from "@k8slens/application"; +import type { RenderResult } from "@testing-library/react"; +import { render, act } from "@testing-library/react"; +import renderInjectable from "./render-application/render.injectable"; +import { reactApplicationChildrenInjectionToken } from "./react-application/react-application-children-injection-token"; +import React from "react"; +import { Discover, discoverFor } from "@k8slens/react-testing-library-discovery"; +import { + ReactApplicationHigherOrderComponent, + reactApplicationHigherOrderComponentInjectionToken, +} from "./react-application/react-application-higher-order-component-injection-token"; + +const SomeContent = () =>
Some children
; + +describe("react-application", () => { + let rendered: RenderResult; + let di: DiContainer; + let discover: Discover; + + beforeEach(async () => { + di = createContainer("some-container"); + + registerInjectableReact(di); + + registerMobX(di); + + runInAction(() => { + registerFeature(di, reactApplicationFeature); + }); + + di.override(renderInjectable, () => (application) => { + rendered = render(application); + }); + + const startApplication = di.inject(startApplicationInjectionToken); + + await startApplication(); + + discover = discoverFor(() => rendered); + }); + + it("renders", () => { + expect(rendered.baseElement).toMatchSnapshot(); + }); + + describe("when content is registered and enabled", () => { + let someObservable: IObservableValue; + + beforeEach(() => { + someObservable = observable.box(true); + + const someContentInjectable = getInjectable({ + id: "some-content", + + instantiate: () => ({ + id: "some-content", + Component: SomeContent, + enabled: computed(() => someObservable.get()), + }), + + injectionToken: reactApplicationChildrenInjectionToken, + }); + + runInAction(() => { + di.register(someContentInjectable); + }); + }); + + it("renders", () => { + expect(rendered.baseElement).toMatchSnapshot(); + }); + + it("renders the content", () => { + const { discovered } = discover.getSingleElement("some-content"); + + expect(discovered).not.toBeNull(); + }); + + describe("when higher order component is registered", () => { + beforeEach(() => { + const SomeHigherOrderComponent: ReactApplicationHigherOrderComponent = ({ children }) => ( +
{children}
+ ); + + const someHigherOrderComponentInjectable = getInjectable({ + id: "some-higher-order-component", + + instantiate: () => SomeHigherOrderComponent, + + injectionToken: reactApplicationHigherOrderComponentInjectionToken, + }); + + runInAction(() => { + di.register(someHigherOrderComponentInjectable); + }); + }); + + it("renders", () => { + expect(rendered.baseElement).toMatchSnapshot(); + }); + + it("renders the content inside the higher order component", () => { + const { discovered } = discover + .getSingleElement("some-higher-order-component") + .getSingleElement("some-content"); + + expect(discovered).not.toBeNull(); + }); + }); + + describe("when content is disabled", () => { + beforeEach(() => { + act(() => { + runInAction(() => { + someObservable.set(false); + }); + }); + }); + + it("renders", () => { + expect(rendered.baseElement).toMatchSnapshot(); + }); + + it("does not render the content", () => { + const { discovered } = discover.querySingleElement("some-content"); + + expect(discovered).toBeNull(); + }); + }); + }); +}); diff --git a/packages/technical-features/react-application/src/react-application/react-application-children-injection-token.ts b/packages/technical-features/react-application/src/react-application/react-application-children-injection-token.ts new file mode 100644 index 0000000000..398f55d89a --- /dev/null +++ b/packages/technical-features/react-application/src/react-application/react-application-children-injection-token.ts @@ -0,0 +1,13 @@ +import { getInjectionToken } from "@ogre-tools/injectable"; +import type React from "react"; +import type { IComputedValue } from "mobx"; + +export interface ReactApplicationChildren { + id: string; + Component: React.ComponentType; + enabled: IComputedValue; +} + +export const reactApplicationChildrenInjectionToken = getInjectionToken({ + id: "react-application-children-injection-token", +}); diff --git a/packages/technical-features/react-application/src/react-application/react-application-content.tsx b/packages/technical-features/react-application/src/react-application/react-application-content.tsx new file mode 100644 index 0000000000..2307969542 --- /dev/null +++ b/packages/technical-features/react-application/src/react-application/react-application-content.tsx @@ -0,0 +1,29 @@ +import { withInjectables } from "@ogre-tools/injectable-react"; +import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx"; +import React from "react"; +import { + ReactApplicationChildren, + reactApplicationChildrenInjectionToken, +} from "./react-application-children-injection-token"; +import type { IComputedValue } from "mobx"; +import { observer, Observer } from "mobx-react"; + +type Dependencies = { contents: IComputedValue }; + +const NonInjectedContent = observer(({ contents }: Dependencies) => ( + <> + {contents.get().map((child) => ( + {() => (child.enabled.get() ? : null)} + ))} + +)); + +export const ReactApplicationContent = withInjectables( + NonInjectedContent, + + { + getProps: (di) => ({ + contents: di.inject(computedInjectManyInjectable)(reactApplicationChildrenInjectionToken), + }), + }, +); diff --git a/packages/technical-features/react-application/src/react-application/react-application-higher-order-component-injection-token.ts b/packages/technical-features/react-application/src/react-application/react-application-higher-order-component-injection-token.ts new file mode 100644 index 0000000000..cae1a6b468 --- /dev/null +++ b/packages/technical-features/react-application/src/react-application/react-application-higher-order-component-injection-token.ts @@ -0,0 +1,11 @@ +import { getInjectionToken } from "@ogre-tools/injectable"; +import type React from "react"; + +export type ReactApplicationHigherOrderComponent = React.ComponentType<{ + children: React.ReactNode; +}>; + +export const reactApplicationHigherOrderComponentInjectionToken = + getInjectionToken({ + id: "react-application-higher-order-component-injection-token", + }); diff --git a/packages/technical-features/react-application/src/react-application/react-application.tsx b/packages/technical-features/react-application/src/react-application/react-application.tsx new file mode 100644 index 0000000000..933d0ad389 --- /dev/null +++ b/packages/technical-features/react-application/src/react-application/react-application.tsx @@ -0,0 +1,37 @@ +import type { DiContainerForInjection } from "@ogre-tools/injectable"; +import { computedInjectManyInjectable } from "@ogre-tools/injectable-extension-for-mobx"; +import { DiContextProvider } from "@ogre-tools/injectable-react"; +import { observer } from "mobx-react"; +import React from "react"; +import { + ReactApplicationHigherOrderComponent, + reactApplicationHigherOrderComponentInjectionToken, +} from "./react-application-higher-order-component-injection-token"; + +import { ReactApplicationContent } from "./react-application-content"; + +interface ReactApplicationProps { + di: DiContainerForInjection; +} + +const render = (components: ReactApplicationHigherOrderComponent[]) => { + const [Component, ...rest] = components; + + if (!Component) { + return null; + } + + return {render(rest)}; +}; + +export const ReactApplication = observer(({ di }: ReactApplicationProps) => { + const computedInjectMany = di.inject(computedInjectManyInjectable); + + const higherOrderComponents = computedInjectMany( + reactApplicationHigherOrderComponentInjectionToken, + ); + + const Components = [...higherOrderComponents.get(), ReactApplicationContent]; + + return {render(Components)}; +}); diff --git a/packages/technical-features/react-application/src/render-application/render-application-when-application-is-ready.injectable.tsx b/packages/technical-features/react-application/src/render-application/render-application-when-application-is-ready.injectable.tsx new file mode 100644 index 0000000000..c18f18fc5f --- /dev/null +++ b/packages/technical-features/react-application/src/render-application/render-application-when-application-is-ready.injectable.tsx @@ -0,0 +1,21 @@ +import { getInjectable } from "@ogre-tools/injectable"; +import { afterApplicationIsLoadedInjectionToken } from "@k8slens/application"; +import renderInjectable from "./render.injectable"; +import { ReactApplication } from "../react-application/react-application"; +import React from "react"; + +export const renderApplicationWhenApplicationIsReadyInjectable = getInjectable({ + id: "render-application-when-application-is-ready", + + instantiate: (di) => { + const render = di.inject(renderInjectable); + + return { + run: () => { + render(); + }, + }; + }, + + injectionToken: afterApplicationIsLoadedInjectionToken, +}); diff --git a/packages/technical-features/react-application/src/render-application/render.injectable.tsx b/packages/technical-features/react-application/src/render-application/render.injectable.tsx new file mode 100644 index 0000000000..944823f10a --- /dev/null +++ b/packages/technical-features/react-application/src/render-application/render.injectable.tsx @@ -0,0 +1,22 @@ +import { getInjectable, getInjectionToken } from "@ogre-tools/injectable"; +import { render } from "react-dom"; +import type React from "react"; + +export type Render = (application: React.ReactElement) => void; + +export const renderInjectionToken = getInjectionToken({ + id: "render-injection-token", +}); + +const renderInjectable = getInjectable({ + id: "render", + + /* c8 ignore next */ + instantiate: () => (application) => render(application, document.getElementById("app")), + + causesSideEffects: true, + + injectionToken: renderInjectionToken, +}); + +export default renderInjectable; diff --git a/packages/technical-features/react-application/tsconfig.json b/packages/technical-features/react-application/tsconfig.json new file mode 100644 index 0000000000..ec29a8f75f --- /dev/null +++ b/packages/technical-features/react-application/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "@k8slens/typescript/config/base.json", + "include": ["**/*.ts", "**/*.tsx"] +} diff --git a/packages/technical-features/react-application/webpack.config.js b/packages/technical-features/react-application/webpack.config.js new file mode 100644 index 0000000000..1cda407f5a --- /dev/null +++ b/packages/technical-features/react-application/webpack.config.js @@ -0,0 +1 @@ +module.exports = require("@k8slens/webpack").configForReact; diff --git a/packages/utility-features/react-testing-library-discovery/.eslintrc.json b/packages/utility-features/react-testing-library-discovery/.eslintrc.json new file mode 100644 index 0000000000..b15115cb69 --- /dev/null +++ b/packages/utility-features/react-testing-library-discovery/.eslintrc.json @@ -0,0 +1,6 @@ +{ + "extends": "@k8slens/eslint-config/eslint", + "parserOptions": { + "project": "./tsconfig.json" + } +} diff --git a/packages/utility-features/react-testing-library-discovery/.prettierrc b/packages/utility-features/react-testing-library-discovery/.prettierrc new file mode 100644 index 0000000000..edd47b479e --- /dev/null +++ b/packages/utility-features/react-testing-library-discovery/.prettierrc @@ -0,0 +1 @@ +"@k8slens/eslint-config/prettier" diff --git a/packages/utility-features/react-testing-library-discovery/index.ts b/packages/utility-features/react-testing-library-discovery/index.ts new file mode 100644 index 0000000000..109c6af90f --- /dev/null +++ b/packages/utility-features/react-testing-library-discovery/index.ts @@ -0,0 +1,13 @@ +export type { + Discover, + GetSingleElement, + QueryAllElements, + QuerySingleElement, +} from "./src/discovery-of-html-elements"; + +export { + discoverFor, + getSingleElement, + queryAllElements, + querySingleElement, +} from "./src/discovery-of-html-elements"; diff --git a/packages/utility-features/react-testing-library-discovery/package.json b/packages/utility-features/react-testing-library-discovery/package.json new file mode 100644 index 0000000000..3f49c82588 --- /dev/null +++ b/packages/utility-features/react-testing-library-discovery/package.json @@ -0,0 +1,33 @@ +{ + "name": "@k8slens/react-testing-library-discovery", + "private": false, + "version": "1.0.0-alpha.0", + "description": "A way to discover HTML-elements using react-testing-library", + "type": "commonjs", + "files": [ + "dist" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/lensapp/lens.git" + }, + "main": "dist/index.js", + "types": "dist/index.d.ts", + "author": { + "name": "OpenLens Authors", + "email": "info@k8slens.dev" + }, + "license": "MIT", + "homepage": "https://github.com/lensapp/lens", + "scripts": { + "clean": "rimraf dist/", + "build": "webpack", + "lint": "lens-lint", + "lint:fix": "lens-lint --fix" + }, + "dependencies": { + "@testing-library/dom": "^8.19.0", + "@testing-library/jest-dom": "^5.16.5", + "@testing-library/react": "^13.4.0" + } +} diff --git a/packages/utility-features/react-testing-library-discovery/src/discovery-of-html-elements.ts b/packages/utility-features/react-testing-library-discovery/src/discovery-of-html-elements.ts new file mode 100644 index 0000000000..0df7d9202d --- /dev/null +++ b/packages/utility-features/react-testing-library-discovery/src/discovery-of-html-elements.ts @@ -0,0 +1,125 @@ +import type { RenderResult } from "@testing-library/react"; +import { prettyDOM as prettyDom } from "@testing-library/dom"; + +type DiscoverySourceTypes = RenderResult | Element; + +export type QuerySingleElement = ( + attributeName: string, + attributeValue?: string, +) => { discovered: Element | null } & Discover; + +type Clickable = { click: () => void }; + +export type GetSingleElement = ( + attributeName: string, + attributeValue?: string, +) => { discovered: Element } & Discover & Clickable; + +export type QueryAllElements = (attributeName: string) => { + discovered: Element[]; + attributeValues: (string | null)[]; +}; + +export interface Discover { + querySingleElement: QuerySingleElement; + queryAllElements: QueryAllElements; + getSingleElement: GetSingleElement; +} + +const getBaseElement = (source: DiscoverySourceTypes) => + "baseElement" in source ? source.baseElement : source; + +export function querySingleElement(getSource: () => DiscoverySourceTypes): QuerySingleElement { + return (attributeName, attributeValue) => { + const source = getSource(); + + const dataAttribute = `data-${attributeName}-test`; + + const selector = attributeValue + ? `[${dataAttribute}="${attributeValue}"]` + : `[${dataAttribute}]`; + + const discovered = getBaseElement(source).querySelector(selector); + + // eslint-disable-next-line @typescript-eslint/no-use-before-define + const nestedDiscover = discoverFor(() => { + if (!discovered) { + throw new Error("Tried to do nested discover using source that does not exist"); + } + + return discovered; + }); + + return { + discovered, + + ...nestedDiscover, + }; + }; +} + +export function queryAllElements(getSource: () => DiscoverySourceTypes): QueryAllElements { + return (attributeName) => { + const source = getSource(); + + const dataAttribute = `data-${attributeName}-test`; + + const results = [...getBaseElement(source).querySelectorAll(`[${dataAttribute}]`)]; + + return { + discovered: results, + + attributeValues: results.map((result) => result.getAttribute(dataAttribute)), + }; + }; +} + +export function getSingleElement(getSource: () => DiscoverySourceTypes): GetSingleElement { + return (attributeName, attributeValue) => { + const dataAttribute = `data-${attributeName}-test`; + + const { discovered, ...nestedDiscover } = querySingleElement(getSource)( + attributeName, + attributeValue, + ); + + if (!discovered) { + // eslint-disable-next-line xss/no-mixed-html + const html = prettyDom(getBaseElement(getSource())); + + if (attributeValue) { + const validValues = queryAllElements(getSource)(attributeName).attributeValues; + + throw new Error( + `Couldn't find HTML-element with attribute "${dataAttribute}" with value "${attributeValue}".\n\nPresent values are:\n\n"${validValues.join( + '",\n"', + )}"\n\nHTML is:\n\n${html}`, + ); + } + + throw new Error( + `Couldn't find HTML-element with attribute "${dataAttribute}"\n\nHTML is:\n\n${html}`, + ); + } + + const click = () => { + if ("click" in discovered && typeof discovered.click === "function") { + discovered.click(); + } else { + throw new Error( + `Tried to click something that was not clickable:\n\n${prettyDom(discovered)}`, + ); + } + }; + + return { discovered, click, ...nestedDiscover }; + }; +} + +export function discoverFor(getSource: () => DiscoverySourceTypes): Discover { + return { + querySingleElement: querySingleElement(getSource), + queryAllElements: queryAllElements(getSource), + getSingleElement: getSingleElement(getSource), + }; +} diff --git a/packages/utility-features/react-testing-library-discovery/tsconfig.json b/packages/utility-features/react-testing-library-discovery/tsconfig.json new file mode 100644 index 0000000000..1819203dc1 --- /dev/null +++ b/packages/utility-features/react-testing-library-discovery/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "@k8slens/typescript/config/base.json", + "include": ["**/*.ts"] +} diff --git a/packages/utility-features/react-testing-library-discovery/webpack.config.js b/packages/utility-features/react-testing-library-discovery/webpack.config.js new file mode 100644 index 0000000000..3183f30179 --- /dev/null +++ b/packages/utility-features/react-testing-library-discovery/webpack.config.js @@ -0,0 +1 @@ +module.exports = require("@k8slens/webpack").configForNode; diff --git a/packages/utility-features/run-many/package.json b/packages/utility-features/run-many/package.json index 1fa8af2ba1..0b3484794d 100644 --- a/packages/utility-features/run-many/package.json +++ b/packages/utility-features/run-many/package.json @@ -20,8 +20,8 @@ "license": "MIT", "homepage": "https://github.com/lensapp/lens", "scripts": { + "clean": "rimraf dist/", "build": "webpack", - "dev": "webpack --mode=development --watch", "test:unit": "jest --coverage --runInBand" }, "peerDependencies": { diff --git a/packages/utility-features/startable-stoppable/package.json b/packages/utility-features/startable-stoppable/package.json index 940e1e91b2..92b434294c 100644 --- a/packages/utility-features/startable-stoppable/package.json +++ b/packages/utility-features/startable-stoppable/package.json @@ -4,15 +4,10 @@ "version": "1.0.0-alpha.1", "description": "TBD", "type": "commonjs", - "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" }, - - "files": [ - "build" - ], "repository": { "type": "git", "url": "git+https://github.com/lensapp/lens.git" @@ -26,8 +21,8 @@ "license": "MIT", "homepage": "https://github.com/lensapp/lens", "scripts": { + "clean": "rimraf dist/", "build": "webpack", - "dev": "webpack --mode=development --watch", "test:unit": "jest --coverage --runInBand", "lint": "lens-lint", "lint:fix": "lens-lint --fix" diff --git a/packages/utility-features/test-utils/package.json b/packages/utility-features/test-utils/package.json index 4336c0682b..746dbcfe99 100644 --- a/packages/utility-features/test-utils/package.json +++ b/packages/utility-features/test-utils/package.json @@ -20,8 +20,8 @@ "license": "MIT", "homepage": "https://github.com/lensapp/lens", "scripts": { - "build": "webpack", - "dev": "webpack --mode=development --watch" + "clean": "rimraf dist/", + "build": "webpack" }, "peerDependencies": { "@ogre-tools/injectable": "^15.1.2", diff --git a/packages/utility-features/utilities/package.json b/packages/utility-features/utilities/package.json index 777907d851..d7bafbe16e 100644 --- a/packages/utility-features/utilities/package.json +++ b/packages/utility-features/utilities/package.json @@ -20,8 +20,8 @@ "license": "MIT", "homepage": "https://github.com/lensapp/lens", "scripts": { + "clean": "rimraf dist/", "build": "webpack", - "dev": "webpack --mode=development --watch", "test:unit": "jest --coverage --runInBand" }, "peerDependencies": { diff --git a/packages/utility-features/utilities/src/abort-controller.ts b/packages/utility-features/utilities/src/abort-controller.ts index 784b495a9e..d434a24c6f 100644 --- a/packages/utility-features/utilities/src/abort-controller.ts +++ b/packages/utility-features/utilities/src/abort-controller.ts @@ -22,3 +22,11 @@ export function setTimeoutFor(controller: AbortController, timeout: number): voi controller.signal.addEventListener("abort", () => clearTimeout(handle)); } + +export function chainSignal(target: AbortController, signal: AbortSignal) { + if (signal.aborted) { + target.abort(); + } else { + signal.addEventListener("abort", (event) => target.abort(event)); + } +} diff --git a/packages/utility-features/utilities/src/collection-functions.ts b/packages/utility-features/utilities/src/collection-functions.ts index e78e215e8b..9a50bb917f 100644 --- a/packages/utility-features/utilities/src/collection-functions.ts +++ b/packages/utility-features/utilities/src/collection-functions.ts @@ -74,13 +74,26 @@ export function getOrInsertWith(map: Map | WeakMap(map: Map, key: K, asyncBuilder: () => Promise): Promise { if (!map.has(key)) { - map.set(key, await asyncBuilder()); + const newValue = await asyncBuilder(); + + runInAction(() => { + map.set(key, newValue); + }); } // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return map.get(key)!; } +/** + * Insert `val` into `map` under `key` and then get the value back + */ +export function setAndGet(map: Map, key: K, val: V): V { + map.set(key, val); + + return map.get(key)!; +} + /** * Set the value associated with `key` iff there was not a previous value * @param map The map to interact with diff --git a/packages/utility-features/utilities/src/delay.ts b/packages/utility-features/utilities/src/delay.ts index d86395026b..83be44be71 100644 --- a/packages/utility-features/utilities/src/delay.ts +++ b/packages/utility-features/utilities/src/delay.ts @@ -10,11 +10,11 @@ * @param timeout The number of milliseconds before resolving * @param failFast An abort controller instance to cause the delay to short-circuit */ -export function delay(timeout = 1000, failFast?: AbortController): Promise { +export function delay(timeout = 1000, failFast?: AbortSignal): Promise { return new Promise(resolve => { const timeoutId = setTimeout(resolve, timeout); - failFast?.signal.addEventListener("abort", () => { + failFast?.addEventListener("abort", () => { clearTimeout(timeoutId); resolve(); }); diff --git a/packages/utility-features/utilities/src/iter.ts b/packages/utility-features/utilities/src/iter.ts index abbf4cc92d..b20f48a67a 100644 --- a/packages/utility-features/utilities/src/iter.ts +++ b/packages/utility-features/utilities/src/iter.ts @@ -10,10 +10,14 @@ interface Iterator extends Iterable { filterMap(fn: (val: T) => Falsy | U): Iterator; find(fn: (val: T) => unknown): T | undefined; collect(fn: (values: Iterable) => U): U; + toArray(): T[]; + toMap(): T extends [infer K, infer V] ? Map : never; + toSet(): Set; map(fn: (val: T) => U): Iterator; flatMap(fn: (val: T) => U[]): Iterator; concat(src2: IterableIterator): Iterator; join(sep?: string): string; + take(count: number): Iterator; } function chain(src: IterableIterator): Iterator { @@ -25,7 +29,11 @@ function chain(src: IterableIterator): Iterator { find: (fn) => find(src, fn), join: (sep) => join(src, sep), collect: (fn) => fn(src), + toArray: () => [...src], + toMap: () => new Map(src as IterableIterator<[any, any]>) as any, + toSet: () => new Set(src), concat: (src2) => chain(concat(src, src2)), + take: (count) => chain(take(src, count)), [Symbol.iterator]: () => src, }; } diff --git a/scenarios/namespace-selector.md b/scenarios/namespace-selector.md new file mode 100644 index 0000000000..e30a708ce3 --- /dev/null +++ b/scenarios/namespace-selector.md @@ -0,0 +1,208 @@ +## Feature: Namespace-selector in "cluster frame" + +### Scenario: Options for namespaces +- Given I’m connected to a cluster for the first time +- And I see the Namespace selector somewhere +- When I mouse-click to select it +- Then I see a list of all the Namespaces in alphabetical order +- But a selection for "All namespaces" as first + +### Scenario: Selecting a single namespace with mouse +- Given I’ve opened namespace selector +- When I hover over a namespace +- Then a toggle appears next to the namespace +- When I click the namespace entry outside of the toggle +- Then the list of namespaces closes +- And the namespace is seen as selected +- When I reopen the list +- Then I see the selected namespace at the top, just below "All namespaces" +- And I see the namespace marked as selected +- And I see the remaining namespaces in previous order not marked as selected + +### Scenario: Selecting All Namespaces option explicitly +- Given I’ve opened namespace selector +- When I click the "All Namespaces" option +- Then I see just the "All Namespaces" option as selected + +### Scenario: Toggling a single namespace after selecting "All Namespaces" option explicitly +- Given I’ve opened namespace selector +- And the "All Namespaces" option is selected +- And the CTRL/CMD is pressed +- When I click a single namespace option +- Then the "All Namespaces" option is shown as not selected +- And that single namespace is shown as not selected +- And all other single namespace options are shown as selected + +### Scenario: Selecting all namespace options explicitly +- Given I’ve opened namespace selector +- When I have selected each of the individual namespace options +- Then the "All Namespaces" option is not shown as selected +- And then every namespace option is shown as selected +- When a new namespace appears +- Then that new namespace is not shown as selected + +### Scenario: An single explicitly selected namespace is deleted +- Given that a single namespace is selected +- When that namespace is deleted +- Then the "All Namespaces" option is selected +- When a new namespace with the same name is created +- The "All Namespaces" is still the option selected + +### Scenario: One of several explicitly selected namespaces is deleted +- Given that more than one namespaces are selected +- When one of those namespace is deleted +- Then the remaining namespaces are shown as selected +- When a new namespace with the same name is created +- Then that new namespace is shown as selected + +### Scenario: Selecting a different single namespace with mouse +- Given I’ve opened namespace selector +- And I have a single namespace selected +- When I select a different namespace with a mouse-click +- Then the list of namespaces closes +- And the namespace is seen as selected in the select control + +### Scenario: Toggling multiple namespaces with mouse +- Given I’ve opened namespace selector +- When I hover over a namespace that isn't selected +- Then a toggle appears next to only that namespace +- When I hover over a namespace that is selected +- Then a toggle replaces the selection marker next to only that namespace +- When I click the checkbox +- Then the list of namespaces does not close +- And the namespace is still seen in original order +- But the namespace is marked as selected +- When I click the select control the namespace selector closes + +### Scenario: Selecting single namespace after multiple namespaces with mouse +- Given I’ve opened namespace selector +- When I hover over a namespace +- Then a checkbox appears next to only that namespace +- When I click the checkbox +- Then the list of namespaces does not close +- And the namespace is still seen in original order +- But the namespace is marked as selected +- When I click any namespace +- Then the list of namespaces closes +- And the namespace is seen as selected in the select control + +### Scenario: Selecting multiple namespaces with CTRL/CMD and mouse +- Given I’ve opened namespace selector +- And CTRL/CMD is pressed +- When I hover a namespace +- Then a checkbox does not appear next to the namespace +- When I click the namespace anywhere +- Then the list of namespaces does not close +- And the namespace is still seen in original order +- But the namespace is marked as selected +- When I click the select control the namespace selector closes + +### Scenario: A new namespace is created while the selector is open +- Given that the namespace selector is open +- And a new namespace is created +- Then new namespace is now visible as an option +- And the new namespace is not shown as selected +- And the namespace is sorted alphabetically into the "never selected" section + +### Scenario: A non-selected namespace is deleted while the selector is open +- Given that the namespace selector is open +- And a namespace that is not selected is deleted +- Then the namespace is no longer visible as an option + +### Scenario: Closing dropdown after selecting multiple namespaces onKeyUp CTRL/CMD and mouse +- Given I’ve opened namespace selector +- And CTRL/CMD is pressed +- When I release CTRL/CMD key +- Then the namespace selector closes + +### Scenario: Closing the namespace selector with outside mouse click +- Given I’ve opened namespace selector +- When I click outside the selector the namespace selector closes + +### Scenario: Reopening namespace selection shows selections first +- Given I’ve already selected namespaces +- When I reopen namespace selector +- Then I see the selected namespaces at the top in alphabetical order, just below "All namespaces" +- And then I see the namespaces that I have ever selected in MRU order +- And then I see the namespaces that I have never selected in alphabetical order + +### Scenario: Default namespace is preselected when present +- Given this is the first time connecting to a cluster +- And a special namespace called "default" is among the namespaces +- Then "default" is selected instead of "All namespaces" + +### Scenario: All namespace is preselected when default is not present +- Given this is the first time connecting to a cluster +- And a special namespace called "default" is not among the namespaces +- Then "All namespaces" is selected + +### Scenario: Focusing namespace selector using keyboard +- Given that I have just opened page with the namespace selector +- Can press TAB +- Then focuses the namespace selector + +### Scenario: Opening namespace selector using keyboard +- Given that the namespace selector is focused and is closed +- Pressing the ENTER key +- Opens the namespace selector + +### Scenario: Closing namespace selector using keyboard +- Given that the namespace selector control is focused and the dropdown is open +- Pressing the ENTER key +- Closes the namespace selector + +### Scenario: Closing namespace selector using keyboard +- Given that the namespace selector is open and either the control or the dropdown is focuses +- Pressing the ESC key +- Closes the namespace selector + +### Scenario: Moving focus through namespace selector dropdown using the keyboard +- Given that the namespace selector is open and is focused +- Regardless of CTRL/CMD press state +- Pressing the DOWN-ARROW moves to the next option in the dropdown, without wrapping around, being sticky +- Pressing the UP-ARROW moves to the previous option in the dropdown, without wrapping around, being sticky +- Pressing the PAGE-DOWN moves to the bottom of the dropdown +- Pressing the PAGE-UP moves to the top of the dropdown + +### Scenario: Toggling namespace as selected using the keyboard +- Given that the namespace selector is open and is focused +- And that a namespace option is focused +- Pressing SPACE toggles the namespace as selected +- And the namespace selector closes + +### Scenario: Toggling multiple namespace as selected using the keyboard +- Given that the namespace selector is open and is focused +- And the CTRL/CMD is pressed +- And that a namespace option is focused +- Pressing SPACE toggles the namespace as selected +- And the namespace selector stays open + +### Scenario: Selecting a single namespace as selected using the keyboard +- Given that the namespace selector is open and is focused +- And that a namespace option is focused +- Pressing ENTER selects that single namespace +- And the namespace selector closes + +### Scenario: Filtering list of namespaces +- Given that the namespace selector is open and is focused +- Typing filters the visible namespace options in the dropdown via contains +- Focus returns to the filter text input field + +### Scenario: Toggling the first option after filtering list of namespace +- Given that the namespace selector is open, focused, and some filtering has been done +- Pressing ENTER toggles the first namespace option +- And the namespace selector closes + +### Scenario: Selecting multiple options after filtering list of namespace +- Given that the namespace selector is open, focused, and some filtering has been done +- And the CTRL/CMD is pressed +- And not all the visible options are selected +- Pressing ENTER selects all the visible namespace options +- And the namespace selector stays open + +### Scenario: Deselecting multiple options after filtering list of namespace +- Given that the namespace selector is open, focused, and some filtering has been done +- And the CTRL/CMD is pressed +- And all the visible options are selected +- Pressing ENTER deselects all the visible namespace options +- And the namespace selector stays open