diff --git a/.azure-pipelines-k8s-matrix.yml b/.azure-pipelines-k8s-matrix.yml index 77129460a3..cdcc18b740 100644 --- a/.azure-pipelines-k8s-matrix.yml +++ b/.azure-pipelines-k8s-matrix.yml @@ -15,7 +15,7 @@ trigger: none jobs: - job: Linux pool: - vmImage: ubuntu-16.04 + vmImage: ubuntu-18.04 strategy: matrix: kube_1.16: @@ -46,7 +46,7 @@ jobs: curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 sudo install minikube-linux-amd64 /usr/local/bin/minikube sudo minikube start --driver=none --kubernetes-version $(kubernetes_version) - # Although the kube and minikube config files are in placed $HOME they are owned by root + sudo mv /root/.kube /root/.minikube $HOME sudo chown -R $USER $HOME/.kube $HOME/.minikube displayName: Install integration test dependencies - script: make node_modules diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml index 3e1b43f875..3c928430ed 100644 --- a/.azure-pipelines.yml +++ b/.azure-pipelines.yml @@ -109,7 +109,7 @@ jobs: - job: Linux pool: - vmImage: ubuntu-16.04 + vmImage: ubuntu-18.04 strategy: matrix: node_14.x: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b319c97770..7590f42289 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,7 +2,7 @@ name: Test on: pull_request: branches: - - "*" + - "**" push: branches: - master @@ -12,7 +12,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-16.04, macos-11, windows-2019] + os: [ubuntu-18.04, macos-11, windows-2019] node-version: [14.x] steps: - name: Checkout Release from lens @@ -67,15 +67,9 @@ jobs: - run: make test-extensions name: Run In-tree Extension tests - - run: | - sudo apt-get update - sudo apt-get install libgconf-2-4 conntrack -y - curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 - sudo install minikube-linux-amd64 /usr/local/bin/minikube - sudo minikube start --driver=none - # Although the kube and minikube config files are in placed $HOME they are owned by root - sudo chown -R $USER $HOME/.kube $HOME/.minikube - name: Install integration test dependencies + - name: Install integration test dependencies + id: minikube + uses: medyagh/setup-minikube@5a9a7104d7322fa40424de8855c84685e89cefd7 if: runner.os == 'Linux' - run: xvfb-run --auto-servernum --server-args='-screen 0, 1600x900x24' make integration diff --git a/.yarnrc b/.yarnrc index 9f7a2e491b..c7a54a1eda 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,3 +1,3 @@ disturl "https://atom.io/download/electron" -target "12.0.15" +target "13.5.1" runtime "electron" diff --git a/RELEASE_GUIDE.md b/RELEASE_GUIDE.md index fde9786459..b11a47c134 100644 --- a/RELEASE_GUIDE.md +++ b/RELEASE_GUIDE.md @@ -2,7 +2,9 @@ Lens releases are built by CICD automatically on git tags. The typical release process flow is the following: -1. From a clean and up to date `master` run `make release-version ` where `` is one of the following: +1. If doing a patch release checkout the `release/vMAJOR.MINOR` branch for the appropriate `MAJOR`/`MINOR` version and manually `cherry-pick` the PRs required for the patch that were commited to master. If there are any conflicts they must be resolved manually. If necessary, get assistance from the PR authors. + +1. From a clean and up to date `master` (or `release/vMAJOR.MINOR` if doing a patch release) run `make release-version ` where `` is one of the following: - `major` - `minor` - `patch` @@ -17,15 +19,15 @@ Lens releases are built by CICD automatically on git tags. The typical release p - `rc` This assumes origin is set to https://github.com/lensapp/lens.git. If not then set GIT_REMOTE to the remote that is set to https://github.com/lensapp/lens.git. For example run `GIT_REMOTE=upstream make release-version ...` -1. Open the PR (git should have printed a link to GitHub in the console) with the contents of all the accepted PRs since the last release. The PR description needs to be filled with the draft release description. From https://github.com/lensapp/lens click on Releases, the draft release should be first in the list, click `Edit` and copy/paste the markdown to the PR description. Add the `skip-changelog` label and click `Create Pull Request`. -1. After the PR is accepted and passes CI (and before merging), go to the same branch and run `make tag-release` (set GIT_REMOTE if necessary). This additionally runs the azure jobs to build the binaries and put them on S3. +1. Open the PR (git should have printed a link to GitHub in the console) with the contents of all the accepted PRs since the last release. The PR description needs to be filled with the draft release description. From https://github.com/lensapp/lens click on Releases, the draft release should be first in the list, click `Edit` and copy/paste the markdown to the PR description. Add the `skip-changelog` label and click `Create Pull Request`. If this is a patch release be sure to set the PR base branch to `release/vMAJOR.MINOR` instead of `master`. +1. After the PR is accepted and passes CI (and before merging), go to the same branch and run `make tag-release` (set GIT_REMOTE if necessary). This additionally triggers the azure jobs to build the binaries and put them on S3. 1. If the CI fails at this stage the problem needs to be fixed. Sometimes an azure job fails due to outside service issues (e.g. Apple signing occasionally fails), in which case the specific azure job can be rerun from https://dev.azure.com/lensapp/lensapp/_build. Otherwise changes to the codebase may need to be done and committed to the release branch and pushed to https://github.com/lensapp/lens. CI will run again. As well the release tag needs to be manually set to this new commit. You can do something like: - `git push origin :refs/tags/vX.Y.Z-beta.N` (removes the tag from https://github.com/lensapp/lens) - `git tag -fa vX.Y.Z-beta.N` (move the tag locally to the current commit) - `git push origin --tags` (update the tags on https://github.com/lensapp/lens to reflect this local change) 1. Once CI passes again go to the releases tab on GitHub. You can use the existing draft release prepared by k8slens-bot (select the correct tag). Or you can create a new release from the tag that was created, and make sure that the change log is the same as that of the PR, and the title is the tag. Either way, click the prerelease checkbox if this is not a new major, minor, or patch version before clicking `Publish release`. -1. Merge the release PR after the release is published. GitHub should delete the branch once it is merged. +1. Merge the release PR after the release is published. If it is a patch release then there is no need to squash the cherry-picked commits as part of the merge. GitHub should delete the branch once it is merged. 1. If you have just released a new major or minor version then create a new `release/vMAJOR.MINOR` branch from that same tag and push it to https://github.com/lensapp/lens. Given the commit of the merged release PR from the master branch you can do this like `git push origin :refs/heads/release/vX.Y` @@ -37,4 +39,4 @@ Other tasks - generate a changelog from the prerelease descriptions (for major/minor releases) - announce the release on lens and lens-hq slack channels (release is announced automatically on the community slack lens channel through the above publishing process) - announce on lens-hq that master is open for PR merges for the next release (for major/minor releases) - - update issues on github (bump those that did not make it into the release to a subsequent release) (for major/minor releases) \ No newline at end of file + - update issues on github (bump those that did not make it into the release to a subsequent release) (for major/minor/patch releases) \ No newline at end of file diff --git a/extensions/kube-object-event-status/package-lock.json b/extensions/kube-object-event-status/package-lock.json index 9dc7b55d28..da9fd338e1 100644 --- a/extensions/kube-object-event-status/package-lock.json +++ b/extensions/kube-object-event-status/package-lock.json @@ -8,9 +8,9 @@ "version": "file:../../src/extensions/npm/extensions", "dev": true, "requires": { - "@material-ui/core": "*", - "@types/node": "*", - "@types/react-select": "*", + "@material-ui/core": "4.12.3", + "@types/node": "14.17.14", + "@types/react-select": "3.1.2", "conf": "^7.0.1", "typed-emitter": "^1.3.1" }, diff --git a/extensions/metrics-cluster-feature/package-lock.json b/extensions/metrics-cluster-feature/package-lock.json index 981cb43dff..c62e13ca4b 100644 --- a/extensions/metrics-cluster-feature/package-lock.json +++ b/extensions/metrics-cluster-feature/package-lock.json @@ -662,10 +662,11 @@ "version": "file:../../src/extensions/npm/extensions", "dev": true, "requires": { - "@material-ui/core": "*", - "@types/node": "*", - "@types/react-select": "*", - "conf": "^7.0.1" + "@material-ui/core": "4.12.3", + "@types/node": "14.17.14", + "@types/react-select": "3.1.2", + "conf": "^7.0.1", + "typed-emitter": "^1.3.1" }, "dependencies": { "@babel/runtime": { @@ -1329,6 +1330,12 @@ "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", "dev": true }, + "typed-emitter": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/typed-emitter/-/typed-emitter-1.3.1.tgz", + "integrity": "sha512-2h7utWyXgd2R2u2IuL8B4yu1gqMxbgUj2VS/MGVbFhEVQNJKXoQQoS5CBMh+eW31zFeSmDfEQ3qQf4xy5SlPVQ==", + "dev": true + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -1742,9 +1749,9 @@ } }, "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "ansi-styles": { @@ -6645,9 +6652,9 @@ } }, "tmpl": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", - "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, "to-arraybuffer": { diff --git a/extensions/node-menu/package-lock.json b/extensions/node-menu/package-lock.json index 02d953b830..0c8fc08c0f 100644 --- a/extensions/node-menu/package-lock.json +++ b/extensions/node-menu/package-lock.json @@ -628,9 +628,9 @@ "version": "file:../../src/extensions/npm/extensions", "dev": true, "requires": { - "@material-ui/core": "*", - "@types/node": "*", - "@types/react-select": "*", + "@material-ui/core": "4.12.3", + "@types/node": "14.17.14", + "@types/react-select": "3.1.2", "conf": "^7.0.1", "typed-emitter": "^1.3.1" }, @@ -1707,9 +1707,9 @@ } }, "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "ansi-styles": { @@ -6608,9 +6608,9 @@ } }, "tmpl": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", - "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, "to-arraybuffer": { diff --git a/extensions/pod-menu/package-lock.json b/extensions/pod-menu/package-lock.json index 3abe5a107f..07f3ae754e 100644 --- a/extensions/pod-menu/package-lock.json +++ b/extensions/pod-menu/package-lock.json @@ -628,9 +628,9 @@ "version": "file:../../src/extensions/npm/extensions", "dev": true, "requires": { - "@material-ui/core": "*", - "@types/node": "*", - "@types/react-select": "*", + "@material-ui/core": "4.12.3", + "@types/node": "14.17.14", + "@types/react-select": "3.1.2", "conf": "^7.0.1", "typed-emitter": "^1.3.1" }, @@ -1660,9 +1660,9 @@ } }, "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true }, "ansi-styles": { @@ -6561,9 +6561,9 @@ } }, "tmpl": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", - "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, "to-arraybuffer": { diff --git a/integration/__tests__/app-preferences.tests.ts b/integration/__tests__/app-preferences.tests.ts index fe3817bf0c..4939686e41 100644 --- a/integration/__tests__/app-preferences.tests.ts +++ b/integration/__tests__/app-preferences.tests.ts @@ -25,16 +25,24 @@ TEST_NAMESPACE namespace. This is done to minimize destructive impact of the cluster tests on an existing minikube cluster and vice versa. */ -import type { Page } from "playwright"; +import type { ElectronApplication, Page } from "playwright"; import * as utils from "../helpers/utils"; describe("preferences page tests", () => { let window: Page, cleanup: () => Promise; beforeEach(async () => { - ({ window, cleanup } = await utils.start()); + let app: ElectronApplication; + + ({ window, cleanup, app } = await utils.start()); await utils.clickWelcomeButton(window); - await window.keyboard.press("Meta+,"); + + await app.evaluate(async ({ app }) => { + await app.applicationMenu + .getMenuItemById(process.platform === "darwin" ? "root" : "file") + .submenu.getMenuItemById("preferences") + .click(); + }); }, 10*60*1000); afterEach(async () => { diff --git a/integration/__tests__/command-palette.tests.ts b/integration/__tests__/command-palette.tests.ts index 1f2e49114b..da87b95a41 100644 --- a/integration/__tests__/command-palette.tests.ts +++ b/integration/__tests__/command-palette.tests.ts @@ -19,14 +19,14 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import type { Page } from "playwright"; +import type { ElectronApplication, Page } from "playwright"; import * as utils from "../helpers/utils"; describe("Lens command palette", () => { - let window: Page, cleanup: () => Promise; + let window: Page, cleanup: () => Promise, app: ElectronApplication; beforeEach(async () => { - ({ window, cleanup } = await utils.start()); + ({ window, cleanup, app } = await utils.start()); await utils.clickWelcomeButton(window); }, 10*60*1000); @@ -35,8 +35,13 @@ describe("Lens command palette", () => { }, 10*60*1000); describe("menu", () => { - it("opens command dialog from keyboard shortcut", async () => { - await window.keyboard.press("Meta+Shift+p"); + it("opens command dialog from menu", async () => { + await app.evaluate(async ({ app }) => { + await app.applicationMenu + .getMenuItemById("view") + .submenu.getMenuItemById("command-palette") + .click(); + }); await window.waitForSelector(".Select__option >> text=Hotbar: Switch"); }, 10*60*1000); }); diff --git a/integration/helpers/utils.ts b/integration/helpers/utils.ts index f9a10e1d4d..e52ae1d87f 100644 --- a/integration/helpers/utils.ts +++ b/integration/helpers/utils.ts @@ -104,10 +104,7 @@ function minikubeEntityId() { * From the catalog, click the minikube entity and wait for it to connect, returning its frame */ export async function lauchMinikubeClusterFromCatalog(window: Page): Promise { - await window.waitForSelector("div.TableCell"); await window.click("div.TableCell >> text='minikube'"); - await window.waitForSelector("div.drawer-title-text >> text='KubernetesCluster: minikube'"); - await window.click("div.EntityIcon div.HotbarIcon div div.MuiAvatar-root"); const minikubeFrame = await window.waitForSelector(`#cluster-frame-${minikubeEntityId()}`); diff --git a/package.json b/package.json index ef59d10728..e8288fde7b 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "productName": "OpenLens", "description": "OpenLens - Open Source IDE for Kubernetes", "homepage": "https://github.com/lensapp/lens", - "version": "5.2.0", + "version": "5.3.0-alpha.0", "main": "static/build/main.js", "copyright": "© 2021 OpenLens Authors", "license": "MIT", @@ -197,11 +197,12 @@ "conf": "^7.0.1", "crypto-js": "^4.1.1", "electron-devtools-installer": "^3.2.0", - "electron-updater": "^4.4.3", + "electron-updater": "^4.6.0", "electron-window-state": "^5.0.3", "filehound": "^1.17.4", "fs-extra": "^9.0.1", "glob-to-regexp": "^0.4.1", + "got": "^11.8.2", "grapheme-splitter": "^1.0.4", "handlebars": "^4.7.7", "http-proxy": "^1.18.1", @@ -209,24 +210,23 @@ "joi": "^17.4.2", "js-yaml": "^3.14.0", "jsdom": "^16.7.0", - "jsonpath": "^1.0.2", + "jsonpath": "^1.1.1", "lodash": "^4.17.15", "mac-ca": "^1.0.6", "marked": "^2.0.3", "md5-file": "^5.0.0", "mobx": "^6.3.0", "mobx-observable-history": "^2.0.1", - "mobx-react": "^7.1.0", + "mobx-react": "^7.2.0", "mock-fs": "^4.14.0", "moment": "^2.29.1", "moment-timezone": "^0.5.33", "monaco-editor": "^0.26.1", "node-fetch": "^2.6.1", "node-pty": "^0.10.1", - "npm": "^6.14.8", - "openid-client": "^3.15.2", + "npm": "^6.14.15", "p-limit": "^3.1.0", - "path-to-regexp": "^6.1.0", + "path-to-regexp": "^6.2.0", "proper-lockfile": "^4.1.2", "react": "^17.0.2", "react-dom": "^17.0.2", @@ -237,16 +237,17 @@ "readable-stream": "^3.6.0", "request": "^2.88.2", "request-promise-native": "^1.0.9", + "rfc6902": "^4.0.2", "semver": "^7.3.2", "serializr": "^2.0.5", "shell-env": "^3.0.1", "spdy": "^4.0.2", "tar": "^6.1.10", - "tcp-port-used": "^1.0.1", - "tempy": "^0.5.0", - "url-parse": "^1.5.1", + "tcp-port-used": "^1.0.2", + "tempy": "1.0.1", + "url-parse": "^1.5.3", "uuid": "^8.3.2", - "win-ca": "^3.2.0", + "win-ca": "^3.4.5", "winston": "^3.3.3", "winston-console-format": "^1.0.8", "winston-transport-browserconsole": "^1.0.5", @@ -274,9 +275,9 @@ "@types/glob-to-regexp": "^0.4.1", "@types/hapi": "^18.0.6", "@types/hoist-non-react-statics": "^3.3.1", - "@types/html-webpack-plugin": "^3.2.3", + "@types/html-webpack-plugin": "^3.2.6", "@types/http-proxy": "^1.17.7", - "@types/jest": "^26.0.22", + "@types/jest": "^26.0.24", "@types/js-yaml": "^3.12.4", "@types/jsdom": "^16.2.4", "@types/jsonpath": "^0.2.0", @@ -290,19 +291,19 @@ "@types/node-fetch": "^2.5.12", "@types/npm": "^2.0.32", "@types/progress-bar-webpack-plugin": "^2.1.2", - "@types/proper-lockfile": "^4.1.1", + "@types/proper-lockfile": "^4.1.2", "@types/randomcolor": "^0.5.6", "@types/react": "^17.0.0", "@types/react-beautiful-dnd": "^13.1.1", "@types/react-dom": "^17.0.9", - "@types/react-router-dom": "^5.1.6", + "@types/react-router-dom": "^5.3.1", "@types/react-select": "3.1.2", - "@types/react-table": "^7.7.0", + "@types/react-table": "^7.7.6", "@types/react-virtualized-auto-sizer": "^1.0.1", "@types/react-window": "^1.8.5", "@types/readable-stream": "^2.3.9", "@types/request": "^2.48.7", - "@types/request-promise-native": "^1.0.17", + "@types/request-promise-native": "^1.0.18", "@types/semver": "^7.2.0", "@types/sharp": "^0.28.3", "@types/spdy": "^3.4.4", @@ -313,26 +314,26 @@ "@types/url-parse": "^1.4.3", "@types/uuid": "^8.3.1", "@types/webdriverio": "^4.13.0", - "@types/webpack": "^4.41.17", - "@types/webpack-dev-server": "^3.11.1", - "@types/webpack-env": "^1.15.2", + "@types/webpack": "^4.41.31", + "@types/webpack-dev-server": "^3.11.6", + "@types/webpack-env": "^1.16.2", "@types/webpack-node-externals": "^1.7.1", - "@typescript-eslint/eslint-plugin": "^4.29.0", + "@typescript-eslint/eslint-plugin": "^4.33.0", "@typescript-eslint/parser": "^4.29.1", "ansi_up": "^5.0.1", "chart.js": "^2.9.4", "circular-dependency-plugin": "^5.2.2", "color": "^3.1.2", "concurrently": "^5.2.0", - "css-loader": "^5.2.6", - "deepdash": "^5.3.5", + "css-loader": "^5.2.7", + "deepdash": "^5.3.9", "dompurify": "^2.3.1", - "electron": "^12.0.17", + "electron": "^13.5.1", "electron-builder": "^22.11.11", "electron-notarize": "^0.3.0", "esbuild": "^0.12.24", - "esbuild-loader": "^2.13.1", - "eslint": "^7.7.0", + "esbuild-loader": "^2.15.1", + "eslint": "^7.32.0", "eslint-plugin-header": "^3.1.1", "eslint-plugin-react": "^7.24.0", "eslint-plugin-react-hooks": "^4.2.0", @@ -348,9 +349,8 @@ "jest-canvas-mock": "^2.3.1", "jest-fetch-mock": "^3.0.3", "jest-mock-extended": "^1.0.16", - "json-to-pretty-yaml": "^1.2.2", "make-plural": "^6.2.2", - "mini-css-extract-plugin": "^1.6.0", + "mini-css-extract-plugin": "^1.6.2", "node-gyp": "7.1.2", "node-loader": "^1.0.3", "nodemon": "^2.0.12", @@ -358,6 +358,7 @@ "postcss": "^8.3.6", "postcss-loader": "4.3.0", "postinstall-postinstall": "^2.1.0", + "prettier": "^2.4.1", "progress-bar-webpack-plugin": "^2.1.0", "randomcolor": "^0.6.2", "raw-loader": "^4.0.2", @@ -368,20 +369,20 @@ "react-select-event": "^5.1.0", "react-table": "^7.7.0", "react-window": "^1.8.5", - "sass": "^1.39.0", + "sass": "^1.41.1", "sass-loader": "^8.0.2", - "sharp": "^0.29.0", + "sharp": "^0.29.1", "style-loader": "^2.0.0", "tailwindcss": "^2.2.4", "ts-jest": "26.5.6", "ts-loader": "^7.0.5", - "ts-node": "^10.1.0", + "ts-node": "^10.2.1", "type-fest": "^1.0.2", "typed-emitter": "^1.3.1", - "typedoc": "0.21.0-beta.2", + "typedoc": "0.22.5", "typedoc-plugin-markdown": "^3.9.0", "typeface-roboto": "^1.1.13", - "typescript": "^4.3.2", + "typescript": "^4.4.3", "typescript-plugin-css-modules": "^3.4.0", "url-loader": "^4.1.1", "webpack": "^4.46.0", diff --git a/src/common/base-store.ts b/src/common/base-store.ts index 54f6d2a5a2..c5fc7383b1 100644 --- a/src/common/base-store.ts +++ b/src/common/base-store.ts @@ -46,6 +46,10 @@ export abstract class BaseStore extends Singleton { protected constructor(protected params: BaseStoreParams) { super(); makeObservable(this); + + if (ipcRenderer) { + params.migrations = undefined; // don't run migrations on renderer + } } /** diff --git a/src/common/catalog-entities/kubernetes-cluster.ts b/src/common/catalog-entities/kubernetes-cluster.ts index b2ff284069..3335d3803d 100644 --- a/src/common/catalog-entities/kubernetes-cluster.ts +++ b/src/common/catalog-entities/kubernetes-cluster.ts @@ -51,6 +51,7 @@ export interface KubernetesClusterSpec extends CatalogEntitySpec { material?: string; background?: string; }; + accessibleNamespaces?: string[]; } export interface KubernetesClusterMetadata extends CatalogEntityMetadata { diff --git a/src/common/catalog-entities/web-link.ts b/src/common/catalog-entities/web-link.ts index d54aa8533a..8309945f24 100644 --- a/src/common/catalog-entities/web-link.ts +++ b/src/common/catalog-entities/web-link.ts @@ -57,6 +57,10 @@ export class WebLink extends CatalogEntity(this) + ?.emit("contextMenuOpen", this, context); } } diff --git a/src/common/catalog/catalog-run-event.ts b/src/common/catalog/catalog-run-event.ts new file mode 100644 index 0000000000..00c475771d --- /dev/null +++ b/src/common/catalog/catalog-run-event.ts @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import type { CatalogEntity } from "../catalog"; + +export class CatalogRunEvent { + #defaultPrevented: boolean; + #target: CatalogEntity; + + get defaultPrevented() { + return this.#defaultPrevented; + } + + get target() { + return this.#target; + } + + constructor({ target }: { target: CatalogEntity }) { + this.#defaultPrevented = false; + this.#target = target; + } + + preventDefault() { + this.#defaultPrevented = true; + } +} diff --git a/src/common/k8s-api/endpoints/persistent-volume-claims.api.ts b/src/common/k8s-api/endpoints/persistent-volume-claims.api.ts index 1ac5d8df9a..a8822aed7b 100644 --- a/src/common/k8s-api/endpoints/persistent-volume-claims.api.ts +++ b/src/common/k8s-api/endpoints/persistent-volume-claims.api.ts @@ -31,11 +31,13 @@ export class PersistentVolumeClaimsApi extends KubeApi { } export function getMetricsForPvc(pvc: PersistentVolumeClaim): Promise { + const opts = { category: "pvc", pvc: pvc.getName(), namespace: pvc.getNs() }; + return metricsApi.getMetrics({ - diskUsage: { category: "pvc", pvc: pvc.getName() }, - diskCapacity: { category: "pvc", pvc: pvc.getName() } + diskUsage: opts, + diskCapacity: opts }, { - namespace: pvc.getNs() + namespace: opts.namespace }); } diff --git a/src/common/k8s-api/endpoints/resource-applier.api.ts b/src/common/k8s-api/endpoints/resource-applier.api.ts index b6a35d3a60..14b5d5ad46 100644 --- a/src/common/k8s-api/endpoints/resource-applier.api.ts +++ b/src/common/k8s-api/endpoints/resource-applier.api.ts @@ -22,19 +22,27 @@ import jsYaml from "js-yaml"; import type { KubeJsonApiData } from "../kube-json-api"; import { apiBase } from "../index"; +import type { Patch } from "rfc6902"; -export const resourceApplierApi = { - annotations: [ - "kubectl.kubernetes.io/last-applied-configuration" - ], +export const annotations = [ + "kubectl.kubernetes.io/last-applied-configuration" +]; - async update(resource: object | string): Promise { - if (typeof resource === "string") { - resource = jsYaml.safeLoad(resource); - } - - const [data = null] = await apiBase.post("/stack", { data: resource }); - - return data; +export async function update(resource: object | string): Promise { + if (typeof resource === "string") { + resource = jsYaml.safeLoad(resource); } -}; + + return apiBase.post("/stack", { data: resource }); +} + +export async function patch(name: string, kind: string, ns: string, patch: Patch): Promise { + return apiBase.patch("/stack", { + data: { + name, + kind, + ns, + patch, + }, + }); +} diff --git a/src/common/k8s-api/kube-object.store.ts b/src/common/k8s-api/kube-object.store.ts index dc428eb08b..7c8118da1f 100644 --- a/src/common/k8s-api/kube-object.store.ts +++ b/src/common/k8s-api/kube-object.store.ts @@ -32,6 +32,7 @@ import { parseKubeApi } from "./kube-api-parse"; import type { KubeJsonApiData } from "./kube-json-api"; import type { RequestInit } from "node-fetch"; import AbortController from "abort-controller"; +import type { Patch } from "rfc6902"; export interface KubeObjectStoreLoadingParams { namespaces: string[]; @@ -159,7 +160,7 @@ export abstract class KubeObjectStore extends ItemStore this.loadedNamespaces = namespaces; return Promise // load resources per namespace - .all(namespaces.map(namespace => api.list({ namespace, reqInit }))) + .all(namespaces.map(namespace => api.list({ namespace, reqInit }, this.query))) .then(items => items.flat().filter(Boolean)); } } @@ -279,19 +280,29 @@ export abstract class KubeObjectStore extends ItemStore return newItem; } - async update(item: T, data: Partial): Promise { - const rawItem = await item.update(data); + private postUpdate(rawItem: KubeJsonApiData): T { const newItem = new this.api.objectConstructor(rawItem); + const index = this.items.findIndex(item => item.getId() === newItem.getId()); ensureObjectSelfLink(this.api, newItem); - const index = this.items.findIndex(item => item.getId() === newItem.getId()); - - this.items.splice(index, 1, newItem); + if (index < 0) { + this.items.push(newItem); + } else { + this.items[index] = newItem; + } return newItem; } + async patch(item: T, patch: Patch): Promise { + return this.postUpdate(await item.patch(patch)); + } + + async update(item: T, data: Partial): Promise { + return this.postUpdate(await item.update(data)); + } + async remove(item: T) { await item.delete(); this.items.remove(item); diff --git a/src/common/k8s-api/kube-object.ts b/src/common/k8s-api/kube-object.ts index c1e40eb2e5..0cb4ac15f6 100644 --- a/src/common/k8s-api/kube-object.ts +++ b/src/common/k8s-api/kube-object.ts @@ -27,9 +27,9 @@ import { autoBind, formatDuration } from "../utils"; import type { ItemObject } from "../item.store"; import { apiKube } from "./index"; import type { JsonApiParams } from "./json-api"; -import { resourceApplierApi } from "./endpoints/resource-applier.api"; +import * as resourceApplierApi from "./endpoints/resource-applier.api"; import { hasOptionalProperty, hasTypedProperty, isObject, isString, bindPredicate, isTypedArray, isRecord } from "../../common/utils/type-narrowing"; -import _ from "lodash"; +import type { Patch } from "rfc6902"; export type KubeObjectConstructor = (new (data: KubeJsonApiData | any) => K) & { kind?: string; @@ -98,6 +98,12 @@ export interface KubeObjectStatus { export type KubeMetaField = keyof KubeObjectMetadata; +export class KubeCreationError extends Error { + constructor(message: string, public data: any) { + super(message); + } +} + export class KubeObject implements ItemObject { static readonly kind: string; static readonly namespaced: boolean; @@ -191,18 +197,32 @@ export class KubeObject `${name}=${value}`); } - protected static readonly nonEditableFields = [ - "apiVersion", - "kind", - "metadata.name", - "metadata.selfLink", - "metadata.resourceVersion", - "metadata.uid", - "managedFields", - "status", + /** + * These must be RFC6902 compliant paths + */ + private static readonly nonEditiablePathPrefixes = [ + "/metadata/managedFields", + "/status", ]; + private static readonly nonEditablePaths = new Set([ + "/apiVersion", + "/kind", + "/metadata/name", + "/metadata/selfLink", + "/metadata/resourceVersion", + "/metadata/uid", + ...KubeObject.nonEditiablePathPrefixes, + ]); constructor(data: KubeJsonApiData) { + if (typeof data !== "object") { + throw new TypeError(`Cannot create a KubeObject from ${typeof data}`); + } + + if (!data.metadata || typeof data.metadata !== "object") { + throw new KubeCreationError(`Cannot create a KubeObject from an object without metadata`, data); + } + Object.assign(this, data); autoBind(this); } @@ -264,7 +284,7 @@ export class KubeObject ({ ...ownerRef, namespace })); @@ -286,14 +306,31 @@ export class KubeObject): Promise { - for (const field of KubeObject.nonEditableFields) { - if (!_.isEqual(_.get(this, field), _.get(data, field))) { - throw new Error(`Failed to update Kube Object: ${field} has been modified`); + async patch(patch: Patch): Promise { + for (const op of patch) { + if (KubeObject.nonEditablePaths.has(op.path)) { + throw new Error(`Failed to update ${this.kind}: JSON pointer ${op.path} has been modified`); + } + + for (const pathPrefix of KubeObject.nonEditiablePathPrefixes) { + if (op.path.startsWith(`${pathPrefix}/`)) { + throw new Error(`Failed to update ${this.kind}: Child JSON pointer of ${op.path} has been modified`); + } } } + return resourceApplierApi.patch(this.getName(), this.kind, this.getNs(), patch); + } + + /** + * Perform a full update (or more specifically a replace) + * + * Note: this is brittle if `data` is not actually partial (but instead whole). + * As fields such as `resourceVersion` will probably out of date. This is a + * common race condition. + */ + async update(data: Partial): Promise { + // use unified resource-applier api for updating all k8s objects return resourceApplierApi.update({ ...this.toPlainObject(), ...data, diff --git a/src/common/logger.ts b/src/common/logger.ts index 825fa2493b..ca975871b8 100644 --- a/src/common/logger.ts +++ b/src/common/logger.ts @@ -59,24 +59,24 @@ if (ipcMain) { ), }) ); + + if (!isTestEnv) { + transports.push( + new winston.transports.File({ + handleExceptions: false, + level: logLevel, + filename: "lens.log", + dirname: getPath("logs"), + maxsize: 16 * 1024, + maxFiles: 16, + tailable: true, + }) + ); + } } else { transports.push(new BrowserConsole()); } -if (!isTestEnv) { - transports.push( - new winston.transports.File({ - handleExceptions: false, - level: logLevel, - filename: "lens.log", - dirname: getPath("logs"), - maxsize: 16 * 1024, - maxFiles: 16, - tailable: true, - }) - ); -} - export default winston.createLogger({ format: format.simple(), transports, diff --git a/src/common/routes/catalog.ts b/src/common/routes/catalog.ts index 1221e931d0..7759d13b62 100644 --- a/src/common/routes/catalog.ts +++ b/src/common/routes/catalog.ts @@ -30,4 +30,17 @@ export const catalogRoute: RouteProps = { path: "/catalog/:group?/:kind?" }; +export const getPreviousTabUrl = (path: string) => { + const [group, kind] = path.split("/"); + + return catalogURL({ + params: { + group: group || browseCatalogTab, + kind + } + }); +}; + export const catalogURL = buildURL(catalogRoute.path); + +export const browseCatalogTab = "browse"; diff --git a/src/common/routes/index.ts b/src/common/routes/index.ts index 6cdd81e2b4..82a81d29dc 100644 --- a/src/common/routes/index.ts +++ b/src/common/routes/index.ts @@ -40,6 +40,7 @@ export * from "./network-policies"; export * from "./network"; export * from "./nodes"; export * from "./pod-disruption-budgets"; +export * from "./port-forwards"; export * from "./preferences"; export * from "./releases"; export * from "./resource-quotas"; diff --git a/src/common/routes/network.ts b/src/common/routes/network.ts index 17b51e200a..15338675a6 100644 --- a/src/common/routes/network.ts +++ b/src/common/routes/network.ts @@ -25,6 +25,7 @@ import { endpointRoute } from "./endpoints"; import { ingressRoute } from "./ingresses"; import { networkPoliciesRoute } from "./network-policies"; import { servicesRoute, servicesURL } from "./services"; +import { portForwardsRoute } from "./port-forwards"; export const networkRoute: RouteProps = { path: [ @@ -32,6 +33,7 @@ export const networkRoute: RouteProps = { endpointRoute, ingressRoute, networkPoliciesRoute, + portForwardsRoute, ].map(route => route.path.toString()) }; diff --git a/src/renderer/components/+entity-settings/entity-settings.scss b/src/common/routes/port-forwards.ts similarity index 76% rename from src/renderer/components/+entity-settings/entity-settings.scss rename to src/common/routes/port-forwards.ts index 30c01b8a67..d05b8583ab 100644 --- a/src/renderer/components/+entity-settings/entity-settings.scss +++ b/src/common/routes/port-forwards.ts @@ -19,26 +19,14 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -.EntitySettings { - $spacing: $padding * 3; +import type { RouteProps } from "react-router"; +import { buildURL } from "../utils/buildUrl"; +export const portForwardsRoute: RouteProps = { + path: "/port-forwards" +}; - // TODO: move sub-component styles to separate files - .admin-note { - font-size: small; - opacity: 0.5; - margin-left: $margin; - } - - .button-area { - margin-top: $margin * 2; - } - - .file-loader { - margin-top: $margin * 2; - } - - .Input, .Select { - margin-top: $padding; - } +export interface PortForwardsRouteParams { } + +export const portForwardsURL = buildURL(portForwardsRoute.path); diff --git a/src/common/user-store/preferences-helpers.ts b/src/common/user-store/preferences-helpers.ts index 59f201f55f..d981049d01 100644 --- a/src/common/user-store/preferences-helpers.ts +++ b/src/common/user-store/preferences-helpers.ts @@ -200,6 +200,19 @@ const openAtLogin: PreferenceDescription = { }, }; +const terminalCopyOnSelect: PreferenceDescription = { + fromStore(val) { + return val ?? false; + }, + toStore(val) { + if (!val) { + return undefined; + } + + return val; + }, +}; + const hiddenTableColumns: PreferenceDescription<[string, string[]][], Map>> = { fromStore(val) { return new Map( @@ -274,4 +287,5 @@ export const DESCRIPTORS = { hiddenTableColumns, syncKubeconfigEntries, editorConfiguration, + terminalCopyOnSelect, }; diff --git a/src/common/user-store/user-store.ts b/src/common/user-store/user-store.ts index ee0022da1b..1bd18e06e8 100644 --- a/src/common/user-store/user-store.ts +++ b/src/common/user-store/user-store.ts @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { app } from "electron"; +import { app, ipcMain } from "electron"; import semver from "semver"; import { action, computed, observable, reaction, makeObservable } from "mobx"; import { BaseStore } from "../base-store"; @@ -48,7 +48,11 @@ export class UserStore extends BaseStore /* implements UserStore }); makeObservable(this); - fileNameMigration(); + + if (ipcMain) { + fileNameMigration(); + } + this.load(); } @@ -70,6 +74,7 @@ export class UserStore extends BaseStore /* implements UserStore @observable shell?: string; @observable downloadBinariesPath?: string; @observable kubectlBinariesPath?: string; + @observable terminalCopyOnSelect: boolean; /** * Download kubectl binaries matching cluster version @@ -212,6 +217,7 @@ export class UserStore extends BaseStore /* implements UserStore this.hiddenTableColumns.replace(DESCRIPTORS.hiddenTableColumns.fromStore(preferences?.hiddenTableColumns)); this.syncKubeconfigEntries.replace(DESCRIPTORS.syncKubeconfigEntries.fromStore(preferences?.syncKubeconfigEntries)); this.editorConfiguration = DESCRIPTORS.editorConfiguration.fromStore(preferences?.editorConfiguration); + this.terminalCopyOnSelect = DESCRIPTORS.terminalCopyOnSelect.fromStore(preferences?.terminalCopyOnSelect); } toJSON(): UserStoreModel { @@ -233,6 +239,7 @@ export class UserStore extends BaseStore /* implements UserStore hiddenTableColumns: DESCRIPTORS.hiddenTableColumns.toStore(this.hiddenTableColumns), syncKubeconfigEntries: DESCRIPTORS.syncKubeconfigEntries.toStore(this.syncKubeconfigEntries), editorConfiguration: DESCRIPTORS.editorConfiguration.toStore(this.editorConfiguration), + terminalCopyOnSelect: DESCRIPTORS.terminalCopyOnSelect.toStore(this.terminalCopyOnSelect), }, }; diff --git a/src/common/utils/app-version.ts b/src/common/utils/app-version.ts index 84d4221498..f05877a7ec 100644 --- a/src/common/utils/app-version.ts +++ b/src/common/utils/app-version.ts @@ -33,8 +33,9 @@ export function getBundledKubectlVersion(): string { export async function getAppVersionFromProxyServer(proxyPort: number): Promise { const response = await requestPromise({ method: "GET", - uri: `http://localhost:${proxyPort}/version`, - resolveWithFullResponse: true + uri: `http://127.0.0.1:${proxyPort}/version`, + resolveWithFullResponse: true, + proxy: undefined, }); return JSON.parse(response.body).version; diff --git a/src/common/vars.ts b/src/common/vars.ts index 6462be4db5..b18a1cb97e 100644 --- a/src/common/vars.ts +++ b/src/common/vars.ts @@ -69,7 +69,7 @@ export const apiKubePrefix = "/api-kube" as string; // k8s cluster apis // Links export const issuesTrackerUrl = "https://github.com/lensapp/lens/issues" as string; -export const slackUrl = "https://join.slack.com/t/k8slens/shared_invite/enQtOTc5NjAyNjYyOTk4LWU1NDQ0ZGFkOWJkNTRhYTc2YjVmZDdkM2FkNGM5MjhiYTRhMDU2NDQ1MzIyMDA4ZGZlNmExOTc0N2JmY2M3ZGI" as string; +export const slackUrl = "https://join.slack.com/t/k8slens/shared_invite/zt-wcl8jq3k-68R5Wcmk1o95MLBE5igUDQ" as string; export const supportUrl = "https://docs.k8slens.dev/latest/support/" as string; export const appSemVer = new SemVer(packageInfo.version); diff --git a/src/extensions/lens-renderer-extension.ts b/src/extensions/lens-renderer-extension.ts index abaac22308..4a5abadbad 100644 --- a/src/extensions/lens-renderer-extension.ts +++ b/src/extensions/lens-renderer-extension.ts @@ -77,7 +77,7 @@ export class LensRendererExtension extends LensExtension { } /** - * Add a filtering function for the catalog catogries. This will be removed if the extension is disabled. + * Add a filtering function for the catalog categories. This will be removed if the extension is disabled. * @param fn The function which should return a truthy value for those categories which should be kept. * @returns A function to clean up the filter */ diff --git a/src/extensions/npm/extensions/package.json b/src/extensions/npm/extensions/package.json index 594f26f56e..09db697b35 100644 --- a/src/extensions/npm/extensions/package.json +++ b/src/extensions/npm/extensions/package.json @@ -16,9 +16,9 @@ "name": "OpenLens Authors" }, "dependencies": { - "@types/node": "*", - "@types/react-select": "*", - "@material-ui/core": "*", + "@types/node": "14.17.14", + "@types/react-select": "3.1.2", + "@material-ui/core": "4.12.3", "conf": "^7.0.1", "typed-emitter": "^1.3.1" } diff --git a/src/extensions/registries/base-registry.ts b/src/extensions/registries/base-registry.ts index af00fb48fd..9b596172c9 100644 --- a/src/extensions/registries/base-registry.ts +++ b/src/extensions/registries/base-registry.ts @@ -25,7 +25,7 @@ import { Singleton } from "../../common/utils"; import { LensExtension } from "../lens-extension"; export class BaseRegistry extends Singleton { - private items = observable.map([], {deep: false}); + private items = observable.map([], { deep: false }); constructor() { super(); diff --git a/src/extensions/registries/command-registry.ts b/src/extensions/registries/command-registry.ts index 5c38e32c71..3916b0f721 100644 --- a/src/extensions/registries/command-registry.ts +++ b/src/extensions/registries/command-registry.ts @@ -25,9 +25,9 @@ import { BaseRegistry } from "./base-registry"; import type { LensExtension } from "../lens-extension"; import type { CatalogEntity } from "../../common/catalog"; -export type CommandContext = { +export interface CommandContext { entity?: CatalogEntity; -}; +} export interface CommandRegistration { id: string; diff --git a/src/extensions/renderer-api/catalog.ts b/src/extensions/renderer-api/catalog.ts index 2843bf8737..e329847e85 100644 --- a/src/extensions/renderer-api/catalog.ts +++ b/src/extensions/renderer-api/catalog.ts @@ -22,7 +22,8 @@ import type { CatalogCategory, CatalogEntity } from "../../common/catalog"; import { catalogEntityRegistry as registry } from "../../renderer/api/catalog-entity-registry"; - +import type { CatalogEntityOnBeforeRun } from "../../renderer/api/catalog-entity-registry"; +import type { Disposer } from "../../common/utils"; export { catalogCategoryRegistry as catalogCategories } from "../../common/catalog/catalog-category-registry"; export class CatalogEntityRegistry { @@ -48,6 +49,18 @@ export class CatalogEntityRegistry { getItemsForCategory(category: CatalogCategory): T[] { return registry.getItemsForCategory(category); } + + /** + * Add a onBeforeRun hook to a catalog entities. If `onBeforeRun` was previously + * added then it will not be added again. + * @param onBeforeRun The function to be called with a `CatalogRunEvent` + * event target will be the catalog entity. onBeforeRun hook can call event.preventDefault() + * to stop run sequence + * @returns A function to remove that hook + */ + addOnBeforeRun(onBeforeRun: CatalogEntityOnBeforeRun): Disposer { + return registry.addOnBeforeRun(onBeforeRun); + } } export const catalogEntities = new CatalogEntityRegistry(); diff --git a/src/jest.setup.ts b/src/jest.setup.ts index 29b0465c8b..a234960386 100644 --- a/src/jest.setup.ts +++ b/src/jest.setup.ts @@ -21,10 +21,17 @@ import fetchMock from "jest-fetch-mock"; import configurePackages from "./common/configure-packages"; +import { configure } from "mobx"; // setup default configuration for external npm-packages configurePackages(); +configure({ + // Needed because we want to use jest.spyOn() + // ref https://github.com/mobxjs/mobx/issues/2784 + safeDescriptors: false, +}); + // rewire global.fetch to call 'fetchMock' fetchMock.enableMocks(); diff --git a/src/main/__test__/lens-proxy.test.ts b/src/main/__test__/lens-proxy.test.ts new file mode 100644 index 0000000000..f7767fc685 --- /dev/null +++ b/src/main/__test__/lens-proxy.test.ts @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import { isLongRunningRequest } from "../lens-proxy"; + +describe("isLongRunningRequest", () => { + it("returns true on watches", () => { + ["watch=true", "watch=1", "watch"].forEach((param) => { + expect( + isLongRunningRequest(`/api/v1/namespaces/default/pods?${param}`) + ).toBeTruthy(); + }); + }); + + it("returns false on disabled watches", () => { + ["watch=false", "watch=0", ""].forEach((param) => { + expect( + isLongRunningRequest(`/api/v1/namespaces/default/pods?${param}`) + ).toBeFalsy(); + }); + }); + + it("returns true on follows", () => { + ["follow=true", "follow=1", "follow"].forEach((param) => { + expect( + isLongRunningRequest(`/api/v1/namespaces/default/pods/foo/log?${param}`) + ).toBeTruthy(); + }); + }); + + it("returns false on disabled follows", () => { + ["follow=false", "follow=0", ""].forEach((param) => { + expect( + isLongRunningRequest(`/api/v1/namespaces/default/pods/foo/log?${param}`) + ).toBeFalsy(); + }); + }); +}); diff --git a/src/main/cluster-manager.ts b/src/main/cluster-manager.ts index fdc3bf3e1a..a5c9179499 100644 --- a/src/main/cluster-manager.ts +++ b/src/main/cluster-manager.ts @@ -131,11 +131,11 @@ export class ClusterManager extends Singleton { entity.spec.metrics.prometheus = prometheus; } + // Only set the icon if the preference is set. If the preference is not set + // then let the source determine if a cluster has an icon. if (cluster.preferences.icon) { entity.spec.icon ??= {}; entity.spec.icon.src = cluster.preferences.icon; - } else { - entity.spec.icon = null; } catalogEntityRegistry.items.splice(index, 1, entity); @@ -193,7 +193,10 @@ export class ClusterManager extends Singleton { } else { cluster.kubeConfigPath = entity.spec.kubeconfigPath; cluster.contextName = entity.spec.kubeconfigContext; - cluster.accessibleNamespaces = entity.spec.accessibleNamespaces ?? []; + + if (entity.spec.accessibleNamespace) { + cluster.accessibleNamespaces = entity.spec.accessibleNamespaces; + } this.updateEntityFromCluster(cluster); } diff --git a/src/main/context-handler.ts b/src/main/context-handler.ts index 82c365e821..b6e14df4e5 100644 --- a/src/main/context-handler.ts +++ b/src/main/context-handler.ts @@ -117,10 +117,10 @@ export class ContextHandler { return `http://127.0.0.1:${this.kubeAuthProxy.port}${path}`; } - async getApiTarget(isWatchRequest = false): Promise { - const timeout = isWatchRequest ? 4 * 60 * 60 * 1000 : 30000; // 4 hours for watch request, 30 seconds for the rest + async getApiTarget(isLongRunningRequest = false): Promise { + const timeout = isLongRunningRequest ? 4 * 60 * 60_000 : 30_000; // 4 hours for long running request, 30 seconds for the rest - if (isWatchRequest) { + if (isLongRunningRequest) { return this.newApiTarget(timeout); } diff --git a/src/main/kubectl.ts b/src/main/kubectl.ts index fbfd044dab..4695e8272e 100644 --- a/src/main/kubectl.ts +++ b/src/main/kubectl.ts @@ -275,7 +275,7 @@ export class Kubectl { return false; }); - isValid = !await this.checkBinary(this.path, false); + isValid = await this.checkBinary(this.path, false); } if (!isValid) { diff --git a/src/main/lens-proxy.ts b/src/main/lens-proxy.ts index 3cd5182677..47f4c4756f 100644 --- a/src/main/lens-proxy.ts +++ b/src/main/lens-proxy.ts @@ -31,6 +31,7 @@ import { Singleton } from "../common/utils"; import type { Cluster } from "./cluster"; import type { ProxyApiRequestArgs } from "./proxy-functions"; import { appEventBus } from "../common/event-bus"; +import { getBoolean } from "./utils/parse-query"; type GetClusterForRequest = (req: http.IncomingMessage) => Cluster | null; @@ -40,6 +41,15 @@ export interface LensProxyFunctions { kubeApiRequest: (args: ProxyApiRequestArgs) => void | Promise; } +const watchParam = "watch"; +const followParam = "follow"; + +export function isLongRunningRequest(reqUrl: string) { + const url = new URL(reqUrl, "http://localhost"); + + return getBoolean(url.searchParams, watchParam) || getBoolean(url.searchParams, followParam); +} + export class LensProxy extends Singleton { protected origin: string; protected proxyServer: http.Server; @@ -174,9 +184,8 @@ export class LensProxy extends Singleton { if (req.url.startsWith(apiKubePrefix)) { delete req.headers.authorization; req.url = req.url.replace(apiKubePrefix, ""); - const isWatchRequest = req.url.includes("watch="); - return contextHandler.getApiTarget(isWatchRequest); + return contextHandler.getApiTarget(isLongRunningRequest(req.url)); } } diff --git a/src/main/menu.ts b/src/main/menu.ts index 92782cd3d0..088f06e06e 100644 --- a/src/main/menu.ts +++ b/src/main/menu.ts @@ -19,19 +19,23 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { app, BrowserWindow, dialog, IpcMainEvent, Menu, MenuItem, MenuItemConstructorOptions, webContents, shell } from "electron"; +import { app, BrowserWindow, dialog, Menu, MenuItem, MenuItemConstructorOptions, webContents, shell } from "electron"; import { autorun } from "mobx"; import type { WindowManager } from "./window-manager"; -import { appName, isMac, isWindows, isTestEnv, docsUrl, supportUrl, productName } from "../common/vars"; +import { appName, isMac, isWindows, docsUrl, supportUrl, productName } from "../common/vars"; import { MenuRegistry } from "../extensions/registries/menu-registry"; import logger from "./logger"; import { exitApp } from "./exit-app"; -import { broadcastMessage, ipcMainOn } from "../common/ipc"; +import { broadcastMessage } from "../common/ipc"; import * as packageJson from "../../package.json"; import { preferencesURL, extensionsURL, addClusterURL, catalogURL, welcomeURL } from "../common/routes"; export type MenuTopId = "mac" | "file" | "edit" | "view" | "help"; +interface MenuItemsOpts extends MenuItemConstructorOptions { + submenu?: MenuItemConstructorOptions[]; +} + export function initMenu(windowManager: WindowManager) { return autorun(() => buildMenu(windowManager), { delay: 100 @@ -68,11 +72,13 @@ export function buildMenu(windowManager: WindowManager) { await windowManager.navigate(url); } - const macAppMenu: MenuItemConstructorOptions = { + const macAppMenu: MenuItemsOpts = { label: app.getName(), + id: "root", submenu: [ { label: `About ${productName}`, + id: "about", click(menuItem: MenuItem, browserWindow: BrowserWindow) { showAbout(browserWindow); } @@ -81,6 +87,7 @@ export function buildMenu(windowManager: WindowManager) { { label: "Preferences", accelerator: "CmdOrCtrl+,", + id: "preferences", click() { navigate(preferencesURL()); }, @@ -88,6 +95,7 @@ export function buildMenu(windowManager: WindowManager) { { label: "Extensions", accelerator: "CmdOrCtrl+Shift+E", + id: "extensions", click() { navigate(extensionsURL()); } @@ -102,18 +110,21 @@ export function buildMenu(windowManager: WindowManager) { { label: "Quit", accelerator: "Cmd+Q", + id: "quit", click() { exitApp(); } } ], }; - const fileMenu: MenuItemConstructorOptions = { + const fileMenu: MenuItemsOpts = { label: "File", + id: "file", submenu: [ { label: "Add Cluster", accelerator: "CmdOrCtrl+Shift+A", + id: "add-cluster", click() { navigate(addClusterURL()); } @@ -122,6 +133,7 @@ export function buildMenu(windowManager: WindowManager) { { type: "separator" }, { label: "Preferences", + id: "preferences", accelerator: "Ctrl+,", click() { navigate(preferencesURL()); @@ -145,6 +157,7 @@ export function buildMenu(windowManager: WindowManager) { { label: "Exit", accelerator: "Alt+F4", + id: "quit", click() { exitApp(); } @@ -152,8 +165,9 @@ export function buildMenu(windowManager: WindowManager) { ]) ], }; - const editMenu: MenuItemConstructorOptions = { + const editMenu: MenuItemsOpts = { label: "Edit", + id: "edit", submenu: [ { role: "undo" }, { role: "redo" }, @@ -166,12 +180,14 @@ export function buildMenu(windowManager: WindowManager) { { role: "selectAll" }, ] }; - const viewMenu: MenuItemConstructorOptions = { + const viewMenu: MenuItemsOpts = { label: "View", + id: "view", submenu: [ { label: "Catalog", accelerator: "Shift+CmdOrCtrl+C", + id: "catalog", click() { navigate(catalogURL()); } @@ -179,6 +195,7 @@ export function buildMenu(windowManager: WindowManager) { { label: "Command Palette...", accelerator: "Shift+CmdOrCtrl+P", + id: "command-palette", click() { broadcastMessage("command-palette:open"); } @@ -187,6 +204,7 @@ export function buildMenu(windowManager: WindowManager) { { label: "Back", accelerator: "CmdOrCtrl+[", + id: "go-back", click() { webContents.getAllWebContents().filter(wc => wc.getType() === "window").forEach(wc => wc.goBack()); } @@ -194,6 +212,7 @@ export function buildMenu(windowManager: WindowManager) { { label: "Forward", accelerator: "CmdOrCtrl+]", + id: "go-forward", click() { webContents.getAllWebContents().filter(wc => wc.getType() === "window").forEach(wc => wc.goForward()); } @@ -201,6 +220,7 @@ export function buildMenu(windowManager: WindowManager) { { label: "Reload", accelerator: "CmdOrCtrl+R", + id: "reload", click() { windowManager.reload(); } @@ -214,23 +234,27 @@ export function buildMenu(windowManager: WindowManager) { { role: "togglefullscreen" } ] }; - const helpMenu: MenuItemConstructorOptions = { + const helpMenu: MenuItemsOpts = { role: "help", + id: "help", submenu: [ { label: "Welcome", + id: "welcome", click() { navigate(welcomeURL()); }, }, { label: "Documentation", + id: "documentation", click: async () => { shell.openExternal(docsUrl); }, }, { label: "Support", + id: "support", click: async () => { shell.openExternal(supportUrl); }, @@ -238,6 +262,7 @@ export function buildMenu(windowManager: WindowManager) { ...ignoreOnMac([ { label: `About ${productName}`, + id: "about", click(menuItem: MenuItem, browserWindow: BrowserWindow) { showAbout(browserWindow); } @@ -246,69 +271,28 @@ export function buildMenu(windowManager: WindowManager) { ] }; // Prepare menu items order - const appMenu: Record = { - mac: macAppMenu, - file: fileMenu, - edit: editMenu, - view: viewMenu, - help: helpMenu, - }; + const appMenu = new Map([ + ["mac", macAppMenu], + ["file", fileMenu], + ["edit", editMenu], + ["view", viewMenu], + ["help", helpMenu], + ]); // Modify menu from extensions-api - MenuRegistry.getInstance().getItems().forEach(({ parentId, ...menuItem }) => { - try { - const topMenu = appMenu[parentId as MenuTopId].submenu as MenuItemConstructorOptions[]; + for (const { parentId, ...menuItem } of MenuRegistry.getInstance().getItems()) { + if (!appMenu.has(parentId)) { + logger.error(`[MENU]: cannot register menu item for parentId=${parentId}, parent item doesn't exist`, { menuItem }); - topMenu.push(menuItem); - } catch (err) { - logger.error(`[MENU]: can't register menu item, parentId=${parentId}`, { menuItem }); + continue; } - }); + + appMenu.get(parentId).submenu.push(menuItem); + } if (!isMac) { - delete appMenu.mac; + appMenu.delete("mac"); } - const menu = Menu.buildFromTemplate(Object.values(appMenu)); - - Menu.setApplicationMenu(menu); - - if (isTestEnv) { - // this is a workaround for the test environment (spectron) not being able to directly access - // the application menus (https://github.com/electron-userland/spectron/issues/21) - ipcMainOn("test-menu-item-click", (event: IpcMainEvent, ...names: string[]) => { - let menu: Menu = Menu.getApplicationMenu(); - const parentLabels: string[] = []; - let menuItem: MenuItem; - - for (const name of names) { - parentLabels.push(name); - menuItem = menu?.items?.find(item => item.label === name); - - if (!menuItem) { - break; - } - menu = menuItem.submenu; - } - - const menuPath: string = parentLabels.join(" -> "); - - if (!menuItem) { - logger.info(`[MENU:test-menu-item-click] Cannot find menu item ${menuPath}`); - - return; - } - - const { enabled, visible, click } = menuItem; - - if (enabled === false || visible === false || typeof click !== "function") { - logger.info(`[MENU:test-menu-item-click] Menu item ${menuPath} not clickable`); - - return; - } - - logger.info(`[MENU:test-menu-item-click] Menu item ${menuPath} click!`); - menuItem.click(); - }); - } + Menu.setApplicationMenu(Menu.buildFromTemplate([...appMenu.values()])); } diff --git a/src/main/prometheus/operator.ts b/src/main/prometheus/operator.ts index f037ef8a53..6fb2cb5cc6 100644 --- a/src/main/prometheus/operator.ts +++ b/src/main/prometheus/operator.ts @@ -38,9 +38,9 @@ export class PrometheusOperator extends PrometheusProvider { case "cluster": switch (queryName) { case "memoryUsage": - return `sum(node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes))`.replace(/_bytes/g, `_bytes * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"}`); + return `sum(node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes))`.replace(/_bytes/g, `_bytes{node=~"${opts.nodes}"}`); case "workloadMemoryUsage": - return `sum(container_memory_working_set_bytes{container!="POD",container!="",instance=~"${opts.nodes}"}) by (component)`; + return `sum(container_memory_working_set_bytes{container!="", instance=~"${opts.nodes}"}) by (component)`; case "memoryRequests": return `sum(kube_pod_container_resource_requests{node=~"${opts.nodes}", resource="memory"})`; case "memoryLimits": @@ -50,7 +50,7 @@ export class PrometheusOperator extends PrometheusProvider { case "memoryAllocatableCapacity": return `sum(kube_node_status_allocatable{node=~"${opts.nodes}", resource="memory"})`; case "cpuUsage": - return `sum(rate(node_cpu_seconds_total{mode=~"user|system"}[${this.rateAccuracy}])* on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"})`; + return `sum(rate(node_cpu_seconds_total{node=~"${opts.nodes}", mode=~"user|system"}[${this.rateAccuracy}]))`; case "cpuRequests": return `sum(kube_pod_container_resource_requests{node=~"${opts.nodes}", resource="cpu"})`; case "cpuLimits": @@ -66,61 +66,61 @@ export class PrometheusOperator extends PrometheusProvider { case "podAllocatableCapacity": return `sum(kube_node_status_allocatable{node=~"${opts.nodes}", resource="pods"})`; case "fsSize": - return `sum(node_filesystem_size_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"})`; + return `sum(node_filesystem_size_bytes{node=~"${opts.nodes}", mountpoint="/"}) by (node)`; case "fsUsage": - return `sum(node_filesystem_size_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"} - node_filesystem_avail_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"})`; + return `sum(node_filesystem_size_bytes{node=~"${opts.nodes}", mountpoint="/"} - node_filesystem_avail_bytes{node=~"${opts.nodes}", mountpoint="/"}) by (node)`; } break; case "nodes": switch (queryName) { case "memoryUsage": - return `sum((node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)) * on (pod,namespace) group_left(node) kube_pod_info) by (node)`; + return `sum (node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)) by (node)`; case "workloadMemoryUsage": - return `sum(container_memory_working_set_bytes{container!="POD",container!=""}) by (node)`; + return `sum(container_memory_working_set_bytes{container!=""}) by (node)`; case "memoryCapacity": return `sum(kube_node_status_capacity{resource="memory"}) by (node)`; case "memoryAllocatableCapacity": return `sum(kube_node_status_allocatable{resource="memory"}) by (node)`; case "cpuUsage": - return `sum(rate(node_cpu_seconds_total{mode=~"user|system"}[${this.rateAccuracy}]) * on (pod,namespace) group_left(node) kube_pod_info) by (node)`; + return `sum(rate(node_cpu_seconds_total{mode=~"user|system"}[${this.rateAccuracy}])) by(node)`; case "cpuCapacity": return `sum(kube_node_status_allocatable{resource="cpu"}) by (node)`; case "cpuAllocatableCapacity": return `sum(kube_node_status_allocatable{resource="cpu"}) by (node)`; case "fsSize": - return `sum(node_filesystem_size_bytes{mountpoint="/"} * on (pod,namespace) group_left(node) kube_pod_info) by (node)`; + return `sum(node_filesystem_size_bytes{mountpoint="/"}) by (node)`; case "fsUsage": - return `sum((node_filesystem_size_bytes{mountpoint="/"} - node_filesystem_avail_bytes{mountpoint="/"}) * on (pod,namespace) group_left(node) kube_pod_info) by (node)`; + return `sum(node_filesystem_size_bytes{mountpoint="/"} - node_filesystem_avail_bytes{mountpoint="/"}) by (node)`; } break; case "pods": switch (queryName) { case "cpuUsage": - return `sum(rate(container_cpu_usage_seconds_total{container!="POD",container!="",image!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`; + return `sum(rate(container_cpu_usage_seconds_total{container!="", image!="", pod=~"${opts.pods}", namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`; case "cpuRequests": - return `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}",resource="cpu",namespace="${opts.namespace}"}) by (${opts.selector})`; + return `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}", resource="cpu", namespace="${opts.namespace}"}) by (${opts.selector})`; case "cpuLimits": - return `sum(kube_pod_container_resource_limits{pod=~"${opts.pods}",resource="cpu",namespace="${opts.namespace}"}) by (${opts.selector})`; + return `sum(kube_pod_container_resource_limits{pod=~"${opts.pods}", resource="cpu", namespace="${opts.namespace}"}) by (${opts.selector})`; case "memoryUsage": - return `sum(container_memory_working_set_bytes{container!="POD",container!="",image!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}) by (${opts.selector})`; + return `sum(container_memory_working_set_bytes{container!="", image!="", pod=~"${opts.pods}", namespace="${opts.namespace}"}) by (${opts.selector})`; case "memoryRequests": - return `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}",resource="memory",namespace="${opts.namespace}"}) by (${opts.selector})`; + return `sum(kube_pod_container_resource_requests{pod=~"${opts.pods}", resource="memory", namespace="${opts.namespace}"}) by (${opts.selector})`; case "memoryLimits": - return `sum(kube_pod_container_resource_limits{pod=~"${opts.pods}",resource="memory",namespace="${opts.namespace}"}) by (${opts.selector})`; + return `sum(kube_pod_container_resource_limits{pod=~"${opts.pods}", resource="memory", namespace="${opts.namespace}"}) by (${opts.selector})`; case "fsUsage": - return `sum(container_fs_usage_bytes{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}) by (${opts.selector})`; + return `sum(container_fs_usage_bytes{container!="", pod=~"${opts.pods}", namespace="${opts.namespace}"}) by (${opts.selector})`; case "networkReceive": - return `sum(rate(container_network_receive_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`; + return `sum(rate(container_network_receive_bytes_total{pod=~"${opts.pods}", namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`; case "networkTransmit": - return `sum(rate(container_network_transmit_bytes_total{pod=~"${opts.pods}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`; + return `sum(rate(container_network_transmit_bytes_total{pod=~"${opts.pods}", namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (${opts.selector})`; } break; case "pvc": switch (queryName) { case "diskUsage": - return `sum(kubelet_volume_stats_used_bytes{persistentvolumeclaim="${opts.pvc}",namespace="${opts.namespace}"}) by (persistentvolumeclaim, namespace)`; + return `sum(kubelet_volume_stats_used_bytes{persistentvolumeclaim="${opts.pvc}", namespace="${opts.namespace}"}) by (persistentvolumeclaim, namespace)`; case "diskCapacity": - return `sum(kubelet_volume_stats_capacity_bytes{persistentvolumeclaim="${opts.pvc}",namespace="${opts.namespace}"}) by (persistentvolumeclaim, namespace)`; + return `sum(kubelet_volume_stats_capacity_bytes{persistentvolumeclaim="${opts.pvc}", namespace="${opts.namespace}"}) by (persistentvolumeclaim, namespace)`; } break; case "ingress": @@ -130,9 +130,9 @@ export class PrometheusOperator extends PrometheusProvider { case "bytesSentFailure": return this.bytesSent(opts.ingress, opts.namespace, "^5\\\\d*"); case "requestDurationSeconds": - return `sum(rate(nginx_ingress_controller_request_duration_seconds_sum{ingress="${opts.ingress}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (ingress, namespace)`; + return `sum(rate(nginx_ingress_controller_request_duration_seconds_sum{ingress="${opts.ingress}", namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (ingress, namespace)`; case "responseDurationSeconds": - return `sum(rate(nginx_ingress_controller_response_duration_seconds_sum{ingress="${opts.ingress}",namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (ingress, namespace)`; + return `sum(rate(nginx_ingress_controller_response_duration_seconds_sum{ingress="${opts.ingress}", namespace="${opts.namespace}"}[${this.rateAccuracy}])) by (ingress, namespace)`; } break; } diff --git a/src/main/protocol-handler/__test__/router.test.ts b/src/main/protocol-handler/__test__/router.test.ts index 1216403838..2045266ba3 100644 --- a/src/main/protocol-handler/__test__/router.test.ts +++ b/src/main/protocol-handler/__test__/router.test.ts @@ -126,7 +126,7 @@ describe("protocol router tests", () => { } await delay(50); - expect(broadcastMessage).toHaveBeenCalledWith(ProtocolHandlerInternal, "lens://app/", "matched"); + expect(broadcastMessage).toHaveBeenCalledWith(ProtocolHandlerInternal, "lens://app", "matched"); expect(broadcastMessage).toHaveBeenCalledWith(ProtocolHandlerExtension, "lens://extension/@mirantis/minikube", "matched"); }); diff --git a/src/main/resource-applier.ts b/src/main/resource-applier.ts index f4772c5275..a305ff1091 100644 --- a/src/main/resource-applier.ts +++ b/src/main/resource-applier.ts @@ -22,55 +22,96 @@ import type { Cluster } from "./cluster"; import type { KubernetesObject } from "@kubernetes/client-node"; import { exec } from "child_process"; -import fs from "fs"; +import fs from "fs-extra"; import * as yaml from "js-yaml"; import path from "path"; import * as tempy from "tempy"; import logger from "./logger"; import { appEventBus } from "../common/event-bus"; import { cloneJsonObject } from "../common/utils"; +import type { Patch } from "rfc6902"; +import { promiseExecFile } from "./promise-exec"; export class ResourceApplier { - constructor(protected cluster: Cluster) { + constructor(protected cluster: Cluster) {} + + /** + * Patch a kube resource's manifest, throwing any error that occurs. + * @param name The name of the kube resource + * @param kind The kind of the kube resource + * @param patch The list of JSON operations + * @param ns The optional namespace of the kube resource + */ + async patch(name: string, kind: string, patch: Patch, ns?: string): Promise { + appEventBus.emit({ name: "resource", action: "patch" }); + + const kubectl = await this.cluster.ensureKubectl(); + const kubectlPath = await kubectl.getPath(); + const proxyKubeconfigPath = await this.cluster.getProxyKubeconfigPath(); + const args = [ + "--kubeconfig", proxyKubeconfigPath, + "patch", + kind, + name, + ]; + + if (ns) { + args.push("--namespace", ns); + } + + args.push( + "--type", "json", + "--patch", JSON.stringify(patch), + "-o", "json" + ); + + try { + const { stdout } = await promiseExecFile(kubectlPath, args); + + return stdout; + } catch (error) { + throw error.stderr ?? error; + } } async apply(resource: KubernetesObject | any): Promise { resource = this.sanitizeObject(resource); appEventBus.emit({ name: "resource", action: "apply" }); - return await this.kubectlApply(yaml.safeDump(resource)); + return this.kubectlApply(yaml.safeDump(resource)); } protected async kubectlApply(content: string): Promise { const kubectl = await this.cluster.ensureKubectl(); const kubectlPath = await kubectl.getPath(); const proxyKubeconfigPath = await this.cluster.getProxyKubeconfigPath(); + const fileName = tempy.file({ name: "resource.yaml" }); + const args = [ + "apply", + "--kubeconfig", proxyKubeconfigPath, + "-o", "json", + "-f", fileName, + ]; - return new Promise((resolve, reject) => { - const fileName = tempy.file({ name: "resource.yaml" }); + logger.debug(`shooting manifests with ${kubectlPath}`, { args }); - fs.writeFileSync(fileName, content); - const cmd = `"${kubectlPath}" apply --kubeconfig "${proxyKubeconfigPath}" -o json -f "${fileName}"`; + const execEnv = { ...process.env }; + const httpsProxy = this.cluster.preferences?.httpsProxy; - logger.debug(`shooting manifests with: ${cmd}`); - const execEnv: NodeJS.ProcessEnv = Object.assign({}, process.env); - const httpsProxy = this.cluster.preferences?.httpsProxy; + if (httpsProxy) { + execEnv.HTTPS_PROXY = httpsProxy; + } - if (httpsProxy) { - execEnv["HTTPS_PROXY"] = httpsProxy; - } - exec(cmd, { env: execEnv }, - (error, stdout, stderr) => { - if (stderr != "") { - fs.unlinkSync(fileName); - reject(stderr); + try { + await fs.writeFile(fileName, content); + const { stdout } = await promiseExecFile(kubectlPath, args); - return; - } - fs.unlinkSync(fileName); - resolve(JSON.parse(stdout)); - }); - }); + return stdout; + } catch (error) { + throw error?.stderr ?? error; + } finally { + await fs.unlink(fileName); + } } public async kubectlApplyAll(resources: string[], extraArgs = ["-o", "json"]): Promise { diff --git a/src/main/router.ts b/src/main/router.ts index 50175aa9de..9a7ded8a37 100644 --- a/src/main/router.ts +++ b/src/main/router.ts @@ -179,8 +179,11 @@ export class Router { this.router.add({ method: "post", path: `${apiPrefix}/metrics` }, MetricsRoute.routeMetrics); this.router.add({ method: "get", path: `${apiPrefix}/metrics/providers` }, MetricsRoute.routeMetricsProviders); - // Port-forward API - this.router.add({ method: "post", path: `${apiPrefix}/pods/{namespace}/{resourceType}/{resourceName}/port-forward/{port}` }, PortForwardRoute.routePortForward); + // Port-forward API (the container port and local forwarding port are obtained from the query parameters) + this.router.add({ method: "post", path: `${apiPrefix}/pods/port-forward/{namespace}/{resourceType}/{resourceName}` }, PortForwardRoute.routePortForward); + this.router.add({ method: "get", path: `${apiPrefix}/pods/port-forward/{namespace}/{resourceType}/{resourceName}` }, PortForwardRoute.routeCurrentPortForward); + this.router.add({ method: "get", path: `${apiPrefix}/pods/port-forwards` }, PortForwardRoute.routeAllPortForwards); + this.router.add({ method: "delete", path: `${apiPrefix}/pods/port-forward/{namespace}/{resourceType}/{resourceName}` }, PortForwardRoute.routeCurrentPortForwardStop); // Helm API this.router.add({ method: "get", path: `${apiPrefix}/v2/charts` }, HelmApiRoute.listCharts); @@ -198,5 +201,6 @@ export class Router { // Resource Applier API this.router.add({ method: "post", path: `${apiPrefix}/stack` }, ResourceApplierApiRoute.applyResource); + this.router.add({ method: "patch", path: `${apiPrefix}/stack` }, ResourceApplierApiRoute.patchResource); } } diff --git a/src/main/routes/helm-route.ts b/src/main/routes/helm-route.ts index 5e104f6a11..2f217a3193 100644 --- a/src/main/routes/helm-route.ts +++ b/src/main/routes/helm-route.ts @@ -23,7 +23,7 @@ import type { LensApiRequest } from "../router"; import { helmService } from "../helm/helm-service"; import logger from "../logger"; import { respondJson, respondText } from "../utils/http-responses"; -import { getBoolean } from "./utils/parse-query"; +import { getBoolean } from "../utils/parse-query"; export class HelmApiRoute { static async listCharts(request: LensApiRequest) { diff --git a/src/main/routes/port-forward-route.ts b/src/main/routes/port-forward-route.ts index e97b4171cb..763ea16b43 100644 --- a/src/main/routes/port-forward-route.ts +++ b/src/main/routes/port-forward-route.ts @@ -22,7 +22,6 @@ import type { LensApiRequest } from "../router"; import { spawn, ChildProcessWithoutNullStreams } from "child_process"; import { Kubectl } from "../kubectl"; -import { shell } from "electron"; import * as tcpPortUsed from "tcp-port-used"; import logger from "../logger"; import { getPortFrom } from "../utils/get-port"; @@ -33,7 +32,8 @@ interface PortForwardArgs { kind: string; namespace: string; name: string; - port: string; + port: number; + forwardPort: number; } const internalPortRegex = /^forwarding from (?
.+) ->/i; @@ -56,8 +56,8 @@ class PortForward { public kind: string; public namespace: string; public name: string; - public port: string; - public internalPort?: number; + public port: number; + public forwardPort: number; constructor(public kubeConfig: string, args: PortForwardArgs) { this.clusterId = args.clusterId; @@ -65,16 +65,17 @@ class PortForward { this.namespace = args.namespace; this.name = args.name; this.port = args.port; + this.forwardPort = args.forwardPort; } public async start() { - const kubectlBin = await Kubectl.bundled().getPath(); + const kubectlBin = await Kubectl.bundled().getPath(true); const args = [ "--kubeconfig", this.kubeConfig, "port-forward", "-n", this.namespace, `${this.kind}/${this.name}`, - `:${this.port}` + `${this.forwardPort ?? ""}:${this.port}` ]; this.process = spawn(kubectlBin, args, { @@ -89,12 +90,15 @@ class PortForward { } }); - this.internalPort = await getPortFrom(this.process.stdout, { + const internalPort = await getPortFrom(this.process.stdout, { lineRegex: internalPortRegex, }); try { - await tcpPortUsed.waitUntilUsed(this.internalPort, 500, 15000); + await tcpPortUsed.waitUntilUsed(internalPort, 500, 15000); + + // make sure this.forwardPort is set to the actual port used (if it was 0 then an available port is found by 'kubectl port-forward') + this.forwardPort = internalPort; return true; } catch (error) { @@ -104,58 +108,113 @@ class PortForward { } } - public open() { - shell.openExternal(`http://localhost:${this.internalPort}`) - .catch(error => logger.error(`[PORT-FORWARD]: failed to open external shell: ${error}`, { - clusterId: this.clusterId, - port: this.port, - kind: this.kind, - namespace: this.namespace, - name: this.name, - })); + public async stop() { + this.process.kill(); } } export class PortForwardRoute { static async routePortForward(request: LensApiRequest) { - const { params, response, cluster} = request; - const { namespace, port, resourceType, resourceName } = params; - let portForward = PortForward.getPortforward({ - clusterId: cluster.id, kind: resourceType, name: resourceName, - namespace, port - }); + const { params, query, response, cluster } = request; + const { namespace, resourceType, resourceName } = params; + const port = Number(query.get("port")); + const forwardPort = Number(query.get("forwardPort")); - if (!portForward) { - logger.info(`Creating a new port-forward ${namespace}/${resourceType}/${resourceName}:${port}`); - portForward = new PortForward(await cluster.getProxyKubeconfigPath(), { - clusterId: cluster.id, - kind: resourceType, - namespace, - name: resourceName, - port, + try { + let portForward = PortForward.getPortforward({ + clusterId: cluster.id, kind: resourceType, name: resourceName, + namespace, port, forwardPort, }); - try { + let thePort = 0; + + if (forwardPort > 0 && forwardPort < 65536) { + thePort = forwardPort; + } + + if (!portForward) { + logger.info(`Creating a new port-forward ${namespace}/${resourceType}/${resourceName}:${port}`); + portForward = new PortForward(await cluster.getProxyKubeconfigPath(), { + clusterId: cluster.id, + kind: resourceType, + namespace, + name: resourceName, + port, + forwardPort: thePort, + }); + const started = await portForward.start(); if (!started) { - logger.warn("[PORT-FORWARD-ROUTE]: failed to start a port-forward", { namespace, port, resourceType, resourceName }); + logger.error("[PORT-FORWARD-ROUTE]: failed to start a port-forward", { namespace, port, resourceType, resourceName }); return respondJson(response, { - message: "Failed to open port-forward" + message: `Failed to forward port ${port} to ${thePort ? forwardPort : "random port"}` }, 400); } - } catch (error) { - logger.warn(`[PORT-FORWARD-ROUTE]: failed to open a port-forward: ${error}`, { namespace, port, resourceType, resourceName }); - - return respondJson(response, { - message: error?.toString() || "Failed to open port-forward", - }, 400); } + + respondJson(response, { port: portForward.forwardPort }); + } catch (error) { + logger.error(`[PORT-FORWARD-ROUTE]: failed to open a port-forward: ${error}`, { namespace, port, resourceType, resourceName }); + + return respondJson(response, { + message: `Failed to forward port ${port}` + }, 400); } + } - portForward.open(); + static async routeCurrentPortForward(request: LensApiRequest) { + const { params, query, response, cluster } = request; + const { namespace, resourceType, resourceName } = params; + const port = Number(query.get("port")); + const forwardPort = Number(query.get("forwardPort")); - respondJson(response, {}); + const portForward = PortForward.getPortforward({ + clusterId: cluster.id, kind: resourceType, name: resourceName, + namespace, port, forwardPort + }); + + respondJson(response, { port: portForward?.forwardPort ?? null }); + } + + static async routeAllPortForwards(request: LensApiRequest) { + const { response } = request; + + const portForwards: PortForwardArgs[] = PortForward.portForwards.map(f => ( + { + clusterId: f.clusterId, + kind: f.kind, + namespace: f.namespace, + name: f.name, + port: f.port, + forwardPort: f.forwardPort, + }) + ); + + respondJson(response, { portForwards }); + } + + static async routeCurrentPortForwardStop(request: LensApiRequest) { + const { params, query, response, cluster } = request; + const { namespace, resourceType, resourceName } = params; + const port = Number(query.get("port")); + const forwardPort = Number(query.get("forwardPort")); + + const portForward = PortForward.getPortforward({ + clusterId: cluster.id, kind: resourceType, name: resourceName, + namespace, port, forwardPort, + }); + + try { + await portForward.stop(); + respondJson(response, { status: true }); + } catch (error) { + logger.error("[PORT-FORWARD-ROUTE]: error stopping a port-forward", { namespace, port, forwardPort, resourceType, resourceName }); + + return respondJson(response, { + message: `error stopping a forward port ${port}` + }, 400); + } } } diff --git a/src/main/routes/resource-applier-route.ts b/src/main/routes/resource-applier-route.ts index cb716cf1f5..0adf916188 100644 --- a/src/main/routes/resource-applier-route.ts +++ b/src/main/routes/resource-applier-route.ts @@ -30,7 +30,19 @@ export class ResourceApplierApiRoute { try { const resource = await new ResourceApplier(cluster).apply(payload); - respondJson(response, [resource], 200); + respondJson(response, resource, 200); + } catch (error) { + respondText(response, error, 422); + } + } + + static async patchResource(request: LensApiRequest) { + const { response, cluster, payload } = request; + + try { + const resource = await new ResourceApplier(cluster).patch(payload.name, payload.kind, payload.patch, payload.ns); + + respondJson(response, resource, 200); } catch (error) { respondText(response, error, 422); } diff --git a/src/main/utils/http-responses.ts b/src/main/utils/http-responses.ts index 36287806f1..5d57b6dd76 100644 --- a/src/main/utils/http-responses.ts +++ b/src/main/utils/http-responses.ts @@ -21,14 +21,37 @@ import type http from "http"; -export function respondJson(res: http.ServerResponse, content: any, status = 200) { - respond(res, JSON.stringify(content), "application/json", status); +/** + * Respond to a HTTP request with a body of JSON data + * @param res The HTTP response to write data to + * @param content The data or its JSON stringified version of it + * @param status [200] The status code to respond with + */ +export function respondJson(res: http.ServerResponse, content: Object | string, status = 200) { + const normalizedContent = typeof content === "object" + ? JSON.stringify(content) + : content; + + respond(res, normalizedContent, "application/json", status); } +/** + * Respond to a HTTP request with a body of plain text data + * @param res The HTTP response to write data to + * @param content The string data to respond with + * @param status [200] The status code to respond with + */ export function respondText(res: http.ServerResponse, content: string, status = 200) { respond(res, content, "text/plain", status); } +/** + * Respond to a HTTP request with a body of plain text data + * @param res The HTTP response to write data to + * @param content The string data to respond with + * @param contentType The HTTP Content-Type header value + * @param status [200] The status code to respond with + */ export function respond(res: http.ServerResponse, content: string, contentType: string, status = 200) { res.setHeader("Content-Type", contentType); res.statusCode = status; diff --git a/src/main/routes/utils/parse-query.ts b/src/main/utils/parse-query.ts similarity index 100% rename from src/main/routes/utils/parse-query.ts rename to src/main/utils/parse-query.ts diff --git a/src/main/window-manager.ts b/src/main/window-manager.ts index be6d71498e..f751d79790 100644 --- a/src/main/window-manager.ts +++ b/src/main/window-manager.ts @@ -31,7 +31,6 @@ import { IpcRendererNavigationEvents } from "../renderer/navigation/events"; import logger from "./logger"; import { productName } from "../common/vars"; import { LensProxy } from "./lens-proxy"; -import * as path from "path"; function isHideable(window: BrowserWindow | null): boolean { return Boolean(window && !window.isDestroyed()); @@ -85,7 +84,6 @@ export class WindowManager extends Singleton { titleBarStyle: "hiddenInset", backgroundColor: "#1e2124", webPreferences: { - preload: path.join(__static, "build", "preload.js"), nodeIntegration: true, nodeIntegrationInSubFrames: true, enableRemoteModule: true, @@ -111,10 +109,6 @@ export class WindowManager extends Singleton { app.dock?.hide(); // hide icon in dock (mac-os) }) .webContents - .on("new-window", (event, url) => { - event.preventDefault(); - shell.openExternal(url); - }) .on("dom-ready", () => { appEventBus.emit({ name: "app", action: "dom-ready" }); }) @@ -152,6 +146,10 @@ export class WindowManager extends Singleton { // Always disable Node.js integration for all webviews webPreferences.nodeIntegration = false; + }).setWindowOpenHandler((details) => { + shell.openExternal(details.url); + + return { action: "deny" }; }); } diff --git a/src/migrations/user-store/file-name-migration.ts b/src/migrations/user-store/file-name-migration.ts index 10d7a7c99b..932e542642 100644 --- a/src/migrations/user-store/file-name-migration.ts +++ b/src/migrations/user-store/file-name-migration.ts @@ -19,12 +19,12 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +import { app } from "electron"; import fse from "fs-extra"; import path from "path"; -import { getPath } from "../../common/utils/getPath"; export function fileNameMigration() { - const userDataPath = getPath("userData"); + const userDataPath = app.getPath("userData"); const configJsonPath = path.join(userDataPath, "config.json"); const lensUserStoreJsonPath = path.join(userDataPath, "lens-user-store.json"); diff --git a/src/renderer/api/catalog-entity-registry.ts b/src/renderer/api/catalog-entity-registry.ts index 7b09ff273b..1b7e79e67c 100644 --- a/src/renderer/api/catalog-entity-registry.ts +++ b/src/renderer/api/catalog-entity-registry.ts @@ -27,15 +27,22 @@ import type { Cluster } from "../../main/cluster"; import { ClusterStore } from "../../common/cluster-store"; import { Disposer, iter } from "../utils"; import { once } from "lodash"; +import logger from "../../common/logger"; +import { catalogEntityRunContext } from "./catalog-entity"; +import { CatalogRunEvent } from "../../common/catalog/catalog-run-event"; export type EntityFilter = (entity: CatalogEntity) => any; +export type CatalogEntityOnBeforeRun = (event: CatalogRunEvent) => void | Promise; export class CatalogEntityRegistry { - @observable.ref activeEntity: CatalogEntity; + @observable protected activeEntityId: string | undefined = undefined; protected _entities = observable.map([], { deep: true }); protected filters = observable.set([], { deep: false, }); + protected onBeforeRunHooks = observable.set([], { + deep: false, + }); /** * Buffer for keeping entities that don't yet have CatalogCategory synced @@ -46,6 +53,22 @@ export class CatalogEntityRegistry { makeObservable(this); } + get activeEntity(): CatalogEntity | null { + return this._entities.get(this.activeEntityId) || null; + } + + set activeEntity(raw: CatalogEntity | string | null) { + if (raw) { + const id = typeof raw === "string" + ? raw + : raw.getId(); + + this.activeEntityId = id; + } else { + this.activeEntityId = undefined; + } + } + init() { ipcRendererOn("catalog:items", (event, items: (CatalogEntityData & CatalogEntityKindData)[]) => { this.updateItems(items); @@ -153,6 +176,60 @@ export class CatalogEntityRegistry { return once(() => void this.filters.delete(fn)); } + + /** + * Add a onBeforeRun hook. If `onBeforeRun` was previously added then it will not be added again + * @param onBeforeRun The function that should return a boolean if the onRun of catalog entity should be triggered. + * @returns A function to remove that hook + */ + addOnBeforeRun(onBeforeRun: CatalogEntityOnBeforeRun): Disposer { + logger.debug(`[CATALOG-ENTITY-REGISTRY]: adding onBeforeRun hook`); + + this.onBeforeRunHooks.add(onBeforeRun); + + return once(() => void this.onBeforeRunHooks.delete(onBeforeRun)); + } + + /** + * Runs all the registered `onBeforeRun` hooks, short circuiting on the first event that's preventDefaulted + * @param entity The entity to run the hooks on + * @returns Whether the entities `onRun` method should be executed + */ + async onBeforeRun(entity: CatalogEntity): Promise { + logger.debug(`[CATALOG-ENTITY-REGISTRY]: run onBeforeRun on ${entity.getId()}`); + + const runEvent = new CatalogRunEvent({ target: entity }); + + for (const onBeforeRun of this.onBeforeRunHooks) { + try {  + await onBeforeRun(runEvent); + } catch (error) { + logger.warn(`[CATALOG-ENTITY-REGISTRY]: entity ${entity.getId()} onBeforeRun threw an error`, error); + } + + if (runEvent.defaultPrevented) { + return false; + } + } + + return true; + } + + /** + * Perform the onBeforeRun check and, if successful, then proceed to call `entity`'s onRun method + * @param entity The instance to invoke the hooks and then execute the onRun + */ + onRun(entity: CatalogEntity): void { + this.onBeforeRun(entity) + .then(doOnRun => { + if (doOnRun) { + return entity.onRun?.(catalogEntityRunContext); + } else { + logger.debug(`onBeforeRun for ${entity.getId()} returned false`); + } + }) + .catch(error => logger.error(`[CATALOG-ENTITY-REGISTRY]: entity ${entity.getId()} onRun threw an error`, error)); + } } export const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry); diff --git a/src/renderer/api/terminal-api.ts b/src/renderer/api/terminal-api.ts index 347dfac224..59e248fba5 100644 --- a/src/renderer/api/terminal-api.ts +++ b/src/renderer/api/terminal-api.ts @@ -58,7 +58,6 @@ export class TerminalApi extends WebSocketApi { public onReady = new EventEmitter<[]>(); @observable public isReady = false; - @observable public shellRunCommandsFinished = false; public readonly url: string; constructor(protected options: TerminalApiQuery) { @@ -92,7 +91,6 @@ export class TerminalApi extends WebSocketApi { connect() { this.emitStatus("Connecting ..."); this.onData.addListener(this._onReady, { prepend: true }); - this.onData.addListener(this._onShellRunCommandsFinished); super.connect(this.url); } @@ -109,24 +107,6 @@ export class TerminalApi extends WebSocketApi { this.onReady.removeAllListeners(); } - _onShellRunCommandsFinished = (data: string) => { - if (!data) { - return; - } - - /** - * This is a heuistic for ditermining when a shell has finished executing - * its own rc file (or RunCommands file) such as `.bashrc` or `.zshrc`. - * - * This heuistic assumes that the prompt line of a terminal is a single line - * and ends with a whitespace character. - */ - if (data.match(/\r?\n/) === null && data.match(/\s$/)) { - this.shellRunCommandsFinished = true; - this.onData.removeListener(this._onShellRunCommandsFinished); - } - }; - @boundMethod protected _onReady(data: string) { if (!data) return true; diff --git a/src/renderer/bootstrap.tsx b/src/renderer/bootstrap.tsx index b6b97cfc6b..d381f560e9 100644 --- a/src/renderer/bootstrap.tsx +++ b/src/renderer/bootstrap.tsx @@ -29,7 +29,7 @@ import * as ReactRouterDom from "react-router-dom"; import * as LensExtensionsCommonApi from "../extensions/common-api"; import * as LensExtensionsRendererApi from "../extensions/renderer-api"; import { monaco } from "react-monaco-editor"; -import { render, unmountComponentAtNode } from "react-dom"; +import { render } from "react-dom"; import { delay } from "../common/utils"; import { isMac, isDevelopment } from "../common/vars"; import { ClusterStore } from "../common/cluster-store"; @@ -66,7 +66,7 @@ async function attachChromeDebugger() { } type AppComponent = React.ComponentType & { - init?(): Promise; + init?(rootElem: HTMLElement): Promise; }; export async function bootstrap(App: AppComponent) { @@ -125,16 +125,8 @@ export async function bootstrap(App: AppComponent) { // init app's dependencies if any if (App.init) { - await App.init(); + await App.init(rootElem); } - window.addEventListener("message", (ev: MessageEvent) => { - if (ev.data === "teardown") { - UserStore.getInstance(false)?.unregisterIpcListener(); - ClusterStore.getInstance(false)?.unregisterIpcListener(); - unmountComponentAtNode(rootElem); - window.location.href = "about:blank"; - } - }); render(<> {isMac &&
} {DefaultProps(App)} diff --git a/src/renderer/components/+add-cluster/add-cluster.tsx b/src/renderer/components/+add-cluster/add-cluster.tsx index e987c1d4bb..81a75019fa 100644 --- a/src/renderer/components/+add-cluster/add-cluster.tsx +++ b/src/renderer/components/+add-cluster/add-cluster.tsx @@ -62,7 +62,7 @@ export class AddCluster extends React.Component { @observable kubeContexts = observable.map(); @observable customConfig = ""; @observable isWaiting = false; - @observable errorText: string; + @observable errors: string[] = []; constructor(props: {}) { super(props); @@ -75,7 +75,7 @@ export class AddCluster extends React.Component { @computed get allErrors(): string[] { return [ - this.errorText, + ...this.errors, ...iter.map(this.kubeContexts.values(), ({ error }) => error) ].filter(Boolean); } @@ -85,7 +85,14 @@ export class AddCluster extends React.Component { const { config, error } = loadConfigFromString(this.customConfig.trim() || "{}"); this.kubeContexts.replace(getContexts(config)); - this.errorText = error?.toString(); + + if (error) { + this.errors.push(error.toString()); + } + + if (config.contexts.length === 0) { + this.errors.push('No contexts defined, either missing the "contexts" field, or it is empty.'); + } }, 500); @action @@ -124,7 +131,7 @@ export class AddCluster extends React.Component { value={this.customConfig} onChange={value => { this.customConfig = value; - this.errorText = ""; + this.errors.length = 0; this.refreshContexts(); }} /> diff --git a/src/renderer/components/+apps-helm-charts/helm-charts.tsx b/src/renderer/components/+apps-helm-charts/helm-charts.tsx index 8a47654adc..da5f9f8c5a 100644 --- a/src/renderer/components/+apps-helm-charts/helm-charts.tsx +++ b/src/renderer/components/+apps-helm-charts/helm-charts.tsx @@ -55,6 +55,14 @@ export class HelmCharts extends Component { return helmChartStore.getByName(chartName, repo); } + onDetails = (chart: HelmChart) => { + if (chart === this.selectedChart) { + this.hideDetails(); + } else { + this.showDetails(chart); + } + }; + showDetails = (chart: HelmChart) => { if (!chart) { navigation.push(helmChartsURL()); @@ -121,7 +129,7 @@ export class HelmCharts extends Component { { className: "menu" } ]} detailsItem={this.selectedChart} - onDetails={this.showDetails} + onDetails={this.onDetails} /> {this.selectedChart && ( { @observable details: IReleaseDetails; @observable values = ""; @observable valuesLoading = false; - @observable showOnlyUserSuppliedValues = false; + @observable showOnlyUserSuppliedValues = true; @observable saving = false; @observable releaseSecret: Secret; @@ -165,7 +165,7 @@ export class ReleaseDetails extends Component { onChange={text => this.values = text} theme={ThemeStore.getInstance().activeTheme.monacoTheme} className={cssNames("MonacoEditor", {loading: valuesLoading})} - options={{readOnly: valuesLoading || this.showOnlyUserSuppliedValues, ...UserStore.getInstance().getEditorOptions()}} + options={{readOnly: valuesLoading, ...UserStore.getInstance().getEditorOptions()}} > {valuesLoading && } diff --git a/src/renderer/components/+apps-releases/releases.tsx b/src/renderer/components/+apps-releases/releases.tsx index d43174cc10..b7777b8c67 100644 --- a/src/renderer/components/+apps-releases/releases.tsx +++ b/src/renderer/components/+apps-releases/releases.tsx @@ -75,6 +75,14 @@ export class HelmReleases extends Component { }); } + onDetails = (item: HelmRelease) => { + if (item === this.selectedRelease) { + this.hideDetails(); + } else { + this.showDetails(item); + } + }; + showDetails = (item: HelmRelease) => { navigation.push(releaseURL({ params: { @@ -169,7 +177,7 @@ export class HelmReleases extends Component { message: this.renderRemoveDialogMessage(selectedItems) })} detailsItem={this.selectedRelease} - onDetails={this.showDetails} + onDetails={this.onDetails} /> { @@ -68,8 +67,10 @@ export class CatalogEntityDetails extends Component item.onRun(catalogEntityRunContext)} - size={128} /> + onClick={() => item.onRun()} + size={128} + data-testid="detail-panel-hot-bar-icon" + /> {item?.enabled && (
Click to open diff --git a/src/renderer/components/+catalog/catalog-entity-drawer-menu.tsx b/src/renderer/components/+catalog/catalog-entity-drawer-menu.tsx index c7ab341988..fb6a0c934a 100644 --- a/src/renderer/components/+catalog/catalog-entity-drawer-menu.tsx +++ b/src/renderer/components/+catalog/catalog-entity-drawer-menu.tsx @@ -30,7 +30,7 @@ import { MenuItem } from "../menu"; import { ConfirmDialog } from "../confirm-dialog"; import { HotbarStore } from "../../../common/hotbar-store"; import { Icon } from "../icon"; -import type { CatalogEntityItem } from "./catalog-entity.store"; +import type { CatalogEntityItem } from "./catalog-entity-item"; export interface CatalogEntityDrawerMenuProps extends MenuActionsProps { item: CatalogEntityItem | null | undefined; diff --git a/src/renderer/components/+catalog/catalog-entity-item.tsx b/src/renderer/components/+catalog/catalog-entity-item.tsx new file mode 100644 index 0000000000..b16406758f --- /dev/null +++ b/src/renderer/components/+catalog/catalog-entity-item.tsx @@ -0,0 +1,114 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +import styles from "./catalog.module.css"; +import React from "react"; +import { action, computed } from "mobx"; +import type { CatalogEntity } from "../../api/catalog-entity"; +import type { ItemObject } from "../../../common/item.store"; +import { Badge } from "../badge"; +import { navigation } from "../../navigation"; +import { searchUrlParam } from "../input"; +import { makeCss } from "../../../common/utils/makeCss"; +import { KubeObject } from "../../../common/k8s-api/kube-object"; +import type { CatalogEntityRegistry } from "../../api/catalog-entity-registry"; + +const css = makeCss(styles); + +export class CatalogEntityItem implements ItemObject { + constructor(public entity: T, private registry: CatalogEntityRegistry) {} + + get kind() { + return this.entity.kind; + } + + get apiVersion() { + return this.entity.apiVersion; + } + + get name() { + return this.entity.metadata.name; + } + + getName() { + return this.entity.metadata.name; + } + + get id() { + return this.entity.metadata.uid; + } + + getId() { + return this.id; + } + + @computed get phase() { + return this.entity.status.phase; + } + + get enabled() { + return this.entity.status.enabled ?? true; + } + + get labels() { + return KubeObject.stringifyLabels(this.entity.metadata.labels); + } + + getLabelBadges(onClick?: React.MouseEventHandler) { + return this.labels + .map(label => ( + { + navigation.searchParams.set(searchUrlParam.name, label); + onClick?.(event); + event.stopPropagation(); + }} + expandable={false} + /> + )); + } + + get source() { + return this.entity.metadata.source || "unknown"; + } + + get searchFields() { + return [ + this.name, + this.id, + this.phase, + `source=${this.source}`, + ...this.labels, + ]; + } + + onRun() { + this.registry.onRun(this.entity); + } + + @action + async onContextMenuOpen(ctx: any) { + return this.entity.onContextMenuOpen(ctx); + } +} diff --git a/src/renderer/components/+catalog/catalog-entity.store.tsx b/src/renderer/components/+catalog/catalog-entity.store.tsx index 05f317b7b1..8bbc335010 100644 --- a/src/renderer/components/+catalog/catalog-entity.store.tsx +++ b/src/renderer/components/+catalog/catalog-entity.store.tsx @@ -19,106 +19,16 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import styles from "./catalog.module.css"; - -import React from "react"; -import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from "mobx"; -import { catalogEntityRegistry } from "../../api/catalog-entity-registry"; -import type { CatalogEntity, CatalogEntityActionContext } from "../../api/catalog-entity"; -import { ItemObject, ItemStore } from "../../../common/item.store"; +import { computed, makeObservable, observable, reaction } from "mobx"; +import { catalogEntityRegistry, CatalogEntityRegistry } from "../../api/catalog-entity-registry"; +import type { CatalogEntity } from "../../api/catalog-entity"; +import { ItemStore } from "../../../common/item.store"; import { CatalogCategory, catalogCategoryRegistry } from "../../../common/catalog"; -import { autoBind } from "../../../common/utils"; -import { Badge } from "../badge"; -import { navigation } from "../../navigation"; -import { searchUrlParam } from "../input"; -import { makeCss } from "../../../common/utils/makeCss"; -import { KubeObject } from "../../../common/k8s-api/kube-object"; - -const css = makeCss(styles); - -export class CatalogEntityItem implements ItemObject { - constructor(public entity: T) {} - - get kind() { - return this.entity.kind; - } - - get apiVersion() { - return this.entity.apiVersion; - } - - get name() { - return this.entity.metadata.name; - } - - getName() { - return this.entity.metadata.name; - } - - get id() { - return this.entity.metadata.uid; - } - - getId() { - return this.id; - } - - @computed get phase() { - return this.entity.status.phase; - } - - get enabled() { - return this.entity.status.enabled ?? true; - } - - get labels() { - return KubeObject.stringifyLabels(this.entity.metadata.labels); - } - - getLabelBadges(onClick?: React.MouseEventHandler) { - return this.labels - .map(label => ( - { - navigation.searchParams.set(searchUrlParam.name, label); - onClick?.(event); - event.stopPropagation(); - }} - expandable={false} - /> - )); - } - - get source() { - return this.entity.metadata.source || "unknown"; - } - - get searchFields() { - return [ - this.name, - this.id, - this.phase, - `source=${this.source}`, - ...this.labels, - ]; - } - - onRun(ctx: CatalogEntityActionContext) { - this.entity.onRun(ctx); - } - - @action - async onContextMenuOpen(ctx: any) { - return this.entity.onContextMenuOpen(ctx); - } -} +import { autoBind, disposer } from "../../../common/utils"; +import { CatalogEntityItem } from "./catalog-entity-item"; export class CatalogEntityStore extends ItemStore> { - constructor() { + constructor(private registry: CatalogEntityRegistry = catalogEntityRegistry) { super(); makeObservable(this); autoBind(this); @@ -129,10 +39,10 @@ export class CatalogEntityStore extends ItemStore new CatalogEntityItem(entity)); + return this.registry.filteredItems.map(entity => new CatalogEntityItem(entity, this.registry)); } - return catalogEntityRegistry.getItemsForCategory(this.activeCategory, { filtered: true }).map(entity => new CatalogEntityItem(entity)); + return this.registry.getItemsForCategory(this.activeCategory, { filtered: true }).map(entity => new CatalogEntityItem(entity, this.registry)); } @computed get selectedItem() { @@ -140,12 +50,10 @@ export class CatalogEntityStore extends ItemStore this.entities, () => this.loadAll()), - reaction(() => this.activeCategory, () => this.loadAll(), { delay: 100}) - ]; - - return () => disposers.forEach((dispose) => dispose()); + reaction(() => this.activeCategory, () => this.loadAll(), { delay: 100}), + ); } loadAll() { diff --git a/src/renderer/components/+catalog/catalog-menu.module.css b/src/renderer/components/+catalog/catalog-menu.module.css index 84584e089d..1c3f08f6d7 100644 --- a/src/renderer/components/+catalog/catalog-menu.module.css +++ b/src/renderer/components/+catalog/catalog-menu.module.css @@ -32,4 +32,4 @@ .catalog { @apply p-5 font-bold text-2xl; color: var(--textColorAccent); -} \ No newline at end of file +} diff --git a/src/renderer/components/+catalog/catalog-menu.tsx b/src/renderer/components/+catalog/catalog-menu.tsx index 6634b2399d..b2d013dfcd 100644 --- a/src/renderer/components/+catalog/catalog-menu.tsx +++ b/src/renderer/components/+catalog/catalog-menu.tsx @@ -41,11 +41,15 @@ function getCategories() { } function getCategoryIcon(category: CatalogCategory) { - if (!category.metadata?.icon) return null; + const { icon } = category.metadata ?? {}; - return category.metadata.icon.includes(" - : ; + if (typeof icon === "string") { + return icon.includes(" + : ; + } + + return null; } function Item(props: TreeItemProps) { diff --git a/src/renderer/components/+catalog/catalog.test.tsx b/src/renderer/components/+catalog/catalog.test.tsx new file mode 100644 index 0000000000..bb1ce35a5b --- /dev/null +++ b/src/renderer/components/+catalog/catalog.test.tsx @@ -0,0 +1,366 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import React from "react"; +import { render, screen } from "@testing-library/react"; +import userEvent from "@testing-library/user-event"; +import { Catalog } from "./catalog"; +import { createMemoryHistory } from "history"; +import { mockWindow } from "../../../../__mocks__/windowMock"; +import { kubernetesClusterCategory } from "../../../common/catalog-entities/kubernetes-cluster"; +import { catalogCategoryRegistry, CatalogCategoryRegistry } from "../../../common/catalog"; +import { CatalogEntityRegistry } from "../../../renderer/api/catalog-entity-registry"; +import { CatalogEntityDetailRegistry } from "../../../extensions/registries"; +import { CatalogEntityItem } from "./catalog-entity-item"; +import { CatalogEntityStore } from "./catalog-entity.store"; + +mockWindow(); + +// avoid TypeError: Cannot read property 'getPath' of undefined +jest.mock("@electron/remote", () => { + return { + app: { + getPath: () => { + // avoid TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received undefined + return ""; + }, + }, + }; +}); + +describe("", () => { + const history = createMemoryHistory(); + const mockLocation = { + pathname: "", + search: "", + state: "", + hash: "", + }; + const mockMatch = { + params: { + // will be used to match activeCategory + // need to be the same as property values in kubernetesClusterCategory + group: "entity.k8slens.dev", + kind: "KubernetesCluster", + }, + isExact: true, + path: "", + url: "", + }; + + const catalogEntityUid = "a_catalogEntity_uid"; + const catalogEntity = { + enabled: true, + apiVersion: "api", + kind: "kind", + metadata: { + uid: catalogEntityUid, + name: "a catalog entity", + labels: { + test: "label", + }, + }, + status: { + phase: "", + }, + spec: {}, + }; + const catalogEntityItemMethods = { + getId: () => catalogEntity.metadata.uid, + getName: () => catalogEntity.metadata.name, + onContextMenuOpen: () => {}, + onSettingsOpen: () => {}, + onRun: () => {}, + }; + + beforeEach(() => { + CatalogEntityDetailRegistry.createInstance(); + // mock the return of getting CatalogCategoryRegistry.filteredItems + jest + .spyOn(catalogCategoryRegistry, "filteredItems", "get") + .mockImplementation(() => { + return [kubernetesClusterCategory]; + }); + + // we don't care what this.renderList renders in this test case. + jest.spyOn(Catalog.prototype, "renderList").mockImplementation(() => { + return empty renderList; + }); + }); + + afterEach(() => { + CatalogEntityDetailRegistry.resetInstance(); + jest.clearAllMocks(); + jest.restoreAllMocks(); + }); + + it("can use catalogEntityRegistry.addOnBeforeRun to add hooks for catalog entities", (done) => { + const catalogCategoryRegistry = new CatalogCategoryRegistry(); + const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry); + const catalogEntityStore = new CatalogEntityStore(catalogEntityRegistry); + const onRun = jest.fn(); + const catalogEntityItem = new CatalogEntityItem({ + ...catalogEntity, + ...catalogEntityItemMethods, + onRun, + }, catalogEntityRegistry); + + // mock as if there is a selected item > the detail panel opens + jest + .spyOn(catalogEntityStore, "selectedItem", "get") + .mockImplementation(() => catalogEntityItem); + + catalogEntityRegistry.addOnBeforeRun( + (event) => { + expect(event.target).toMatchInlineSnapshot(` + Object { + "apiVersion": "api", + "enabled": true, + "getId": [Function], + "getName": [Function], + "kind": "kind", + "metadata": Object { + "labels": Object { + "test": "label", + }, + "name": "a catalog entity", + "uid": "a_catalogEntity_uid", + }, + "onContextMenuOpen": [Function], + "onRun": [MockFunction], + "onSettingsOpen": [Function], + "spec": Object {}, + "status": Object { + "phase": "", + }, + } + `); + + setTimeout(() => { + expect(onRun).toHaveBeenCalled(); + done(); + }, 500); + } + ); + + render( + + ); + + userEvent.click(screen.getByTestId("detail-panel-hot-bar-icon")); + }); + + it("onBeforeRun prevents event => onRun wont be triggered", (done) => { + const catalogCategoryRegistry = new CatalogCategoryRegistry(); + const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry); + const catalogEntityStore = new CatalogEntityStore(catalogEntityRegistry); + const onRun = jest.fn(); + const catalogEntityItem = new CatalogEntityItem({ + ...catalogEntity, + ...catalogEntityItemMethods, + onRun, + }, catalogEntityRegistry); + + // mock as if there is a selected item > the detail panel opens + jest + .spyOn(catalogEntityStore, "selectedItem", "get") + .mockImplementation(() => catalogEntityItem); + + catalogEntityRegistry.addOnBeforeRun( + (e) => { + setTimeout(() => { + expect(onRun).not.toHaveBeenCalled(); + done(); + }, 500); + e.preventDefault(); + } + ); + + render( + + ); + + userEvent.click(screen.getByTestId("detail-panel-hot-bar-icon")); + }); + + it("addOnBeforeRun throw an exception => onRun will be triggered", (done) => { + const catalogCategoryRegistry = new CatalogCategoryRegistry(); + const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry); + const catalogEntityStore = new CatalogEntityStore(catalogEntityRegistry); + const onRun = jest.fn(); + const catalogEntityItem = new CatalogEntityItem({ + ...catalogEntity, + ...catalogEntityItemMethods, + onRun, + }, catalogEntityRegistry); + + // mock as if there is a selected item > the detail panel opens + jest + .spyOn(catalogEntityStore, "selectedItem", "get") + .mockImplementation(() => catalogEntityItem); + + catalogEntityRegistry.addOnBeforeRun( + () => { + setTimeout(() => { + expect(onRun).toHaveBeenCalled(); + done(); + }, 500); + + throw new Error("error!"); + } + ); + + render( + + ); + + userEvent.click(screen.getByTestId("detail-panel-hot-bar-icon")); + }); + + it("addOnRunHook return a promise and does not prevent run event => onRun()", (done) => { + const catalogCategoryRegistry = new CatalogCategoryRegistry(); + const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry); + const catalogEntityStore = new CatalogEntityStore(catalogEntityRegistry); + const onRun = jest.fn(() => done()); + const catalogEntityItem = new CatalogEntityItem({ + ...catalogEntity, + ...catalogEntityItemMethods, + onRun, + }, catalogEntityRegistry); + + // mock as if there is a selected item > the detail panel opens + jest + .spyOn(catalogEntityStore, "selectedItem", "get") + .mockImplementation(() => catalogEntityItem); + + catalogEntityRegistry.addOnBeforeRun( + async () => { + // no op + } + ); + + render( + + ); + + userEvent.click(screen.getByTestId("detail-panel-hot-bar-icon")); + }); + + it("addOnRunHook return a promise and prevents event wont be triggered", (done) => { + const catalogCategoryRegistry = new CatalogCategoryRegistry(); + const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry); + const catalogEntityStore = new CatalogEntityStore(catalogEntityRegistry); + const onRun = jest.fn(); + const catalogEntityItem = new CatalogEntityItem({ + ...catalogEntity, + ...catalogEntityItemMethods, + onRun, + }, catalogEntityRegistry); + + // mock as if there is a selected item > the detail panel opens + jest + .spyOn(catalogEntityStore, "selectedItem", "get") + .mockImplementation(() => catalogEntityItem); + + catalogEntityRegistry.addOnBeforeRun( + async (e) => { + expect(onRun).not.toBeCalled(); + + setTimeout(() => { + expect(onRun).not.toBeCalled(); + done(); + }, 500); + + e.preventDefault(); + } + ); + + render( + + ); + + userEvent.click(screen.getByTestId("detail-panel-hot-bar-icon")); + }); + + it("addOnRunHook return a promise and reject => onRun will be triggered", (done) => { + const catalogCategoryRegistry = new CatalogCategoryRegistry(); + const catalogEntityRegistry = new CatalogEntityRegistry(catalogCategoryRegistry); + const catalogEntityStore = new CatalogEntityStore(catalogEntityRegistry); + const onRun = jest.fn(); + const catalogEntityItem = new CatalogEntityItem({ + ...catalogEntity, + ...catalogEntityItemMethods, + onRun, + }, catalogEntityRegistry); + + // mock as if there is a selected item > the detail panel opens + jest + .spyOn(catalogEntityStore, "selectedItem", "get") + .mockImplementation(() => catalogEntityItem); + + catalogEntityRegistry.addOnBeforeRun( + async () => { + setTimeout(() => { + expect(onRun).toHaveBeenCalled(); + done(); + }, 500); + + throw new Error("rejection!"); + } + ); + + render( + + ); + + userEvent.click(screen.getByTestId("detail-panel-hot-bar-icon")); + }); +}); diff --git a/src/renderer/components/+catalog/catalog.tsx b/src/renderer/components/+catalog/catalog.tsx index db7288e5be..8c3d935b02 100644 --- a/src/renderer/components/+catalog/catalog.tsx +++ b/src/renderer/components/+catalog/catalog.tsx @@ -25,7 +25,8 @@ import React from "react"; import { disposeOnUnmount, observer } from "mobx-react"; import { ItemListLayout } from "../item-object-list"; import { action, makeObservable, observable, reaction, runInAction, when } from "mobx"; -import { CatalogEntityItem, CatalogEntityStore } from "./catalog-entity.store"; +import { CatalogEntityStore } from "./catalog-entity.store"; +import type { CatalogEntityItem } from "./catalog-entity-item"; import { navigate } from "../../navigation"; import { MenuItem, MenuActions } from "../menu"; import type { CatalogEntityContextMenu, CatalogEntityContextMenuContext } from "../../api/catalog-entity"; @@ -39,13 +40,13 @@ import { MainLayout } from "../layout/main-layout"; import { createAppStorage, cssNames } from "../../utils"; import { makeCss } from "../../../common/utils/makeCss"; import { CatalogEntityDetails } from "./catalog-entity-details"; -import { catalogURL, CatalogViewRouteParam } from "../../../common/routes"; +import { browseCatalogTab, catalogURL, CatalogViewRouteParam } from "../../../common/routes"; import { CatalogMenu } from "./catalog-menu"; import { HotbarIcon } from "../hotbar/hotbar-icon"; import { RenderDelay } from "../render-delay/render-delay"; import { Icon } from "../icon"; -export const previousActiveTab = createAppStorage("catalog-previous-active-tab", ""); +export const previousActiveTab = createAppStorage("catalog-previous-active-tab", browseCatalogTab); enum sortBy { name = "name", @@ -56,7 +57,10 @@ enum sortBy { const css = makeCss(styles); -interface Props extends RouteComponentProps {} +interface Props extends RouteComponentProps { + catalogEntityStore?: CatalogEntityStore; +} + @observer export class Catalog extends React.Component { @observable private catalogEntityStore?: CatalogEntityStore; @@ -66,8 +70,11 @@ export class Catalog extends React.Component { constructor(props: Props) { super(props); makeObservable(this); - this.catalogEntityStore = new CatalogEntityStore(); + this.catalogEntityStore = props.catalogEntityStore; } + static defaultProps = { + catalogEntityStore: new CatalogEntityStore(), + }; get routeActiveTab(): string { const { group, kind } = this.props.match.params ?? {}; @@ -76,7 +83,7 @@ export class Catalog extends React.Component { return `${group}/${kind}`; } - return ""; + return browseCatalogTab; } async componentDidMount() { @@ -90,7 +97,7 @@ export class Catalog extends React.Component { previousActiveTab.set(this.routeActiveTab); try { - await when(() => (routeTab === "" || !!catalogCategoryRegistry.filteredItems.find(i => i.getId() === routeTab)), { timeout: 5_000 }); // we need to wait because extensions might take a while to load + await when(() => (routeTab === browseCatalogTab || !!catalogCategoryRegistry.filteredItems.find(i => i.getId() === routeTab)), { timeout: 5_000 }); // we need to wait because extensions might take a while to load const item = catalogCategoryRegistry.filteredItems.find(i => i.getId() === routeTab); runInAction(() => { @@ -118,12 +125,17 @@ export class Catalog extends React.Component { } })); } + addToHotbar(item: CatalogEntityItem): void { HotbarStore.getInstance().addToHotbar(item.entity); } onDetails = (item: CatalogEntityItem) => { - this.catalogEntityStore.selectedItemId = item.getId(); + if (this.catalogEntityStore.selectedItemId) { + this.catalogEntityStore.selectedItemId = null; + } else { + item.onRun(); + } }; onMenuItemClick(menuItem: CatalogEntityContextMenu) { @@ -154,7 +166,7 @@ export class Catalog extends React.Component { if (activeCategory) { navigate(catalogURL({ params: {group: activeCategory.spec.group, kind: activeCategory.spec.names.kind }})); } else { - navigate(catalogURL()); + navigate(catalogURL({ params: { group: browseCatalogTab }})); } }; @@ -173,6 +185,9 @@ export class Catalog extends React.Component { return ( + this.catalogEntityStore.selectedItemId = item.getId()}> + View Details + { this.contextMenu.menuItems.map((menuItem, index) => ( this.onMenuItemClick(menuItem)}> diff --git a/src/renderer/components/+cluster/cluster-issues.tsx b/src/renderer/components/+cluster/cluster-issues.tsx index 9b5f73b9a6..fb5b98bc28 100644 --- a/src/renderer/components/+cluster/cluster-issues.tsx +++ b/src/renderer/components/+cluster/cluster-issues.tsx @@ -33,7 +33,7 @@ import { boundMethod, cssNames, prevDefault } from "../../utils"; import type { ItemObject } from "../../../common/item.store"; import { Spinner } from "../spinner"; import { ThemeStore } from "../../theme.store"; -import { kubeSelectedUrlParam, showDetails } from "../kube-detail-params"; +import { kubeSelectedUrlParam, toggleDetails } from "../kube-detail-params"; import { kubeWatchApi } from "../../../common/k8s-api/kube-watch-api"; import { apiManager } from "../../../common/k8s-api/api-manager"; @@ -124,7 +124,7 @@ export class ClusterIssues extends React.Component { key={getId()} sortItem={warning} selected={selfLink === kubeSelectedUrlParam.get()} - onClick={prevDefault(() => showDetails(selfLink))} + onClick={prevDefault(() => toggleDetails(selfLink))} > {message} diff --git a/src/renderer/components/+config-maps/config-map-details.tsx b/src/renderer/components/+config-maps/config-map-details.tsx index abbd31c97a..6150aa1ad9 100644 --- a/src/renderer/components/+config-maps/config-map-details.tsx +++ b/src/renderer/components/+config-maps/config-map-details.tsx @@ -72,6 +72,8 @@ export class ConfigMapDetails extends React.Component { <>ConfigMap {configMap.getName()} successfully updated.

); + } catch (error) { + Notifications.error(`Failed to save config map: ${error}`); } finally { this.isSaving = false; } diff --git a/src/renderer/components/+entity-settings/entity-settings.module.css b/src/renderer/components/+entity-settings/entity-settings.module.css new file mode 100644 index 0000000000..604ecd5c5b --- /dev/null +++ b/src/renderer/components/+entity-settings/entity-settings.module.css @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +.entityName { + @apply font-bold overflow-hidden; + word-break: break-word; + color: var(--textColorAccent); + display: -webkit-box; + /* Simulate text-overflow:ellipsis styles but for multiple text lines */ + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; +} \ No newline at end of file diff --git a/src/renderer/components/+entity-settings/entity-settings.tsx b/src/renderer/components/+entity-settings/entity-settings.tsx index f656b6fa9d..bcb5895413 100644 --- a/src/renderer/components/+entity-settings/entity-settings.tsx +++ b/src/renderer/components/+entity-settings/entity-settings.tsx @@ -19,7 +19,7 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import "./entity-settings.scss"; +import styles from "./entity-settings.module.css"; import React from "react"; import { observable, makeObservable } from "mobx"; @@ -98,7 +98,9 @@ export class EntitySettings extends React.Component { source={this.entity.metadata.source} src={this.entity.spec.icon?.src} /> -

{this.entity.metadata.name}

+
+ {this.entity.metadata.name} +
{ groups.map((group, groupIndex) => ( @@ -138,7 +140,6 @@ export class EntitySettings extends React.Component { return ( diff --git a/src/renderer/components/+events/kube-event-details.tsx b/src/renderer/components/+events/kube-event-details.tsx index 7f00fd6a15..aa00a3b9d1 100644 --- a/src/renderer/components/+events/kube-event-details.tsx +++ b/src/renderer/components/+events/kube-event-details.tsx @@ -26,6 +26,7 @@ import { observer } from "mobx-react"; import type { KubeObject } from "../../../common/k8s-api/kube-object"; import { DrawerItem, DrawerTitle } from "../drawer"; import { cssNames } from "../../utils"; +import { LocaleDate } from "../locale-date"; import { eventStore } from "./event.store"; export interface KubeEventDetailsProps { @@ -74,7 +75,7 @@ export class KubeEventDetails extends React.Component { {involvedObject.fieldPath} - {lastTimestamp} +
); diff --git a/src/main/routes/utils/index.ts b/src/renderer/components/+network-port-forwards/index.ts similarity index 97% rename from src/main/routes/utils/index.ts rename to src/renderer/components/+network-port-forwards/index.ts index 4a7de80225..2b0c81f833 100644 --- a/src/main/routes/utils/index.ts +++ b/src/renderer/components/+network-port-forwards/index.ts @@ -18,4 +18,5 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -export * from "./parse-query"; + +export * from "./port-forwards"; diff --git a/src/renderer/components/+network-port-forwards/port-forward-menu.tsx b/src/renderer/components/+network-port-forwards/port-forward-menu.tsx new file mode 100644 index 0000000000..7b0f35ef43 --- /dev/null +++ b/src/renderer/components/+network-port-forwards/port-forward-menu.tsx @@ -0,0 +1,73 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import React from "react"; +import { boundMethod, cssNames } from "../../utils"; +import { openPortForward, PortForwardItem, removePortForward } from "../../port-forward"; +import { MenuActions, MenuActionsProps } from "../menu/menu-actions"; +import { MenuItem } from "../menu"; +import { Icon } from "../icon"; +import { PortForwardDialog } from "../../port-forward"; + +interface Props extends MenuActionsProps { + portForward: PortForwardItem; + hideDetails?(): void; +} + +export class PortForwardMenu extends React.Component { + @boundMethod + remove() { + return removePortForward(this.props.portForward); + } + + renderContent() { + const { portForward, toolbar } = this.props; + + if (!portForward) return null; + + return ( + <> + openPortForward(this.props.portForward)}> + + Open + + PortForwardDialog.open(portForward)}> + + Edit + + + ); + } + + render() { + const { className, ...menuProps } = this.props; + + return ( + + {this.renderContent()} + + ); + } +} diff --git a/src/renderer/components/+network-port-forwards/port-forwards.scss b/src/renderer/components/+network-port-forwards/port-forwards.scss new file mode 100644 index 0000000000..0c048fc94d --- /dev/null +++ b/src/renderer/components/+network-port-forwards/port-forwards.scss @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +.PortForwards { + .TableCell { + &.warning { + @include table-cell-warning; + } + } +} \ No newline at end of file diff --git a/src/renderer/components/+network-port-forwards/port-forwards.tsx b/src/renderer/components/+network-port-forwards/port-forwards.tsx new file mode 100644 index 0000000000..c056dab454 --- /dev/null +++ b/src/renderer/components/+network-port-forwards/port-forwards.tsx @@ -0,0 +1,103 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import "./port-forwards.scss"; + +import React from "react"; +import { disposeOnUnmount, observer } from "mobx-react"; +import { ItemListLayout } from "../item-object-list/item-list-layout"; +import { PortForwardItem, portForwardStore } from "../../port-forward"; +import { PortForwardMenu } from "./port-forward-menu"; + +enum columnId { + name = "name", + namespace = "namespace", + kind = "kind", + port = "port", + forwardPort = "forwardPort", +} + +@observer +export class PortForwards extends React.Component { + + componentDidMount() { + disposeOnUnmount(this, [ + portForwardStore.watch(), + ]); + } + + renderRemoveDialogMessage(selectedItems: PortForwardItem[]) { + const forwardPorts = selectedItems.map(item => item.getForwardPort()).join(", "); + + return ( +
+ <>Stop forwarding from {forwardPorts}? +
+ ); + } + + + render() { + return ( + <> + item.getName(), + [columnId.namespace]: item => item.getNs(), + [columnId.kind]: item => item.getKind(), + [columnId.port]: item => item.getPort(), + [columnId.forwardPort]: item => item.getForwardPort(), + }} + searchFilters={[ + item => item.getSearchFields(), + ]} + renderHeaderTitle="Port Forwarding" + renderTableHeader={[ + { title: "Name", className: "name", sortBy: columnId.name, id: columnId.name }, + { title: "Namespace", className: "namespace", sortBy: columnId.namespace, id: columnId.namespace }, + { title: "Kind", className: "kind", sortBy: columnId.kind, id: columnId.kind }, + { title: "Pod Port", className: "port", sortBy: columnId.port, id: columnId.port }, + { title: "Local Port", className: "forwardPort", sortBy: columnId.forwardPort, id: columnId.forwardPort }, + ]} + renderTableContents={item => [ + item.getName(), + item.getNs(), + item.getKind(), + item.getPort(), + item.getForwardPort(), + ]} + renderItemMenu={pf => ( + + )} + customizeRemoveDialog={selectedItems => ({ + message: this.renderRemoveDialogMessage(selectedItems) + })} + /> + + ); + } +} diff --git a/src/renderer/components/+network-services/service-details.tsx b/src/renderer/components/+network-services/service-details.tsx index 66a09672a1..38ed8d6c68 100644 --- a/src/renderer/components/+network-services/service-details.tsx +++ b/src/renderer/components/+network-services/service-details.tsx @@ -32,6 +32,7 @@ import { ServicePortComponent } from "./service-port-component"; import { endpointStore } from "../+network-endpoints/endpoints.store"; import { ServiceDetailsEndpoint } from "./service-details-endpoint"; import { kubeWatchApi } from "../../../common/k8s-api/kube-watch-api"; +import { portForwardStore } from "../../port-forward"; interface Props extends KubeObjectDetailsProps { } @@ -46,6 +47,7 @@ export class ServiceDetails extends React.Component { preload: true, namespaces: [service.getNs()], }), + portForwardStore.watch(), ]); } diff --git a/src/renderer/components/+network-services/service-port-component.scss b/src/renderer/components/+network-services/service-port-component.scss index 89369f560f..c22f41d1e0 100644 --- a/src/renderer/components/+network-services/service-port-component.scss +++ b/src/renderer/components/+network-services/service-port-component.scss @@ -33,11 +33,13 @@ cursor: pointer; color: $primary; text-decoration: underline; + padding-right: 1em; } - .Spinner { - --spinner-size: #{$unit * 2}; - margin-left: $margin; - position: absolute; + .portInput { + display: inline-block !important; + width: 70px; + margin-left: 10px; + margin-right: 10px; } } diff --git a/src/renderer/components/+network-services/service-port-component.tsx b/src/renderer/components/+network-services/service-port-component.tsx index 96bb7385cb..d49a1eb94a 100644 --- a/src/renderer/components/+network-services/service-port-component.tsx +++ b/src/renderer/components/+network-services/service-port-component.tsx @@ -22,12 +22,15 @@ import "./service-port-component.scss"; import React from "react"; -import { observer } from "mobx-react"; +import { disposeOnUnmount, observer } from "mobx-react"; import type { Service, ServicePort } from "../../../common/k8s-api/endpoints"; -import { apiBase } from "../../api"; -import { observable, makeObservable } from "mobx"; +import { observable, makeObservable, reaction } from "mobx"; import { cssNames } from "../../utils"; import { Notifications } from "../notifications"; +import { Button } from "../button"; +import { addPortForward, getPortForward, openPortForward, PortForwardDialog, portForwardStore, removePortForward } from "../../port-forward"; +import type { ForwardedPort } from "../../port-forward"; +import logger from "../../../common/logger"; import { Spinner } from "../spinner"; interface Props { @@ -38,20 +41,86 @@ interface Props { @observer export class ServicePortComponent extends React.Component { @observable waiting = false; + @observable forwardPort = 0; + @observable isPortForwarded = false; constructor(props: Props) { super(props); makeObservable(this); + this.init(); + } + + componentDidMount() { + disposeOnUnmount(this, [ + reaction(() => [ portForwardStore.portForwards, this.props.service ], () => this.init()), + ]); + } + + init() { + this.checkExistingPortForwarding().catch(error => { + logger.error(error); + }); + } + + async checkExistingPortForwarding() { + const { service, port } = this.props; + const portForward: ForwardedPort = { + kind: "service", + name: service.getName(), + namespace: service.getNs(), + port: port.port, + forwardPort: this.forwardPort, + }; + const activePort = await getPortForward(portForward) ?? 0; + + this.forwardPort = activePort; + this.isPortForwarded = activePort ? true : false; } async portForward() { const { service, port } = this.props; + const portForward: ForwardedPort = { + kind: "service", + name: service.getName(), + namespace: service.getNs(), + port: port.port, + forwardPort: this.forwardPort, + }; + + this.waiting = true; + this.isPortForwarded = false; + + try { + this.forwardPort = await addPortForward(portForward); + + if (this.forwardPort) { + portForward.forwardPort = this.forwardPort; + openPortForward(portForward); + this.isPortForwarded = true; + } + } catch (error) { + Notifications.error(error); + } finally { + this.waiting = false; + } + } + + async stopPortForward() { + const { service, port } = this.props; + const portForward: ForwardedPort = { + kind: "service", + name: service.getName(), + namespace: service.getNs(), + port: port.port, + forwardPort: this.forwardPort, + }; this.waiting = true; try { - await apiBase.post(`/pods/${service.getNs()}/service/${service.getName()}/port-forward/${port.port}`, {}); - } catch(error) { + await removePortForward(portForward); + this.isPortForwarded = false; + } catch (error) { Notifications.error(error); } finally { this.waiting = false; @@ -59,16 +128,33 @@ export class ServicePortComponent extends React.Component { } render() { - const { port } = this.props; + const { port, service } = this.props; + + const portForwardAction = async () => { + if (this.isPortForwarded) { + await this.stopPortForward(); + } else { + const portForward: ForwardedPort = { + kind: "service", + name: service.getName(), + namespace: service.getNs(), + port: port.port, + forwardPort: this.forwardPort, + }; + + PortForwardDialog.open(portForward, { openInBrowser: true }); + } + }; return (
- this.portForward() }> + this.portForward()}> {port.toString()} - {this.waiting && ( - - )} + + {this.waiting && ( + + )}
); } diff --git a/src/renderer/components/+network/network.tsx b/src/renderer/components/+network/network.tsx index 7c177ee2a3..083996374c 100644 --- a/src/renderer/components/+network/network.tsx +++ b/src/renderer/components/+network/network.tsx @@ -28,6 +28,7 @@ import { Services } from "../+network-services"; import { Endpoints } from "../+network-endpoints"; import { Ingresses } from "../+network-ingresses"; import { NetworkPolicies } from "../+network-policies"; +import { PortForwards } from "../+network-port-forwards"; import { isAllowedResource } from "../../../common/utils/allowed-resource"; import * as routes from "../../../common/routes"; @@ -72,6 +73,13 @@ export class Network extends React.Component { }); } + tabs.push({ + title: "Port Forwarding", + component: PortForwards, + url: routes.portForwardsURL(), + routePath: routes.portForwardsRoute.path.toString(), + }); + return tabs; } diff --git a/src/renderer/components/+nodes/nodes.tsx b/src/renderer/components/+nodes/nodes.tsx index e8a70b99d0..0d701e4ee7 100644 --- a/src/renderer/components/+nodes/nodes.tsx +++ b/src/renderer/components/+nodes/nodes.tsx @@ -236,8 +236,8 @@ export class Nodes extends React.Component { this.renderDiskUsage(node), <> {node.getTaints().length} - - {node.getTaints().map(({ key, effect }) => `${key}: ${effect}`).join("\n")} + + {node.getTaints().map(({ key, value, effect }) => `${key}=${value}:${effect}`).join("\n")} , node.getRoleLabels(), diff --git a/src/renderer/components/+preferences/application.tsx b/src/renderer/components/+preferences/application.tsx index ac05012f2f..ba86dc2ea8 100644 --- a/src/renderer/components/+preferences/application.tsx +++ b/src/renderer/components/+preferences/application.tsx @@ -72,6 +72,20 @@ export const Application = observer(() => { /> +
+ + UserStore.getInstance().terminalCopyOnSelect = v.target.checked} + name="terminalCopyOnSelect" + /> + } + /> +
+
diff --git a/src/renderer/components/+workloads-deployments/deployment-replicasets.tsx b/src/renderer/components/+workloads-deployments/deployment-replicasets.tsx index e332b19fe8..13770b0abf 100644 --- a/src/renderer/components/+workloads-deployments/deployment-replicasets.tsx +++ b/src/renderer/components/+workloads-deployments/deployment-replicasets.tsx @@ -75,6 +75,7 @@ export class DeploymentReplicaSets extends React.Component { sortable={this.sortingCallbacks} sortByDefault={{ sortBy: sortBy.pods, orderBy: "desc" }} sortSyncWithUrl={false} + tableId="deployment_replica_sets_view" className="box grow" > diff --git a/src/renderer/components/+workloads-deployments/deployment-scale-dialog.test.tsx b/src/renderer/components/+workloads-deployments/deployment-scale-dialog.test.tsx index 22c90dda2c..3bc22b8263 100644 --- a/src/renderer/components/+workloads-deployments/deployment-scale-dialog.test.tsx +++ b/src/renderer/components/+workloads-deployments/deployment-scale-dialog.test.tsx @@ -112,6 +112,7 @@ const dummyDeployment: Deployment = { toPlainObject: jest.fn(), update: jest.fn(), delete: jest.fn(), + patch: jest.fn(), }; describe("", () => { diff --git a/src/renderer/components/+workloads-pods/pod-container-port.scss b/src/renderer/components/+workloads-pods/pod-container-port.scss index 80d2425aac..efd5592065 100644 --- a/src/renderer/components/+workloads-pods/pod-container-port.scss +++ b/src/renderer/components/+workloads-pods/pod-container-port.scss @@ -34,11 +34,13 @@ color: $primary; text-decoration: underline; position: relative; + padding-right: 1em; } - .Spinner { - --spinner-size: #{$unit * 2}; - margin-left: $margin; - position: absolute; + .portInput { + display: inline-block !important; + width: 70px; + margin-left: 10px; + margin-right: 10px; } -} \ No newline at end of file +} diff --git a/src/renderer/components/+workloads-pods/pod-container-port.tsx b/src/renderer/components/+workloads-pods/pod-container-port.tsx index c8bb435b55..4c468c46c8 100644 --- a/src/renderer/components/+workloads-pods/pod-container-port.tsx +++ b/src/renderer/components/+workloads-pods/pod-container-port.tsx @@ -22,12 +22,15 @@ import "./pod-container-port.scss"; import React from "react"; -import { observer } from "mobx-react"; +import { disposeOnUnmount, observer } from "mobx-react"; import type { Pod } from "../../../common/k8s-api/endpoints"; -import { apiBase } from "../../api"; -import { observable, makeObservable } from "mobx"; +import { observable, makeObservable, reaction } from "mobx"; import { cssNames } from "../../utils"; import { Notifications } from "../notifications"; +import { Button } from "../button"; +import { addPortForward, getPortForward, openPortForward, PortForwardDialog, portForwardStore, removePortForward } from "../../port-forward"; +import type { ForwardedPort } from "../../port-forward"; +import logger from "../../../common/logger"; import { Spinner } from "../spinner"; interface Props { @@ -42,20 +45,86 @@ interface Props { @observer export class PodContainerPort extends React.Component { @observable waiting = false; + @observable forwardPort = 0; + @observable isPortForwarded = false; constructor(props: Props) { super(props); makeObservable(this); + this.init(); + } + + componentDidMount() { + disposeOnUnmount(this, [ + reaction(() => [ portForwardStore.portForwards, this.props.pod ], () => this.init()), + ]); + } + + init() { + this.checkExistingPortForwarding().catch(error => { + logger.error(error); + }); + } + + async checkExistingPortForwarding() { + const { pod, port } = this.props; + const portForward: ForwardedPort = { + kind: "pod", + name: pod.getName(), + namespace: pod.getNs(), + port: port.containerPort, + forwardPort: this.forwardPort, + }; + const activePort = await getPortForward(portForward) ?? 0; + + this.forwardPort = activePort; + this.isPortForwarded = activePort ? true : false; } async portForward() { const { pod, port } = this.props; + const portForward: ForwardedPort = { + kind: "pod", + name: pod.getName(), + namespace: pod.getNs(), + port: port.containerPort, + forwardPort: this.forwardPort, + }; + + this.waiting = true; + this.isPortForwarded = false; + + try { + this.forwardPort = await addPortForward(portForward); + + if (this.forwardPort) { + portForward.forwardPort = this.forwardPort; + openPortForward(portForward); + this.isPortForwarded = true; + } + } catch (error) { + Notifications.error(error); + } finally { + this.waiting = false; + } + } + + async stopPortForward() { + const { pod, port } = this.props; + const portForward: ForwardedPort = { + kind: "pod", + name: pod.getName(), + namespace: pod.getNs(), + port: port.containerPort, + forwardPort: this.forwardPort, + }; this.waiting = true; try { - await apiBase.post(`/pods/${pod.getNs()}/pod/${pod.getName()}/port-forward/${port.containerPort}`, {}); - } catch(error) { + await removePortForward(portForward); + this.isPortForwarded = false; + } catch (error) { Notifications.error(error); } finally { this.waiting = false; @@ -63,18 +132,35 @@ export class PodContainerPort extends React.Component { } render() { - const { port } = this.props; + const { pod, port } = this.props; const { name, containerPort, protocol } = port; const text = `${name ? `${name}: ` : ""}${containerPort}/${protocol}`; + const portForwardAction = async () => { + if (this.isPortForwarded) { + await this.stopPortForward(); + } else { + const portForward: ForwardedPort = { + kind: "pod", + name: pod.getName(), + namespace: pod.getNs(), + port: port.containerPort, + forwardPort: this.forwardPort, + }; + + PortForwardDialog.open(portForward, { openInBrowser: true }); + } + }; + return (
- this.portForward() }> + this.portForward()}> {text} - {this.waiting && ( - - )} + + {this.waiting && ( + + )}
); } diff --git a/src/renderer/components/+workloads-pods/pod-details-container.tsx b/src/renderer/components/+workloads-pods/pod-details-container.tsx index c39ed80751..b4a4c5eaf2 100644 --- a/src/renderer/components/+workloads-pods/pod-details-container.tsx +++ b/src/renderer/components/+workloads-pods/pod-details-container.tsx @@ -35,6 +35,8 @@ import { ContainerCharts } from "./container-charts"; import { LocaleDate } from "../locale-date"; import { getActiveClusterEntity } from "../../api/catalog-entity-registry"; import { ClusterMetricsResourceType } from "../../../common/cluster-types"; +import { portForwardStore } from "../../port-forward/port-forward.store"; +import { disposeOnUnmount, observer } from "mobx-react"; interface Props { pod: Pod; @@ -42,8 +44,15 @@ interface Props { metrics?: { [key: string]: IMetrics }; } +@observer export class PodDetailsContainer extends React.Component { + componentDidMount() { + disposeOnUnmount(this, [ + portForwardStore.watch(), + ]); + } + renderStatus(state: string, status: IPodContainerStatus) { const ready = status ? status.ready : ""; diff --git a/src/renderer/components/+workloads-replicasets/replicaset-scale-dialog.test.tsx b/src/renderer/components/+workloads-replicasets/replicaset-scale-dialog.test.tsx index 7f8c31e99c..e948d3a339 100755 --- a/src/renderer/components/+workloads-replicasets/replicaset-scale-dialog.test.tsx +++ b/src/renderer/components/+workloads-replicasets/replicaset-scale-dialog.test.tsx @@ -107,6 +107,7 @@ const dummyReplicaSet: ReplicaSet = { toPlainObject: jest.fn(), update: jest.fn(), delete: jest.fn(), + patch: jest.fn(), }; describe("", () => { diff --git a/src/renderer/components/+workloads-statefulsets/statefulset-scale-dialog.test.tsx b/src/renderer/components/+workloads-statefulsets/statefulset-scale-dialog.test.tsx index b27d8b0892..016d431eb2 100755 --- a/src/renderer/components/+workloads-statefulsets/statefulset-scale-dialog.test.tsx +++ b/src/renderer/components/+workloads-statefulsets/statefulset-scale-dialog.test.tsx @@ -117,6 +117,7 @@ const dummyStatefulSet: StatefulSet = { toPlainObject: jest.fn(), update: jest.fn(), delete: jest.fn(), + patch: jest.fn(), }; describe("", () => { diff --git a/src/renderer/components/activate-entity-command/activate-entity-command.tsx b/src/renderer/components/activate-entity-command/activate-entity-command.tsx new file mode 100644 index 0000000000..f0b06769d8 --- /dev/null +++ b/src/renderer/components/activate-entity-command/activate-entity-command.tsx @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +import { computed } from "mobx"; +import { observer } from "mobx-react"; +import React from "react"; +import type { CatalogEntity } from "../../api/catalog-entity"; +import { catalogEntityRegistry } from "../../api/catalog-entity-registry"; +import { CommandOverlay } from "../command-palette"; +import { Select } from "../select"; + +@observer +export class ActivateEntityCommand extends React.Component { + @computed get options() { + return catalogEntityRegistry.items.map(entity => ({ + label: `${entity.kind}: ${entity.getName()}`, + value: entity, + })); + } + + onSelect(entity: CatalogEntity): void { + catalogEntityRegistry.onRun(entity); + CommandOverlay.close(); + } + + render() { + return ( + + + + + ); + } + + render() { + const { className, ...dialogProps } = this.props; + const resourceName = this.portForward?.name ?? ""; + const header = ( +
+ Port Forwarding for {resourceName} +
+ ); + + return ( + + + + {this.renderContents()} + + + + ); + } +} diff --git a/src/renderer/port-forward/port-forward-item.ts b/src/renderer/port-forward/port-forward-item.ts new file mode 100644 index 0000000000..9251b0fcef --- /dev/null +++ b/src/renderer/port-forward/port-forward-item.ts @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +import type { ItemObject } from "../../common/item.store"; +import { autoBind } from "../../common/utils"; + +export interface ForwardedPort { + clusterId?: string; + kind: string; + namespace: string; + name: string; + port: number; + forwardPort: number; +} + +export class PortForwardItem implements ItemObject { + clusterId: string; + kind: string; + namespace: string; + name: string; + port: number; + forwardPort: number; + + constructor(pf: ForwardedPort) { + this.clusterId = pf.clusterId; + this.kind = pf.kind; + this.namespace = pf.namespace; + this.name = pf.name; + this.port = pf.port; + this.forwardPort = pf.forwardPort; + + autoBind(this); + } + + getName() { + return this.name; + } + + getNs() { + return this.namespace; + } + + get id() { + return this.forwardPort; + } + + getId() { + return String(this.forwardPort); + } + + getKind() { + return this.kind; + } + + getPort() { + return this.port; + } + + getForwardPort() { + return this.forwardPort; + } + + getSearchFields() { + return [ + this.name, + this.id, + this.kind, + this.port, + this.forwardPort, + ]; + } +} diff --git a/src/renderer/port-forward/port-forward.store.ts b/src/renderer/port-forward/port-forward.store.ts new file mode 100644 index 0000000000..49ebee61d4 --- /dev/null +++ b/src/renderer/port-forward/port-forward.store.ts @@ -0,0 +1,179 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +import { makeObservable, observable, reaction } from "mobx"; +import { ItemStore } from "../../common/item.store"; +import { autoBind, createStorage, disposer, getHostedClusterId, openExternal } from "../utils"; +import { ForwardedPort, PortForwardItem } from "./port-forward-item"; +import { apiBase } from "../api"; +import { waitUntilFree } from "tcp-port-used"; +import { Notifications } from "../components/notifications"; +import logger from "../../common/logger"; + +export class PortForwardStore extends ItemStore { + private storage = createStorage("port_forwards", undefined); + + @observable portForwards: PortForwardItem[]; + + constructor() { + super(); + makeObservable(this); + autoBind(this); + + this.init(); + } + + private async init() { + await this.storage.whenReady; + + const savedPortForwards = this.storage.get(); // undefined on first load + + if (Array.isArray(savedPortForwards)) { + logger.info("[PORT_FORWARD] starting saved port-forwards"); + await Promise.all(savedPortForwards.map(addPortForward)); + } + } + + watch() { + return disposer( + reaction(() => this.portForwards, () => this.loadAll()), + ); + } + + loadAll() { + return this.loadItems(async () => { + let portForwards = await getPortForwards(); + + // filter out any not for this cluster + portForwards = portForwards.filter(pf => pf.clusterId == getHostedClusterId()); + this.storage.set(portForwards); + + this.reset(); + portForwards.map(pf => this.portForwards.push(new PortForwardItem(pf))); + + return this.portForwards; + }); + } + + reset() { + this.portForwards = []; + } + + async removeSelectedItems() { + return Promise.all(this.selectedItems.map(removePortForward)); + } +} + +interface PortForwardResult { + port: number; +} + +interface PortForwardsResult { + portForwards: ForwardedPort[]; +} + +export async function addPortForward(portForward: ForwardedPort): Promise { + let response: PortForwardResult; + + try { + response = await apiBase.post(`/pods/port-forward/${portForward.namespace}/${portForward.kind}/${portForward.name}?port=${portForward.port}&forwardPort=${portForward.forwardPort}`); + + if (response?.port && response.port != +portForward.forwardPort) { + logger.warn(`specified ${portForward.forwardPort} got ${response.port}`); + } + } catch (error) { + logger.warn(error); // don't care, caller must check + } + portForwardStore.reset(); + + return response?.port; +} + +export async function getPortForward(portForward: ForwardedPort): Promise { + let response: PortForwardResult; + + try { + response = await apiBase.get(`/pods/port-forward/${portForward.namespace}/${portForward.kind}/${portForward.name}?port=${portForward.port}&forwardPort=${portForward.forwardPort}`); + } catch (error) { + logger.warn(error); // don't care, caller must check + } + + return response?.port; +} + +export async function modifyPortForward(portForward: ForwardedPort, desiredPort: number): Promise { + let port = 0; + + try { + await removePortForward(portForward); + portForward.forwardPort = desiredPort; + port = await addPortForward(portForward); + } catch (error) { + logger.warn(error); // don't care, caller must check + } + portForwardStore.reset(); + + return port; +} + + +export async function removePortForward(portForward: ForwardedPort) { + try { + await apiBase.del(`/pods/port-forward/${portForward.namespace}/${portForward.kind}/${portForward.name}?port=${portForward.port}&forwardPort=${portForward.forwardPort}`); + await waitUntilFree(+portForward.forwardPort, 200, 1000); + } catch (error) { + logger.warn(error); // don't care, caller must check + } + portForwardStore.reset(); +} + +export async function getPortForwards(): Promise { + try { + const response = await apiBase.get(`/pods/port-forwards`); + + return response.portForwards; + } catch (error) { + logger.warn(error); // don't care, caller must check + + return []; + } +} + +export function openPortForward(portForward: ForwardedPort) { + const browseTo = `http://localhost:${portForward.forwardPort}`; + + openExternal(browseTo) + .catch(error => { + logger.error(`failed to open in browser: ${error}`, { + clusterId: portForward.clusterId, + port: portForward.port, + kind: portForward.kind, + namespace: portForward.namespace, + name: portForward.name, + }); + Notifications.error(`Failed to open ${browseTo} in browser`); + } + ); + +} + +export const portForwardStore = new PortForwardStore(); diff --git a/webpack.main.ts b/webpack.main.ts index 33d9daf6af..62e51bf4a2 100755 --- a/webpack.main.ts +++ b/webpack.main.ts @@ -22,7 +22,7 @@ import path from "path"; import type webpack from "webpack"; import ForkTsCheckerPlugin from "fork-ts-checker-webpack-plugin"; -import { isProduction, mainDir, buildDir, isDevelopment, preloadEntrypoint } from "./src/common/vars"; +import { isProduction, mainDir, buildDir, isDevelopment } from "./src/common/vars"; import nodeExternals from "webpack-node-externals"; import ProgressBarPlugin from "progress-bar-webpack-plugin"; import * as vars from "./src/common/vars"; @@ -68,41 +68,4 @@ configs.push((): webpack.Configuration => { }; }); -configs.push((): webpack.Configuration => { - console.info("WEBPACK:preload", vars); - - return { - context: __dirname, - target: "electron-main", - mode: isProduction ? "production" : "development", - devtool: isProduction ? "source-map" : "cheap-eval-source-map", - cache: isDevelopment, - entry: { - main: path.resolve(preloadEntrypoint), - }, - output: { - libraryTarget: "global", - path: buildDir, - filename: "preload.js" - }, - resolve: { - extensions: [".json", ".js", ".ts"], - mainFields: ["main"] - }, - module: { - rules: [ - { - test: /\.node$/, - use: "node-loader" - }, - getTSLoader(/\.ts$/) - ] - }, - plugins: [ - new ProgressBarPlugin(), - new ForkTsCheckerPlugin(), - ].filter(Boolean) - }; -}); - export default configs; diff --git a/webpack.renderer.ts b/webpack.renderer.ts index 5be6ba09dc..5e43213bd0 100755 --- a/webpack.renderer.ts +++ b/webpack.renderer.ts @@ -79,6 +79,9 @@ export function webpackLensRenderer({ showVars = true } = {}): webpack.Configura ".ts", ".tsx", ] }, + externals: { + "node-fetch": "commonjs node-fetch" + }, optimization: { minimize: false }, diff --git a/yarn.lock b/yarn.lock index 3f24e65868..02d9b55fff 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,27 +7,20 @@ resolved "https://registry.yarnpkg.com/7zip-bin/-/7zip-bin-5.1.1.tgz#9274ec7460652f9c632c59addf24efb1684ef876" integrity sha512-sAP4LldeWNz0lNzmTird3uWfFDWWTeg6V/MsmyyLR9X1idwKBWIgt/ZvinqQldJm3LecKEs1emkbquO6PCiLVQ== -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3": +"@babel/code-frame@7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" + integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== + dependencies: + "@babel/highlight" "^7.10.4" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.1", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.8.3": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.14.5.tgz#23b08d740e83f49c5e59945fbf1b43e80bbf4edb" integrity sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw== dependencies: "@babel/highlight" "^7.14.5" -"@babel/code-frame@^7.10.1": - version "7.10.1" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.1.tgz#d5481c5095daa1c57e16e54c6f9198443afb49ff" - integrity sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw== - dependencies: - "@babel/highlight" "^7.10.1" - -"@babel/code-frame@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" - integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== - dependencies: - "@babel/highlight" "^7.10.4" - "@babel/core@^7.1.0", "@babel/core@^7.7.5": version "7.10.2" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.10.2.tgz#bd6786046668a925ac2bd2fd95b579b92a23b36a" @@ -164,15 +157,6 @@ "@babel/traverse" "^7.10.1" "@babel/types" "^7.10.1" -"@babel/highlight@^7.10.1", "@babel/highlight@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" - integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== - dependencies: - "@babel/helper-validator-identifier" "^7.14.5" - chalk "^2.0.0" - js-tokens "^4.0.0" - "@babel/highlight@^7.10.4": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" @@ -182,6 +166,15 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.14.5": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" + integrity sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg== + dependencies: + "@babel/helper-validator-identifier" "^7.14.5" + chalk "^2.0.0" + js-tokens "^4.0.0" + "@babel/parser@^7.1.0", "@babel/parser@^7.10.1", "@babel/parser@^7.10.2": version "7.10.2" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.10.2.tgz#871807f10442b92ff97e4783b9b54f6a0ca812d0" @@ -332,6 +325,18 @@ exec-sh "^0.3.2" minimist "^1.2.0" +"@cspotcode/source-map-consumer@0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" + integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg== + +"@cspotcode/source-map-support@0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.6.1.tgz#118511f316e2e87ee4294761868e254d3da47960" + integrity sha512-DX3Z+T5dt1ockmPdobJS/FAsQPW4V4SrWEhD2iYQT2Cb2tQsiMnYxrcUH9By/Z3B+v0S5LMBkQtV/XOBbpLEOg== + dependencies: + "@cspotcode/source-map-consumer" "0.8.0" + "@dabh/diagnostics@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@dabh/diagnostics/-/diagnostics-2.0.2.tgz#290d08f7b381b8f94607dc8f471a12c675f9db31" @@ -473,19 +478,18 @@ resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46" integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA== -"@eslint/eslintrc@^0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.2.1.tgz#f72069c330461a06684d119384435e12a5d76e3c" - integrity sha512-XRUeBZ5zBWLYgSANMpThFddrZZkEbGHgUdt5UJjZfnlN9BGCiUBrf+nvbRupSjMvqzwnQN0qwCmOxITt1cfywA== +"@eslint/eslintrc@^0.4.3": + version "0.4.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c" + integrity sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw== dependencies: ajv "^6.12.4" debug "^4.1.1" espree "^7.3.0" - globals "^12.1.0" + globals "^13.9.0" ignore "^4.0.6" import-fresh "^3.2.1" js-yaml "^3.13.1" - lodash "^4.17.19" minimatch "^3.0.4" strip-json-comments "^3.1.1" @@ -595,6 +599,20 @@ "@hapi/bourne" "2.x.x" "@hapi/hoek" "9.x.x" +"@humanwhocodes/config-array@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" + integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg== + dependencies: + "@humanwhocodes/object-schema" "^1.2.0" + debug "^4.1.1" + minimatch "^3.0.4" + +"@humanwhocodes/object-schema@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz#87de7af9c231826fdd68ac7258f77c429e0e5fcf" + integrity sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w== + "@iarna/cli@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@iarna/cli/-/cli-1.2.0.tgz#0f7af5e851afe895104583c4ca07377a8094d641" @@ -780,17 +798,6 @@ source-map "^0.6.1" write-file-atomic "^3.0.0" -"@jest/types@^26.6.1": - version "26.6.1" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.1.tgz#2638890e8031c0bc8b4681e0357ed986e2f866c5" - integrity sha512-ywHavIKNpAVrStiRY5wiyehvcktpijpItvGiK72RAn5ctqmzvPk8OvKnvHeBqa1XdQr959CTWAJMqxI8BTibyg== - dependencies: - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^15.0.0" - chalk "^4.0.0" - "@jest/types@^26.6.2": version "26.6.2" resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e" @@ -1289,7 +1296,7 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2" integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg== -"@tsconfig/node16@^1.0.1": +"@tsconfig/node16@^1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== @@ -1508,15 +1515,6 @@ "@types/minimatch" "*" "@types/node" "*" -"@types/got@^9.6.9": - version "9.6.11" - resolved "https://registry.yarnpkg.com/@types/got/-/got-9.6.11.tgz#482b402cc5ee459481aeeadb08142ebb1a9afb26" - integrity sha512-dr3IiDNg5TDesGyuwTrN77E1Cd7DCdmCFtEfSGqr83jMMtcwhf/SGPbN2goY4JUWQfvxwY56+e5tjfi+oXeSdA== - dependencies: - "@types/node" "*" - "@types/tough-cookie" "*" - form-data "^2.5.0" - "@types/graceful-fs@^4.1.2": version "4.1.3" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.3.tgz#039af35fe26bec35003e8d86d2ee9c586354348f" @@ -1538,12 +1536,7 @@ "@types/shot" "*" joi "^17.3.0" -"@types/history@*": - version "4.7.6" - resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.6.tgz#ed8fc802c45b8e8f54419c2d054e55c9ea344356" - integrity sha512-GRTZLeLJ8ia00ZH8mxMO8t0aC9M1N9bN461Z2eaRurJo6Fpa+utgCwLzI4jQHcrdzuzp5WPN9jRwpsCQ1VhJ5w== - -"@types/history@^4.7.8": +"@types/history@*", "@types/history@^4.7.8": version "4.7.8" resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934" integrity sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA== @@ -1570,30 +1563,21 @@ "@types/relateurl" "*" "@types/uglify-js" "*" -"@types/html-webpack-plugin@^3.2.3": - version "3.2.3" - resolved "https://registry.yarnpkg.com/@types/html-webpack-plugin/-/html-webpack-plugin-3.2.3.tgz#865323e30e82560c0ca898dbf9f6f9d1c541cd7f" - integrity sha512-Y7dsVhTn75IaD4lMIY02UP1L8e0ou8KQu8DKPJAegEFKdJR28/8ejayDG8ykfR0DtYCx3dCEHIkdpN8AOB6txQ== +"@types/html-webpack-plugin@^3.2.6": + version "3.2.6" + resolved "https://registry.yarnpkg.com/@types/html-webpack-plugin/-/html-webpack-plugin-3.2.6.tgz#07951aaf0fa260dbf626f9644f1d13106d537625" + integrity sha512-U8uJSvlf9lwrKG6sKFnMhqY4qJw2QXad+PHlX9sqEXVUMilVt96aVvFde73tzsdXD+QH9JS6kEytuGO2JcYZog== dependencies: "@types/html-minifier" "*" - "@types/tapable" "*" - "@types/webpack" "*" + "@types/tapable" "^1" + "@types/webpack" "^4" "@types/http-cache-semantics@*": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz#9140779736aa2655635ee756e2467d787cfe8a2a" integrity sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A== -"@types/http-proxy-middleware@*": - version "0.19.3" - resolved "https://registry.yarnpkg.com/@types/http-proxy-middleware/-/http-proxy-middleware-0.19.3.tgz#b2eb96fbc0f9ac7250b5d9c4c53aade049497d03" - integrity sha512-lnBTx6HCOUeIJMLbI/LaL5EmdKLhczJY5oeXZpX/cXE4rRqb3RmV7VcMpiEfYkmTjipv3h7IAyIINe4plEv7cA== - dependencies: - "@types/connect" "*" - "@types/http-proxy" "*" - "@types/node" "*" - -"@types/http-proxy@*", "@types/http-proxy@^1.17.7": +"@types/http-proxy@^1.17.5", "@types/http-proxy@^1.17.7": version "1.17.7" resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.7.tgz#30ea85cc2c868368352a37f0d0d3581e24834c6f" integrity sha512-9hdj6iXH64tHSLTY+Vt2eYOGzSogC+JQ2H7bdPWkuh7KXP5qLllWx++t+K9Wk556c3dkDdPws/SpMRi0sdCT1w== @@ -1626,18 +1610,10 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@*": - version "26.0.15" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.15.tgz#12e02c0372ad0548e07b9f4e19132b834cb1effe" - integrity sha512-s2VMReFXRg9XXxV+CW9e5Nz8fH2K1aEhwgjUqPPbQd7g95T0laAcvLv032EhFHIa5GHsZ8W7iJEQVaJq6k3Gog== - dependencies: - jest-diff "^26.0.0" - pretty-format "^26.0.0" - -"@types/jest@^26.0.22": - version "26.0.22" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.22.tgz#8308a1debdf1b807aa47be2838acdcd91e88fbe6" - integrity sha512-eeWwWjlqxvBxc4oQdkueW5OF/gtfSceKk4OnOAGlUSwS/liBRtZppbJuz1YkgbrbfGOoeBHun9fOvXnjNwrSOw== +"@types/jest@*", "@types/jest@^26.0.24": + version "26.0.24" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.24.tgz#943d11976b16739185913a1936e0de0c4a7d595a" + integrity sha512-E/X5Vib8BWqZNRlDxj9vYXhsDwPYbPINqKF9BsnSoon4RQ0D9moEuLD8txgyypFLH7J4+Lho9Nr/c8H0Fi+17w== dependencies: jest-diff "^26.0.0" pretty-format "^26.0.0" @@ -1764,12 +1740,7 @@ "@types/node" "*" form-data "^3.0.0" -"@types/node@*", "@types/node@^14.6.2": - version "14.17.5" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.5.tgz#b59daf6a7ffa461b5648456ca59050ba8e40ed54" - integrity sha512-bjqH2cX/O33jXT/UmReo2pM7DIJREPMnarixbQ57DOOzzFaI6D2+IcwaJQaJpv0M1E9TIhPCYVxrkcityLjlqA== - -"@types/node@14.17.14": +"@types/node@*", "@types/node@14.17.14", "@types/node@^14.6.2": version "14.17.14" resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.14.tgz#6fda9785b41570eb628bac27be4b602769a3f938" integrity sha512-rsAj2u8Xkqfc332iXV12SqIsjVi07H479bOP4q94NAcjzmAvapumEhuVIt53koEf7JFrpjgNKjBga5Pnn/GL8A== @@ -1839,10 +1810,10 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== -"@types/proper-lockfile@^4.1.1": - version "4.1.1" - resolved "https://registry.yarnpkg.com/@types/proper-lockfile/-/proper-lockfile-4.1.1.tgz#99f026cbfdbe6305bdd454ffd5fefc1bd064beb1" - integrity sha512-HAjVfDa73pFgivViHyDu8HHHcds+W4MgOuZZAdyFJrHS8ngtCXmhl4hc2YXqSOwO6Bsa+iF2Sgxb2+gv874VOQ== +"@types/proper-lockfile@^4.1.2": + version "4.1.2" + resolved "https://registry.yarnpkg.com/@types/proper-lockfile/-/proper-lockfile-4.1.2.tgz#49537cee7134055ee13a1833b76a1c298f39bb26" + integrity sha512-kd4LMvcnpYkspDcp7rmXKedn8iJSCoa331zRRamUp5oanKt/CefbEGPQP7G89enz7sKD4bvsr8mHSsC8j5WOvA== dependencies: "@types/retry" "*" @@ -1885,10 +1856,10 @@ hoist-non-react-statics "^3.3.0" redux "^4.0.0" -"@types/react-router-dom@^5.1.6": - version "5.1.6" - resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.1.6.tgz#07b14e7ab1893a837c8565634960dc398564b1fb" - integrity sha512-gjrxYqxz37zWEdMVvQtWPFMFj1dRDb4TGOcgyOfSXTrEXdF92L00WE3C471O3TV/RF1oskcStkXsOU0Ete4s/g== +"@types/react-router-dom@^5.3.1": + version "5.3.1" + resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.3.1.tgz#76700ccce6529413ec723024b71f01fc77a4a980" + integrity sha512-UvyRy73318QI83haXlaMwmklHHzV9hjl3u71MmM6wYNu0hOVk9NLTa0vGukf8zXUqnwz4O06ig876YSPpeK28A== dependencies: "@types/history" "*" "@types/react" "*" @@ -1911,10 +1882,10 @@ "@types/react-dom" "*" "@types/react-transition-group" "*" -"@types/react-table@^7.7.0": - version "7.7.0" - resolved "https://registry.yarnpkg.com/@types/react-table/-/react-table-7.7.0.tgz#cb441a03af7303efd570b816b5ed0ea4d35ce705" - integrity sha512-xx2PJO9a0FEY7s96KWmadrhWNGxkNZgMnoKbXKPepqzz4FHKVds1tPDqWOU7NpP+dAsRli/wn0ysZ4MxWZI4Nw== +"@types/react-table@^7.7.6": + version "7.7.6" + resolved "https://registry.yarnpkg.com/@types/react-table/-/react-table-7.7.6.tgz#4899ccc46b4a1de08cc1daf120842e37e112e51e" + integrity sha512-ZMFHh1sG5AGDmhVRpz9mgGByGmBFAqnZ7QnyqGa5iAlKtcSC3vb/gul47lM0kJ1uvlawc+qN5k+++pe+GBdJ+g== dependencies: "@types/react" "*" @@ -1969,10 +1940,10 @@ resolved "https://registry.yarnpkg.com/@types/relateurl/-/relateurl-0.2.28.tgz#6bda7db8653fa62643f5ee69e9f69c11a392e3a6" integrity sha1-a9p9uGU/piZD9e5p6facEaOS46Y= -"@types/request-promise-native@^1.0.17": - version "1.0.17" - resolved "https://registry.yarnpkg.com/@types/request-promise-native/-/request-promise-native-1.0.17.tgz#74a2d7269aebf18b9bdf35f01459cf0a7bfc7fab" - integrity sha512-05/d0WbmuwjtGMYEdHIBZ0tqMJJQ2AD9LG2F6rKNBGX1SSFR27XveajH//2N/XYtual8T9Axwl+4v7oBtPUZqg== +"@types/request-promise-native@^1.0.18": + version "1.0.18" + resolved "https://registry.yarnpkg.com/@types/request-promise-native/-/request-promise-native-1.0.18.tgz#437ee2d0b772e01c9691a983b558084b4b3efc2c" + integrity sha512-tPnODeISFc/c1LjWyLuZUY+Z0uLB3+IMfNoQyDEi395+j6kTFTTRAqjENjoPJUid4vHRGEozoTrcTrfZM+AcbA== dependencies: "@types/request" "*" @@ -2054,12 +2025,7 @@ dependencies: "@types/node" "*" -"@types/tapable@*", "@types/tapable@^1.0.5": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.5.tgz#9adbc12950582aa65ead76bffdf39fe0c27a3c02" - integrity sha512-/gG2M/Imw7cQFp8PGvz/SwocNrmKFjFsm5Pb8HdbHkZ1K8pmuPzOX4VeVoiEecFCVf4CsN1r3/BRvx+6sNqwtQ== - -"@types/tapable@^1": +"@types/tapable@^1", "@types/tapable@^1.0.5": version "1.0.8" resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.8.tgz#b94a4391c85666c7b73299fd3ad79d4faa435310" integrity sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ== @@ -2140,21 +2106,21 @@ dependencies: "@types/node" "*" -"@types/webpack-dev-server@^3.11.1": - version "3.11.1" - resolved "https://registry.yarnpkg.com/@types/webpack-dev-server/-/webpack-dev-server-3.11.1.tgz#f8f4dac1da226d530bd15a1d5dc34b23ba766ccb" - integrity sha512-rIb+LtUkKnh7+oIJm3WiMJONd71Q0lZuqGLcSqhZ5qjN9gV/CNmZe7Bai+brnBPZ/KVYOsr+4bFLiNZwjBicLw== +"@types/webpack-dev-server@^3.11.6": + version "3.11.6" + resolved "https://registry.yarnpkg.com/@types/webpack-dev-server/-/webpack-dev-server-3.11.6.tgz#d8888cfd2f0630203e13d3ed7833a4d11b8a34dc" + integrity sha512-XCph0RiiqFGetukCTC3KVnY1jwLcZ84illFRMbyFzCcWl90B/76ew0tSqF46oBhnLC4obNDG7dMO0JfTN0MgMQ== dependencies: "@types/connect-history-api-fallback" "*" "@types/express" "*" - "@types/http-proxy-middleware" "*" "@types/serve-static" "*" - "@types/webpack" "*" + "@types/webpack" "^4" + http-proxy-middleware "^1.0.0" -"@types/webpack-env@^1.15.2": - version "1.15.2" - resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.15.2.tgz#927997342bb9f4a5185a86e6579a0a18afc33b0a" - integrity sha512-67ZgZpAlhIICIdfQrB5fnDvaKFcDxpKibxznfYRVAT4mQE41Dido/3Ty+E3xGBmTogc5+0Qb8tWhna+5B8z1iQ== +"@types/webpack-env@^1.16.2": + version "1.16.2" + resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.16.2.tgz#8db514b059c1b2ae14ce9d7bb325296de6a9a0fa" + integrity sha512-vKx7WNQNZDyJveYcHAm9ZxhqSGLYwoyLhrHjLBOkw3a7cT76sTdjgtwyijhk1MaHyRIuSztcVwrUOO/NEu68Dw== "@types/webpack-node-externals@^1.7.1": version "1.7.1" @@ -2172,10 +2138,10 @@ "@types/source-list-map" "*" source-map "^0.7.3" -"@types/webpack@*", "@types/webpack@^4", "@types/webpack@^4.41.17", "@types/webpack@^4.41.8": - version "4.41.30" - resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.30.tgz#fd3db6d0d41e145a8eeeafcd3c4a7ccde9068ddc" - integrity sha512-GUHyY+pfuQ6haAfzu4S14F+R5iGRwN6b2FRNJY7U0NilmFAqbsOfK6j1HwuLBAqwRIT+pVdNDJGJ6e8rpp0KHA== +"@types/webpack@*", "@types/webpack@^4", "@types/webpack@^4.41.31", "@types/webpack@^4.41.8": + version "4.41.31" + resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.31.tgz#c35f252a3559ddf9c85c0d8b0b42019025e581aa" + integrity sha512-/i0J7sepXFIp1ZT7FjUGi1eXMCg8HCCzLJEQkKsOtbJFontsJLolBcDC+3qxn5pPwiCt1G0ZdRmYRzNBtvpuGQ== dependencies: "@types/node" "*" "@types/tapable" "^1" @@ -2224,28 +2190,29 @@ dependencies: "@types/node" "*" -"@typescript-eslint/eslint-plugin@^4.29.0", "@typescript-eslint/eslint-plugin@^4.5.0": - version "4.29.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.29.0.tgz#b866c9cd193bfaba5e89bade0015629ebeb27996" - integrity sha512-eiREtqWRZ8aVJcNru7cT/AMVnYd9a2UHsfZT8MR1dW3UUEg6jDv9EQ9Cq4CUPZesyQ58YUpoAADGv71jY8RwgA== +"@typescript-eslint/eslint-plugin@^4.33.0", "@typescript-eslint/eslint-plugin@^4.5.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz#c24dc7c8069c7706bc40d99f6fa87edcb2005276" + integrity sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg== dependencies: - "@typescript-eslint/experimental-utils" "4.29.0" - "@typescript-eslint/scope-manager" "4.29.0" + "@typescript-eslint/experimental-utils" "4.33.0" + "@typescript-eslint/scope-manager" "4.33.0" debug "^4.3.1" functional-red-black-tree "^1.0.1" + ignore "^5.1.8" regexpp "^3.1.0" semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/experimental-utils@4.29.0": - version "4.29.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.29.0.tgz#19b1417602d0e1ef325b3312ee95f61220542df5" - integrity sha512-FpNVKykfeaIxlArLUP/yQfv/5/3rhl1ov6RWgud4OgbqWLkEq7lqgQU9iiavZRzpzCRQV4XddyFz3wFXdkiX9w== +"@typescript-eslint/experimental-utils@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz#6f2a786a4209fa2222989e9380b5331b2810f7fd" + integrity sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q== dependencies: "@types/json-schema" "^7.0.7" - "@typescript-eslint/scope-manager" "4.29.0" - "@typescript-eslint/types" "4.29.0" - "@typescript-eslint/typescript-estree" "4.29.0" + "@typescript-eslint/scope-manager" "4.33.0" + "@typescript-eslint/types" "4.33.0" + "@typescript-eslint/typescript-estree" "4.33.0" eslint-scope "^5.1.1" eslint-utils "^3.0.0" @@ -2259,14 +2226,6 @@ "@typescript-eslint/typescript-estree" "4.29.1" debug "^4.3.1" -"@typescript-eslint/scope-manager@4.29.0": - version "4.29.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.29.0.tgz#cf5474f87321bedf416ef65839b693bddd838599" - integrity sha512-HPq7XAaDMM3DpmuijxLV9Io8/6pQnliiXMQUcAdjpJJSR+fdmbD/zHCd7hMkjJn04UQtCQBtshgxClzg6NIS2w== - dependencies: - "@typescript-eslint/types" "4.29.0" - "@typescript-eslint/visitor-keys" "4.29.0" - "@typescript-eslint/scope-manager@4.29.1": version "4.29.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.29.1.tgz#f25da25bc6512812efa2ce5ebd36619d68e61358" @@ -2275,28 +2234,23 @@ "@typescript-eslint/types" "4.29.1" "@typescript-eslint/visitor-keys" "4.29.1" -"@typescript-eslint/types@4.29.0": - version "4.29.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.29.0.tgz#c8f1a1e4441ea4aca9b3109241adbc145f7f8a4e" - integrity sha512-2YJM6XfWfi8pgU2HRhTp7WgRw78TCRO3dOmSpAvIQ8MOv4B46JD2chnhpNT7Jq8j0APlIbzO1Bach734xxUl4A== +"@typescript-eslint/scope-manager@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz#d38e49280d983e8772e29121cf8c6e9221f280a3" + integrity sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ== + dependencies: + "@typescript-eslint/types" "4.33.0" + "@typescript-eslint/visitor-keys" "4.33.0" "@typescript-eslint/types@4.29.1": version "4.29.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.29.1.tgz#94cce6cf7cc83451df03339cda99d326be2feaf5" integrity sha512-Jj2yu78IRfw4nlaLtKjVaGaxh/6FhofmQ/j8v3NXmAiKafbIqtAPnKYrf0sbGjKdj0hS316J8WhnGnErbJ4RCA== -"@typescript-eslint/typescript-estree@4.29.0": - version "4.29.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.29.0.tgz#af7ab547757b86c91bfdbc54ff86845410856256" - integrity sha512-8ZpNHDIOyqzzgZrQW9+xQ4k5hM62Xy2R4RPO3DQxMc5Rq5QkCdSpk/drka+DL9w6sXNzV5nrdlBmf8+x495QXQ== - dependencies: - "@typescript-eslint/types" "4.29.0" - "@typescript-eslint/visitor-keys" "4.29.0" - debug "^4.3.1" - globby "^11.0.3" - is-glob "^4.0.1" - semver "^7.3.5" - tsutils "^3.21.0" +"@typescript-eslint/types@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.33.0.tgz#a1e59036a3b53ae8430ceebf2a919dc7f9af6d72" + integrity sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ== "@typescript-eslint/typescript-estree@4.29.1": version "4.29.1" @@ -2311,13 +2265,18 @@ semver "^7.3.5" tsutils "^3.21.0" -"@typescript-eslint/visitor-keys@4.29.0": - version "4.29.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.29.0.tgz#1ff60f240def4d85ea68d4fd2e4e9759b7850c04" - integrity sha512-LoaofO1C/jAJYs0uEpYMXfHboGXzOJeV118X4OsZu9f7rG7Pr9B3+4HTU8+err81rADa4xfQmAxnRnPAI2jp+Q== +"@typescript-eslint/typescript-estree@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz#0dfb51c2908f68c5c08d82aefeaf166a17c24609" + integrity sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA== dependencies: - "@typescript-eslint/types" "4.29.0" - eslint-visitor-keys "^2.0.0" + "@typescript-eslint/types" "4.33.0" + "@typescript-eslint/visitor-keys" "4.33.0" + debug "^4.3.1" + globby "^11.0.3" + is-glob "^4.0.1" + semver "^7.3.5" + tsutils "^3.21.0" "@typescript-eslint/visitor-keys@4.29.1": version "4.29.1" @@ -2327,6 +2286,14 @@ "@typescript-eslint/types" "4.29.1" eslint-visitor-keys "^2.0.0" +"@typescript-eslint/visitor-keys@4.33.0": + version "4.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz#2a22f77a41604289b7a186586e9ec48ca92ef1dd" + integrity sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg== + dependencies: + "@typescript-eslint/types" "4.33.0" + eslint-visitor-keys "^2.0.0" + "@webassemblyjs/ast@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" @@ -2523,10 +2490,10 @@ acorn-globals@^6.0.0: acorn "^7.1.1" acorn-walk "^7.1.1" -acorn-jsx@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe" - integrity sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ== +acorn-jsx@^5.3.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn-node@^1.6.1: version "1.8.2" @@ -2537,15 +2504,15 @@ acorn-node@^1.6.1: acorn-walk "^7.0.0" xtend "^4.0.2" -acorn-walk@^7.0.0: +acorn-walk@^7.0.0, acorn-walk@^7.1.1: version "7.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== -acorn-walk@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.1.1.tgz#345f0dffad5c735e7373d2fec9a1023e6a44b83e" - integrity sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ== +acorn-walk@^8.1.1: + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== acorn@^6.4.1: version "6.4.1" @@ -2557,10 +2524,10 @@ acorn@^7.0.0, acorn@^7.1.1, acorn@^7.4.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.2.4: - version "8.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.4.1.tgz#56c36251fc7cabc7096adc18f05afe814321a28c" - integrity sha512-asabaBSkEKosYKMITunzX177CXxQ4Q8BSSzMTKD+FefUhipQC70gfW5SiUDhYQ3vk8G+81HqQk7Fv9OXwwn9KA== +acorn@^8.2.4, acorn@^8.4.1: + version "8.5.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.5.0.tgz#4512ccb99b3698c752591e9bb4472e38ad43cee2" + integrity sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q== agent-base@4, agent-base@^4.3.0: version "4.3.0" @@ -2590,14 +2557,6 @@ agentkeepalive@^3.4.1: dependencies: humanize-ms "^1.2.1" -aggregate-error@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.0.1.tgz#db2fe7246e536f40d9b5442a39e117d7dd6a24e0" - integrity sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA== - dependencies: - clean-stack "^2.0.0" - indent-string "^4.0.0" - aggregate-error@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" @@ -2626,6 +2585,16 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.0, ajv@^6.12.2, ajv@^6.12.4, ajv json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^8.0.1: + version "8.6.3" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.3.tgz#11a66527761dc3e9a3845ea775d2d3c0414e8764" + integrity sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + ansi-align@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f" @@ -2677,6 +2646,11 @@ ansi-regex@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -2720,7 +2694,7 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" -anymatch@^3.0.0, anymatch@~3.1.2: +anymatch@^3.0.0, anymatch@^3.0.3, anymatch@~3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== @@ -2728,14 +2702,6 @@ anymatch@^3.0.0, anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" -anymatch@^3.0.3: - version "3.1.1" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" - integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - app-builder-bin@3.7.1: version "3.7.1" resolved "https://registry.yarnpkg.com/app-builder-bin/-/app-builder-bin-3.7.1.tgz#cb0825c5e12efc85b196ac3ed9c89f076c61040e" @@ -2948,10 +2914,10 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= -astral-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" - integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== async-each@^1.0.1: version "1.0.3" @@ -3138,11 +3104,6 @@ base64-js@^1.5.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -base64url@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/base64url/-/base64url-3.0.1.tgz#6399d572e2bc3f90a9a8b22d5dbb0a32d33f788d" - integrity sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A== - base@^0.11.1: version "0.11.2" resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" @@ -3501,6 +3462,14 @@ builder-util-runtime@8.8.1: debug "^4.3.2" sax "^1.2.4" +builder-util-runtime@8.9.0: + version "8.9.0" + resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-8.9.0.tgz#5d8e7ef101c0bf8a54fec3a70f0c551e8fdd58c2" + integrity sha512-XT7asdRMiSqUj/7EtvSW1mzVARvnhj0Nv4Ei4kD0p8GrKMFJt1Nadm4XwD+PrI2+srrtU+l8JMoBgSe4LX8EmQ== + dependencies: + debug "^4.3.2" + sax "^1.2.4" + builder-util@22.13.1: version "22.13.1" resolved "https://registry.yarnpkg.com/builder-util/-/builder-util-22.13.1.tgz#fb2165c725b9405f0605a765cf91ec1870995ada" @@ -4495,10 +4464,10 @@ css-box-model@^1.2.0: dependencies: tiny-invariant "^1.0.6" -css-loader@^5.2.6: - version "5.2.6" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.6.tgz#c3c82ab77fea1f360e587d871a6811f4450cc8d1" - integrity sha512-0wyN5vXMQZu6BvjbrPdUJvkCzGEO24HC7IS7nW4llc6BBFC+zwR9CKtYGv63Puzsg10L/o12inMY5/2ByzfD6w== +css-loader@^5.2.7: + version "5.2.7" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.7.tgz#9b9f111edf6fb2be5dc62525644cbc9c232064ae" + integrity sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg== dependencies: icss-utils "^5.1.0" loader-utils "^2.0.0" @@ -4669,12 +4638,12 @@ debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2: dependencies: ms "2.1.2" -debug@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.0.tgz#373687bffa678b38b1cd91f861b63850035ddc87" - integrity sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg== +debug@4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" + integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== dependencies: - ms "^2.1.1" + ms "2.1.2" debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.6: version "3.2.7" @@ -4746,13 +4715,13 @@ deep-is@^0.1.3, deep-is@~0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= -deepdash@^5.3.5: - version "5.3.5" - resolved "https://registry.yarnpkg.com/deepdash/-/deepdash-5.3.5.tgz#611bec9c1f2829832d21971dcbefe712e408647d" - integrity sha512-1ZdPPCI1pCEqeAWGSw+Nbpb/2iIV4w3sGPc22H/PDtcApb8+psTzPIoOVD040iBaT2wqZab29Kjrz6G+cd5mqQ== +deepdash@^5.3.9: + version "5.3.9" + resolved "https://registry.yarnpkg.com/deepdash/-/deepdash-5.3.9.tgz#2aa92570d7b1787ed281e2fafe0be24df00ddfe4" + integrity sha512-GRzJ0q9PDj2T+J2fX+b+TlUa2NlZ11l6vJ8LHNKVGeZ8CfxCuJaCychTq07iDRTvlfO8435jlvVS1QXBrW9kMg== dependencies: - lodash "^4.17.20" - lodash-es "^4.17.20" + lodash "^4.17.21" + lodash-es "^4.17.21" deepmerge@^4.2.2: version "4.2.2" @@ -4836,6 +4805,20 @@ del@^4.1.1: pify "^4.0.1" rimraf "^2.6.3" +del@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/del/-/del-6.0.0.tgz#0b40d0332cea743f1614f818be4feb717714c952" + integrity sha512-1shh9DQ23L16oXSZKB2JxpL7iMy2E0S9d517ptA1P8iw0alkPtQcrKH7ru31rYtKwF499HkTu+DRzq3TCKDFRQ== + dependencies: + globby "^11.0.1" + graceful-fs "^4.2.4" + is-glob "^4.0.1" + is-path-cwd "^2.2.0" + is-path-inside "^3.0.2" + p-map "^4.0.0" + rimraf "^3.0.2" + slash "^3.0.0" + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -4916,11 +4899,6 @@ didyoumean@^1.2.1: resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.1.tgz#e92edfdada6537d484d73c0172fd1eba0c4976ff" integrity sha1-6S7f2tplN9SE1zwBcv0eugxJdv8= -diff-sequences@^26.5.0: - version "26.5.0" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.5.0.tgz#ef766cf09d43ed40406611f11c6d8d9dd8b2fefd" - integrity sha512-ZXx86srb/iYy6jG71k++wBN9P9J05UNQ5hQHQd9MtMPvcqXPx/vKU69jfHV637D00Q2gSgPk2D+jSx3l1lDW/Q== - diff-sequences@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" @@ -5246,13 +5224,13 @@ electron-publish@22.13.1: lazy-val "^1.0.5" mime "^2.5.2" -electron-updater@^4.4.3: - version "4.5.2" - resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-4.5.2.tgz#2a2bf3659a83ea41519bf809069aad2005bc970a" - integrity sha512-W6V+nOcSYTQnRyHaxQsDoPdyPvPW4D78VP7HJx1125mkXVbbejEWAPsHmheQVSTEPDcy3YKkVGbFZAKuH5/H4w== +electron-updater@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/electron-updater/-/electron-updater-4.6.0.tgz#17fc25a16d28f749cd3980430461bf7e6927d925" + integrity sha512-evvAwfDn100sIBZ/GLDpcWtWfaug48RGxVuYiSp0foKQkaXJGFyqYy5ADyt+HPML/mSTMPBvUq55cLEW45BroQ== dependencies: "@types/semver" "^7.3.6" - builder-util-runtime "8.8.1" + builder-util-runtime "8.9.0" fs-extra "^10.0.0" js-yaml "^4.1.0" lazy-val "^1.0.5" @@ -5268,10 +5246,10 @@ electron-window-state@^5.0.3: jsonfile "^4.0.0" mkdirp "^0.5.1" -electron@^12.0.17: - version "12.0.17" - resolved "https://registry.yarnpkg.com/electron/-/electron-12.0.17.tgz#9707c9bfd0a29ae63b8b66f1b3acf8894f7b63f4" - integrity sha512-jkOMKSEj/X9i++5LD7NKqYK/ORi6H0kHYk6rrvcJNQfbDpYX5lxNNexZ2ikPeKxS2B84+WcSFrw5Ce9y8B+pmA== +electron@^13.5.1: + version "13.5.1" + resolved "https://registry.yarnpkg.com/electron/-/electron-13.5.1.tgz#76c02c39be228532f886a170b472cbd3d93f0d0f" + integrity sha512-ZyxhIhmdaeE3xiIGObf0zqEyCyuIDqZQBv9NKX8w5FNzGm87j4qR0H1+GQg6vz+cA1Nnv1x175Zvimzc0/UwEQ== dependencies: "@electron/get" "^1.0.1" "@types/node" "^14.6.2" @@ -5486,28 +5464,23 @@ es6-promisify@^5.0.0: dependencies: es6-promise "^4.0.3" -esbuild-loader@^2.13.1: - version "2.13.1" - resolved "https://registry.yarnpkg.com/esbuild-loader/-/esbuild-loader-2.13.1.tgz#9c89e654390a9a25d99b2f6d803ade30f4335418" - integrity sha512-Tzc5nB5tVUmigXz6m4j1OYozJCjdix7E9vtd5RaE54fqz2Rz34Is9S8FbAf8uqR4xvQUBAXIi6Jkn1OeMxw2aQ== +esbuild-loader@^2.15.1: + version "2.15.1" + resolved "https://registry.yarnpkg.com/esbuild-loader/-/esbuild-loader-2.15.1.tgz#5a3940f5d20317f1a35720efa33e933f97c923e9" + integrity sha512-JRBL6uTeWplMbylNBt9gxLKMjD8wKnqGq786QV/cm/nPBSNA9/kC7/vNwCXTDPfYqHoWsjyfH7ub9ekN0kdAYQ== dependencies: - esbuild "^0.11.19" + esbuild "^0.12.21" joycon "^3.0.1" json5 "^2.2.0" loader-utils "^2.0.0" tapable "^2.2.0" - type-fest "^1.0.1" + type-fest "^1.4.0" webpack-sources "^2.2.0" -esbuild@^0.11.19: - version "0.11.23" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.11.23.tgz#c42534f632e165120671d64db67883634333b4b8" - integrity sha512-iaiZZ9vUF5wJV8ob1tl+5aJTrwDczlvGP0JoMmnpC2B0ppiMCu8n8gmy5ZTGl5bcG081XBVn+U+jP+mPFm5T5Q== - -esbuild@^0.12.24: - version "0.12.24" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.12.24.tgz#21966fad25a80f368ed308101e88102bce0dc68f" - integrity sha512-C0ibY+HsXzYB6L/pLWEiWjMpghKsIc58Q5yumARwBQsHl9DXPakW+5NI/Y9w4YXiz0PEP6XTGTT/OV4Nnsmb4A== +esbuild@^0.12.21, esbuild@^0.12.24: + version "0.12.29" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.12.29.tgz#be602db7c4dc78944a9dbde0d1ea19d36c1f882d" + integrity sha512-w/XuoBCSwepyiZtIRsKsetiLDUVGPVw1E/R3VTFSecIy8UR7Cq3SOtwKHJMFoVqqVG36aGkzh4e8BvpO1Fdc7g== escalade@^3.1.1: version "3.1.1" @@ -5615,7 +5588,7 @@ eslint-scope@^4.0.3: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint-scope@^5.1.0, eslint-scope@^5.1.1: +eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== @@ -5647,29 +5620,32 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== -eslint@^7.11.0: - version "7.14.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.14.0.tgz#2d2cac1d28174c510a97b377f122a5507958e344" - integrity sha512-5YubdnPXrlrYAFCKybPuHIAH++PINe1pmKNc5wQRB9HSbqIK1ywAnntE3Wwua4giKu0bjligf1gLF6qxMGOYRA== +eslint@^7.11.0, eslint@^7.32.0: + version "7.32.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" + integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA== dependencies: - "@babel/code-frame" "^7.0.0" - "@eslint/eslintrc" "^0.2.1" + "@babel/code-frame" "7.12.11" + "@eslint/eslintrc" "^0.4.3" + "@humanwhocodes/config-array" "^0.5.0" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" debug "^4.0.1" doctrine "^3.0.0" enquirer "^2.3.5" + escape-string-regexp "^4.0.0" eslint-scope "^5.1.1" eslint-utils "^2.1.0" eslint-visitor-keys "^2.0.0" - espree "^7.3.0" - esquery "^1.2.0" + espree "^7.3.1" + esquery "^1.4.0" esutils "^2.0.2" - file-entry-cache "^5.0.1" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" functional-red-black-tree "^1.0.1" - glob-parent "^5.0.0" - globals "^12.1.0" + glob-parent "^5.1.2" + globals "^13.6.0" ignore "^4.0.6" import-fresh "^3.0.0" imurmurhash "^0.1.4" @@ -5677,7 +5653,7 @@ eslint@^7.11.0: js-yaml "^3.13.1" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" - lodash "^4.17.19" + lodash.merge "^4.6.2" minimatch "^3.0.4" natural-compare "^1.4.0" optionator "^0.9.1" @@ -5686,59 +5662,17 @@ eslint@^7.11.0: semver "^7.2.1" strip-ansi "^6.0.0" strip-json-comments "^3.1.0" - table "^5.2.3" + table "^6.0.9" text-table "^0.2.0" v8-compile-cache "^2.0.3" -eslint@^7.7.0: - version "7.7.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.7.0.tgz#18beba51411927c4b64da0a8ceadefe4030d6073" - integrity sha512-1KUxLzos0ZVsyL81PnRN335nDtQ8/vZUD6uMtWbF+5zDtjKcsklIi78XoE0MVL93QvWTu+E5y44VyyCsOMBrIg== - dependencies: - "@babel/code-frame" "^7.0.0" - ajv "^6.10.0" - chalk "^4.0.0" - cross-spawn "^7.0.2" - debug "^4.0.1" - doctrine "^3.0.0" - enquirer "^2.3.5" - eslint-scope "^5.1.0" - eslint-utils "^2.1.0" - eslint-visitor-keys "^1.3.0" - espree "^7.2.0" - esquery "^1.2.0" - esutils "^2.0.2" - file-entry-cache "^5.0.1" - functional-red-black-tree "^1.0.1" - glob-parent "^5.0.0" - globals "^12.1.0" - ignore "^4.0.6" - import-fresh "^3.0.0" - imurmurhash "^0.1.4" - is-glob "^4.0.0" - js-yaml "^3.13.1" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" - lodash "^4.17.19" - minimatch "^3.0.4" - natural-compare "^1.4.0" - optionator "^0.9.1" - progress "^2.0.0" - regexpp "^3.1.0" - semver "^7.2.1" - strip-ansi "^6.0.0" - strip-json-comments "^3.1.0" - table "^5.2.3" - text-table "^0.2.0" - v8-compile-cache "^2.0.3" - -espree@^7.2.0, espree@^7.3.0: - version "7.3.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.0.tgz#dc30437cf67947cf576121ebd780f15eeac72348" - integrity sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw== +espree@^7.3.0, espree@^7.3.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" + integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== dependencies: acorn "^7.4.0" - acorn-jsx "^5.2.0" + acorn-jsx "^5.3.1" eslint-visitor-keys "^1.3.0" esprima@1.2.2: @@ -5751,10 +5685,10 @@ esprima@^4.0.0, esprima@^4.0.1: resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57" - integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ== +esquery@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" + integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== dependencies: estraverse "^5.1.0" @@ -6031,7 +5965,7 @@ extsprintf@^1.2.0: resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= -fast-deep-equal@^3.1.1: +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== @@ -6118,12 +6052,12 @@ figgy-pudding@^3.4.1, figgy-pudding@^3.5.1: resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== -file-entry-cache@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" - integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== dependencies: - flat-cache "^2.0.1" + flat-cache "^3.0.4" file-js@0.3.0: version "0.3.0" @@ -6252,19 +6186,18 @@ findup-sync@^3.0.0: micromatch "^3.0.4" resolve-dir "^1.0.1" -flat-cache@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" - integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== dependencies: - flatted "^2.0.0" - rimraf "2.6.3" - write "1.0.3" + flatted "^3.1.0" + rimraf "^3.0.2" -flatted@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" - integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== +flatted@^3.1.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.2.tgz#64bfed5cb68fe3ca78b3eb214ad97b63bedce561" + integrity sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA== flex.box@^3.4.4: version "3.4.4" @@ -6411,7 +6344,7 @@ fs-extra@^9.0.0, fs-extra@^9.0.1: jsonfile "^6.0.1" universalify "^2.0.0" -fs-minipass@^1.2.5: +fs-minipass@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== @@ -6612,10 +6545,10 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob-parent@^5.0.0, glob-parent@^5.1.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" - integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== +glob-parent@^5.1.0, glob-parent@^5.1.2, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" @@ -6626,22 +6559,15 @@ glob-parent@^6.0.0: dependencies: is-glob "^4.0.1" -glob-parent@~5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - glob-to-regexp@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: - version "7.1.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== +glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" @@ -6735,12 +6661,12 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^12.1.0: - version "12.4.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" - integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== +globals@^13.6.0, globals@^13.9.0: + version "13.11.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.11.0.tgz#40ef678da117fe7bd2e28f1fab24951bd0255be7" + integrity sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g== dependencies: - type-fest "^0.8.1" + type-fest "^0.20.2" globalthis@^1.0.1: version "1.0.1" @@ -6749,7 +6675,7 @@ globalthis@^1.0.1: dependencies: define-properties "^1.1.3" -globby@^11.0.3: +globby@^11.0.1, globby@^11.0.3: version "11.0.4" resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5" integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg== @@ -6772,7 +6698,7 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" -got@^11.8.0: +got@^11.8.0, got@^11.8.2: version "11.8.2" resolved "https://registry.yarnpkg.com/got/-/got-11.8.2.tgz#7abb3959ea28c31f3576f1576c1effce23f33599" integrity sha512-D0QywKgIe30ODs+fm8wMZiAcZjypcCodPNuMz5H9Mny7RJ+IjJ10BdmGW7OM7fHXP+O7r6ZwapQ/YQmMSvB0UQ== @@ -6823,12 +6749,7 @@ got@^9.6.0: to-readable-stream "^1.0.0" url-parse-lax "^3.0.0" -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.2.2, graceful-fs@^4.2.4: - version "4.2.6" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" - integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== - -graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.3: +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4: version "4.2.8" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== @@ -7176,6 +7097,17 @@ http-proxy-middleware@0.19.1: lodash "^4.17.11" micromatch "^3.1.10" +http-proxy-middleware@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-1.3.1.tgz#43700d6d9eecb7419bf086a128d0f7205d9eb665" + integrity sha512-13eVVDYS4z79w7f1+NPllJtOQFx/FdUW4btIvVRMaRlUY9VGstAbo5MOhLEuUgZFRHn3x50ufn25zkj/boZnEg== + dependencies: + "@types/http-proxy" "^1.17.5" + http-proxy "^1.18.1" + is-glob "^4.0.1" + is-plain-obj "^3.0.0" + micromatch "^4.0.2" + http-proxy@^1.17.0, http-proxy@^1.18.1: version "1.18.1" resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" @@ -7318,7 +7250,7 @@ ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.1.4: +ignore@^5.1.4, ignore@^5.1.8: version "5.1.8" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== @@ -7345,15 +7277,7 @@ import-cwd@^3.0.0: dependencies: import-from "^3.0.0" -import-fresh@^3.0.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" - integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" - -import-fresh@^3.1.0: +import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -7361,14 +7285,6 @@ import-fresh@^3.1.0: parent-module "^1.0.0" resolve-from "^4.0.0" -import-fresh@^3.2.1: - version "3.2.2" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.2.tgz#fc129c160c5d68235507f4331a6baad186bdbc3e" - integrity sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" - import-from@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/import-from/-/import-from-3.0.0.tgz#055cfec38cd5a27d8057ca51376d7d3bf0891966" @@ -7508,6 +7424,11 @@ ip-regex@^2.1.0: resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= +ip-regex@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5" + integrity sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q== + ip@1.1.5, ip@^1.1.0, ip@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" @@ -7814,7 +7735,7 @@ is-obj@^2.0.0: resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== -is-path-cwd@^2.0.0: +is-path-cwd@^2.0.0, is-path-cwd@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== @@ -7850,6 +7771,11 @@ is-path-inside@^3.0.2: resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== +is-plain-obj@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" + integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== + is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -7933,7 +7859,7 @@ is-typedarray@^1.0.0, is-typedarray@~1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= -is-url@^1.2.2: +is-url@^1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/is-url/-/is-url-1.2.4.tgz#04a4df46d28c4cff3d73d01ff06abeb318a1aa52" integrity sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww== @@ -7965,14 +7891,14 @@ is-yarn-global@^0.3.0: resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== -is2@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is2/-/is2-2.0.1.tgz#8ac355644840921ce435d94f05d3a94634d3481a" - integrity sha512-+WaJvnaA7aJySz2q/8sLjMb2Mw14KTplHmSwcSpZ/fWJPkUmqw3YTzSWbPJ7OAwRvdYTWF2Wg+yYJ1AdP5Z8CA== +is2@^2.0.6: + version "2.0.7" + resolved "https://registry.yarnpkg.com/is2/-/is2-2.0.7.tgz#d084e10cab3bd45d6c9dfde7a48599fcbb93fcac" + integrity sha512-4vBQoURAXC6hnLFxD4VW7uc04XiwTTl/8ydYJxKvPwkWQrSjInkuM5VZVg6BGr1/natq69zDuvO9lGpLClJqvA== dependencies: deep-is "^0.1.3" - ip-regex "^2.1.0" - is-url "^1.2.2" + ip-regex "^4.1.0" + is-url "^1.2.4" isarray@0.0.1: version "0.0.1" @@ -8134,17 +8060,7 @@ jest-config@^26.6.3: micromatch "^4.0.2" pretty-format "^26.6.2" -jest-diff@^26.0.0: - version "26.6.1" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.1.tgz#38aa194979f454619bb39bdee299fb64ede5300c" - integrity sha512-BBNy/zin2m4kG5In126O8chOBxLLS/XMTuuM2+YhgyHk87ewPzKTuTJcqj3lOWOi03NNgrl+DkMeV/exdvG9gg== - dependencies: - chalk "^4.0.0" - diff-sequences "^26.5.0" - jest-get-type "^26.3.0" - pretty-format "^26.6.1" - -jest-diff@^26.6.2: +jest-diff@^26.0.0, jest-diff@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394" integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA== @@ -8491,13 +8407,6 @@ joi@^17.3.0, joi@^17.4.2: "@sideway/formula" "^3.0.0" "@sideway/pinpoint" "^2.0.0" -jose@^1.27.1: - version "1.27.1" - resolved "https://registry.yarnpkg.com/jose/-/jose-1.27.1.tgz#a1de2ecb5b3ae1ae28f0d9d0cc536349ada27ec8" - integrity sha512-VyHM6IJPw0TTGqHVNlPWg16/ASDPAmcChcLqSb3WNBvwWFoWPeFqlmAUCm8/oIG1GjZwAlUDuRKFfycowarcVA== - dependencies: - "@panva/asn1.js" "^1.0.0" - jose@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/jose/-/jose-2.0.5.tgz#29746a18d9fff7dcf9d5d2a6f62cb0c7cd27abd3" @@ -8608,6 +8517,11 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + json-schema-typed@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/json-schema-typed/-/json-schema-typed-7.0.3.tgz#23ff481b8b4eebcd2ca123b4fa0409e66469a2d9" @@ -8628,14 +8542,6 @@ json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= -json-to-pretty-yaml@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/json-to-pretty-yaml/-/json-to-pretty-yaml-1.2.2.tgz#f4cd0bd0a5e8fe1df25aaf5ba118b099fd992d5b" - integrity sha1-9M0L0KXo/h3yWq9boRiwmf2ZLVs= - dependencies: - remedial "^1.0.7" - remove-trailing-spaces "^1.0.6" - json3@^3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" @@ -8660,6 +8566,11 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" +jsonc-parser@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.0.0.tgz#abdd785701c7e7eaca8a9ec8cf070ca51a745a22" + integrity sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA== + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -8686,14 +8597,14 @@ jsonpath-plus@^0.19.0: resolved "https://registry.yarnpkg.com/jsonpath-plus/-/jsonpath-plus-0.19.0.tgz#b901e57607055933dc9a8bef0cc25160ee9dd64c" integrity sha512-GSVwsrzW9LsA5lzsqe4CkuZ9wp+kxBb2GwNniaWzI2YFn5Ig42rSW8ZxVpWXaAfakXNrx5pgY5AbQq7kzX29kg== -jsonpath@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/jsonpath/-/jsonpath-1.0.2.tgz#e6aae681d03e9a77b4651d5d96eac5fc63b1fd13" - integrity sha512-rmzlgFZiQPc6q4HDyK8s9Qb4oxBnI5sF61y/Co5PV0lc3q2bIuRsNdueVbhoSHdKM4fxeimphOAtfz47yjCfeA== +jsonpath@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/jsonpath/-/jsonpath-1.1.1.tgz#0ca1ed8fb65bb3309248cc9d5466d12d5b0b9901" + integrity sha512-l6Cg7jRpixfbgoWgkrl77dgEj8RPvND0wMH6TwQmi9Qs4TFfS9u5cUFnbeKTwj5ga5Y3BTGGNI28k117LJ009w== dependencies: esprima "1.2.2" static-eval "2.0.2" - underscore "1.7.0" + underscore "1.12.1" jsprim@^1.2.2: version "1.4.1" @@ -9168,7 +9079,7 @@ lockfile@^1.0.4: dependencies: signal-exit "^3.0.2" -lodash-es@^4.17.20: +lodash-es@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== @@ -9211,6 +9122,11 @@ lodash.isequal@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + lodash.toarray@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" @@ -9221,6 +9137,11 @@ lodash.topath@^4.5.2: resolved "https://registry.yarnpkg.com/lodash.topath/-/lodash.topath-4.5.2.tgz#3616351f3bba61994a0931989660bd03254fd009" integrity sha1-NhY1Hzu6YZlKCTGYlmC9AyVP0Ak= +lodash.truncate@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= + lodash.union@~4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" @@ -9398,10 +9319,10 @@ marked@^2.0.3: resolved "https://registry.yarnpkg.com/marked/-/marked-2.0.3.tgz#3551c4958c4da36897bda2a16812ef1399c8d6b0" integrity sha512-5otztIIcJfPc2qGTN8cVtOJEjNJZ0jwa46INMagrYfk0EvqtRuEHLsEe0LrFS0/q+ZRKT0+kXK7P2T1AN5lWRA== -marked@^2.0.7: - version "2.0.7" - resolved "https://registry.yarnpkg.com/marked/-/marked-2.0.7.tgz#bc5b857a09071b48ce82a1f7304913a993d4b7d1" - integrity sha512-BJXxkuIfJchcXOJWTT2DOL+yFWifFv2yGYOUzvXg8Qz610QKw+sHCvTMYwA+qWGhlA2uivBezChZ/pBy1tWdkQ== +marked@^3.0.4: + version "3.0.6" + resolved "https://registry.yarnpkg.com/marked/-/marked-3.0.6.tgz#d268f7229717ff55f81ff759c519d23c90def40e" + integrity sha512-a1hY8eqdP9JgmsaO0MYYhO9Li2nfY/5pAj+gWU5r41Lze6AV4Xty1cseLWDcOYimJnaVfQAomaA6NK+z2IyR+w== matcher@^3.0.0: version "3.0.0" @@ -9599,10 +9520,10 @@ mini-create-react-context@^0.4.0: "@babel/runtime" "^7.5.5" tiny-warning "^1.0.3" -mini-css-extract-plugin@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.0.tgz#b4db2525af2624899ed64a23b0016e0036411893" - integrity sha512-nPFKI7NSy6uONUo9yn2hIfb9vyYvkFu95qki0e21DQ9uaqNKDP15DGpK0KnV6wDroWxPHtExrdEwx/yDQ8nVRw== +mini-css-extract-plugin@^1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.2.tgz#83172b4fd812f8fc4a09d6f6d16f924f53990ca8" + integrity sha512-WhDvO3SjGm40oV5y26GjMJYjd2UMqrLAGKy5YS2/3QKJy2F7jgynuHTir/tgUUOiNQu5saXHdc8reo7YuhhT4Q== dependencies: loader-utils "^2.0.0" schema-utils "^3.0.0" @@ -9618,7 +9539,7 @@ minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= -minimatch@3.0.4, minimatch@^3.0.0, minimatch@^3.0.3, minimatch@^3.0.4: +minimatch@3.0.4, minimatch@^3.0.3, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -9630,7 +9551,7 @@ minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== -minipass@^2.3.5, minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: +minipass@^2.3.5, minipass@^2.6.0, minipass@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== @@ -9645,7 +9566,7 @@ minipass@^3.0.0: dependencies: yallist "^4.0.0" -minizlib@^1.2.1: +minizlib@^1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== @@ -9694,7 +9615,7 @@ mkdirp@1.x, mkdirp@^1.0.3, mkdirp@~1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@^0.5.5, mkdirp@~0.5.0: +mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@^0.5.5, mkdirp@~0.5.0: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== @@ -9715,10 +9636,10 @@ mobx-react-lite@^3.2.0: resolved "https://registry.yarnpkg.com/mobx-react-lite/-/mobx-react-lite-3.2.0.tgz#331d7365a6b053378dfe9c087315b4e41c5df69f" integrity sha512-q5+UHIqYCOpBoFm/PElDuOhbcatvTllgRp3M1s+Hp5j0Z6XNgDbgqxawJ0ZAUEyKM8X1zs70PCuhAIzX1f4Q/g== -mobx-react@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/mobx-react/-/mobx-react-7.1.0.tgz#d947cada3cfad294b4b6f692e969c242b9c16eaf" - integrity sha512-DxvA6VXmnZ+N9f/UTtolWtdRnAAQY2iHWTSPLktfpj8NKlXUe4dabBAjuXrBcZUM8GjLWnxD1ZEjssXq1M0RAw== +mobx-react@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/mobx-react/-/mobx-react-7.2.0.tgz#241e925e963bb83a31d269f65f9f379e37ecbaeb" + integrity sha512-KHUjZ3HBmZlNnPd1M82jcdVsQRDlfym38zJhZEs33VxyVQTvL77hODCArq6+C1P1k/6erEeo2R7rpE7ZeOL7dg== dependencies: mobx-react-lite "^3.2.0" @@ -9894,10 +9815,10 @@ node-addon-api@^1.6.3: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-1.7.2.tgz#3df30b95720b53c24e59948b49532b662444f54d" integrity sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg== -node-addon-api@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.0.0.tgz#ac128f43eff7fac4b5f5ef2f39d6d7c2709efead" - integrity sha512-ALmRVBFzfwldBfk3SbKfl6+PVMXiCPKZBEfsJqB/EjXAMAI+MfFrEHR+GMRBuI162DihZ1QjEZ8ieYKuRCJ8Hg== +node-addon-api@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.2.0.tgz#117cbb5a959dff0992e1c586ae0393573e4d2a87" + integrity sha512-eazsqzwG2lskuzBqCGPi7Ac2UgOoMz8JVOXVhTvvPDYhthvNpefx8jWD8Np7Gv+2Sz0FlPWZk0nJV0z598Wn8Q== node-emoji@^1.8.1: version "1.10.0" @@ -9925,11 +9846,6 @@ node-forge@^0.10.0: resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== -node-forge@^0.8.2: - version "0.8.5" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.8.5.tgz#57906f07614dc72762c84cef442f427c0e1b86ee" - integrity sha512-vFMQIWt+J/7FLNyKouZ9TazT74PRV3wgv9UT4cRjC8BffxFbKXkgIWR42URCPSnHm/QDz6BOlb2Q0U4+VQT67Q== - node-gyp@7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-7.1.2.tgz#21a810aebb187120251c3bcec979af1587b188ae" @@ -10222,10 +10138,10 @@ npm-user-validate@^1.0.1: resolved "https://registry.yarnpkg.com/npm-user-validate/-/npm-user-validate-1.0.1.tgz#31428fc5475fe8416023f178c0ab47935ad8c561" integrity sha512-uQwcd/tY+h1jnEaze6cdX/LrhWhoBxfSknxentoqmIuStxUExxjWd3ULMLFPiFUrZKbOVMowH6Jq2FRWfmhcEw== -npm@^6.14.8: - version "6.14.13" - resolved "https://registry.yarnpkg.com/npm/-/npm-6.14.13.tgz#e88bcb6c48209869c40b5cedad8a1508e58e6f30" - integrity sha512-SRl4jJi0EBHY2xKuu98FLRMo3VhYQSA6otyLnjSEiHoSG/9shXCFNJy9tivpUJvtkN9s6VDdItHa5Rn+fNBzag== +npm@^6.14.15: + version "6.14.15" + resolved "https://registry.yarnpkg.com/npm/-/npm-6.14.15.tgz#97dd51af5b5d6225b611b40c5cb4d31da1d467fe" + integrity sha512-dkcQc4n+DiJAMYG2haNAMyJbmuvevjXz+WC9dCUzodw8EovwTIc6CATSsTEplCY6c0jG4OshxFGFJsrnKJguWA== dependencies: JSONStream "^1.3.5" abbrev "~1.1.1" @@ -10328,7 +10244,7 @@ npm@^6.14.8: sorted-union-stream "~2.1.3" ssri "^6.0.2" stringify-package "^1.0.1" - tar "^4.4.13" + tar "^4.4.19" text-table "~0.2.0" tiny-relative-date "^1.3.0" uid-number "0.0.6" @@ -10502,11 +10418,6 @@ obuf@^1.0.0, obuf@^1.1.2: resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== -oidc-token-hash@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/oidc-token-hash/-/oidc-token-hash-5.0.0.tgz#acdfb1f4310f58e64d5d74a4e8671a426986e888" - integrity sha512-8Yr4CZSv+Tn8ZkN3iN2i2w2G92mUKClp4z7EGUfdsERiYSbj7P4i/NHm72ft+aUdsiFx9UdIPSTwbyzQ6C4URg== - oidc-token-hash@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/oidc-token-hash/-/oidc-token-hash-5.0.1.tgz#ae6beec3ec20f0fd885e5400d175191d6e2f10c6" @@ -10564,22 +10475,6 @@ opener@^1.5.2: resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== -openid-client@^3.15.2: - version "3.15.2" - resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-3.15.2.tgz#d48a6d7991d5d6117f4598bb1d19399262164128" - integrity sha512-50aBsfiSZds48ec/VvUZ3hwtpEW0L5s7gGxlsJSvo5mAeK4TG3gv0VRWI+SvCAae4MEwRegLJD3YjpkJMLi2GQ== - dependencies: - "@types/got" "^9.6.9" - base64url "^3.0.1" - got "^9.6.0" - jose "^1.27.1" - lodash "^4.17.15" - lru-cache "^5.1.1" - make-error "^1.3.6" - object-hash "^2.0.1" - oidc-token-hash "^5.0.0" - p-any "^3.0.0" - openid-client@^4.1.1: version "4.7.4" resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-4.7.4.tgz#bd9978456d53d38adb89856b14a8fbd094f7732e" @@ -10663,14 +10558,6 @@ osenv@^0.1.4, osenv@^0.1.5: os-homedir "^1.0.0" os-tmpdir "^1.0.0" -p-any@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/p-any/-/p-any-3.0.0.tgz#79847aeed70b5d3a10ea625296c0c3d2e90a87b9" - integrity sha512-5rqbqfsRWNb0sukt0awwgJMlaep+8jV45S15SKKB34z4UuzjcofIfnriCBhWjZP2jbVtjt9yRl7buB6RlKsu9w== - dependencies: - p-cancelable "^2.0.0" - p-some "^5.0.0" - p-cancelable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" @@ -10738,6 +10625,13 @@ p-map@^2.0.0: resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + p-retry@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-3.0.1.tgz#316b4c8893e2c8dc1cfa891f406c4b422bebf328" @@ -10745,14 +10639,6 @@ p-retry@^3.0.1: dependencies: retry "^0.12.0" -p-some@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/p-some/-/p-some-5.0.0.tgz#8b730c74b4fe5169d7264a240ad010b6ebc686a4" - integrity sha512-Js5XZxo6vHjB9NOYAzWDYAIyyiPvva0DWESAIWIK7uhSpGsyg5FwUPxipU/SOQx5x9EqhOh545d1jo6cVkitig== - dependencies: - aggregate-error "^3.0.0" - p-cancelable "^2.0.0" - p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" @@ -10975,10 +10861,10 @@ path-to-regexp@^1.7.0: dependencies: isarray "0.0.1" -path-to-regexp@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.1.0.tgz#0b18f88b7a0ce0bfae6a25990c909ab86f512427" - integrity sha512-h9DqehX3zZZDCEm+xbfU0ZmwCGFCAAraPJWMXJ4+v32NjZJilVg3k1TcKsRgIb8IQ/izZSaydDc1OhJCZvs2Dw== +path-to-regexp@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.0.tgz#f7b3803336104c346889adece614669230645f38" + integrity sha512-f66KywYG6+43afgE/8j/GoiNyygk/bnoCbps++3ErRKsIYkGGupyv07R2Ok5m9i67Iqc+T2g1eAUGUPzWhYTyg== path-type@^2.0.0: version "2.0.0" @@ -11313,6 +11199,11 @@ prepend-http@^2.0.0: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= +prettier@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.4.1.tgz#671e11c89c14a4cfc876ce564106c4a6726c9f5c" + integrity sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA== + pretty-error@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.1.tgz#5f4f87c8f91e5ae3f3ba87ab4cf5e03b1a17f1a3" @@ -11321,17 +11212,7 @@ pretty-error@^2.1.1: renderkid "^2.0.1" utila "~0.4" -pretty-format@^26.0.0, pretty-format@^26.6.1: - version "26.6.1" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.1.tgz#af9a2f63493a856acddeeb11ba6bcf61989660a8" - integrity sha512-MeqqsP5PYcRBbGMvwzsyBdmAJ4EFX7pWFyl7x4+dMVg5pE0ZDdBIvEH2ergvIO+Gvwv1wh64YuOY9y5LuyY/GA== - dependencies: - "@jest/types" "^26.6.1" - ansi-regex "^5.0.0" - ansi-styles "^4.0.0" - react-is "^17.0.1" - -pretty-format@^26.6.2: +pretty-format@^26.0.0, pretty-format@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93" integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg== @@ -12101,21 +11982,11 @@ relateurl@^0.2.7: resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= -remedial@^1.0.7: - version "1.0.8" - resolved "https://registry.yarnpkg.com/remedial/-/remedial-1.0.8.tgz#a5e4fd52a0e4956adbaf62da63a5a46a78c578a0" - integrity sha512-/62tYiOe6DzS5BqVsNpH/nkGlX45C/Sp6V+NtiN6JQNS1Viay7cWkazmRkrQrdFj2eshDe96SIQNIoMxqhzBOg== - remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= -remove-trailing-spaces@^1.0.6: - version "1.0.8" - resolved "https://registry.yarnpkg.com/remove-trailing-spaces/-/remove-trailing-spaces-1.0.8.tgz#4354d22f3236374702f58ee373168f6d6887ada7" - integrity sha512-O3vsMYfWighyFbTd8hk8VaSj9UAGENxAtX+//ugIst2RMk5e03h6RoIS+0ylsFxY1gvmPuAY/PO4It+gPEeySA== - renderkid@^2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.3.tgz#380179c2ff5ae1365c522bf2fcfcff01c5b74149" @@ -12184,6 +12055,11 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + require-main-filename@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" @@ -12316,12 +12192,10 @@ rfc4648@^1.3.0: resolved "https://registry.yarnpkg.com/rfc4648/-/rfc4648-1.3.0.tgz#2a69c76f05bc0e388feab933672de9b492af95f1" integrity sha512-x36K12jOflpm1V8QjPq3I+pt7Z1xzeZIjiC8J2Oxd7bE1efTrOG241DTYVJByP/SxR9jl1t7iZqYxDX864jgBQ== -rimraf@2.6.3: - version "2.6.3" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" - integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== - dependencies: - glob "^7.1.3" +rfc6902@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/rfc6902/-/rfc6902-4.0.2.tgz#ce99d3562b9e3287d403462e6bcc81eead8fcea0" + integrity sha512-MJOC4iDSv3Qn5/QvhPbrNoRongti6moXSShcRmtbNqOk0WPxlviEdMV4bb9PaULhSxLUXzWd4AjAMKQ3j3y54w== rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.2, rimraf@^2.6.3, rimraf@^2.7.1: version "2.7.1" @@ -12381,7 +12255,7 @@ rxjs@^6.5.2: dependencies: tslib "^1.9.0" -safe-buffer@*, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: +safe-buffer@*, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -12436,17 +12310,10 @@ sass-loader@^8.0.2: schema-utils "^2.6.1" semver "^6.3.0" -sass@^1.32.13: - version "1.35.2" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.35.2.tgz#b732314fcdaf7ef8d0f1698698adc378043cb821" - integrity sha512-jhO5KAR+AMxCEwIH3v+4zbB2WB0z67V1X0jbapfVwQQdjHZUGUyukpnoM6+iCMfsIUC016w9OPKQ5jrNOS9uXw== - dependencies: - chokidar ">=3.0.0 <4.0.0" - -sass@^1.39.0: - version "1.39.0" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.39.0.tgz#6c64695d1c437767c8f1a4e471288e831f81d035" - integrity sha512-F4o+RhJkNOIG0b6QudYU8c78ZADKZjKDk5cyrf8XTKWfrgbtyVVXImFstJrc+1pkQDCggyidIOytq6gS4gCCZg== +sass@^1.32.13, sass@^1.41.1: + version "1.41.1" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.41.1.tgz#bca5bed2154192779c29f48fca9c644c60c38d98" + integrity sha512-vIjX7izRxw3Wsiez7SX7D+j76v7tenfO18P59nonjr/nzCkZuoHuF7I/Fo0ZRZPKr88v29ivIdE9BqGDgQD/Nw== dependencies: chokidar ">=3.0.0 <4.0.0" @@ -12672,14 +12539,14 @@ shallow-clone@^3.0.0: dependencies: kind-of "^6.0.2" -sharp@^0.29.0: - version "0.29.0" - resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.29.0.tgz#1fa302bd5f60292138c823aa0905609f64d710ba" - integrity sha512-mdN1Up0eN+SwyForPls59dWO0nx64J1XRQYy5ZiKSADAccGYCB10UAGJHSVG9VObzJdhHqrVJzQcq6gx8USyoA== +sharp@^0.29.1: + version "0.29.1" + resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.29.1.tgz#f60b50f24f399464a24187c86bd2da41aae50b85" + integrity sha512-DpgdAny9TuS+oWCQ7MRS8XyY9x6q1+yW3a5wNx0J3HrGuB/Jot/8WcT+lElHY9iJu2pwtegSGxqMaqFiMhs4rQ== dependencies: color "^4.0.1" detect-libc "^1.0.3" - node-addon-api "^4.0.0" + node-addon-api "^4.1.0" prebuild-install "^6.1.4" semver "^7.3.5" simple-get "^3.1.0" @@ -12733,13 +12600,14 @@ shellwords@^0.1.1: resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== -shiki@^0.9.3: - version "0.9.3" - resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.9.3.tgz#7bf7bcf3ed50ca525ec89cc09254abce4264d5ca" - integrity sha512-NEjg1mVbAUrzRv2eIcUt3TG7X9svX7l3n3F5/3OdFq+/BxUdmBOeKGiH4icZJBLHy354Shnj6sfBTemea2e7XA== +shiki@^0.9.11: + version "0.9.11" + resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.9.11.tgz#07d75dab2abb6dc12a01f79a397cb1c391fa22d8" + integrity sha512-tjruNTLFhU0hruCPoJP0y+B9LKOmcqUhTpxn7pcJB3fa+04gFChuEmxmrUfOJ7ZO6Jd+HwMnDHgY3lv3Tqonuw== dependencies: + jsonc-parser "^3.0.0" onigasm "^2.2.5" - vscode-textmate "^5.2.0" + vscode-textmate "5.2.0" side-channel@^1.0.4: version "1.0.4" @@ -12793,14 +12661,14 @@ slice-ansi@^1.0.0: dependencies: is-fullwidth-code-point "^2.0.0" -slice-ansi@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" - integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== dependencies: - ansi-styles "^3.2.0" - astral-regex "^1.0.0" - is-fullwidth-code-point "^2.0.0" + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" slide@^1.1.6, slide@~1.1.3, slide@~1.1.6: version "1.1.6" @@ -12921,7 +12789,7 @@ source-map-resolve@^0.6.0: atob "^2.1.2" decode-uri-component "^0.2.0" -source-map-support@^0.5.17, source-map-support@^0.5.19, source-map-support@^0.5.6, source-map-support@~0.5.12: +source-map-support@^0.5.19, source-map-support@^0.5.6, source-map-support@~0.5.12: version "0.5.19" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== @@ -13201,6 +13069,15 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" +string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string.prototype.matchall@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.5.tgz#59370644e1db7e4c0c045277690cf7b01203c4da" @@ -13326,12 +13203,12 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" - integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: - ansi-regex "^5.0.0" + ansi-regex "^5.0.1" strip-bom@^3.0.0: version "3.0.0" @@ -13360,12 +13237,7 @@ strip-indent@^3.0.0: dependencies: min-indent "^1.0.0" -strip-json-comments@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.0.tgz#7638d31422129ecf4457440009fba03f9f9ac180" - integrity sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w== - -strip-json-comments@^3.1.1: +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== @@ -13443,15 +13315,17 @@ symbol-tree@^3.2.4: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== -table@^5.2.3: - version "5.4.6" - resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" - integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== +table@^6.0.9: + version "6.7.2" + resolved "https://registry.yarnpkg.com/table/-/table-6.7.2.tgz#a8d39b9f5966693ca8b0feba270a78722cbaf3b0" + integrity sha512-UFZK67uvyNivLeQbVtkiUs8Uuuxv24aSL4/Vil2PJVtMgU8Lx0CYkP12uCGa3kjyQzOSgV1+z9Wkb82fCGsO0g== dependencies: - ajv "^6.10.2" - lodash "^4.17.14" - slice-ansi "^2.1.0" - string-width "^3.0.0" + ajv "^8.0.1" + lodash.clonedeep "^4.5.0" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" tailwindcss@^2.2.4: version "2.2.4" @@ -13521,18 +13395,18 @@ tar-stream@^2.1.4: inherits "^2.0.3" readable-stream "^3.1.1" -tar@^4.4.10, tar@^4.4.12, tar@^4.4.13: - version "4.4.13" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" - integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== +tar@^4.4.10, tar@^4.4.12, tar@^4.4.19: + version "4.4.19" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3" + integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA== dependencies: - chownr "^1.1.1" - fs-minipass "^1.2.5" - minipass "^2.8.6" - minizlib "^1.2.1" - mkdirp "^0.5.0" - safe-buffer "^5.1.2" - yallist "^3.0.3" + chownr "^1.1.4" + fs-minipass "^1.2.7" + minipass "^2.9.0" + minizlib "^1.3.3" + mkdirp "^0.5.5" + safe-buffer "^5.2.1" + yallist "^3.1.1" tar@^6.0.2, tar@^6.1.10: version "6.1.10" @@ -13546,13 +13420,13 @@ tar@^6.0.2, tar@^6.1.10: mkdirp "^1.0.3" yallist "^4.0.0" -tcp-port-used@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/tcp-port-used/-/tcp-port-used-1.0.1.tgz#46061078e2d38c73979a2c2c12b5a674e6689d70" - integrity sha512-rwi5xJeU6utXoEIiMvVBMc9eJ2/ofzB+7nLOdnZuFTmNCLqRiQh2sMG9MqCxHU/69VC/Fwp5dV9306Qd54ll1Q== +tcp-port-used@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/tcp-port-used/-/tcp-port-used-1.0.2.tgz#9652b7436eb1f4cfae111c79b558a25769f6faea" + integrity sha512-l7ar8lLUD3XS1V2lfoJlCBaeoaWo/2xfYt81hM7VlvR4RrMVFqfmzfhLVk40hAb368uitje5gPtBRL1m/DGvLA== dependencies: - debug "4.1.0" - is2 "2.0.1" + debug "4.3.1" + is2 "^2.0.6" temp-dir@^2.0.0: version "2.0.0" @@ -13567,14 +13441,26 @@ temp-file@^3.4.0: async-exit-hook "^2.0.1" fs-extra "^10.0.0" -tempy@*, tempy@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/tempy/-/tempy-0.5.0.tgz#2785c89df39fcc4d1714fc554813225e1581d70b" - integrity sha512-VEY96x7gbIRfsxqsafy2l5yVxxp3PhwAGoWMyC2D2Zt5DmEv+2tGiPOrquNRpf21hhGnKLVEsuqleqiZmKG/qw== +tempy@*: + version "0.7.1" + resolved "https://registry.yarnpkg.com/tempy/-/tempy-0.7.1.tgz#5a654e6dbd1747cdd561efb112350b55cd9c1d46" + integrity sha512-vXPxwOyaNVi9nyczO16mxmHGpl6ASC5/TVhRRHpqeYHvKQm58EaWNvZXxAhR0lYYnBOQFjXjhzeLsaXdjxLjRg== dependencies: + del "^6.0.0" is-stream "^2.0.0" temp-dir "^2.0.0" - type-fest "^0.12.0" + type-fest "^0.16.0" + unique-string "^2.0.0" + +tempy@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tempy/-/tempy-1.0.1.tgz#30fe901fd869cfb36ee2bd999805aa72fbb035de" + integrity sha512-biM9brNqxSc04Ee71hzFbryD11nX7VPhQQY32AdDmjFvodsRFz/3ufeoTZ6uYkRFfGo188tENcASNs3vTdsM0w== + 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" term-size@^1.2.0: @@ -13705,9 +13591,9 @@ tmp@^0.2.0, tmp@^0.2.1: rimraf "^3.0.0" tmpl@1.0.x: - version "1.0.4" - resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" - integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= + version "1.0.5" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== to-arraybuffer@^1.0.0: version "1.0.1" @@ -13841,20 +13727,22 @@ ts-loader@^7.0.5: micromatch "^4.0.0" semver "^6.0.0" -ts-node@^10.1.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.1.0.tgz#e656d8ad3b61106938a867f69c39a8ba6efc966e" - integrity sha512-6szn3+J9WyG2hE+5W8e0ruZrzyk1uFLYye6IGMBadnOzDh8aP7t8CbFpsfCiEx2+wMixAhjFt7lOZC4+l+WbEA== +ts-node@^10.2.1: + version "10.2.1" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.2.1.tgz#4cc93bea0a7aba2179497e65bb08ddfc198b3ab5" + integrity sha512-hCnyOyuGmD5wHleOQX6NIjJtYVIO8bPP8F2acWkB4W06wdlkgyvJtubO/I9NkI88hCFECbsEgoLc0VNkYmcSfw== dependencies: + "@cspotcode/source-map-support" "0.6.1" "@tsconfig/node10" "^1.0.7" "@tsconfig/node12" "^1.0.7" "@tsconfig/node14" "^1.0.0" - "@tsconfig/node16" "^1.0.1" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" arg "^4.1.0" create-require "^1.1.0" diff "^4.0.1" make-error "^1.1.1" - source-map-support "^0.5.17" yn "3.1.1" tsconfig-paths@^3.9.0: @@ -13930,16 +13818,16 @@ type-fest@^0.11.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== -type-fest@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.12.0.tgz#f57a27ab81c68d136a51fd71467eff94157fa1ee" - integrity sha512-53RyidyjvkGpnWPMF9bQgFtWp+Sl8O2Rp13VavmJgfAP9WWG6q6TkrKU8iyJdnwnfgHI6k2hTlgqH4aSdjoTbg== - type-fest@^0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== +type-fest@^0.16.0: + version "0.16.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.16.0.tgz#3240b891a78b0deae910dbeb86553e552a148860" + integrity sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg== + type-fest@^0.20.2: version "0.20.2" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" @@ -13955,15 +13843,10 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== -type-fest@^1.0.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.2.1.tgz#232990aa513f3f5223abf54363975dfe3a121a2e" - integrity sha512-SbmIRuXhJs8KTneu77Ecylt9zuqL683tuiLYpTRil4H++eIhqCmx6ko6KAFem9dty8sOdnEiX7j4K1nRE628fQ== - -type-fest@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.0.2.tgz#3f9c39982859f385c77c38b7e5f1432b8a3661c6" - integrity sha512-a720oz3Kjbp3ll0zkeN9qjRhO7I34MKMhPGQiQJAmaZQZQ1lo+NWThK322f7sXV+kTg9B1Ybt16KgBXWgteT8w== +type-fest@^1.0.2, type-fest@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.4.0.tgz#e9fb813fe3bf1744ec359d55d1affefa76f14be1" + integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA== type-is@~1.6.17, type-is@~1.6.18: version "1.6.18" @@ -13990,11 +13873,6 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typedoc-default-themes@^0.12.10: - version "0.12.10" - resolved "https://registry.yarnpkg.com/typedoc-default-themes/-/typedoc-default-themes-0.12.10.tgz#614c4222fe642657f37693ea62cad4dafeddf843" - integrity sha512-fIS001cAYHkyQPidWXmHuhs8usjP5XVJjWB8oZGqkTowZaz3v7g3KDZeeqE82FBrmkAnIBOY3jgy7lnPnqATbA== - typedoc-plugin-markdown@^3.9.0: version "3.9.0" resolved "https://registry.yarnpkg.com/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.9.0.tgz#d9c0390b8ddeeda56fdbf01264521ef04b3c19c7" @@ -14002,20 +13880,16 @@ typedoc-plugin-markdown@^3.9.0: dependencies: handlebars "^4.7.7" -typedoc@0.21.0-beta.2: - version "0.21.0-beta.2" - resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.21.0-beta.2.tgz#e26927bf9c55c691f5401cd1608d99e4e7852ada" - integrity sha512-oCjdueR4glcSHT6ynR2q5HTpBkBM1ihf3/Wfpl0qriDJutrSg4idZovN+1jx66s+p9QmJE7yzDCXPrZ2GW/Xbw== +typedoc@0.22.5: + version "0.22.5" + resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.22.5.tgz#c1a7c33fcdc808f57c766584a6afb47762944229" + integrity sha512-KFrWGU1iKiTGw0RcyjLNYDmhd7uICU14HgBNPmFKY/sT4Pm/fraaLyWyisst9vGTUAKxqibqoDITR7+ZcAkhHg== dependencies: - glob "^7.1.6" - handlebars "^4.7.7" - lodash "^4.17.21" + glob "^7.2.0" lunr "^2.3.9" - marked "^2.0.7" - minimatch "^3.0.0" - progress "^2.0.3" - shiki "^0.9.3" - typedoc-default-themes "^0.12.10" + marked "^3.0.4" + minimatch "^3.0.4" + shiki "^0.9.11" typeface-roboto@^1.1.13: version "1.1.13" @@ -14041,15 +13915,10 @@ typescript-plugin-css-modules@^3.4.0: stylus "^0.54.8" tsconfig-paths "^3.9.0" -typescript@^4.0.3: - version "4.1.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.2.tgz#6369ef22516fe5e10304aae5a5c4862db55380e9" - integrity sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ== - -typescript@^4.3.2: - version "4.3.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.2.tgz#399ab18aac45802d6f2498de5054fcbbe716a805" - integrity sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw== +typescript@^4.0.3, typescript@^4.4.3: + version "4.4.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.3.tgz#bdc5407caa2b109efd4f82fe130656f977a29324" + integrity sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA== uglify-js@^3.1.4: version "3.9.4" @@ -14085,15 +13954,10 @@ undefsafe@^2.0.3: dependencies: debug "^2.2.0" -underscore@1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.7.0.tgz#6bbaf0877500d36be34ecaa584e0db9fef035209" - integrity sha1-a7rwh3UA02vjTsqlhODbn+8DUgk= - -underscore@^1.9.1: - version "1.10.2" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.10.2.tgz#73d6aa3668f3188e4adb0f1943bd12cfd7efaaaf" - integrity sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg== +underscore@1.12.1, underscore@^1.9.1: + version "1.12.1" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.12.1.tgz#7bb8cc9b3d397e201cf8553336d262544ead829e" + integrity sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw== union-value@^1.0.0: version "1.0.1" @@ -14278,9 +14142,17 @@ url-parse-lax@^3.0.0: prepend-http "^2.0.0" url-parse@^1.4.3, url-parse@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.1.tgz#d5fa9890af8a5e1f274a2c98376510f6425f6e3b" - integrity sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q== + version "1.5.2" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.2.tgz#a4eff6fd5ff9fe6ab98ac1f79641819d13247cda" + integrity sha512-6bTUPERy1muxxYClbzoRo5qtQuyoGEbzbQvi0SW4/8U8UyVkAQhWFBlnigqJkRm4su4x1zDQfNbEzWkt+vchcg== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + +url-parse@^1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.3.tgz#71c1303d38fb6639ade183c2992c8cc0686df862" + integrity sha512-IIORyIQD9rvj0A4CLWsHkBBJuNqWpFQe224b6j9t/ABmquIS0qDU2pY6kl6AuOrL5OkCXHMCFNe1jBcuAggjvQ== dependencies: querystringify "^2.1.1" requires-port "^1.0.0" @@ -14420,10 +14292,10 @@ vm-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== -vscode-textmate@^5.2.0: - version "5.4.0" - resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-5.4.0.tgz#4b25ffc1f14ac3a90faf9a388c67a01d24257cd7" - integrity sha512-c0Q4zYZkcLizeYJ3hNyaVUM2AA8KDhNCA3JvXY8CeZSJuBdAy3bAvSbv46RClC4P3dSO9BdwhnKEx2zOo6vP/w== +vscode-textmate@5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-5.2.0.tgz#01f01760a391e8222fe4f33fbccbd1ad71aed74e" + integrity sha512-Uw5ooOQxRASHgu6C7GVvUxisKXfSgW4oFlO+aa+PAkgmH89O3CXxEEzNRNtHSqtXFTl0nAC1uYj0GMSH27uwtQ== w3c-hr-time@^1.0.2: version "1.0.2" @@ -14704,14 +14576,14 @@ widest-line@^3.1.0: dependencies: string-width "^4.0.0" -win-ca@^3.2.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/win-ca/-/win-ca-3.2.1.tgz#66a6055bd9687fc438fabe18a9a01e0254ee0f74" - integrity sha512-EgxAx4hQMcOZshFDW1hFTv8Anpv+CVmUCKi8svVB/80V21Rbec0cPeSGinRJNZkE5/LvzKtpWZ1PHWMGeHJwBA== +win-ca@^3.4.5: + version "3.4.5" + resolved "https://registry.yarnpkg.com/win-ca/-/win-ca-3.4.5.tgz#634f5603e3b1c57e273da2334cf849f6c53a61d1" + integrity sha512-2xTLq3jah7Sg8Pt8me2rbTnDMxulrX6gSfU9lscyqjyE4gj34sd9w6LK0v8aNHzow+s0WEX1vve58EixZbXiLg== dependencies: is-electron "^2.2.0" make-dir "^1.3.0" - node-forge "^0.8.2" + node-forge "^0.10.0" split "^1.0.1" winston-console-format@^1.0.8: @@ -14830,13 +14702,6 @@ write-file-atomic@^3.0.0, write-file-atomic@^3.0.3: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" -write@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" - integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== - dependencies: - mkdirp "^0.5.1" - ws@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" @@ -14924,7 +14789,7 @@ yallist@^2.1.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= -yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: +yallist@^3.0.0, yallist@^3.0.2, yallist@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==