1
0
mirror of https://github.com/lensapp/lens.git synced 2025-05-20 05:10:56 +00:00

Merge branch 'master' into issue-3498

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2023-02-15 12:18:13 -05:00
commit 8cce15a002
85 changed files with 9640 additions and 5855 deletions

102
.github/workflows/daily-alpha.yml vendored Normal file
View File

@ -0,0 +1,102 @@
name: Release daily alpha
on:
schedule:
- cron: 0 0 30 * 1-5 # At 12:30am UTC work day
workflow_dispatch: # for testing
jobs:
tag:
outputs:
tagname: v${{ steps.version.outputs.VERSION }}
version: ${{ steps.version.outputs.VERSION }}
continue: ${{ steps.create-branch.outputs.CONTINUE }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: "16.x"
registry-url: "https://npm.pkg.github.com"
- name: Install deps
run: |
yarn install
sudo apt-get install -y ripgrep
cd packages/bump-version-for-cron && yarn build
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Bump version
id: version
run: |
node packages/bump-version-for-cron/dist/index.js --path lerna.json
- name: Check if branch already exists
id: check-branch
run: git ls-remote --exit-code --tags origin v${{ steps.version.outputs.VERSION }}
continue-on-error: true
- name: Create branch and tag and push
id: create-branch
run: |
# failure means that the tag doesn't exist so we should create it
if [ ${{ steps.check-branch.outcome }} != 'failure' ]; then
echo "CONTINUE=false" >> "$GITHUB_OUTPUT"
exit 0
fi
git config --global user.email "bot@k8slens.dev"
git config --global user.name "k8slens bot"
git checkout -b release/v${{ steps.version.outputs.VERSION }}
git add .
git commit -sm "Release ${{ steps.version.outputs.VERSION }}"
git tag v${{ steps.version.outputs.VERSION }}
git push origin v${{ steps.version.outputs.VERSION }}
echo "CONTINUE=true" >> "$GITHUB_OUTPUT"
create_release:
outputs:
upload_url: ${{ steps.create_release.outputs.upload_url }}
version: ${{ needs.tag.outputs.version }}
needs: [tag]
if: ${{ needs.tag.outputs.continue == 'true' }}
runs-on: ubuntu-20.04
steps:
- name: Create GitHub release
uses: softprops/action-gh-release@v1
id: create_release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ needs.tag.outputs.tagname }}
name: ${{ needs.tag.outputs.tagname }}
generate_release_notes: true
prerelease: true
release_packages:
needs: [create_release]
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
with:
ref: v${{ needs.create_release.outputs.version }}
- name: Use Node.js ${{ env.NODE_VERSION }}
uses: actions/setup-node@v3
with:
node-version: "16.x"
registry-url: "https://npm.pkg.github.com"
- name: Build package
shell: bash
run: |
yarn install --frozen-lockfile
yarn run build
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Release to GitHub NPM registry
run: |
npm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}"
yarn lerna \
publish from-package \
--no-push \
--no-git-tag-version \
--yes \
--dist-tag cron
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@ -1,38 +0,0 @@
name: Publish NPM Package `master`
on:
pull_request:
types:
- closed
concurrency:
group: publish-master-npm
cancel-in-progress: true
jobs:
publish:
name: Publish Extensions NPM Package `master`
runs-on: ubuntu-latest
if: ${{ github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'area/extension') }}
strategy:
matrix:
node-version: [16.x]
steps:
- name: Checkout Release
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Using Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Generate NPM packages
run: |
yarn install --frozen-lockfile
yarn run build
- name: Publish NPM package
run: |
npm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}"
yarn lerna publish from-package --dist-tag master --no-push --no-git-tag-version --yes
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

View File

@ -38,7 +38,9 @@ jobs:
- name: Publish NPM packages - name: Publish NPM packages
run: | run: |
npm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}" npm config set '//registry.npmjs.org/:_authToken' "${NPM_TOKEN}"
DIST_TAG=$(cat lerna.json | jq '.version' --raw-output | xargs node ./packages/semver/dist/index.mjs --prerelease 0) VERSION=$(cat lerna.json | jq '.version' --raw-output)
echo ${VERSION}
DIST_TAG=$(node packages/semver/dist/index.js --prerelease 0 ${VERSION})
yarn lerna \ yarn lerna \
publish from-package \ publish from-package \
--no-push \ --no-push \

View File

@ -4,7 +4,7 @@
"packages": [ "packages": [
"packages/*" "packages/*"
], ],
"version": "6.4.0-beta.10", "version": "6.4.0-beta.13",
"npmClient": "yarn", "npmClient": "yarn",
"npmClientArgs": [ "npmClientArgs": [
"--network-timeout=100000" "--network-timeout=100000"

View File

@ -18,13 +18,14 @@
"test:unit": "lerna run --stream test:unit", "test:unit": "lerna run --stream test:unit",
"test:integration": "lerna run --stream test:integration", "test:integration": "lerna run --stream test:integration",
"bump-version": "lerna version --no-git-tag-version --no-push", "bump-version": "lerna version --no-git-tag-version --no-push",
"create-release-pr": "node packages/release-tool", "precreate-release-pr": "cd packages/release-tool && yarn run build",
"postinstall": "lerna bootstrap" "create-release-pr": "node packages/release-tool/dist/index.js",
"postinstall": "lerna bootstrap --ignore open-lens && lerna bootstrap --scope open-lens --include-dependencies"
}, },
"devDependencies": { "devDependencies": {
"adr": "^1.4.3", "adr": "^1.4.3",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"lerna": "^6.4.1", "lerna": "^6.5.0",
"rimraf": "^4.1.2" "rimraf": "^4.1.2"
} }
} }

View File

@ -0,0 +1,2 @@
dist/
node_modules/

View File

@ -0,0 +1,9 @@
{
"$schema": "https://json.schemastore.org/swcrc",
"jsc": {
"parser": {
"syntax": "typescript"
},
"target": "es2022"
}
}

View File

@ -0,0 +1,31 @@
{
"name": "@k8slens/bump-version-for-cron",
"version": "6.4.0-cron.4db172da60",
"description": "CLI to bump the version to during a cron daily alpha release",
"license": "MIT",
"scripts": {
"clean": "rimraf dist/",
"build": "swc ./src/index.ts -d ./dist"
},
"type": "module",
"bin": "./dist/index.js",
"files": [
"dist"
],
"private": false,
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
},
"dependencies": {
"arg": "^5.0.2",
"semver": "^7.3.8"
},
"devDependencies": {
"@swc/cli": "^0.1.61",
"@swc/core": "^1.3.35",
"@types/node": "^16.18.11",
"@types/semver": "^7.3.13",
"rimraf": "^4.1.2"
}
}

View File

@ -0,0 +1,56 @@
import * as child_process from "child_process";
import { readFile, writeFile } from "fs/promises";
import semver from "semver";
import { promisify } from "util";
import arg from "arg";
const { SemVer } = semver;
const exec = promisify(child_process.exec);
const args = arg({
"--path": String,
});
const versionJsonPath = args["--path"];
if (!versionJsonPath) {
throw new Error("Missing required '--path'");
}
try {
const packageJson = JSON.parse(await readFile(versionJsonPath, "utf-8"));
const { stdout: gitRevParseOutput } = await exec("git rev-parse --short HEAD");
const currentHash = gitRevParseOutput.trim();
const currentVersion = new SemVer(packageJson.version);
const partialVersion = `${currentVersion.major}.${currentVersion.minor}.${currentVersion.patch}`;
const prereleasePart = `cron.${currentHash}`;
const newVersion = `${partialVersion}-${prereleasePart}`;
await writeFile(
versionJsonPath,
JSON.stringify(
{
...packageJson,
version: newVersion,
},
null,
2,
),
);
if (process.env.GITHUB_OUTPUT) {
await writeFile(process.env.GITHUB_OUTPUT, `VERSION=${newVersion}`, {
flag: "a+",
});
}
await exec(`yarn run bump-version --yes ${newVersion}`);
} catch (error) {
console.error(error);
process.exit(1);
}

View File

@ -0,0 +1,19 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"composite": true,
"outDir": "dist/",
"paths": {
"*": [
"node_modules/*",
"types/*"
]
},
},
"include": [
"src/**/*",
],
"exclude": [
"node_modules",
]
}

View File

@ -0,0 +1,890 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@mole-inc/bin-wrapper@^8.0.1":
version "8.0.1"
resolved "https://registry.yarnpkg.com/@mole-inc/bin-wrapper/-/bin-wrapper-8.0.1.tgz#d7fd0ceb1cfa8a855293a3ed9d7d135f4d442f0e"
integrity sha512-sTGoeZnjI8N4KS+sW2AN95gDBErhAguvkw/tWdCjeM8bvxpz5lqrnd0vOJABA1A+Ic3zED7PYoLP/RANLgVotA==
dependencies:
bin-check "^4.1.0"
bin-version-check "^5.0.0"
content-disposition "^0.5.4"
ext-name "^5.0.0"
file-type "^17.1.6"
filenamify "^5.0.2"
got "^11.8.5"
os-filter-obj "^2.0.0"
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==
dependencies:
"@nodelib/fs.stat" "2.0.5"
run-parallel "^1.1.9"
"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
version "2.0.5"
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
"@nodelib/fs.walk@^1.2.3":
version "1.2.8"
resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
dependencies:
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"
"@sindresorhus/is@^4.0.0":
version "4.6.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f"
integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==
"@swc/cli@^0.1.61":
version "0.1.61"
resolved "https://registry.yarnpkg.com/@swc/cli/-/cli-0.1.61.tgz#202c70644b30e7b5f9c2590045f7fda72920b998"
integrity sha512-HeYMJ+8gKfJzM9xgcZqTpAHJYAJVGSljBSmWRUx2B6UiGraLsLjEcqxITwi6/t6Af+QboBMiQX5Wwll89oPK7g==
dependencies:
"@mole-inc/bin-wrapper" "^8.0.1"
commander "^7.1.0"
fast-glob "^3.2.5"
semver "^7.3.8"
slash "3.0.0"
source-map "^0.7.3"
"@swc/core-darwin-arm64@1.3.35":
version "1.3.35"
resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.35.tgz#55ff0cc069769ce8bf6562ca0e724fe9c43deb8c"
integrity sha512-zQUFkHx4gZpu0uo2IspvPnKsz8bsdXd5bC33xwjtoAI1cpLerDyqo4v2zIahEp+FdKZjyVsLHtfJiQiA1Qka3A==
"@swc/core-darwin-x64@1.3.35":
version "1.3.35"
resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.3.35.tgz#270543a2aad6ddbc2d8e8d9a0b025bc08cef9a48"
integrity sha512-oOSkSGWtALovaw22lNevKD434OQTPf8X+dVPvPMrJXJpJ34dWDlFWpLntoc+arvKLNZ7LQmTuk8rR1hkrAY7cw==
"@swc/core-linux-arm-gnueabihf@1.3.35":
version "1.3.35"
resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.35.tgz#d2c81406202202962c09451ec58e37efacb15c38"
integrity sha512-Yie8k00O6O8BCATS/xeKStquV4OYSskUGRDXBQVDw1FrE23PHaSeHCgg4q6iNZjJzXCOJbaTCKnYoIDn9DMf7A==
"@swc/core-linux-arm64-gnu@1.3.35":
version "1.3.35"
resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.35.tgz#f4670584fbe71525d919fa06db3ad778cee242e6"
integrity sha512-Zlv3WHa/4x2p51HSvjUWXHfSe1Gl2prqImUZJc8NZOlj75BFzVuR0auhQ+LbwvIQ3gaA1LODX9lyS9wXL3yjxA==
"@swc/core-linux-arm64-musl@1.3.35":
version "1.3.35"
resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.35.tgz#fd116255cca2d8e098637e95f38ae08f95a47db6"
integrity sha512-u6tCYsrSyZ8U+4jLMA/O82veBfLy2aUpn51WxQaeH7wqZGy9TGSJXoO8vWxARQ6b72vjsnKDJHP4MD8hFwcctg==
"@swc/core-linux-x64-gnu@1.3.35":
version "1.3.35"
resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.35.tgz#7a0fb187f1e9baa38d05273a7576c4eaf80a96b8"
integrity sha512-Dtxf2IbeH7XlNhP1Qt2/MvUPkpEbn7hhGfpSRs4ot8D3Vf5QEX4S/QtC1OsFWuciiYgHAT1Ybjt4xZic9DSkmA==
"@swc/core-linux-x64-musl@1.3.35":
version "1.3.35"
resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.35.tgz#ad2632b9ae0ea2bfd1461f121b324063c3d6755e"
integrity sha512-4XavNJ60GprjpTiESCu5daJUnmErixPAqDitJSMu4TV32LNIE8G00S9pDLXinDTW1rgcGtQdq1NLkNRmwwovtg==
"@swc/core-win32-arm64-msvc@1.3.35":
version "1.3.35"
resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.35.tgz#5761d0f6efd9affa5571104f1a1951b8b530ec45"
integrity sha512-dNGfKCUSX2M4qVyaS80Lyos0FkXyHRCvrdQ2Y4Hrg3FVokiuw3yY6fLohpUfQ5ws3n2A39dh7jGDeh34+l0sGA==
"@swc/core-win32-ia32-msvc@1.3.35":
version "1.3.35"
resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.35.tgz#53ebcf1a6abb0e5152c17da3871d695dfdc07338"
integrity sha512-ChuPSrDR+JBf7S7dEKPicnG8A3bM0uWPsW2vG+V2wH4iNfNxKVemESHosmYVeEZXqMpomNMvLyeHep1rjRsc0Q==
"@swc/core-win32-x64-msvc@1.3.35":
version "1.3.35"
resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.35.tgz#186170a7f33d1a08ce00800baf293e6d114659a9"
integrity sha512-/RvphT4WfuGfIK84Ha0dovdPrKB1bW/mc+dtdmhv2E3EGkNc5FoueNwYmXWRimxnU7X0X7IkcRhyKB4G5DeAmg==
"@swc/core@^1.3.35":
version "1.3.35"
resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.3.35.tgz#a96fa52651e56dc00af7b0b83750be33e151bfa8"
integrity sha512-KmiBin0XSVzJhzX19zTiCqmLslZ40Cl7zqskJcTDeIrRhfgKdiAsxzYUanJgMJIRjYtl9Kcg1V/Ip2o2wL8v3w==
optionalDependencies:
"@swc/core-darwin-arm64" "1.3.35"
"@swc/core-darwin-x64" "1.3.35"
"@swc/core-linux-arm-gnueabihf" "1.3.35"
"@swc/core-linux-arm64-gnu" "1.3.35"
"@swc/core-linux-arm64-musl" "1.3.35"
"@swc/core-linux-x64-gnu" "1.3.35"
"@swc/core-linux-x64-musl" "1.3.35"
"@swc/core-win32-arm64-msvc" "1.3.35"
"@swc/core-win32-ia32-msvc" "1.3.35"
"@swc/core-win32-x64-msvc" "1.3.35"
"@szmarczak/http-timer@^4.0.5":
version "4.0.6"
resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807"
integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==
dependencies:
defer-to-connect "^2.0.0"
"@tokenizer/token@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.3.0.tgz#fe98a93fe789247e998c75e74e9c7c63217aa276"
integrity sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==
"@types/cacheable-request@^6.0.1":
version "6.0.3"
resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz#a430b3260466ca7b5ca5bfd735693b36e7a9d183"
integrity sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==
dependencies:
"@types/http-cache-semantics" "*"
"@types/keyv" "^3.1.4"
"@types/node" "*"
"@types/responselike" "^1.0.0"
"@types/http-cache-semantics@*":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812"
integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==
"@types/keyv@^3.1.4":
version "3.1.4"
resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6"
integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==
dependencies:
"@types/node" "*"
"@types/node@*":
version "18.13.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.13.0.tgz#0400d1e6ce87e9d3032c19eb6c58205b0d3f7850"
integrity sha512-gC3TazRzGoOnoKAhUx+Q0t8S9Tzs74z7m0ipwGpSqQrleP14hKxP4/JUeEQcD3W1/aIpnWl8pHowI7WokuZpXg==
"@types/node@^16.18.11":
version "16.18.12"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.12.tgz#e3bfea80e31523fde4292a6118f19ffa24fd6f65"
integrity sha512-vzLe5NaNMjIE3mcddFVGlAXN1LEWueUsMsOJWaT6wWMJGyljHAWHznqfnKUQWGzu7TLPrGvWdNAsvQYW+C0xtw==
"@types/responselike@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29"
integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==
dependencies:
"@types/node" "*"
"@types/semver@^7.3.13":
version "7.3.13"
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91"
integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==
arch@^2.1.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11"
integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==
arg@^5.0.2:
version "5.0.2"
resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c"
integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==
bin-check@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/bin-check/-/bin-check-4.1.0.tgz#fc495970bdc88bb1d5a35fc17e65c4a149fc4a49"
integrity sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA==
dependencies:
execa "^0.7.0"
executable "^4.1.0"
bin-version-check@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/bin-version-check/-/bin-version-check-5.0.0.tgz#958de30174d3fcedadbfc52f89f37bca62594877"
integrity sha512-Q3FMQnS5eZmrBGqmDXLs4dbAn/f+52voP6ykJYmweSA60t6DyH4UTSwZhtbK5UH+LBoWvDljILUQMLRUtsynsA==
dependencies:
bin-version "^6.0.0"
semver "^7.3.5"
semver-truncate "^2.0.0"
bin-version@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/bin-version/-/bin-version-6.0.0.tgz#08ecbe5fc87898b441425e145f9e105064d00315"
integrity sha512-nk5wEsP4RiKjG+vF+uG8lFsEn4d7Y6FVDamzzftSunXOoOcOOkzcWdKVlGgFFwlUQCj63SgnUkLLGF8v7lufhw==
dependencies:
execa "^5.0.0"
find-versions "^5.0.0"
braces@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
dependencies:
fill-range "^7.0.1"
cacheable-lookup@^5.0.3:
version "5.0.4"
resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005"
integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==
cacheable-request@^7.0.2:
version "7.0.2"
resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.2.tgz#ea0d0b889364a25854757301ca12b2da77f91d27"
integrity sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==
dependencies:
clone-response "^1.0.2"
get-stream "^5.1.0"
http-cache-semantics "^4.0.0"
keyv "^4.0.0"
lowercase-keys "^2.0.0"
normalize-url "^6.0.1"
responselike "^2.0.0"
clone-response@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3"
integrity sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==
dependencies:
mimic-response "^1.0.0"
commander@^7.1.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
content-disposition@^0.5.4:
version "0.5.4"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==
dependencies:
safe-buffer "5.2.1"
cross-spawn@^5.0.1:
version "5.1.0"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
integrity sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==
dependencies:
lru-cache "^4.0.1"
shebang-command "^1.2.0"
which "^1.2.9"
cross-spawn@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
dependencies:
path-key "^3.1.0"
shebang-command "^2.0.0"
which "^2.0.1"
decompress-response@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc"
integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==
dependencies:
mimic-response "^3.1.0"
defer-to-connect@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587"
integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==
end-of-stream@^1.1.0:
version "1.4.4"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
dependencies:
once "^1.4.0"
escape-string-regexp@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz#4683126b500b61762f2dbebace1806e8be31b1c8"
integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==
execa@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777"
integrity sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==
dependencies:
cross-spawn "^5.0.1"
get-stream "^3.0.0"
is-stream "^1.1.0"
npm-run-path "^2.0.0"
p-finally "^1.0.0"
signal-exit "^3.0.0"
strip-eof "^1.0.0"
execa@^5.0.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==
dependencies:
cross-spawn "^7.0.3"
get-stream "^6.0.0"
human-signals "^2.1.0"
is-stream "^2.0.0"
merge-stream "^2.0.0"
npm-run-path "^4.0.1"
onetime "^5.1.2"
signal-exit "^3.0.3"
strip-final-newline "^2.0.0"
executable@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c"
integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==
dependencies:
pify "^2.2.0"
ext-list@^2.0.0:
version "2.2.2"
resolved "https://registry.yarnpkg.com/ext-list/-/ext-list-2.2.2.tgz#0b98e64ed82f5acf0f2931babf69212ef52ddd37"
integrity sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA==
dependencies:
mime-db "^1.28.0"
ext-name@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/ext-name/-/ext-name-5.0.0.tgz#70781981d183ee15d13993c8822045c506c8f0a6"
integrity sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ==
dependencies:
ext-list "^2.0.0"
sort-keys-length "^1.0.0"
fast-glob@^3.2.5:
version "3.2.12"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80"
integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==
dependencies:
"@nodelib/fs.stat" "^2.0.2"
"@nodelib/fs.walk" "^1.2.3"
glob-parent "^5.1.2"
merge2 "^1.3.0"
micromatch "^4.0.4"
fastq@^1.6.0:
version "1.15.0"
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a"
integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==
dependencies:
reusify "^1.0.4"
file-type@^17.1.6:
version "17.1.6"
resolved "https://registry.yarnpkg.com/file-type/-/file-type-17.1.6.tgz#18669e0577a4849ef6e73a41f8bdf1ab5ae21023"
integrity sha512-hlDw5Ev+9e883s0pwUsuuYNu4tD7GgpUnOvykjv1Gya0ZIjuKumthDRua90VUn6/nlRKAjcxLUnHNTIUWwWIiw==
dependencies:
readable-web-to-node-stream "^3.0.2"
strtok3 "^7.0.0-alpha.9"
token-types "^5.0.0-alpha.2"
filename-reserved-regex@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/filename-reserved-regex/-/filename-reserved-regex-3.0.0.tgz#3d5dd6d4e2d73a3fed2ebc4cd0b3448869a081f7"
integrity sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==
filenamify@^5.0.2:
version "5.1.1"
resolved "https://registry.yarnpkg.com/filenamify/-/filenamify-5.1.1.tgz#a1ccc5ae678a5e34f578afcb9b72898264d166d2"
integrity sha512-M45CbrJLGACfrPOkrTp3j2EcO9OBkKUYME0eiqOCa7i2poaklU0jhlIaMlr8ijLorT0uLAzrn3qXOp5684CkfA==
dependencies:
filename-reserved-regex "^3.0.0"
strip-outer "^2.0.0"
trim-repeated "^2.0.0"
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
dependencies:
to-regex-range "^5.0.1"
find-versions@^5.0.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/find-versions/-/find-versions-5.1.0.tgz#973f6739ce20f5e439a27eba8542a4b236c8e685"
integrity sha512-+iwzCJ7C5v5KgcBuueqVoNiHVoQpwiUK5XFLjf0affFTep+Wcw93tPvmb8tqujDNmzhBDPddnWV/qgWSXgq+Hg==
dependencies:
semver-regex "^4.0.5"
get-stream@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
integrity sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==
get-stream@^5.1.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==
dependencies:
pump "^3.0.0"
get-stream@^6.0.0:
version "6.0.1"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
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"
got@^11.8.5:
version "11.8.6"
resolved "https://registry.yarnpkg.com/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a"
integrity sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==
dependencies:
"@sindresorhus/is" "^4.0.0"
"@szmarczak/http-timer" "^4.0.5"
"@types/cacheable-request" "^6.0.1"
"@types/responselike" "^1.0.0"
cacheable-lookup "^5.0.3"
cacheable-request "^7.0.2"
decompress-response "^6.0.0"
http2-wrapper "^1.0.0-beta.5.2"
lowercase-keys "^2.0.0"
p-cancelable "^2.0.0"
responselike "^2.0.0"
http-cache-semantics@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a"
integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==
http2-wrapper@^1.0.0-beta.5.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d"
integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==
dependencies:
quick-lru "^5.1.1"
resolve-alpn "^1.0.0"
human-signals@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
ieee754@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
inherits@^2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
is-extglob@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
is-glob@^4.0.1:
version "4.0.3"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
dependencies:
is-extglob "^2.1.1"
is-number@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
is-plain-obj@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
integrity sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==
is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==
is-stream@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077"
integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
json-buffer@3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==
keyv@^4.0.0:
version "4.5.2"
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.2.tgz#0e310ce73bf7851ec702f2eaf46ec4e3805cce56"
integrity sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==
dependencies:
json-buffer "3.0.1"
lowercase-keys@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479"
integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==
lru-cache@^4.0.1:
version "4.1.5"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd"
integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==
dependencies:
pseudomap "^1.0.2"
yallist "^2.1.2"
lru-cache@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
dependencies:
yallist "^4.0.0"
merge-stream@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
merge2@^1.3.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
micromatch@^4.0.4:
version "4.0.5"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
dependencies:
braces "^3.0.2"
picomatch "^2.3.1"
mime-db@^1.28.0:
version "1.52.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
mimic-fn@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
mimic-response@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
mimic-response@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9"
integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==
normalize-url@^6.0.1:
version "6.1.0"
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a"
integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==
npm-run-path@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
integrity sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==
dependencies:
path-key "^2.0.0"
npm-run-path@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==
dependencies:
path-key "^3.0.0"
once@^1.3.1, once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
dependencies:
wrappy "1"
onetime@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
dependencies:
mimic-fn "^2.1.0"
os-filter-obj@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/os-filter-obj/-/os-filter-obj-2.0.0.tgz#1c0b62d5f3a2442749a2d139e6dddee6e81d8d16"
integrity sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg==
dependencies:
arch "^2.1.0"
p-cancelable@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf"
integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==
p-finally@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==
path-key@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==
path-key@^3.0.0, path-key@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
peek-readable@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-5.0.0.tgz#7ead2aff25dc40458c60347ea76cfdfd63efdfec"
integrity sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==
picomatch@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
pify@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==
pseudomap@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==
pump@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
dependencies:
end-of-stream "^1.1.0"
once "^1.3.1"
queue-microtask@^1.2.2:
version "1.2.3"
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
quick-lru@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932"
integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==
readable-stream@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
dependencies:
inherits "^2.0.3"
string_decoder "^1.1.1"
util-deprecate "^1.0.1"
readable-web-to-node-stream@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz#5d52bb5df7b54861fd48d015e93a2cb87b3ee0bb"
integrity sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==
dependencies:
readable-stream "^3.6.0"
resolve-alpn@^1.0.0:
version "1.2.1"
resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9"
integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==
responselike@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.1.tgz#9a0bc8fdc252f3fb1cca68b016591059ba1422bc"
integrity sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==
dependencies:
lowercase-keys "^2.0.0"
reusify@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
rimraf@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-4.1.2.tgz#20dfbc98083bdfaa28b01183162885ef213dbf7c"
integrity sha512-BlIbgFryTbw3Dz6hyoWFhKk+unCcHMSkZGrTFVAx2WmttdBSonsdtRlwiuTbDqTKr+UlXIUqJVS4QT5tUzGENQ==
run-parallel@^1.1.9:
version "1.2.0"
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
dependencies:
queue-microtask "^1.2.2"
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==
semver-regex@^4.0.5:
version "4.0.5"
resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-4.0.5.tgz#fbfa36c7ba70461311f5debcb3928821eb4f9180"
integrity sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==
semver-truncate@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/semver-truncate/-/semver-truncate-2.0.0.tgz#cce4c0af36dbd97e898b821be870d17fbfb67f56"
integrity sha512-Rh266MLDYNeML5h90ttdMwfXe1+Nc4LAWd9X1KdJe8pPHP4kFmvLZALtsMNHNdvTyQygbEC0D59sIz47DIaq8w==
dependencies:
semver "^6.0.0"
semver@^6.0.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
semver@^7.3.5, semver@^7.3.8:
version "7.3.8"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798"
integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==
dependencies:
lru-cache "^6.0.0"
shebang-command@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==
dependencies:
shebang-regex "^1.0.0"
shebang-command@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
dependencies:
shebang-regex "^3.0.0"
shebang-regex@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==
shebang-regex@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
signal-exit@^3.0.0, signal-exit@^3.0.3:
version "3.0.7"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
slash@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
sort-keys-length@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/sort-keys-length/-/sort-keys-length-1.0.1.tgz#9cb6f4f4e9e48155a6aa0671edd336ff1479a188"
integrity sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw==
dependencies:
sort-keys "^1.0.0"
sort-keys@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad"
integrity sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==
dependencies:
is-plain-obj "^1.0.0"
source-map@^0.7.3:
version "0.7.4"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656"
integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==
string_decoder@^1.1.1:
version "1.3.0"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
dependencies:
safe-buffer "~5.2.0"
strip-eof@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
integrity sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==
strip-final-newline@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
strip-outer@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/strip-outer/-/strip-outer-2.0.0.tgz#c45c724ed9b1ff6be5f660503791404f4714084b"
integrity sha512-A21Xsm1XzUkK0qK1ZrytDUvqsQWict2Cykhvi0fBQntGG5JSprESasEyV1EZ/4CiR5WB5KjzLTrP/bO37B0wPg==
strtok3@^7.0.0-alpha.9:
version "7.0.0"
resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-7.0.0.tgz#868c428b4ade64a8fd8fee7364256001c1a4cbe5"
integrity sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==
dependencies:
"@tokenizer/token" "^0.3.0"
peek-readable "^5.0.0"
to-regex-range@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
dependencies:
is-number "^7.0.0"
token-types@^5.0.0-alpha.2:
version "5.0.1"
resolved "https://registry.yarnpkg.com/token-types/-/token-types-5.0.1.tgz#aa9d9e6b23c420a675e55413b180635b86a093b4"
integrity sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==
dependencies:
"@tokenizer/token" "^0.3.0"
ieee754 "^1.2.1"
trim-repeated@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/trim-repeated/-/trim-repeated-2.0.0.tgz#5d60556d6d40d9461b7c7e06c3ac20b6b1d50090"
integrity sha512-QUHBFTJGdOwmp0tbOG505xAgOp/YliZP/6UgafFXYZ26WT1bvQmSMJUvkeVSASuJJHbqsFbynTvkd5W8RBTipg==
dependencies:
escape-string-regexp "^5.0.0"
util-deprecate@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
which@^1.2.9:
version "1.3.1"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
dependencies:
isexe "^2.0.0"
which@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
dependencies:
isexe "^2.0.0"
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
yallist@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==
yallist@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==

View File

@ -3,7 +3,7 @@
"productName": "", "productName": "",
"description": "Lens Desktop Core", "description": "Lens Desktop Core",
"homepage": "https://github.com/lensapp/lens", "homepage": "https://github.com/lensapp/lens",
"version": "6.4.0-beta.10", "version": "6.4.0-beta.13",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/lensapp/lens.git" "url": "git+https://github.com/lensapp/lens.git"
@ -125,9 +125,9 @@
}, },
"dependencies": { "dependencies": {
"@astronautlabs/jsonpath": "^1.1.0", "@astronautlabs/jsonpath": "^1.1.0",
"@hapi/call": "^9.0.0", "@hapi/call": "^9.0.1",
"@hapi/subtext": "^7.0.4", "@hapi/subtext": "^7.0.4",
"@k8slens/node-fetch": "^6.4.0-beta.10", "@k8slens/node-fetch": "^6.4.0-beta.13",
"@kubernetes/client-node": "^0.18.1", "@kubernetes/client-node": "^0.18.1",
"@material-ui/styles": "^4.11.5", "@material-ui/styles": "^4.11.5",
"@ogre-tools/fp": "^12.0.1", "@ogre-tools/fp": "^12.0.1",
@ -200,10 +200,9 @@
"@material-ui/core": "^4.12.3", "@material-ui/core": "^4.12.3",
"@material-ui/icons": "^4.11.2", "@material-ui/icons": "^4.11.2",
"@material-ui/lab": "^4.0.0-alpha.60", "@material-ui/lab": "^4.0.0-alpha.60",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
"@sentry/types": "^6.19.7", "@sentry/types": "^6.19.7",
"@swc/cli": "^0.1.59", "@swc/cli": "^0.1.61",
"@swc/core": "^1.3.31", "@swc/core": "^1.3.35",
"@swc/jest": "^0.2.24", "@swc/jest": "^0.2.24",
"@testing-library/dom": "^7.31.2", "@testing-library/dom": "^7.31.2",
"@testing-library/jest-dom": "^5.16.5", "@testing-library/jest-dom": "^5.16.5",
@ -221,7 +220,6 @@
"@types/glob-to-regexp": "^0.4.1", "@types/glob-to-regexp": "^0.4.1",
"@types/hapi__call": "^9.0.0", "@types/hapi__call": "^9.0.0",
"@types/hapi__subtext": "^7.0.0", "@types/hapi__subtext": "^7.0.0",
"@types/html-webpack-plugin": "^3.2.6",
"@types/http-proxy": "^1.17.9", "@types/http-proxy": "^1.17.9",
"@types/jest": "^28.1.6", "@types/jest": "^28.1.6",
"@types/js-yaml": "^4.0.5", "@types/js-yaml": "^4.0.5",
@ -254,8 +252,8 @@
"@types/webpack-dev-server": "^4.7.2", "@types/webpack-dev-server": "^4.7.2",
"@types/webpack-env": "^1.18.0", "@types/webpack-env": "^1.18.0",
"@types/webpack-node-externals": "^2.5.3", "@types/webpack-node-externals": "^2.5.3",
"@typescript-eslint/eslint-plugin": "^5.49.0", "@typescript-eslint/eslint-plugin": "^5.52.0",
"@typescript-eslint/parser": "^5.49.0", "@typescript-eslint/parser": "^5.52.0",
"adr": "^1.4.3", "adr": "^1.4.3",
"ansi_up": "^5.1.0", "ansi_up": "^5.1.0",
"chalk": "^4.1.2", "chalk": "^4.1.2",
@ -267,11 +265,11 @@
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"css-loader": "^6.7.3", "css-loader": "^6.7.3",
"deepdash": "^5.3.9", "deepdash": "^5.3.9",
"dompurify": "^2.4.3", "dompurify": "^2.4.4",
"electron": "^19.1.9", "electron": "^19.1.9",
"electron-builder": "^23.6.0", "electron-builder": "^23.6.0",
"electron-notarize": "^0.3.0", "electron-notarize": "^0.3.0",
"esbuild": "^0.17.5", "esbuild": "^0.17.7",
"esbuild-loader": "^2.21.0", "esbuild-loader": "^2.21.0",
"eslint": "^8.33.0", "eslint": "^8.33.0",
"eslint-import-resolver-typescript": "^3.5.3", "eslint-import-resolver-typescript": "^3.5.3",
@ -281,7 +279,6 @@
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-unused-imports": "^2.0.0", "eslint-plugin-unused-imports": "^2.0.0",
"fork-ts-checker-webpack-plugin": "^7.3.0", "fork-ts-checker-webpack-plugin": "^7.3.0",
"html-webpack-plugin": "^5.5.0",
"identity-obj-proxy": "^3.0.0", "identity-obj-proxy": "^3.0.0",
"ignore-loader": "^0.1.2", "ignore-loader": "^0.1.2",
"include-media": "^1.4.9", "include-media": "^1.4.9",
@ -313,7 +310,7 @@
"react-table": "^7.8.0", "react-table": "^7.8.0",
"react-window": "^1.8.8", "react-window": "^1.8.8",
"rimraf": "^4.1.2", "rimraf": "^4.1.2",
"sass": "^1.57.1", "sass": "^1.58.0",
"sass-loader": "^12.6.0", "sass-loader": "^12.6.0",
"style-loader": "^3.3.1", "style-loader": "^3.3.1",
"tailwindcss": "^3.2.4", "tailwindcss": "^3.2.4",

View File

@ -33,6 +33,20 @@ export class Namespace extends KubeObject<
getStatus() { getStatus() {
return this.status?.phase ?? "-"; return this.status?.phase ?? "-";
} }
isSubnamespace() {
return this.getAnnotations().find(annotation => annotation.includes("hnc.x-k8s.io/subnamespace-of"));
}
isChildOf(parentName: string) {
return this.getLabels().find(label => label === `${parentName}.tree.hnc.x-k8s.io/depth=1`);
}
isControlledByHNC() {
const hierarchicalNamesaceControllerLabel = "hnc.x-k8s.io/included-namespace=true";
return this.getLabels().find(label => label === hierarchicalNamesaceControllerLabel);
}
} }
export class NamespaceApi extends KubeApi<Namespace> { export class NamespaceApi extends KubeApi<Namespace> {

View File

@ -7,5 +7,3 @@
export { applicationInformationToken } from "./vars/application-information-token"; export { applicationInformationToken } from "./vars/application-information-token";
export type { ApplicationInformation } from "./vars/application-information-token"; export type { ApplicationInformation } from "./vars/application-information-token";
export { bundledExtensionInjectionToken } from "../extensions/extension-discovery/bundled-extension-token"; export { bundledExtensionInjectionToken } from "../extensions/extension-discovery/bundled-extension-token";
export * as extensionApi from "../extensions/common-api";

View File

@ -3,20 +3,13 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { createLogger, format } from "winston";
import type { Logger } from "./logger"; import type { Logger } from "./logger";
import { loggerTransportInjectionToken } from "./logger/transports"; import winstonLoggerInjectable from "./winston-logger.injectable";
const loggerInjectable = getInjectable({ const loggerInjectable = getInjectable({
id: "logger", id: "logger",
instantiate: (di): Logger => { instantiate: (di): Logger => {
const baseLogger = createLogger({ const baseLogger = di.inject(winstonLoggerInjectable);
format: format.combine(
format.splat(),
format.simple(),
),
transports: di.injectMany(loggerTransportInjectionToken),
});
return { return {
debug: (message, ...data) => baseLogger.debug(message, ...data), debug: (message, ...data) => baseLogger.debug(message, ...data),

View File

@ -23,7 +23,7 @@ export const defaultTerminalConfig: TerminalConfig = {
fontFamily: defaultTerminalFontFamily, fontFamily: defaultTerminalFontFamily,
}; };
interface BaseEditorConfiguration extends Required<Pick<editor.IStandaloneEditorConstructionOptions, "minimap" | "tabSize" | "fontSize" | "fontFamily">> { export interface BaseEditorConfiguration extends Required<Pick<editor.IStandaloneEditorConstructionOptions, "minimap" | "tabSize" | "fontSize" | "fontFamily">> {
lineNumbers: NonNullable<Exclude<editor.IStandaloneEditorConstructionOptions["lineNumbers"], Function>>; lineNumbers: NonNullable<Exclude<editor.IStandaloneEditorConstructionOptions["lineNumbers"], Function>>;
} }

View File

@ -0,0 +1,20 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { createLogger, format } from "winston";
import { loggerTransportInjectionToken } from "./logger/transports";
const winstonLoggerInjectable = getInjectable({
id: "winston-logger",
instantiate: (di) => createLogger({
format: format.combine(
format.splat(),
format.simple(),
),
transports: di.injectMany(loggerTransportInjectionToken),
}),
});
export default winstonLoggerInjectable;

View File

@ -0,0 +1,103 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { runInAction } from "mobx";
import type { ApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder";
import type { FakeExtensionOptions } from "../../renderer/components/test-utils/get-extension-fake";
import getHashInjectable from "../../extensions/extension-loader/file-system-provisioner-store/get-hash.injectable";
import fsInjectable from "../../common/fs/fs.injectable";
describe("configurable directories for extension files", () => {
let builder: ApplicationBuilder;
beforeEach(async () => {
builder = getApplicationBuilder();
builder.beforeApplicationStart(
(mainDi) => {
runInAction(() => {
mainDi.override(getHashInjectable, () => x => x);
});
},
);
await builder.startHidden();
const window = builder.applicationWindow.create("some-window-id");
await window.start();
});
describe("when extension with a specific store name is enabled", () => {
let testExtensionOptions: FakeExtensionOptions;
beforeEach(() => {
testExtensionOptions = {
id: "some-extension",
name: "some-extension-name",
mainOptions: {
manifest: {
name: "irrelevant",
storeName: "some-specific-store-name",
version: "0",
engines: {
lens: "0",
},
},
},
};
builder.extensions.enable(testExtensionOptions);
});
it("creates extension directory for specific store name", async () => {
const fs = builder.mainDi.inject(fsInjectable);
await builder.extensions.get("some-extension").main.getExtensionFileFolder();
const nonHashedExtensionDirectories = await fs.readdir("/some-directory-for-app-data/some-product-name/extension_data");
expect(nonHashedExtensionDirectories).toContain("some-specific-store-name");
});
});
describe("when extension with no specific store name is enabled", () => {
let testExtensionOptions: FakeExtensionOptions;
beforeEach(() => {
testExtensionOptions = {
id: "some-extension",
name: "some-extension-name",
mainOptions: {
manifest: {
name: "some-package-name",
storeName: undefined,
version: "0",
engines: {
lens: "0",
},
},
},
};
builder.extensions.enable(testExtensionOptions);
});
it("creates extension directory for package name", async () => {
const fs = builder.mainDi.inject(fsInjectable);
await builder.extensions.get("some-extension").main.getExtensionFileFolder();
const nonHashedExtensionDirectories = await fs.readdir("/some-directory-for-app-data/some-product-name/extension_data");
expect(nonHashedExtensionDirectories).toContain("some-package-name");
});
});
});

View File

@ -3,31 +3,21 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
// APIs
import { App } from "./app";
import * as EventBus from "./event-bus";
import * as Store from "./stores";
import { Util } from "./utils";
import * as Catalog from "./catalog";
import * as Types from "./types";
import * as Proxy from "./proxy";
import loggerInjectable from "../../common/logger.injectable"; import loggerInjectable from "../../common/logger.injectable";
import { asLegacyGlobalForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api"; import { asLegacyGlobalForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-object-for-extension-api";
import type { Logger } from "../../common/logger";
import type { LensExtension, LensExtensionManifest } from "../lens-extension";
const logger = asLegacyGlobalForExtensionApi(loggerInjectable); // APIs
export { App } from "./app";
export * as EventBus from "./event-bus";
export * as Store from "./stores";
export * as Util from "./utils";
export * as Catalog from "./catalog";
export * as Types from "./types";
export * as Proxy from "./proxy";
export { export type { Logger } from "../../common/logger";
App, export type { LensExtension, LensExtensionManifest } from "../lens-extension";
EventBus, export type { InstalledExtension } from "../extension-discovery/extension-discovery";
Catalog, export type { PackageJson } from "type-fest";
Store,
Types, export const logger = asLegacyGlobalForExtensionApi(loggerInjectable);
Util,
logger,
Proxy,
Logger,
LensExtension,
LensExtensionManifest,
};

View File

@ -108,13 +108,16 @@ export type { CreateKubeApiForLocalClusterConfig as ILocalKubeApiConfig } from "
export { export {
KubeObject, KubeObject,
KubeStatus, KubeStatus,
type OwnerReference, } from "../../common/k8s-api/kube-object";
type KubeObjectMetadata, export type {
type NamespaceScopedMetadata, OwnerReference,
type ClusterScopedMetadata, KubeObjectMetadata,
type BaseKubeJsonApiObjectMetadata, NamespaceScopedMetadata,
type KubeJsonApiObjectMetadata, ClusterScopedMetadata,
type KubeStatusData, BaseKubeJsonApiObjectMetadata,
KubeJsonApiObjectMetadata,
KubeStatusData,
KubeJsonApiDataFor,
} from "../../common/k8s-api/kube-object"; } from "../../common/k8s-api/kube-object";
export type { export type {
@ -177,11 +180,11 @@ export abstract class KubeObjectStore<
} }
} }
export { export type {
type JsonPatch, JsonPatch,
type KubeObjectStoreLoadAllParams, KubeObjectStoreLoadAllParams,
type KubeObjectStoreLoadingParams, KubeObjectStoreLoadingParams,
type KubeObjectStoreSubscribeParams, KubeObjectStoreSubscribeParams,
} from "../../common/k8s-api/kube-object.store"; } from "../../common/k8s-api/kube-object.store";
/** /**

View File

@ -1,17 +0,0 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
export type { StatusBarRegistration } from "../../renderer/components/status-bar/status-bar-registration";
export type { KubeObjectMenuRegistration, KubeObjectMenuComponents } from "../../renderer/components/kube-object-menu/kube-object-menu-registration";
export type { AppPreferenceRegistration, AppPreferenceComponents } from "../../features/preferences/renderer/compliance-for-legacy-extension-api/app-preference-registration";
export type { KubeObjectDetailRegistration, KubeObjectDetailComponents } from "../../renderer/components/kube-object-details/kube-object-detail-registration";
export type { KubeObjectStatusRegistration } from "../../renderer/components/kube-object-status-icon/kube-object-status-registration";
export type { PageRegistration, RegisteredPage, PageParams, PageComponentProps, PageComponents, PageTarget } from "../../renderer/routes/page-registration";
export type { ClusterPageMenuRegistration, ClusterPageMenuComponents } from "../../renderer/components/layout/cluster-page-menu";
export type { ProtocolHandlerRegistration, RouteParams as ProtocolRouteParams, RouteHandler as ProtocolRouteHandler } from "../../common/protocol-handler/registration";
export type { CustomCategoryViewProps, CustomCategoryViewComponents, CustomCategoryViewRegistration } from "../../renderer/components/+catalog/custom-views";
export type { ShellEnvModifier, ShellEnvContext } from "../../main/shell-session/shell-env-modifier/shell-env-modifier-registration";
export type { KubeObjectContextMenuItem, KubeObjectOnContextMenuOpenContext, KubeObjectOnContextMenuOpen, KubeObjectHandlers, KubeObjectHandlerRegistration } from "../../renderer/kube-object/handler";
export type { TrayMenuRegistration } from "../../main/tray/tray-menu-registration";
export type { MenuRegistration } from "../../features/application-menu/main/menu-registration";

View File

@ -3,8 +3,21 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
export type IpcMainInvokeEvent = Electron.IpcMainInvokeEvent; export type IpcMainInvokeEvent = Electron.IpcMainInvokeEvent;
export type IpcRendererEvent = Electron.IpcRendererEvent; export type IpcRendererEvent = Electron.IpcRendererEvent;
export type IpcMainEvent = Electron.IpcMainEvent; export type IpcMainEvent = Electron.IpcMainEvent;
export * from "./registrations"; export type { StatusBarRegistration } from "../../renderer/components/status-bar/status-bar-registration";
export type { KubeObjectMenuRegistration, KubeObjectMenuComponents } from "../../renderer/components/kube-object-menu/kube-object-menu-registration";
export type { AppPreferenceRegistration, AppPreferenceComponents } from "../../features/preferences/renderer/compliance-for-legacy-extension-api/app-preference-registration";
export type { KubeObjectDetailRegistration, KubeObjectDetailComponents } from "../../renderer/components/kube-object-details/kube-object-detail-registration";
export type { KubeObjectStatusRegistration } from "../../renderer/components/kube-object-status-icon/kube-object-status-registration";
export type { PageRegistration, RegisteredPage, PageParams, PageComponentProps, PageComponents, PageTarget } from "../../renderer/routes/page-registration";
export type { ClusterPageMenuRegistration, ClusterPageMenuComponents } from "../../renderer/components/layout/cluster-page-menu";
export type { ProtocolHandlerRegistration, RouteParams as ProtocolRouteParams, RouteHandler as ProtocolRouteHandler } from "../../common/protocol-handler/registration";
export type { CustomCategoryViewProps, CustomCategoryViewComponents, CustomCategoryViewRegistration } from "../../renderer/components/+catalog/custom-views";
export type { ShellEnvModifier, ShellEnvContext } from "../../main/shell-session/shell-env-modifier/shell-env-modifier-registration";
export type { KubeObjectContextMenuItem, KubeObjectOnContextMenuOpenContext, KubeObjectOnContextMenuOpen, KubeObjectHandlers, KubeObjectHandlerRegistration } from "../../renderer/kube-object/handler";
export type { TrayMenuRegistration } from "../../main/tray/tray-menu-registration";
export type { MenuRegistration } from "../../features/application-menu/main/menu-registration";

View File

@ -7,20 +7,23 @@ import openLinkInBrowserInjectable from "../../common/utils/open-link-in-browser
import buildVersionInjectable from "../../main/vars/build-version/build-version.injectable"; import buildVersionInjectable from "../../main/vars/build-version/build-version.injectable";
import { asLegacyGlobalFunctionForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api"; import { asLegacyGlobalFunctionForExtensionApi } from "../as-legacy-globals-for-extension-api/as-legacy-global-function-for-extension-api";
import { getLegacyGlobalDiForExtensionApi } from "../as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; import { getLegacyGlobalDiForExtensionApi } from "../as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api";
import { Singleton } from "../../common/utils";
import { prevDefault, stopPropagation } from "../../renderer/utils/prevDefault";
import { cssNames } from "../../renderer/utils/cssNames";
export const Util = { export { Singleton } from "../../common/utils";
Singleton, export { prevDefault, stopPropagation } from "../../renderer/utils/prevDefault";
prevDefault,
stopPropagation, export type { IClassName, IgnoredClassNames } from "../../renderer/utils";
cssNames, export { cssNames } from "../../renderer/utils/cssNames";
openExternal: asLegacyGlobalFunctionForExtensionApi(openLinkInBrowserInjectable),
openBrowser: asLegacyGlobalFunctionForExtensionApi(openLinkInBrowserInjectable), export type { OpenLinkInBrowser } from "../../common/utils/open-link-in-browser.injectable";
getAppVersion: () => {
export const openExternal = asLegacyGlobalFunctionForExtensionApi(openLinkInBrowserInjectable);
export const openBrowser = asLegacyGlobalFunctionForExtensionApi(openLinkInBrowserInjectable);
export const getAppVersion = () => {
const di = getLegacyGlobalDiForExtensionApi(); const di = getLegacyGlobalDiForExtensionApi();
return di.inject(buildVersionInjectable).get(); return di.inject(buildVersionInjectable).get();
}, };
} as const;
export { disposer } from "../../common/utils";
export type { Disposer, Disposable, ExtendableDisposer } from "../../common/utils";

View File

@ -216,9 +216,6 @@ export class ExtensionDiscovery {
const extension = await this.loadExtensionFromFolder(absPath); const extension = await this.loadExtensionFromFolder(absPath);
if (extension) { if (extension) {
// Remove a broken symlink left by a previous installation if it exists.
await this.dependencies.removePath(extension.manifestPath);
// Install dependencies for the new extension // Install dependencies for the new extension
await this.dependencies.installExtension(extension.absolutePath); await this.dependencies.installExtension(extension.absolutePath);

View File

@ -0,0 +1,43 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { ObservableMap } from "mobx";
import { getInjectable } from "@ogre-tools/injectable";
import { getOrInsertWithAsync } from "../../../common/utils";
import randomBytesInjectable from "../../../common/utils/random-bytes.injectable";
import joinPathsInjectable from "../../../common/path/join-paths.injectable";
import directoryForExtensionDataInjectable from "./directory-for-extension-data.injectable";
import ensureDirInjectable from "../../../common/fs/ensure-dir.injectable";
import getHashInjectable from "./get-hash.injectable";
export type EnsureHashedDirectoryForExtension = (extensionName: string, registeredExtensions: ObservableMap<string, string>) => Promise<string>;
const ensureHashedDirectoryForExtensionInjectable = getInjectable({
id: "ensure-hashed-directory-for-extension",
instantiate: (di): EnsureHashedDirectoryForExtension => {
const randomBytes = di.inject(randomBytesInjectable);
const joinPaths = di.inject(joinPathsInjectable);
const directoryForExtensionData = di.inject(directoryForExtensionDataInjectable);
const ensureDirectory = di.inject(ensureDirInjectable);
const getHash = di.inject(getHashInjectable);
return async (extensionName, registeredExtensions) => {
const dirPath = await getOrInsertWithAsync(registeredExtensions, extensionName, async () => {
const salt = (await randomBytes(32)).toString("hex");
const hashedName = getHash(`${extensionName}/${salt}`);
return joinPaths(directoryForExtensionData, hashedName);
});
await ensureDirectory(dirPath);
return dirPath;
};
},
});
export default ensureHashedDirectoryForExtensionInjectable;

View File

@ -4,10 +4,6 @@
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { FileSystemProvisionerStore } from "./file-system-provisioner-store"; import { FileSystemProvisionerStore } from "./file-system-provisioner-store";
import directoryForExtensionDataInjectable from "./directory-for-extension-data.injectable";
import ensureDirectoryInjectable from "../../../common/fs/ensure-dir.injectable";
import joinPathsInjectable from "../../../common/path/join-paths.injectable";
import randomBytesInjectable from "../../../common/utils/random-bytes.injectable";
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable"; import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
import getConfigurationFileModelInjectable from "../../../common/get-configuration-file-model/get-configuration-file-model.injectable"; import getConfigurationFileModelInjectable from "../../../common/get-configuration-file-model/get-configuration-file-model.injectable";
import loggerInjectable from "../../../common/logger.injectable"; import loggerInjectable from "../../../common/logger.injectable";
@ -18,15 +14,12 @@ import getBasenameOfPathInjectable from "../../../common/path/get-basename.injec
import { enlistMessageChannelListenerInjectionToken } from "../../../common/utils/channel/enlist-message-channel-listener-injection-token"; import { enlistMessageChannelListenerInjectionToken } from "../../../common/utils/channel/enlist-message-channel-listener-injection-token";
import fileSystemProvisionerStoreMigrationVersionInjectable from "./migration-version.injectable"; import fileSystemProvisionerStoreMigrationVersionInjectable from "./migration-version.injectable";
import { sendMessageToChannelInjectionToken } from "../../../common/utils/channel/message-to-channel-injection-token"; import { sendMessageToChannelInjectionToken } from "../../../common/utils/channel/message-to-channel-injection-token";
import ensureHashedDirectoryForExtensionInjectable from "./ensure-hashed-directory-for-extension.injectable";
const fileSystemProvisionerStoreInjectable = getInjectable({ const fileSystemProvisionerStoreInjectable = getInjectable({
id: "file-system-provisioner-store", id: "file-system-provisioner-store",
instantiate: (di) => new FileSystemProvisionerStore({ instantiate: (di) => new FileSystemProvisionerStore({
directoryForExtensionData: di.inject(directoryForExtensionDataInjectable),
ensureDirectory: di.inject(ensureDirectoryInjectable),
joinPaths: di.inject(joinPathsInjectable),
randomBytes: di.inject(randomBytesInjectable),
directoryForUserData: di.inject(directoryForUserDataInjectable), directoryForUserData: di.inject(directoryForUserDataInjectable),
getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable), getConfigurationFileModel: di.inject(getConfigurationFileModelInjectable),
logger: di.inject(loggerInjectable), logger: di.inject(loggerInjectable),
@ -38,6 +31,7 @@ const fileSystemProvisionerStoreInjectable = getInjectable({
enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken), enlistMessageChannelListener: di.inject(enlistMessageChannelListenerInjectionToken),
shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken), shouldDisableSyncInListener: di.inject(shouldBaseStoreDisableSyncInIpcListenerInjectionToken),
sendMessageToChannel: di.inject(sendMessageToChannelInjectionToken), sendMessageToChannel: di.inject(sendMessageToChannelInjectionToken),
ensureHashedDirectoryForExtension: di.inject(ensureHashedDirectoryForExtensionInjectable),
}), }),
}); });

View File

@ -3,25 +3,19 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { SHA256 } from "crypto-js";
import { action, makeObservable, observable } from "mobx"; import { action, makeObservable, observable } from "mobx";
import type { BaseStoreDependencies } from "../../../common/base-store/base-store"; import type { BaseStoreDependencies } from "../../../common/base-store/base-store";
import { BaseStore } from "../../../common/base-store/base-store"; import { BaseStore } from "../../../common/base-store/base-store";
import type { LensExtensionId } from "../../lens-extension"; import type { LensExtensionId } from "../../lens-extension";
import { getOrInsertWithAsync, toJS } from "../../../common/utils"; import { toJS } from "../../../common/utils";
import type { EnsureDirectory } from "../../../common/fs/ensure-dir.injectable"; import type { EnsureHashedDirectoryForExtension } from "./ensure-hashed-directory-for-extension.injectable";
import type { JoinPaths } from "../../../common/path/join-paths.injectable";
import type { RandomBytes } from "../../../common/utils/random-bytes.injectable";
interface FSProvisionModel { interface FSProvisionModel {
extensions: Record<string, string>; // extension names to paths extensions: Record<string, string>; // extension names to paths
} }
interface Dependencies extends BaseStoreDependencies { interface Dependencies extends BaseStoreDependencies {
readonly directoryForExtensionData: string; ensureHashedDirectoryForExtension: EnsureHashedDirectoryForExtension;
ensureDirectory: EnsureDirectory;
joinPaths: JoinPaths;
randomBytes: RandomBytes;
} }
export class FileSystemProvisionerStore extends BaseStore<FSProvisionModel> { export class FileSystemProvisionerStore extends BaseStore<FSProvisionModel> {
@ -43,16 +37,7 @@ export class FileSystemProvisionerStore extends BaseStore<FSProvisionModel> {
* @returns path to the folder that the extension can safely write files to. * @returns path to the folder that the extension can safely write files to.
*/ */
async requestDirectory(extensionName: string): Promise<string> { async requestDirectory(extensionName: string): Promise<string> {
const dirPath = await getOrInsertWithAsync(this.registeredExtensions, extensionName, async () => { return this.dependencies.ensureHashedDirectoryForExtension(extensionName, this.registeredExtensions);
const salt = (await this.dependencies.randomBytes(32)).toString("hex");
const hashedName = SHA256(`${extensionName}/${salt}`).toString();
return this.dependencies.joinPaths(this.dependencies.directoryForExtensionData, hashedName);
});
await this.dependencies.ensureDirectory(dirPath);
return dirPath;
} }
@action @action

View File

@ -0,0 +1,15 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { SHA256 } from "crypto-js";
const getHashInjectable = getInjectable({
id: "get-hash",
instantiate: () => (text: string) => SHA256(text).toString(),
});
export default getHashInjectable;

View File

@ -101,6 +101,6 @@ export abstract class ExtensionStore<T extends object> extends BaseStore<T> {
protected cwd() { protected cwd() {
assert(this.extension, "must call this.load() first"); assert(this.extension, "must call this.load() first");
return path.join(super.cwd(), "extension-store", this.extension.name); return path.join(super.cwd(), "extension-store", this.extension.storeName);
} }
} }

View File

@ -8,7 +8,7 @@ import { action, computed, makeObservable, observable } from "mobx";
import type { PackageJson } from "type-fest"; import type { PackageJson } from "type-fest";
import { disposer } from "../common/utils"; import { disposer } from "../common/utils";
import type { LensExtensionDependencies } from "./lens-extension-set-dependencies"; import type { LensExtensionDependencies } from "./lens-extension-set-dependencies";
import type { ProtocolHandlerRegistration } from "./common-api/registrations"; import type { ProtocolHandlerRegistration } from "../common/protocol-handler/registration";
export type LensExtensionId = string; // path to manifest (package.json) export type LensExtensionId = string; // path to manifest (package.json)
export type LensExtensionConstructor = new (...args: ConstructorParameters<typeof LensExtension>) => LensExtension; export type LensExtensionConstructor = new (...args: ConstructorParameters<typeof LensExtension>) => LensExtension;
@ -27,6 +27,10 @@ export interface LensExtensionManifest extends PackageJson {
npm?: string; npm?: string;
node?: string; node?: string;
}; };
// Specify extension name used for persisting data.
// Useful if extension is renamed but the data should not be lost.
storeName?: string;
} }
export const lensExtensionDependencies = Symbol("lens-extension-dependencies"); export const lensExtensionDependencies = Symbol("lens-extension-dependencies");
@ -62,7 +66,10 @@ export class LensExtension<
constructor({ id, manifest, manifestPath, isBundled }: InstalledExtension) { constructor({ id, manifest, manifestPath, isBundled }: InstalledExtension) {
makeObservable(this); makeObservable(this);
// id is the name of the manifest
this.id = id; this.id = id;
this.manifest = manifest; this.manifest = manifest;
this.manifestPath = manifestPath; this.manifestPath = manifestPath;
this.isBundled = !!isBundled; this.isBundled = !!isBundled;
@ -80,6 +87,11 @@ export class LensExtension<
return this.manifest.description; return this.manifest.description;
} }
// Name of extension for persisting data
get storeName() {
return this.manifest.storeName || this.name;
}
/** /**
* @ignore * @ignore
*/ */
@ -93,7 +105,8 @@ export class LensExtension<
* folder name. * folder name.
*/ */
async getExtensionFileFolder(): Promise<string> { async getExtensionFileFolder(): Promise<string> {
return this[lensExtensionDependencies].fileSystemProvisionerStore.requestDirectory(this.id); // storeName is read from the manifest and has a fallback to the manifest name, which equals id
return this[lensExtensionDependencies].fileSystemProvisionerStore.requestDirectory(this.storeName);
} }
@action @action

View File

@ -29,7 +29,8 @@ import type { KubeObjectDetailRegistration } from "../renderer/components/kube-o
import type { ClusterFrameChildComponent } from "../renderer/frames/cluster-frame/cluster-frame-child-component-injection-token"; import type { ClusterFrameChildComponent } from "../renderer/frames/cluster-frame/cluster-frame-child-component-injection-token";
import type { EntitySettingRegistration } from "../renderer/components/+entity-settings/extension-registrator.injectable"; import type { EntitySettingRegistration } from "../renderer/components/+entity-settings/extension-registrator.injectable";
import type { CatalogEntityDetailRegistration } from "../renderer/components/+catalog/entity-details/token"; import type { CatalogEntityDetailRegistration } from "../renderer/components/+catalog/entity-details/token";
import type { ClusterPageMenuRegistration, PageRegistration } from "./common-api/registrations"; import type { PageRegistration } from "../renderer/routes/page-registration";
import type { ClusterPageMenuRegistration } from "../renderer/components/layout/cluster-page-menu";
export class LensRendererExtension extends LensExtension<LensRendererExtensionDependencies> { export class LensRendererExtension extends LensExtension<LensRendererExtensionDependencies> {
globalPages: PageRegistration[] = []; globalPages: PageRegistration[] = [];

View File

@ -9797,11 +9797,11 @@ exports[`cluster/namespaces - edit namespace from new tab when navigating to nam
class="footer" class="footer"
> >
<div <div
class="Dock" class="Dock isOpen"
tabindex="-1" tabindex="-1"
> >
<div <div
class="ResizingAnchor vertical leading disabled" class="ResizingAnchor vertical leading"
/> />
<div <div
class="tabs-container flex align-center" class="tabs-container flex align-center"
@ -9812,10 +9812,63 @@ exports[`cluster/namespaces - edit namespace from new tab when navigating to nam
> >
<div <div
class="Tabs tabs" class="Tabs tabs"
/> >
<div
class="Tab flex gaps align-center DockTab active"
data-testid="dock-tab-for-some-first-tab-id"
id="tab-some-first-tab-id"
role="tab"
tabindex="0"
>
<i
class="Icon material focusable small"
>
<span
class="icon"
data-icon-name="edit"
>
edit
</span>
</i>
<div
class="label"
>
<div
class="flex align-center"
>
<span
class="title"
>
Namespace: some-name
</span>
<div
class="close"
>
<i
class="Icon material interactive focusable small"
data-testid="dock-tab-close-for-some-first-tab-id"
tabindex="0"
>
<span
class="icon"
data-icon-name="close"
>
close
</span>
</i>
<div
data-testid="tooltip-content-for-dock-tab-close-for-some-first-tab-id"
>
Close ⌘+W
</div>
</div>
</div>
</div>
</div>
</div>
</div> </div>
<div <div
class="toolbar flex gaps align-center box grow pl-0" class="toolbar flex gaps align-center box grow"
> >
<div <div
class="dock-menu box grow" class="dock-menu box grow"
@ -9836,6 +9889,118 @@ exports[`cluster/namespaces - edit namespace from new tab when navigating to nam
New tab New tab
</div> </div>
</div> </div>
<i
class="Icon material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="fullscreen"
>
fullscreen
</span>
</i>
<div>
Fit to window
</div>
<i
class="Icon material interactive focusable"
tabindex="0"
>
<span
class="icon"
data-icon-name="keyboard_arrow_down"
>
keyboard_arrow_down
</span>
</i>
<div>
Minimize
</div>
</div>
</div>
<div
class="tab-content edit-resource"
data-testid="dock-tab-content-for-some-first-tab-id"
style="flex-basis: 300px;"
>
<div
class="EditResource flex column"
>
<div
class="InfoPanel flex gaps align-center"
>
<div
class="controls"
>
<div
class="resource-info flex gaps align-center"
>
<span>
Kind:
</span>
<div
class="badge"
>
Namespace
</div>
<span>
Name:
</span>
<div
class="badge"
>
some-name
</div>
<span>
Namespace:
</span>
<div
class="badge"
>
default
</div>
</div>
</div>
<div
class="flex gaps align-center"
/>
<button
class="Button plain"
data-testid="cancel-edit-resource-from-tab-for-some-first-tab-id"
type="button"
>
Cancel
</button>
<button
class="Button active outlined"
data-testid="save-edit-resource-from-tab-for-some-first-tab-id"
type="button"
>
Save
</button>
<button
class="Button primary active"
data-testid="save-and-close-edit-resource-from-tab-for-some-first-tab-id"
type="button"
>
Save & Close
</button>
</div>
<textarea
data-testid="monaco-editor-for-some-first-tab-id"
>
apiVersion: some-api-version
kind: Namespace
metadata:
uid: some-uid
name: some-name
resourceVersion: some-resource-version
selfLink: /apis/some-api-version/namespaces/some-uid
somePropertyToBeRemoved: some-value
somePropertyToBeChanged: some-old-value
</textarea>
</div> </div>
</div> </div>
</div> </div>

View File

@ -401,8 +401,7 @@ metadata:
expect(rendered.baseElement).toMatchSnapshot(); expect(rendered.baseElement).toMatchSnapshot();
}); });
// TODO: Not doable at the moment because info panel controls closing of the tab it("does not close the dock tab", () => {
xit("does not close the dock tab", () => {
expect( expect(
rendered.getByTestId("dock-tab-for-some-first-tab-id"), rendered.getByTestId("dock-tab-for-some-first-tab-id"),
).toBeInTheDocument(); ).toBeInTheDocument();

View File

@ -443,6 +443,9 @@ exports[`extensions - navigation using application menu when navigating to exten
: you can drag and drop a tarball file to this area : you can drag and drop a tarball file to this area
</small> </small>
</section> </section>
<div
class="size-md"
/>
<div> <div>
<div <div
class="Spinner singleColor center" class="Spinner singleColor center"

View File

@ -11,7 +11,7 @@ const apiBaseHostHeaderInjectable = getInjectable({
instantiate: (di) => { instantiate: (di) => {
const lensProxyPort = di.inject(lensProxyPortInjectable); const lensProxyPort = di.inject(lensProxyPortInjectable);
return `localhost:${lensProxyPort.get()}`; return `lens.app:${lensProxyPort.get()}`;
}, },
injectionToken: apiBaseHostHeaderInjectionToken, injectionToken: apiBaseHostHeaderInjectionToken,
}); });

View File

@ -2,7 +2,6 @@
* Copyright (c) OpenLens Authors. All rights reserved. * Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import type { SelfSignedCert } from "selfsigned";
import { lensProxyCertificateChannel } from "../../common/certificate/lens-proxy-certificate-channel"; import { lensProxyCertificateChannel } from "../../common/certificate/lens-proxy-certificate-channel";
import { getRequestChannelListenerInjectable } from "../utils/channel/channel-listeners/listener-tokens"; import { getRequestChannelListenerInjectable } from "../utils/channel/channel-listeners/listener-tokens";
import lensProxyCertificateInjectable from "../../common/certificate/lens-proxy-certificate.injectable"; import lensProxyCertificateInjectable from "../../common/certificate/lens-proxy-certificate.injectable";
@ -10,7 +9,7 @@ import lensProxyCertificateInjectable from "../../common/certificate/lens-proxy-
const lensProxyCertificateRequestHandlerInjectable = getRequestChannelListenerInjectable({ const lensProxyCertificateRequestHandlerInjectable = getRequestChannelListenerInjectable({
channel: lensProxyCertificateChannel, channel: lensProxyCertificateChannel,
handler: (di) => { handler: (di) => {
const lensProxyCertificate = di.inject(lensProxyCertificateInjectable).get() as SelfSignedCert; const lensProxyCertificate = di.inject(lensProxyCertificateInjectable).get();
return () => ({ return () => ({
cert: lensProxyCertificate.cert, cert: lensProxyCertificate.cert,

View File

@ -10,5 +10,6 @@ export { beforeElectronIsReadyInjectionToken } from "./start-main-application/ru
export { onLoadOfApplicationInjectionToken } from "./start-main-application/runnable-tokens/on-load-of-application-injection-token"; export { onLoadOfApplicationInjectionToken } from "./start-main-application/runnable-tokens/on-load-of-application-injection-token";
export { createApp } from "./create-app"; export { createApp } from "./create-app";
export * as Mobx from "mobx"; export * as Mobx from "mobx";
export * as extensionApi from "../extensions/main-api"; export * as mainExtensionApi from "../extensions/main-api";
export * as commonExtensionApi from "../extensions/common-api";
export * as Pty from "node-pty"; export * as Pty from "node-pty";

View File

@ -27,6 +27,12 @@ export interface UrlSource {
} }
export type ContentSource = RequireExactlyOne<FileSource & UrlSource>; export type ContentSource = RequireExactlyOne<FileSource & UrlSource>;
enum ChromiumNetError {
SUCCESS = 0,
FAILURE = 1,
RESULT_FROM_CHROMIUM,
}
export interface ElectronWindowConfiguration { export interface ElectronWindowConfiguration {
id: string; id: string;
title: string; title: string;
@ -112,6 +118,15 @@ const createElectronWindowInjectable = getInjectable({
applicationWindowState.manage(browserWindow); applicationWindowState.manage(browserWindow);
browserWindow.webContents.session.setCertificateVerifyProc((request, shouldBeTrusted) => {
const { certificate } = request;
const cert = new X509Certificate(certificate.data);
const shouldTrustCert = cert.raw.length === lensProxyX509Cert.raw.length
&& timingSafeEqual(cert.raw, lensProxyX509Cert.raw);
shouldBeTrusted(shouldTrustCert ? ChromiumNetError.SUCCESS : ChromiumNetError.RESULT_FROM_CHROMIUM);
});
browserWindow browserWindow
.on("focus", () => { .on("focus", () => {
configuration.onFocus?.(); configuration.onFocus?.();
@ -126,13 +141,6 @@ const createElectronWindowInjectable = getInjectable({
.webContents.on("dom-ready", () => { .webContents.on("dom-ready", () => {
configuration.onDomReady?.(); configuration.onDomReady?.();
}) })
.on("certificate-error", (event, url, error, certificate, shouldBeTrusted) => {
const cert = new X509Certificate(certificate.data);
const shouldTrustCert = cert.raw.length === lensProxyX509Cert.raw.length
&& timingSafeEqual(cert.raw, lensProxyX509Cert.raw);
shouldBeTrusted(shouldTrustCert);
})
.on("did-fail-load", (_event, code, desc) => { .on("did-fail-load", (_event, code, desc) => {
logger.error( logger.error(
`[CREATE-ELECTRON-WINDOW]: Failed to load window "${configuration.id}"`, `[CREATE-ELECTRON-WINDOW]: Failed to load window "${configuration.id}"`,

View File

@ -54,7 +54,9 @@ export async function bootstrap(di: DiContainer) {
} }
try { try {
await initializeApp(() => unmountComponentAtNode(rootElem)); await initializeApp(() => {
unmountComponentAtNode(rootElem);
});
} catch (error) { } catch (error) {
console.error(`[BOOTSTRAP]: view initialization error: ${error}`, { console.error(`[BOOTSTRAP]: view initialization error: ${error}`, {
origin: location.href, origin: location.href,

View File

@ -37,6 +37,7 @@ import installOnDropInjectable from "./install-on-drop.injectable";
import { supportedExtensionFormats } from "./supported-extension-formats"; import { supportedExtensionFormats } from "./supported-extension-formats";
import extensionInstallationStateStoreInjectable from "../../../extensions/extension-installation-state-store/extension-installation-state-store.injectable"; import extensionInstallationStateStoreInjectable from "../../../extensions/extension-installation-state-store/extension-installation-state-store.injectable";
import type { ExtensionInstallationStateStore } from "../../../extensions/extension-installation-state-store/extension-installation-state-store"; import type { ExtensionInstallationStateStore } from "../../../extensions/extension-installation-state-store/extension-installation-state-store";
import Gutter from "../gutter/gutter";
interface Dependencies { interface Dependencies {
userExtensions: IComputedValue<InstalledExtension[]>; userExtensions: IComputedValue<InstalledExtension[]>;
@ -113,6 +114,8 @@ class NonInjectedExtensions extends React.Component<Dependencies> {
installPath={this.installPath} installPath={this.installPath}
/> />
<Gutter size="md" />
<InstalledExtensions <InstalledExtensions
extensions={userExtensions} extensions={userExtensions}
enable={this.props.enableExtension} enable={this.props.enableExtension}

View File

@ -0,0 +1,937 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<NamespaceTreeView /> collapses item by clicking minus button 1`] = `
<body>
<div>
<div
class="TreeView"
data-testid="namespace-tree-view"
>
<div
class="DrawerTitle title"
>
Tree View
</div>
<ul
aria-multiselectable="false"
class="MuiTreeView-root"
role="tree"
>
<li
aria-expanded="true"
class="MuiTreeItem-root Mui-expanded"
data-testid="namespace-levels-deep"
role="treeitem"
tabindex="0"
>
<div
class="MuiTreeItem-content"
>
<div
class="MuiTreeItem-iconContainer"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
data-testid="minus-square"
focusable="false"
style="width: 14px; height: 14px;"
viewBox="0 0 24 24"
>
<path
d="M22.047 22.074v0 0-20.147 0h-20.12v0 20.147 0h20.12zM22.047 24h-20.12q-.803 0-1.365-.562t-.562-1.365v-20.147q0-.776.562-1.351t1.365-.575h20.147q.776 0 1.351.575t.575 1.351v20.147q0 .803-.575 1.365t-1.378.562v0zM17.873 11.023h-11.826q-.375 0-.669.281t-.294.682v0q0 .401.294 .682t.669.281h11.826q.375 0 .669-.281t.294-.682v0q0-.401-.294-.682t-.669-.281z"
/>
</svg>
</div>
<div
class="MuiTypography-root MuiTreeItem-label label MuiTypography-body1"
>
levels-deep
</div>
</div>
<ul
class="MuiCollapse-root MuiTreeItem-group group MuiCollapse-entered"
role="group"
style="min-height: 0px;"
>
<div
class="MuiCollapse-wrapper"
>
<div
class="MuiCollapse-wrapperInner"
>
<li
class="MuiTreeItem-root Mui-expanded"
data-testid="namespace-level-deep-child-a"
role="treeitem"
tabindex="-1"
>
<div
class="MuiTreeItem-content"
>
<div
class="MuiTreeItem-iconContainer"
>
<div
style="opacity: 0.3;"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
data-testid="minus-square"
focusable="false"
style="width: 14px; height: 14px;"
viewBox="0 0 24 24"
>
<path
d="M22.047 22.074v0 0-20.147 0h-20.12v0 20.147 0h20.12zM22.047 24h-20.12q-.803 0-1.365-.562t-.562-1.365v-20.147q0-.776.562-1.351t1.365-.575h20.147q.776 0 1.351.575t.575 1.351v20.147q0 .803-.575 1.365t-1.378.562v0zM17.873 11.023h-11.826q-.375 0-.669.281t-.294.682v0q0 .401.294 .682t.669.281h11.826q.375 0 .669-.281t.294-.682v0q0-.401-.294-.682t-.669-.281z"
/>
</svg>
</div>
</div>
<div
class="MuiTypography-root MuiTreeItem-label label MuiTypography-body1"
>
level-deep-child-a
</div>
</div>
</li>
<li
aria-expanded="false"
class="MuiTreeItem-root"
data-testid="namespace-level-deep-child-b"
role="treeitem"
tabindex="-1"
>
<div
class="MuiTreeItem-content"
>
<div
class="MuiTreeItem-iconContainer"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
data-testid="plus-square"
focusable="false"
style="width: 14px; height: 14px;"
viewBox="0 0 24 24"
>
<path
d="M22.047 22.074v0 0-20.147 0h-20.12v0 20.147 0h20.12zM22.047 24h-20.12q-.803 0-1.365-.562t-.562-1.365v-20.147q0-.776.562-1.351t1.365-.575h20.147q.776 0 1.351.575t.575 1.351v20.147q0 .803-.575 1.365t-1.378.562v0zM17.873 12.977h-4.923v4.896q0 .401-.281.682t-.682.281v0q-.375 0-.669-.281t-.294-.682v-4.896h-4.923q-.401 0-.682-.294t-.281-.669v0q0-.401.281-.682t.682-.281h4.923v-4.896q0-.401.294-.682t.669-.281v0q.401 0 .682.281t.281.682v4.896h4.923q.401 0 .682.281t.281.682v0q0 .375-.281.669t-.682.294z"
/>
</svg>
</div>
<div
class="MuiTypography-root MuiTreeItem-label label MuiTypography-body1"
>
level-deep-child-b
</div>
</div>
<ul
class="MuiCollapse-root MuiTreeItem-group group"
role="group"
style="min-height: 0px; height: 0px; transition-duration: 300ms;"
>
<div
class="MuiCollapse-wrapper"
>
<div
class="MuiCollapse-wrapperInner"
>
<li
class="MuiTreeItem-root Mui-expanded"
data-testid="namespace-level-deep-subchild-a"
role="treeitem"
tabindex="-1"
>
<div
class="MuiTreeItem-content"
>
<div
class="MuiTreeItem-iconContainer"
>
<div
style="opacity: 0.3;"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
data-testid="minus-square"
focusable="false"
style="width: 14px; height: 14px;"
viewBox="0 0 24 24"
>
<path
d="M22.047 22.074v0 0-20.147 0h-20.12v0 20.147 0h20.12zM22.047 24h-20.12q-.803 0-1.365-.562t-.562-1.365v-20.147q0-.776.562-1.351t1.365-.575h20.147q.776 0 1.351.575t.575 1.351v20.147q0 .803-.575 1.365t-1.378.562v0zM17.873 11.023h-11.826q-.375 0-.669.281t-.294.682v0q0 .401.294 .682t.669.281h11.826q.375 0 .669-.281t.294-.682v0q0-.401-.294-.682t-.669-.281z"
/>
</svg>
</div>
</div>
<div
class="MuiTypography-root MuiTreeItem-label label MuiTypography-body1"
>
level-deep-subchild-a
</div>
</div>
</li>
</div>
</div>
</ul>
</li>
</div>
</div>
</ul>
</li>
</ul>
</div>
</div>
</body>
`;
exports[`<NamespaceTreeView /> expands item by clicking plus button 1`] = `
<body>
<div>
<div
class="TreeView"
data-testid="namespace-tree-view"
>
<div
class="DrawerTitle title"
>
Tree View
</div>
<ul
aria-multiselectable="false"
class="MuiTreeView-root"
role="tree"
>
<li
aria-expanded="true"
class="MuiTreeItem-root Mui-expanded"
data-testid="namespace-levels-deep"
role="treeitem"
tabindex="0"
>
<div
class="MuiTreeItem-content"
>
<div
class="MuiTreeItem-iconContainer"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
data-testid="minus-square"
focusable="false"
style="width: 14px; height: 14px;"
viewBox="0 0 24 24"
>
<path
d="M22.047 22.074v0 0-20.147 0h-20.12v0 20.147 0h20.12zM22.047 24h-20.12q-.803 0-1.365-.562t-.562-1.365v-20.147q0-.776.562-1.351t1.365-.575h20.147q.776 0 1.351.575t.575 1.351v20.147q0 .803-.575 1.365t-1.378.562v0zM17.873 11.023h-11.826q-.375 0-.669.281t-.294.682v0q0 .401.294 .682t.669.281h11.826q.375 0 .669-.281t.294-.682v0q0-.401-.294-.682t-.669-.281z"
/>
</svg>
</div>
<div
class="MuiTypography-root MuiTreeItem-label label MuiTypography-body1"
>
levels-deep
</div>
</div>
<ul
class="MuiCollapse-root MuiTreeItem-group group MuiCollapse-entered"
role="group"
style="min-height: 0px;"
>
<div
class="MuiCollapse-wrapper"
>
<div
class="MuiCollapse-wrapperInner"
>
<li
class="MuiTreeItem-root Mui-expanded"
data-testid="namespace-level-deep-child-a"
role="treeitem"
tabindex="-1"
>
<div
class="MuiTreeItem-content"
>
<div
class="MuiTreeItem-iconContainer"
>
<div
style="opacity: 0.3;"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
data-testid="minus-square"
focusable="false"
style="width: 14px; height: 14px;"
viewBox="0 0 24 24"
>
<path
d="M22.047 22.074v0 0-20.147 0h-20.12v0 20.147 0h20.12zM22.047 24h-20.12q-.803 0-1.365-.562t-.562-1.365v-20.147q0-.776.562-1.351t1.365-.575h20.147q.776 0 1.351.575t.575 1.351v20.147q0 .803-.575 1.365t-1.378.562v0zM17.873 11.023h-11.826q-.375 0-.669.281t-.294.682v0q0 .401.294 .682t.669.281h11.826q.375 0 .669-.281t.294-.682v0q0-.401-.294-.682t-.669-.281z"
/>
</svg>
</div>
</div>
<div
class="MuiTypography-root MuiTreeItem-label label MuiTypography-body1"
>
level-deep-child-a
</div>
</div>
</li>
<li
aria-expanded="true"
class="MuiTreeItem-root Mui-expanded"
data-testid="namespace-level-deep-child-b"
role="treeitem"
tabindex="-1"
>
<div
class="MuiTreeItem-content"
>
<div
class="MuiTreeItem-iconContainer"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
data-testid="minus-square"
focusable="false"
style="width: 14px; height: 14px;"
viewBox="0 0 24 24"
>
<path
d="M22.047 22.074v0 0-20.147 0h-20.12v0 20.147 0h20.12zM22.047 24h-20.12q-.803 0-1.365-.562t-.562-1.365v-20.147q0-.776.562-1.351t1.365-.575h20.147q.776 0 1.351.575t.575 1.351v20.147q0 .803-.575 1.365t-1.378.562v0zM17.873 11.023h-11.826q-.375 0-.669.281t-.294.682v0q0 .401.294 .682t.669.281h11.826q.375 0 .669-.281t.294-.682v0q0-.401-.294-.682t-.669-.281z"
/>
</svg>
</div>
<div
class="MuiTypography-root MuiTreeItem-label label MuiTypography-body1"
>
level-deep-child-b
</div>
</div>
<ul
class="MuiCollapse-root MuiTreeItem-group group"
role="group"
style="min-height: 0px; height: 0px; transition-duration: 300ms;"
>
<div
class="MuiCollapse-wrapper"
>
<div
class="MuiCollapse-wrapperInner"
>
<li
class="MuiTreeItem-root Mui-expanded"
data-testid="namespace-level-deep-subchild-a"
role="treeitem"
tabindex="-1"
>
<div
class="MuiTreeItem-content"
>
<div
class="MuiTreeItem-iconContainer"
>
<div
style="opacity: 0.3;"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
data-testid="minus-square"
focusable="false"
style="width: 14px; height: 14px;"
viewBox="0 0 24 24"
>
<path
d="M22.047 22.074v0 0-20.147 0h-20.12v0 20.147 0h20.12zM22.047 24h-20.12q-.803 0-1.365-.562t-.562-1.365v-20.147q0-.776.562-1.351t1.365-.575h20.147q.776 0 1.351.575t.575 1.351v20.147q0 .803-.575 1.365t-1.378.562v0zM17.873 11.023h-11.826q-.375 0-.669.281t-.294.682v0q0 .401.294 .682t.669.281h11.826q.375 0 .669-.281t.294-.682v0q0-.401-.294-.682t-.669-.281z"
/>
</svg>
</div>
</div>
<div
class="MuiTypography-root MuiTreeItem-label label MuiTypography-body1"
>
level-deep-subchild-a
</div>
</div>
</li>
</div>
</div>
</ul>
</li>
</div>
</div>
</ul>
</li>
</ul>
</div>
</div>
</body>
`;
exports[`<NamespaceTreeView /> renders 2 levels deep 1`] = `
<body>
<div>
<div
class="TreeView"
data-testid="namespace-tree-view"
>
<div
class="DrawerTitle title"
>
Tree View
</div>
<ul
aria-multiselectable="false"
class="MuiTreeView-root"
role="tree"
>
<li
aria-expanded="true"
class="MuiTreeItem-root Mui-expanded"
data-testid="namespace-levels-deep"
role="treeitem"
tabindex="0"
>
<div
class="MuiTreeItem-content"
>
<div
class="MuiTreeItem-iconContainer"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
data-testid="minus-square"
focusable="false"
style="width: 14px; height: 14px;"
viewBox="0 0 24 24"
>
<path
d="M22.047 22.074v0 0-20.147 0h-20.12v0 20.147 0h20.12zM22.047 24h-20.12q-.803 0-1.365-.562t-.562-1.365v-20.147q0-.776.562-1.351t1.365-.575h20.147q.776 0 1.351.575t.575 1.351v20.147q0 .803-.575 1.365t-1.378.562v0zM17.873 11.023h-11.826q-.375 0-.669.281t-.294.682v0q0 .401.294 .682t.669.281h11.826q.375 0 .669-.281t.294-.682v0q0-.401-.294-.682t-.669-.281z"
/>
</svg>
</div>
<div
class="MuiTypography-root MuiTreeItem-label label MuiTypography-body1"
>
levels-deep
</div>
</div>
<ul
class="MuiCollapse-root MuiTreeItem-group group MuiCollapse-entered"
role="group"
style="min-height: 0px;"
>
<div
class="MuiCollapse-wrapper"
>
<div
class="MuiCollapse-wrapperInner"
>
<li
class="MuiTreeItem-root Mui-expanded"
data-testid="namespace-level-deep-child-a"
role="treeitem"
tabindex="-1"
>
<div
class="MuiTreeItem-content"
>
<div
class="MuiTreeItem-iconContainer"
>
<div
style="opacity: 0.3;"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
data-testid="minus-square"
focusable="false"
style="width: 14px; height: 14px;"
viewBox="0 0 24 24"
>
<path
d="M22.047 22.074v0 0-20.147 0h-20.12v0 20.147 0h20.12zM22.047 24h-20.12q-.803 0-1.365-.562t-.562-1.365v-20.147q0-.776.562-1.351t1.365-.575h20.147q.776 0 1.351.575t.575 1.351v20.147q0 .803-.575 1.365t-1.378.562v0zM17.873 11.023h-11.826q-.375 0-.669.281t-.294.682v0q0 .401.294 .682t.669.281h11.826q.375 0 .669-.281t.294-.682v0q0-.401-.294-.682t-.669-.281z"
/>
</svg>
</div>
</div>
<div
class="MuiTypography-root MuiTreeItem-label label MuiTypography-body1"
>
level-deep-child-a
</div>
</div>
</li>
<li
aria-expanded="true"
class="MuiTreeItem-root Mui-expanded"
data-testid="namespace-level-deep-child-b"
role="treeitem"
tabindex="-1"
>
<div
class="MuiTreeItem-content"
>
<div
class="MuiTreeItem-iconContainer"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
data-testid="minus-square"
focusable="false"
style="width: 14px; height: 14px;"
viewBox="0 0 24 24"
>
<path
d="M22.047 22.074v0 0-20.147 0h-20.12v0 20.147 0h20.12zM22.047 24h-20.12q-.803 0-1.365-.562t-.562-1.365v-20.147q0-.776.562-1.351t1.365-.575h20.147q.776 0 1.351.575t.575 1.351v20.147q0 .803-.575 1.365t-1.378.562v0zM17.873 11.023h-11.826q-.375 0-.669.281t-.294.682v0q0 .401.294 .682t.669.281h11.826q.375 0 .669-.281t.294-.682v0q0-.401-.294-.682t-.669-.281z"
/>
</svg>
</div>
<div
class="MuiTypography-root MuiTreeItem-label label MuiTypography-body1"
>
level-deep-child-b
</div>
</div>
<ul
class="MuiCollapse-root MuiTreeItem-group group MuiCollapse-entered"
role="group"
style="min-height: 0px;"
>
<div
class="MuiCollapse-wrapper"
>
<div
class="MuiCollapse-wrapperInner"
>
<li
class="MuiTreeItem-root Mui-expanded"
data-testid="namespace-level-deep-subchild-a"
role="treeitem"
tabindex="-1"
>
<div
class="MuiTreeItem-content"
>
<div
class="MuiTreeItem-iconContainer"
>
<div
style="opacity: 0.3;"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
data-testid="minus-square"
focusable="false"
style="width: 14px; height: 14px;"
viewBox="0 0 24 24"
>
<path
d="M22.047 22.074v0 0-20.147 0h-20.12v0 20.147 0h20.12zM22.047 24h-20.12q-.803 0-1.365-.562t-.562-1.365v-20.147q0-.776.562-1.351t1.365-.575h20.147q.776 0 1.351.575t.575 1.351v20.147q0 .803-.575 1.365t-1.378.562v0zM17.873 11.023h-11.826q-.375 0-.669.281t-.294.682v0q0 .401.294 .682t.669.281h11.826q.375 0 .669-.281t.294-.682v0q0-.401-.294-.682t-.669-.281z"
/>
</svg>
</div>
</div>
<div
class="MuiTypography-root MuiTreeItem-label label MuiTypography-body1"
>
level-deep-subchild-a
</div>
</div>
</li>
</div>
</div>
</ul>
</li>
</div>
</div>
</ul>
</li>
</ul>
</div>
</div>
</body>
`;
exports[`<NamespaceTreeView /> renders namespace with 2 children namespaces 1`] = `
<body>
<div>
<div
class="TreeView"
data-testid="namespace-tree-view"
>
<div
class="DrawerTitle title"
>
Tree View
</div>
<ul
aria-multiselectable="false"
class="MuiTreeView-root"
role="tree"
>
<li
aria-expanded="true"
class="MuiTreeItem-root Mui-expanded"
data-testid="namespace-acme-org"
role="treeitem"
tabindex="0"
>
<div
class="MuiTreeItem-content"
>
<div
class="MuiTreeItem-iconContainer"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
data-testid="minus-square"
focusable="false"
style="width: 14px; height: 14px;"
viewBox="0 0 24 24"
>
<path
d="M22.047 22.074v0 0-20.147 0h-20.12v0 20.147 0h20.12zM22.047 24h-20.12q-.803 0-1.365-.562t-.562-1.365v-20.147q0-.776.562-1.351t1.365-.575h20.147q.776 0 1.351.575t.575 1.351v20.147q0 .803-.575 1.365t-1.378.562v0zM17.873 11.023h-11.826q-.375 0-.669.281t-.294.682v0q0 .401.294 .682t.669.281h11.826q.375 0 .669-.281t.294-.682v0q0-.401-.294-.682t-.669-.281z"
/>
</svg>
</div>
<div
class="MuiTypography-root MuiTreeItem-label label MuiTypography-body1"
>
acme-org
</div>
</div>
<ul
class="MuiCollapse-root MuiTreeItem-group group MuiCollapse-entered"
role="group"
style="min-height: 0px;"
>
<div
class="MuiCollapse-wrapper"
>
<div
class="MuiCollapse-wrapperInner"
>
<li
class="MuiTreeItem-root Mui-expanded"
data-testid="namespace-team-a"
role="treeitem"
tabindex="-1"
>
<div
class="MuiTreeItem-content"
>
<div
class="MuiTreeItem-iconContainer"
>
<div
style="opacity: 0.3;"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
data-testid="minus-square"
focusable="false"
style="width: 14px; height: 14px;"
viewBox="0 0 24 24"
>
<path
d="M22.047 22.074v0 0-20.147 0h-20.12v0 20.147 0h20.12zM22.047 24h-20.12q-.803 0-1.365-.562t-.562-1.365v-20.147q0-.776.562-1.351t1.365-.575h20.147q.776 0 1.351.575t.575 1.351v20.147q0 .803-.575 1.365t-1.378.562v0zM17.873 11.023h-11.826q-.375 0-.669.281t-.294.682v0q0 .401.294 .682t.669.281h11.826q.375 0 .669-.281t.294-.682v0q0-.401-.294-.682t-.669-.281z"
/>
</svg>
</div>
</div>
<div
class="MuiTypography-root MuiTreeItem-label label MuiTypography-body1"
>
team-a
</div>
</div>
</li>
<li
class="MuiTreeItem-root Mui-expanded"
data-testid="namespace-team-b"
role="treeitem"
tabindex="-1"
>
<div
class="MuiTreeItem-content"
>
<div
class="MuiTreeItem-iconContainer"
>
<div
style="opacity: 0.3;"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
data-testid="minus-square"
focusable="false"
style="width: 14px; height: 14px;"
viewBox="0 0 24 24"
>
<path
d="M22.047 22.074v0 0-20.147 0h-20.12v0 20.147 0h20.12zM22.047 24h-20.12q-.803 0-1.365-.562t-.562-1.365v-20.147q0-.776.562-1.351t1.365-.575h20.147q.776 0 1.351.575t.575 1.351v20.147q0 .803-.575 1.365t-1.378.562v0zM17.873 11.023h-11.826q-.375 0-.669.281t-.294.682v0q0 .401.294 .682t.669.281h11.826q.375 0 .669-.281t.294-.682v0q0-.401-.294-.682t-.669-.281z"
/>
</svg>
</div>
</div>
<div
class="MuiTypography-root MuiTreeItem-label label MuiTypography-body1"
>
team-b
</div>
</div>
</li>
</div>
</div>
</ul>
</li>
</ul>
</div>
</div>
</body>
`;
exports[`<NamespaceTreeView /> renders namespace with children namespaces and a subnamespace 1`] = `
<body>
<div>
<div
class="TreeView"
data-testid="namespace-tree-view"
>
<div
class="DrawerTitle title"
>
Tree View
</div>
<ul
aria-multiselectable="false"
class="MuiTreeView-root"
role="tree"
>
<li
aria-expanded="true"
class="MuiTreeItem-root Mui-expanded"
data-testid="namespace-org-a"
role="treeitem"
tabindex="0"
>
<div
class="MuiTreeItem-content"
>
<div
class="MuiTreeItem-iconContainer"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
data-testid="minus-square"
focusable="false"
style="width: 14px; height: 14px;"
viewBox="0 0 24 24"
>
<path
d="M22.047 22.074v0 0-20.147 0h-20.12v0 20.147 0h20.12zM22.047 24h-20.12q-.803 0-1.365-.562t-.562-1.365v-20.147q0-.776.562-1.351t1.365-.575h20.147q.776 0 1.351.575t.575 1.351v20.147q0 .803-.575 1.365t-1.378.562v0zM17.873 11.023h-11.826q-.375 0-.669.281t-.294.682v0q0 .401.294 .682t.669.281h11.826q.375 0 .669-.281t.294-.682v0q0-.401-.294-.682t-.669-.281z"
/>
</svg>
</div>
<div
class="MuiTypography-root MuiTreeItem-label label MuiTypography-body1"
>
org-a
</div>
</div>
<ul
class="MuiCollapse-root MuiTreeItem-group group MuiCollapse-entered"
role="group"
style="min-height: 0px;"
>
<div
class="MuiCollapse-wrapper"
>
<div
class="MuiCollapse-wrapperInner"
>
<li
class="MuiTreeItem-root Mui-expanded"
data-testid="namespace-team-c"
role="treeitem"
tabindex="-1"
>
<div
class="MuiTreeItem-content"
>
<div
class="MuiTreeItem-iconContainer"
>
<div
style="opacity: 0.3;"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
data-testid="minus-square"
focusable="false"
style="width: 14px; height: 14px;"
viewBox="0 0 24 24"
>
<path
d="M22.047 22.074v0 0-20.147 0h-20.12v0 20.147 0h20.12zM22.047 24h-20.12q-.803 0-1.365-.562t-.562-1.365v-20.147q0-.776.562-1.351t1.365-.575h20.147q.776 0 1.351.575t.575 1.351v20.147q0 .803-.575 1.365t-1.378.562v0zM17.873 11.023h-11.826q-.375 0-.669.281t-.294.682v0q0 .401.294 .682t.669.281h11.826q.375 0 .669-.281t.294-.682v0q0-.401-.294-.682t-.669-.281z"
/>
</svg>
</div>
</div>
<div
class="MuiTypography-root MuiTreeItem-label label MuiTypography-body1"
>
team-c
</div>
</div>
</li>
<li
class="MuiTreeItem-root Mui-expanded"
data-testid="namespace-service-1"
role="treeitem"
tabindex="-1"
>
<div
class="MuiTreeItem-content"
>
<div
class="MuiTreeItem-iconContainer"
>
<div
style="opacity: 0.3;"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
data-testid="minus-square"
focusable="false"
style="width: 14px; height: 14px;"
viewBox="0 0 24 24"
>
<path
d="M22.047 22.074v0 0-20.147 0h-20.12v0 20.147 0h20.12zM22.047 24h-20.12q-.803 0-1.365-.562t-.562-1.365v-20.147q0-.776.562-1.351t1.365-.575h20.147q.776 0 1.351.575t.575 1.351v20.147q0 .803-.575 1.365t-1.378.562v0zM17.873 11.023h-11.826q-.375 0-.669.281t-.294.682v0q0 .401.294 .682t.669.281h11.826q.375 0 .669-.281t.294-.682v0q0-.401-.294-.682t-.669-.281z"
/>
</svg>
</div>
</div>
<div
class="MuiTypography-root MuiTreeItem-label label MuiTypography-body1"
>
service-1
<span
class="subnamespaceBadge"
data-testid="namespace-details-badge-for-service-1"
id="namespace-details-badge-for-service-1"
>
S
</span>
</div>
</div>
</li>
</div>
</div>
</ul>
</li>
</ul>
</div>
</div>
</body>
`;
exports[`<NamespaceTreeView /> renders one namespace without children 1`] = `
<body>
<div>
<div
class="TreeView"
data-testid="namespace-tree-view"
>
<div
class="DrawerTitle title"
>
Tree View
</div>
<ul
aria-multiselectable="false"
class="MuiTreeView-root"
role="tree"
>
<li
class="MuiTreeItem-root"
data-testid="namespace-single-root"
role="treeitem"
tabindex="0"
>
<div
class="MuiTreeItem-content"
>
<div
class="MuiTreeItem-iconContainer"
>
<div
style="opacity: 0.3;"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
data-testid="minus-square"
focusable="false"
style="width: 14px; height: 14px;"
viewBox="0 0 24 24"
>
<path
d="M22.047 22.074v0 0-20.147 0h-20.12v0 20.147 0h20.12zM22.047 24h-20.12q-.803 0-1.365-.562t-.562-1.365v-20.147q0-.776.562-1.351t1.365-.575h20.147q.776 0 1.351.575t.575 1.351v20.147q0 .803-.575 1.365t-1.378.562v0zM17.873 11.023h-11.826q-.375 0-.669.281t-.294.682v0q0 .401.294 .682t.669.281h11.826q.375 0 .669-.281t.294-.682v0q0-.401-.294-.682t-.669-.281z"
/>
</svg>
</div>
</div>
<div
class="MuiTypography-root MuiTreeItem-label label MuiTypography-body1"
>
single-root
</div>
</div>
</li>
</ul>
</div>
</div>
</body>
`;

View File

@ -0,0 +1,18 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import namespaceStoreInjectable from "./store.injectable";
const hierarchicalNamespacesInjectable = getInjectable({
id: "hierarchical-namespaces",
instantiate: (di) => {
const namespaceStore = di.inject(namespaceStoreInjectable);
return namespaceStore.items.filter(item => item.isControlledByHNC());
},
});
export default hierarchicalNamespacesInjectable;

View File

@ -26,6 +26,9 @@ import limitRangeStoreInjectable from "../+config-limit-ranges/store.injectable"
import resourceQuotaStoreInjectable from "../+config-resource-quotas/store.injectable"; import resourceQuotaStoreInjectable from "../+config-resource-quotas/store.injectable";
import type { Logger } from "../../../common/logger"; import type { Logger } from "../../../common/logger";
import loggerInjectable from "../../../common/logger.injectable"; import loggerInjectable from "../../../common/logger.injectable";
import { NamespaceTreeView } from "./namespace-tree-view";
import namespaceStoreInjectable from "./store.injectable";
import type { NamespaceStore } from "./store";
export interface NamespaceDetailsProps extends KubeObjectDetailsProps<Namespace> { export interface NamespaceDetailsProps extends KubeObjectDetailsProps<Namespace> {
} }
@ -35,6 +38,7 @@ interface Dependencies {
getDetailsUrl: GetDetailsUrl; getDetailsUrl: GetDetailsUrl;
resourceQuotaStore: ResourceQuotaStore; resourceQuotaStore: ResourceQuotaStore;
limitRangeStore: LimitRangeStore; limitRangeStore: LimitRangeStore;
namespaceStore: NamespaceStore;
logger: Logger; logger: Logger;
} }
@ -103,6 +107,10 @@ class NonInjectedNamespaceDetails extends React.Component<NamespaceDetailsProps
</Link> </Link>
))} ))}
</DrawerItem> </DrawerItem>
{namespace.isControlledByHNC() && (
<NamespaceTreeView tree={this.props.namespaceStore.getNamespaceTree(namespace)}/>
)}
</div> </div>
); );
} }
@ -115,6 +123,7 @@ export const NamespaceDetails = withInjectables<Dependencies, NamespaceDetailsPr
getDetailsUrl: di.inject(getDetailsUrlInjectable), getDetailsUrl: di.inject(getDetailsUrlInjectable),
limitRangeStore: di.inject(limitRangeStoreInjectable), limitRangeStore: di.inject(limitRangeStoreInjectable),
resourceQuotaStore: di.inject(resourceQuotaStoreInjectable), resourceQuotaStore: di.inject(resourceQuotaStoreInjectable),
namespaceStore: di.inject(namespaceStoreInjectable),
logger: di.inject(loggerInjectable), logger: di.inject(loggerInjectable),
}), }),
}); });

View File

@ -0,0 +1,205 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { DiContainer } from "@ogre-tools/injectable";
import { observable } from "mobx";
import directoryForKubeConfigsInjectable from "../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
import directoryForUserDataInjectable from "../../../common/app-paths/directory-for-user-data/directory-for-user-data.injectable";
import { Namespace } from "../../../common/k8s-api/endpoints";
import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable";
import createClusterInjectable from "../../cluster/create-cluster.injectable";
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
import storesAndApisCanBeCreatedInjectable from "../../stores-apis-can-be-created.injectable";
import type { NamespaceStore } from "./store";
import namespaceStoreInjectable from "./store.injectable";
function createNamespace(name: string, labels?: Record<string, string>, annotations?: Record<string, string>): Namespace {
return new Namespace({
apiVersion: "v1",
kind: "Namespace",
metadata: {
name,
resourceVersion: "1",
selfLink: `/api/v1/namespaces/${name}`,
uid: `${name}`,
labels: {
...labels,
},
annotations: {
...annotations,
},
},
});
}
const singleRoot = createNamespace("single-root", {
"hnc.x-k8s.io/included-namespace": "true",
});
const acmeGroup = createNamespace("acme-org", {
"hnc.x-k8s.io/included-namespace": "true",
});
const orgA = createNamespace("org-a", {
"hnc.x-k8s.io/included-namespace": "true",
});
const teamA = createNamespace("team-a", {
"hnc.x-k8s.io/included-namespace": "true",
"acme-org.tree.hnc.x-k8s.io/depth": "1",
"kubernetes.io/metadata.name": "team-a",
"team-a.tree.hnc.x-k8s.io/depth": "0",
});
const teamB = createNamespace("team-b", {
"hnc.x-k8s.io/included-namespace": "true",
"acme-org.tree.hnc.x-k8s.io/depth": "1",
"kubernetes.io/metadata.name": "team-b",
"team-b.tree.hnc.x-k8s.io/depth": "0",
});
const teamC = createNamespace("team-c", {
"hnc.x-k8s.io/included-namespace": "true",
"org-a.tree.hnc.x-k8s.io/depth": "1",
"kubernetes.io/metadata.name": "team-c",
"team-c.tree.hnc.x-k8s.io/depth": "0",
});
const service1 = createNamespace("service-1", {
"hnc.x-k8s.io/included-namespace": "true",
"org-a.tree.hnc.x-k8s.io/depth": "1",
"kubernetes.io/metadata.name": "team-c",
"service-1.tree.hnc.x-k8s.io/depth": "0",
}, {
"hnc.x-k8s.io/subnamespace-of": "org-a",
});
const levelsDeep = createNamespace("levels-deep", {
"hnc.x-k8s.io/included-namespace": "true",
});
const levelDeepChildA = createNamespace("level-deep-child-a", {
"hnc.x-k8s.io/included-namespace": "true",
"levels-deep.tree.hnc.x-k8s.io/depth": "1",
"level-deep-child-a.tree.hnc.x-k8s.io/depth": "0",
});
const levelDeepChildB = createNamespace("level-deep-child-b", {
"hnc.x-k8s.io/included-namespace": "true",
"levels-deep.tree.hnc.x-k8s.io/depth": "1",
"level-deep-child-b.tree.hnc.x-k8s.io/depth": "0",
});
const levelDeepSubChildA = createNamespace("level-deep-subchild-a", {
"hnc.x-k8s.io/included-namespace": "true",
"levels-deep.tree.hnc.x-k8s.io/depth": "2",
"level-deep-child-b.tree.hnc.x-k8s.io/depth": "1",
"level-deep-subchild-a.tree.hnc.x-k8s.io/depth": "0",
});
describe("NamespaceStore", () => {
let di: DiContainer;
let namespaceStore: NamespaceStore;
beforeEach(async () => {
di = getDiForUnitTesting({ doGeneralOverrides: true });
di.override(directoryForUserDataInjectable, () => "/some-user-store-path");
di.override(directoryForKubeConfigsInjectable, () => "/some-kube-configs");
di.override(storesAndApisCanBeCreatedInjectable, () => true);
const createCluster = di.inject(createClusterInjectable);
di.override(hostedClusterInjectable, () => createCluster({
contextName: "some-context-name",
id: "some-cluster-id",
kubeConfigPath: "/some-path-to-a-kubeconfig",
}, {
clusterServerUrl: "https://localhost:8080",
}));
namespaceStore = di.inject(namespaceStoreInjectable);
namespaceStore.items = observable.array([
acmeGroup,
orgA,
teamA,
teamB,
teamC,
service1,
levelsDeep,
levelDeepChildA,
levelDeepChildB,
levelDeepSubChildA,
]);
});
it("returns tree for single node", () => {
const tree = namespaceStore.getNamespaceTree(service1);
expect(tree).toEqual({
id: "service-1",
namespace: service1,
children: [],
});
});
it("returns tree for namespace not listed in store", () => {
const tree = namespaceStore.getNamespaceTree(singleRoot);
expect(tree).toEqual({
id: "single-root",
namespace: singleRoot,
children: [],
});
});
it("return tree for namespace with children", () => {
const tree = namespaceStore.getNamespaceTree(acmeGroup);
expect(tree).toEqual({
id: "acme-org",
namespace: acmeGroup,
children: [
{
id: "team-a",
namespace: teamA,
children: [],
},
{
id: "team-b",
namespace: teamB,
children: [],
},
],
});
});
it("return tree for namespace with deep nested children", () => {
const tree = namespaceStore.getNamespaceTree(levelsDeep);
expect(tree).toEqual({
id: "levels-deep",
namespace: levelsDeep,
children: [
{
id: "level-deep-child-a",
namespace: levelDeepChildA,
children: [],
},
{
id: "level-deep-child-b",
namespace: levelDeepChildB,
children: [{
id: "level-deep-subchild-a",
namespace: levelDeepSubChildA,
children: [],
}],
},
],
});
});
});

View File

@ -0,0 +1,14 @@
.TreeView {
.group {
margin-inline-start: var(--margin);
padding-inline-start: calc(var(--padding) * 2);
border-inline-start: 1px dashed var(--borderColor);
}
.label {
font-size: inherit;
line-height: 1.8;
cursor: default;
background-color: transparent!important;
}
}

View File

@ -0,0 +1,334 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type { DiContainer } from "@ogre-tools/injectable";
import { fireEvent } from "@testing-library/react";
import React from "react";
import { Namespace } from "../../../common/k8s-api/endpoints";
import { getDiForUnitTesting } from "../../getDiForUnitTesting";
import type { DiRender } from "../test-utils/renderFor";
import { renderFor } from "../test-utils/renderFor";
import hierarchicalNamespacesInjectable from "./hierarchical-namespaces.injectable";
import { NamespaceTreeView } from "./namespace-tree-view";
import type { NamespaceTree } from "./store";
jest.mock("react-router-dom", () => ({
Link: ({ children }: { children: React.ReactNode }) => children,
}));
function createNamespace(name: string, labels?: Record<string, string>, annotations?: Record<string, string>): Namespace {
return new Namespace({
apiVersion: "v1",
kind: "Namespace",
metadata: {
name,
resourceVersion: "1",
selfLink: `/api/v1/namespaces/${name}`,
uid: `${name}`,
labels: {
...labels,
},
annotations: {
...annotations,
},
},
});
}
const singleRoot = createNamespace("single-root", {
"hnc.x-k8s.io/included-namespace": "true",
});
const acmeGroup = createNamespace("acme-org", {
"hnc.x-k8s.io/included-namespace": "true",
});
const orgA = createNamespace("org-a", {
"hnc.x-k8s.io/included-namespace": "true",
});
const teamA = createNamespace("team-a", {
"hnc.x-k8s.io/included-namespace": "true",
"acme-org.tree.hnc.x-k8s.io/depth": "1",
"kubernetes.io/metadata.name": "team-a",
"team-a.tree.hnc.x-k8s.io/depth": "0",
});
const teamB = createNamespace("team-b", {
"hnc.x-k8s.io/included-namespace": "true",
"acme-org.tree.hnc.x-k8s.io/depth": "1",
"kubernetes.io/metadata.name": "team-b",
"team-b.tree.hnc.x-k8s.io/depth": "0",
});
const teamC = createNamespace("team-c", {
"hnc.x-k8s.io/included-namespace": "true",
"org-a.tree.hnc.x-k8s.io/depth": "1",
"kubernetes.io/metadata.name": "team-c",
"team-c.tree.hnc.x-k8s.io/depth": "0",
});
const service1 = createNamespace("service-1", {
"hnc.x-k8s.io/included-namespace": "true",
"org-a.tree.hnc.x-k8s.io/depth": "1",
"kubernetes.io/metadata.name": "team-c",
"service-1.tree.hnc.x-k8s.io/depth": "0",
}, {
"hnc.x-k8s.io/subnamespace-of": "org-a",
});
const levelsDeep = createNamespace("levels-deep", {
"hnc.x-k8s.io/included-namespace": "true",
});
const levelDeepChildA = createNamespace("level-deep-child-a", {
"hnc.x-k8s.io/included-namespace": "true",
"levels-deep.tree.hnc.x-k8s.io/depth": "1",
"level-deep-child-a.tree.hnc.x-k8s.io/depth": "0",
});
const levelDeepChildB = createNamespace("level-deep-child-b", {
"hnc.x-k8s.io/included-namespace": "true",
"levels-deep.tree.hnc.x-k8s.io/depth": "1",
"level-deep-child-b.tree.hnc.x-k8s.io/depth": "0",
});
const levelDeepSubChildA = createNamespace("level-deep-subchild-a", {
"hnc.x-k8s.io/included-namespace": "true",
"levels-deep.tree.hnc.x-k8s.io/depth": "2",
"level-deep-child-b.tree.hnc.x-k8s.io/depth": "1",
"level-deep-subchild-a.tree.hnc.x-k8s.io/depth": "0",
});
describe("<NamespaceTreeView />", () => {
let di: DiContainer;
let render: DiRender;
beforeEach(async () => {
di = getDiForUnitTesting({ doGeneralOverrides: true });
di.override(hierarchicalNamespacesInjectable, () => [
acmeGroup,
orgA,
teamA,
teamB,
teamC,
service1,
levelsDeep,
levelDeepChildA,
levelDeepChildB,
levelDeepSubChildA,
]);
render = renderFor(di);
});
it("renders one namespace without children", () => {
const tree: NamespaceTree = {
id: "single-root",
namespace: singleRoot,
};
const result = render(<NamespaceTreeView tree={tree} />);
expect(result.baseElement).toMatchSnapshot();
});
it("renders namespace with 2 children namespaces", () => {
const tree: NamespaceTree = {
id: "acme-org",
namespace: acmeGroup,
children: [
{
id: "team-a",
namespace: teamA,
},
{
id: "team-b",
namespace: teamB,
},
],
};
const result = render(<NamespaceTreeView tree={tree} />);
expect(result.baseElement).toMatchSnapshot();
});
it("renders namespace with children namespaces and a subnamespace", () => {
const tree: NamespaceTree = {
id: "org-a",
namespace: orgA,
children: [
{
id: "team-c",
namespace: teamC,
},
{
id: "service-1",
namespace: service1,
},
],
};
const result = render(<NamespaceTreeView tree={tree} />);
expect(result.baseElement).toMatchSnapshot();
});
it("renders an indicator badge for the subnamespace", () => {
const tree: NamespaceTree = {
id: "org-a",
namespace: orgA,
children: [
{
id: "team-c",
namespace: teamC,
},
{
id: "service-1",
namespace: service1,
},
],
};
const result = render(<NamespaceTreeView tree={tree} />);
expect(result.getByTestId("namespace-details-badge-for-service-1")).toBeInTheDocument();
});
it("does not render an indicator badge for the true namespace", () => {
const tree: NamespaceTree = {
id: "org-a",
namespace: orgA,
children: [
{
id: "team-c",
namespace: teamC,
},
{
id: "service-1",
namespace: service1,
},
],
};
const result = render(<NamespaceTreeView tree={tree} />);
const trueNamespace = result.getByTestId("namespace-team-c");
expect(trueNamespace.querySelector("[data-testid='namespace-details-badge-for-team-c']")).toBeNull();
});
it("renders 2 levels deep", () => {
const tree: NamespaceTree = {
id: "levels-deep",
namespace: levelsDeep,
children: [
{
id: "level-deep-child-a",
namespace: levelDeepChildA,
},
{
id: "level-deep-child-b",
namespace: levelDeepChildB,
children: [{
id: "level-deep-subchild-a",
namespace: levelDeepSubChildA,
}],
},
],
};
const result = render(<NamespaceTreeView tree={tree} />);
expect(result.baseElement).toMatchSnapshot();
});
it("expands children items by default", () => {
const tree: NamespaceTree = {
id: "levels-deep",
namespace: levelsDeep,
children: [
{
id: "level-deep-child-a",
namespace: levelDeepChildA,
},
{
id: "level-deep-child-b",
namespace: levelDeepChildB,
children: [{
id: "level-deep-subchild-a",
namespace: levelDeepSubChildA,
}],
},
],
};
const result = render(<NamespaceTreeView tree={tree} />);
const deepest = result.getByTestId("namespace-level-deep-child-b");
expect(deepest).toHaveAttribute("aria-expanded", "true");
});
it("collapses item by clicking minus button", () => {
const tree: NamespaceTree = {
id: "levels-deep",
namespace: levelsDeep,
children: [
{
id: "level-deep-child-a",
namespace: levelDeepChildA,
},
{
id: "level-deep-child-b",
namespace: levelDeepChildB,
children: [{
id: "level-deep-subchild-a",
namespace: levelDeepSubChildA,
}],
},
],
};
const result = render(<NamespaceTreeView tree={tree} />);
const levelB = result.getByTestId("namespace-level-deep-child-b");
const minusButton = levelB.querySelector("[data-testid='minus-square']");
if (minusButton) {
fireEvent.click(minusButton);
}
expect(result.baseElement).toMatchSnapshot();
});
it("expands item by clicking plus button", () => {
const tree: NamespaceTree = {
id: "levels-deep",
namespace: levelsDeep,
children: [
{
id: "level-deep-child-a",
namespace: levelDeepChildA,
},
{
id: "level-deep-child-b",
namespace: levelDeepChildB,
children: [{
id: "level-deep-subchild-a",
namespace: levelDeepSubChildA,
}],
},
],
};
const result = render(<NamespaceTreeView tree={tree} />);
const levelB = result.getByTestId("namespace-level-deep-child-b");
const minusButton = levelB.querySelector("[data-testid='minus-square']");
if (minusButton) {
fireEvent.click(minusButton);
}
const plusButton = levelB.querySelector("[data-testid='plus-square']");
if (plusButton) {
fireEvent.click(plusButton);
}
expect(result.baseElement).toMatchSnapshot();
});
});

View File

@ -0,0 +1,106 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import styles from "./namespace-tree-view.module.scss";
import { SvgIcon } from "@material-ui/core";
import { TreeItem, TreeView } from "@material-ui/lab";
import { withInjectables } from "@ogre-tools/injectable-react";
import React from "react";
import { Link } from "react-router-dom";
import type { Namespace } from "../../../common/k8s-api/endpoints";
import { DrawerTitle } from "../drawer";
import type { GetDetailsUrl } from "../kube-detail-params/get-details-url.injectable";
import getDetailsUrlInjectable from "../kube-detail-params/get-details-url.injectable";
import { SubnamespaceBadge } from "./subnamespace-badge";
import hierarchicalNamespacesInjectable from "./hierarchical-namespaces.injectable";
import { prevDefault } from "../../utils";
import type { NamespaceTree } from "./store";
interface NamespaceTreeViewProps {
tree: NamespaceTree;
}
interface Dependencies {
namespaces: Namespace[];
getDetailsUrl: GetDetailsUrl;
}
function NonInjectableNamespaceTreeView({ tree, namespaces, getDetailsUrl }: Dependencies & NamespaceTreeViewProps) {
const [expandedItems, setExpandedItems] = React.useState<string[]>(namespaces.map(ns => ns.getId()));
const classes = { group: styles.group, label: styles.label };
function renderTree(nodes: NamespaceTree) {
return (
<TreeItem
key={nodes.id}
nodeId={nodes.id}
data-testid={`namespace-${nodes.id}`}
classes={classes}
onIconClick={prevDefault(() => toggleNode(nodes.id))}
label={(
<>
<Link key={nodes.namespace.getId()} to={getDetailsUrl(nodes.namespace.selfLink)}>
{nodes.namespace.getName()}
</Link>
{" "}
{nodes.namespace.isSubnamespace() && (
<SubnamespaceBadge id={`namespace-details-badge-for-${nodes.namespace.getId()}`} />
)}
</>
)}
>
{Array.isArray(nodes.children) ? nodes.children.map((node) => renderTree(node)) : null}
</TreeItem>
);
}
function toggleNode(id: string) {
if (expandedItems.includes(id)) {
setExpandedItems(expandedItems.filter(item => item !== id));
} else {
setExpandedItems([...expandedItems, id]);
}
}
return (
<div data-testid="namespace-tree-view" className={styles.TreeView}>
<DrawerTitle>Tree View</DrawerTitle>
<TreeView
defaultExpanded={[tree.id]}
defaultCollapseIcon={<MinusSquareIcon />}
defaultExpandIcon={<PlusSquareIcon />}
defaultEndIcon={(<div style={{ opacity: 0.3 }}><MinusSquareIcon /></div>)}
expanded={expandedItems}
>
{renderTree(tree)}
</TreeView>
</div>
);
}
function MinusSquareIcon() {
return (
<SvgIcon style={{ width: 14, height: 14 }} data-testid="minus-square">
<path d="M22.047 22.074v0 0-20.147 0h-20.12v0 20.147 0h20.12zM22.047 24h-20.12q-.803 0-1.365-.562t-.562-1.365v-20.147q0-.776.562-1.351t1.365-.575h20.147q.776 0 1.351.575t.575 1.351v20.147q0 .803-.575 1.365t-1.378.562v0zM17.873 11.023h-11.826q-.375 0-.669.281t-.294.682v0q0 .401.294 .682t.669.281h11.826q.375 0 .669-.281t.294-.682v0q0-.401-.294-.682t-.669-.281z" />
</SvgIcon>
);
}
function PlusSquareIcon() {
return (
<SvgIcon style={{ width: 14, height: 14 }} data-testid="plus-square">
<path d="M22.047 22.074v0 0-20.147 0h-20.12v0 20.147 0h20.12zM22.047 24h-20.12q-.803 0-1.365-.562t-.562-1.365v-20.147q0-.776.562-1.351t1.365-.575h20.147q.776 0 1.351.575t.575 1.351v20.147q0 .803-.575 1.365t-1.378.562v0zM17.873 12.977h-4.923v4.896q0 .401-.281.682t-.682.281v0q-.375 0-.669-.281t-.294-.682v-4.896h-4.923q-.401 0-.682-.294t-.281-.669v0q0-.401.281-.682t.682-.281h4.923v-4.896q0-.401.294-.682t.669-.281v0q.401 0 .682.281t.281.682v4.896h4.923q.401 0 .682.281t.281.682v0q0 .375-.281.669t-.682.294z" />
</SvgIcon>
);
}
export const NamespaceTreeView = withInjectables<Dependencies, NamespaceTreeViewProps>(NonInjectableNamespaceTreeView, {
getProps: (di, props) => ({
namespaces: di.inject(hierarchicalNamespacesInjectable),
getDetailsUrl: di.inject(getDetailsUrlInjectable),
...props,
}),
});

View File

@ -22,4 +22,8 @@
@include namespaceStatus; @include namespaceStatus;
} }
} }
.subnamespaceBadge {
margin-inline-start: var(--margin);
}
} }

View File

@ -16,6 +16,7 @@ import { withInjectables } from "@ogre-tools/injectable-react";
import namespaceStoreInjectable from "./store.injectable"; import namespaceStoreInjectable from "./store.injectable";
import { KubeObjectAge } from "../kube-object/age"; import { KubeObjectAge } from "../kube-object/age";
import openAddNamepaceDialogInjectable from "./add-dialog/open.injectable"; import openAddNamepaceDialogInjectable from "./add-dialog/open.injectable";
import { SubnamespaceBadge } from "./subnamespace-badge";
enum columnId { enum columnId {
name = "name", name = "name",
@ -55,7 +56,12 @@ const NonInjectedNamespacesRoute = ({ namespaceStore, openAddNamespaceDialog }:
{ title: "Status", className: "status", sortBy: columnId.status, id: columnId.status }, { title: "Status", className: "status", sortBy: columnId.status, id: columnId.status },
]} ]}
renderTableContents={namespace => [ renderTableContents={namespace => [
namespace.getName(), <>
{namespace.getName()}
{namespace.isSubnamespace() && (
<SubnamespaceBadge className="subnamespaceBadge" id={`namespace-list-badge-for-${namespace.getId()}`} />
)}
</>,
<KubeObjectStatusIcon key="icon" object={namespace} />, <KubeObjectStatusIcon key="icon" object={namespace} />,
namespace.getLabels().map(label => ( namespace.getLabels().map(label => (
<Badge <Badge

View File

@ -12,6 +12,12 @@ import { KubeObjectStore } from "../../../common/k8s-api/kube-object.store";
import type { NamespaceApi } from "../../../common/k8s-api/endpoints/namespace.api"; import type { NamespaceApi } from "../../../common/k8s-api/endpoints/namespace.api";
import { Namespace } from "../../../common/k8s-api/endpoints/namespace.api"; import { Namespace } from "../../../common/k8s-api/endpoints/namespace.api";
export interface NamespaceTree {
id: string;
namespace: Namespace;
children?: NamespaceTree[];
}
interface Dependencies extends KubeObjectStoreDependencies { interface Dependencies extends KubeObjectStoreDependencies {
readonly storage: StorageLayer<string[] | undefined>; readonly storage: StorageLayer<string[] | undefined>;
readonly clusterConfiguredAccessibleNamespaces: IComputedValue<string[]>; readonly clusterConfiguredAccessibleNamespaces: IComputedValue<string[]>;
@ -202,6 +208,16 @@ export class NamespaceStore extends KubeObjectStore<Namespace, NamespaceApi> {
this.selectAll(); this.selectAll();
} }
getNamespaceTree(root: Namespace): NamespaceTree {
const children = this.items.filter(namespace => namespace.isChildOf(root.getName()));
return {
id: root.getId(),
namespace: root,
children: children.map(this.getNamespaceTree),
};
}
@action @action
async remove(item: Namespace) { async remove(item: Namespace) {
await super.remove(item); await super.remove(item);

View File

@ -0,0 +1,11 @@
.subnamespaceBadge {
border-radius: 3px;
padding: 0px 4px;
border: 1px solid cadetblue;
color: cadetblue;
display: inline-flex;
font-size: x-small;
font-weight: bold;
height: 16px;
align-items: center;
}

View File

@ -0,0 +1,31 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import styles from "./subnamespace-badge.module.scss";
import React from "react";
import { Tooltip } from "../tooltip";
import { cssNames } from "../../utils";
interface SubnamespaceBadgeProps extends React.HTMLAttributes<HTMLSpanElement> {
id: string;
}
export function SubnamespaceBadge({ id, className, ...other }: SubnamespaceBadgeProps) {
return (
<>
<span
className={cssNames(styles.subnamespaceBadge, className)}
data-testid={id}
id={id}
{...other}
>
S
</span>
<Tooltip targetId={id}>
Subnamespace
</Tooltip>
</>
);
}

View File

@ -76,7 +76,7 @@ class NonInjectedCreateResource extends React.Component<CreateResourceProps & De
this.error = error.toString(); this.error = error.toString();
}; };
create = async (): Promise<void> => { create = async (): Promise<string | void> => {
const { apiManager, getDetailsUrl, navigate, requestKubeObjectCreation } = this.props; const { apiManager, getDetailsUrl, navigate, requestKubeObjectCreation } = this.props;
if (this.error || !this.data?.trim()) { if (this.error || !this.data?.trim()) {
@ -98,7 +98,7 @@ class NonInjectedCreateResource extends React.Component<CreateResourceProps & De
this.props.logger.warn("Failed to create resource", { resource }, result.error); this.props.logger.warn("Failed to create resource", { resource }, result.error);
this.props.showCheckedErrorNotification(result.error, "Unknown error occured while creating resources"); this.props.showCheckedErrorNotification(result.error, "Unknown error occured while creating resources");
return; throw result.error;
} }
const { kind, apiVersion, metadata: { name, namespace }} = result.response; const { kind, apiVersion, metadata: { name, namespace }} = result.response;
@ -122,7 +122,13 @@ class NonInjectedCreateResource extends React.Component<CreateResourceProps & De
)); ));
}); });
await Promise.allSettled(creatingResources); const results = await Promise.allSettled(creatingResources);
if (results.some(result => result.status === "rejected")) {
return;
}
return "All resources have been successfully created";
}; };
renderControls() { renderControls() {

View File

@ -174,5 +174,7 @@ export class EditResourceModel {
runInAction(() => { runInAction(() => {
this.editingResource.firstDraft = currentValue; this.editingResource.firstDraft = currentValue;
}); });
return result.response.toString();
}; };
} }

View File

@ -93,18 +93,25 @@ class NonInjectedInfoPanel extends Component<InfoPanelProps & Dependencies> {
if (showNotifications && result) { if (showNotifications && result) {
this.props.showSuccessNotification(result); this.props.showSuccessNotification(result);
} }
return result;
} catch (error) { } catch (error) {
if (showNotifications) { if (showNotifications) {
this.props.showCheckedErrorNotification(error, "Unknown error while submitting"); this.props.showCheckedErrorNotification(error, "Unknown error while submitting");
} }
return false;
} finally { } finally {
this.waiting = false; this.waiting = false;
} }
}; };
submitAndClose = async () => { submitAndClose = async () => {
await this.submit(); const result = await this.submit();
if (result) {
this.close(); this.close();
}
}; };
close = () => { close = () => {

View File

@ -12,6 +12,7 @@ import emitAppEventInjectable from "../../../../common/app-event-bus/emit-event.
import loadExtensionsInjectable from "../../load-extensions.injectable"; import loadExtensionsInjectable from "../../load-extensions.injectable";
import loggerInjectable from "../../../../common/logger.injectable"; import loggerInjectable from "../../../../common/logger.injectable";
import showErrorNotificationInjectable from "../../../components/notifications/show-error-notification.injectable"; import showErrorNotificationInjectable from "../../../components/notifications/show-error-notification.injectable";
import closeRendererLogFileInjectable from "../../../logger/close-renderer-log-file.injectable";
const initClusterFrameInjectable = getInjectable({ const initClusterFrameInjectable = getInjectable({
id: "init-cluster-frame", id: "init-cluster-frame",
@ -29,6 +30,7 @@ const initClusterFrameInjectable = getInjectable({
emitAppEvent: di.inject(emitAppEventInjectable), emitAppEvent: di.inject(emitAppEventInjectable),
logger: di.inject(loggerInjectable), logger: di.inject(loggerInjectable),
showErrorNotification: di.inject(showErrorNotificationInjectable), showErrorNotification: di.inject(showErrorNotificationInjectable),
closeFileLogging: di.inject(closeRendererLogFileInjectable),
}); });
}, },
}); });

View File

@ -2,6 +2,7 @@
* Copyright (c) OpenLens Authors. All rights reserved. * Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import { once } from "lodash";
import type { Cluster } from "../../../../common/cluster/cluster"; import type { Cluster } from "../../../../common/cluster/cluster";
import type { CatalogEntityRegistry } from "../../../api/catalog/entity/registry"; import type { CatalogEntityRegistry } from "../../../api/catalog/entity/registry";
import type { ShowNotification } from "../../../components/notifications"; import type { ShowNotification } from "../../../components/notifications";
@ -18,6 +19,7 @@ interface Dependencies {
emitAppEvent: EmitAppEvent; emitAppEvent: EmitAppEvent;
logger: Logger; logger: Logger;
showErrorNotification: ShowNotification; showErrorNotification: ShowNotification;
closeFileLogging: () => void;
} }
const logPrefix = "[CLUSTER-FRAME]:"; const logPrefix = "[CLUSTER-FRAME]:";
@ -30,6 +32,7 @@ export const initClusterFrame = ({
emitAppEvent, emitAppEvent,
logger, logger,
showErrorNotification, showErrorNotification,
closeFileLogging,
}: Dependencies) => }: Dependencies) =>
async (unmountRoot: () => void) => { async (unmountRoot: () => void) => {
await requestSetClusterFrameId(hostedCluster.id); await requestSetClusterFrameId(hostedCluster.id);
@ -69,11 +72,14 @@ export const initClusterFrame = ({
}); });
}); });
window.onbeforeunload = () => { const onCloseFrame = once(() => {
logger.info( logger.info(
`${logPrefix} Unload dashboard, clusterId=${(hostedCluster.id)}, frameId=${frameRoutingId}`, `${logPrefix} Unload dashboard, clusterId=${(hostedCluster.id)}, frameId=${frameRoutingId}`,
); );
closeFileLogging();
unmountRoot(); unmountRoot();
}; });
window.addEventListener("beforeunload", onCloseFrame);
window.addEventListener("pagehide", onCloseFrame);
}; };

View File

@ -12,6 +12,7 @@ import loggerInjectable from "../../../common/logger.injectable";
import { delay } from "../../../common/utils"; import { delay } from "../../../common/utils";
import { broadcastMessage } from "../../../common/ipc"; import { broadcastMessage } from "../../../common/ipc";
import { bundledExtensionsLoaded } from "../../../common/ipc/extension-handling"; import { bundledExtensionsLoaded } from "../../../common/ipc/extension-handling";
import closeRendererLogFileInjectable from "../../logger/close-renderer-log-file.injectable";
const initRootFrameInjectable = getInjectable({ const initRootFrameInjectable = getInjectable({
id: "init-root-frame", id: "init-root-frame",
@ -22,6 +23,7 @@ const initRootFrameInjectable = getInjectable({
const bindProtocolAddRouteHandlers = di.inject(bindProtocolAddRouteHandlersInjectable); const bindProtocolAddRouteHandlers = di.inject(bindProtocolAddRouteHandlersInjectable);
const lensProtocolRouterRenderer = di.inject(lensProtocolRouterRendererInjectable); const lensProtocolRouterRenderer = di.inject(lensProtocolRouterRendererInjectable);
const logger = di.inject(loggerInjectable); const logger = di.inject(loggerInjectable);
const closeRendererLogFile = di.inject(closeRendererLogFileInjectable);
return async (unmountRoot: () => void) => { return async (unmountRoot: () => void) => {
try { try {
@ -55,7 +57,7 @@ const initRootFrameInjectable = getInjectable({
window.addEventListener("beforeunload", () => { window.addEventListener("beforeunload", () => {
logger.info("[ROOT-FRAME]: Unload app"); logger.info("[ROOT-FRAME]: Unload app");
closeRendererLogFile();
unmountRoot(); unmountRoot();
}); });
}; };

View File

@ -12,5 +12,6 @@ export * as Mobx from "mobx";
export * as MobxReact from "mobx-react"; export * as MobxReact from "mobx-react";
export * as ReactRouter from "react-router"; export * as ReactRouter from "react-router";
export * as ReactRouterDom from "react-router-dom"; export * as ReactRouterDom from "react-router-dom";
export * as extensionApi from "../extensions/renderer-api"; export * as rendererExtensionApi from "../extensions/renderer-api";
export * as commonExtensionApi from "../extensions/common-api";
export { createApp } from "./create-app"; export { createApp } from "./create-app";

View File

@ -0,0 +1,22 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import winstonLoggerInjectable from "../../common/winston-logger.injectable";
import rendererFileLoggerTransportInjectable from "./file-transport.injectable";
const closeRendererLogFileInjectable = getInjectable({
id: "close-renderer-log-file",
instantiate: (di) => {
const winstonLogger = di.inject(winstonLoggerInjectable);
const fileLoggingTransport = di.inject(rendererFileLoggerTransportInjectable);
return () => {
fileLoggingTransport.close?.();
winstonLogger.remove(fileLoggingTransport);
};
},
});
export default closeRendererLogFileInjectable;

View File

@ -0,0 +1,44 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { transports } from "winston";
import directoryForLogsInjectable from "../../common/app-paths/directory-for-logs.injectable";
import { loggerTransportInjectionToken } from "../../common/logger/transports";
import windowLocationInjectable from "../../common/k8s-api/window-location.injectable";
import currentlyInClusterFrameInjectable from "../routes/currently-in-cluster-frame.injectable";
import { getClusterIdFromHost } from "../utils";
const rendererFileLoggerTransportInjectable = getInjectable({
id: "renderer-file-logger-transport",
instantiate: (di) => {
let frameId: string;
const currentlyInClusterFrame = di.inject(
currentlyInClusterFrameInjectable,
);
if (currentlyInClusterFrame) {
const { host } = di.inject(windowLocationInjectable);
const clusterId = getClusterIdFromHost(host);
frameId = clusterId ? `cluster-${clusterId}` : "cluster";
} else {
frameId = "main";
}
return new transports.File({
handleExceptions: false,
level: "info",
filename: `lens-renderer-${frameId}.log`,
dirname: di.inject(directoryForLogsInjectable),
maxsize: 1024 * 1024,
maxFiles: 2,
tailable: true,
});
},
injectionToken: loggerTransportInjectionToken,
});
export default rendererFileLoggerTransportInjectable;

View File

@ -6,9 +6,7 @@
import { iter } from "../../common/utils"; import { iter } from "../../common/utils";
export type IgnoredClassNames = number | symbol | Function; export type IgnoredClassNames = number | symbol | Function;
export type IClassName = string | string[] | Record<string, any> | undefined | null | false | IgnoredClassNames;
export type IClassName = string | string[] | IClassNameMap | undefined | null | false | IgnoredClassNames;
export type IClassNameMap = object;
export function cssNames(...classNames: IClassName[]): string { export function cssNames(...classNames: IClassName[]): string {
const classNamesEnabled = new Map<string, boolean>(); const classNamesEnabled = new Map<string, boolean>();

View File

@ -0,0 +1,37 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import type webpack from "webpack";
import path from "path";
import ForkTsCheckerPlugin from "fork-ts-checker-webpack-plugin";
import webpackLensMain from "./main";
import { buildDir } from "./vars";
const webpackLensCommon = (): webpack.Configuration => {
const mainConfig = webpackLensMain();
return {
...mainConfig,
name: "lens-app-common",
entry: {
common: path.resolve(__dirname, "..", "src", "common", "library.ts"),
},
output: {
publicPath: "",
library: {
type: "commonjs2",
},
path: path.resolve(buildDir, "library"),
},
optimization: {
minimize: false,
},
plugins: [
new ForkTsCheckerPlugin({}),
],
};
};
export default webpackLensCommon;

View File

@ -2,118 +2,15 @@
* Copyright (c) OpenLens Authors. All rights reserved. * Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
import MiniCssExtractPlugin from "mini-css-extract-plugin";
import { platform } from "os";
import path from "path";
import { DefinePlugin, optimize } from "webpack";
import main from "./main";
import renderer, { iconsAndImagesWebpackRules } from "./renderer";
import { buildDir, isDevelopment } from "./vars";
const rendererConfig = renderer({ showVars: false }); import webpackLensCommon from "./common";
const mainConfig = main(); import webpackLensMain from "./main";
import { webpackLensRenderer } from "./renderer";
const config = [ const config = [
{ webpackLensMain(),
...mainConfig, webpackLensRenderer(),
entry: { webpackLensCommon(),
main: path.resolve(__dirname, "..", "src", "main", "library.ts"),
},
output: {
library: {
type: "commonjs2",
},
path: path.resolve(buildDir, "library"),
},
optimization: {
minimize: false,
},
module: {
parser: {
javascript: {
commonjsMagicComments: true,
},
},
rules: [
{
test: /\.node$/,
use: "node-loader",
},
{
test: /\.ts$/,
exclude: /node_modules/,
use: {
loader: "ts-loader",
options: {
compilerOptions: {
declaration: true,
sourceMap: false,
},
},
},
},
...iconsAndImagesWebpackRules(),
],
},
plugins: [
new DefinePlugin({
CONTEXT_MATCHER_FOR_NON_FEATURES: `/\\.injectable(\\.${platform})?\\.tsx?$/`,
CONTEXT_MATCHER_FOR_FEATURES: `/\\/(main|common)\\/.+\\.injectable(\\.${platform})?\\.tsx?$/`,
}),
],
},
{
...mainConfig,
name: "lens-app-common",
entry: {
common: path.resolve(__dirname, "..", "src", "common", "library.ts"),
},
output: {
publicPath: "",
library: {
type: "commonjs2",
},
path: path.resolve(buildDir, "library"),
},
optimization: {
minimize: false,
},
plugins: [],
},
{
...rendererConfig,
entry: {
renderer: path.resolve(__dirname, "..", "src", "renderer", "library.ts"),
},
output: {
library: {
type: "commonjs2",
},
path: path.resolve(buildDir, "library"),
},
optimization: {
minimize: false,
},
externals: [
...(rendererConfig.externals as any).filter(Boolean),
{
"monaco-editor": "commonjs monaco-editor",
},
],
plugins: [
new DefinePlugin({
CONTEXT_MATCHER_FOR_NON_FEATURES: `/\\.injectable(\\.${platform})?\\.tsx?$/`,
CONTEXT_MATCHER_FOR_FEATURES: `/\\/(renderer|common)\\/.+\\.injectable(\\.${platform})?\\.tsx?$/`,
}),
new MiniCssExtractPlugin({
filename: "[name].css",
runtime: isDevelopment,
}),
new optimize.LimitChunkCountPlugin({
maxChunks: 1,
}),
],
},
]; ];
export default config; export default config;

View File

@ -5,25 +5,14 @@
import path from "path"; import path from "path";
import type webpack from "webpack"; import type webpack from "webpack";
import ForkTsCheckerPlugin from "fork-ts-checker-webpack-plugin";
import nodeExternals from "webpack-node-externals"; import nodeExternals from "webpack-node-externals";
import { getTypescriptLoader } from "./get-typescript-loader"; import ForkTsCheckerPlugin from "fork-ts-checker-webpack-plugin";
import CircularDependencyPlugin from "circular-dependency-plugin";
import { iconsAndImagesWebpackRules } from "./renderer"; import { iconsAndImagesWebpackRules } from "./renderer";
import type { WebpackPluginInstance } from "webpack";
import { DefinePlugin } from "webpack"; import { DefinePlugin } from "webpack";
import { additionalExternals, buildDir, isDevelopment, mainDir } from "./vars"; import { buildDir, isDevelopment } from "./vars";
import { platform } from "process"; import { platform } from "process";
const main = ({ showVars = true } = {}): webpack.Configuration => { const webpackLensMain = (): webpack.Configuration => {
if (showVars) {
console.info("WEBPACK:main", {
isDevelopment,
mainDir,
buildDir,
});
}
return { return {
name: "lens-app-main", name: "lens-app-main",
context: __dirname, context: __dirname,
@ -32,18 +21,22 @@ const main = ({ showVars = true } = {}): webpack.Configuration => {
devtool: isDevelopment ? "cheap-module-source-map" : "source-map", devtool: isDevelopment ? "cheap-module-source-map" : "source-map",
cache: isDevelopment ? { type: "filesystem" } : false, cache: isDevelopment ? { type: "filesystem" } : false,
entry: { entry: {
main: path.resolve(mainDir, "index.ts"), main: path.resolve(__dirname, "..", "src", "main", "library.ts"),
}, },
output: { output: {
libraryTarget: "global", library: {
path: buildDir, type: "commonjs2",
},
path: path.resolve(buildDir, "library"),
},
optimization: {
minimize: false,
}, },
resolve: { resolve: {
extensions: [".json", ".js", ".ts"], extensions: [".json", ".js", ".ts"],
}, },
externals: [ externals: [
nodeExternals(), nodeExternals(),
...additionalExternals,
], ],
module: { module: {
parser: { parser: {
@ -56,7 +49,19 @@ const main = ({ showVars = true } = {}): webpack.Configuration => {
test: /\.node$/, test: /\.node$/,
use: "node-loader", use: "node-loader",
}, },
getTypescriptLoader({}, /\.ts$/), {
test: /\.ts$/,
exclude: /node_modules/,
use: {
loader: "ts-loader",
options: {
transpileOnly: true,
compilerOptions: {
sourceMap: false,
},
},
},
},
...iconsAndImagesWebpackRules(), ...iconsAndImagesWebpackRules(),
], ],
}, },
@ -65,14 +70,18 @@ const main = ({ showVars = true } = {}): webpack.Configuration => {
CONTEXT_MATCHER_FOR_NON_FEATURES: `/\\.injectable(\\.${platform})?\\.tsx?$/`, CONTEXT_MATCHER_FOR_NON_FEATURES: `/\\.injectable(\\.${platform})?\\.tsx?$/`,
CONTEXT_MATCHER_FOR_FEATURES: `/\\/(main|common)\\/.+\\.injectable(\\.${platform})?\\.tsx?$/`, CONTEXT_MATCHER_FOR_FEATURES: `/\\/(main|common)\\/.+\\.injectable(\\.${platform})?\\.tsx?$/`,
}), }),
new ForkTsCheckerPlugin(), new ForkTsCheckerPlugin({
new CircularDependencyPlugin({ typescript: {
cwd: __dirname, mode: "write-dts",
exclude: /node_modules/, configOverwrite: {
failOnError: true, compilerOptions: {
}) as unknown as WebpackPluginInstance, declaration: true,
},
},
},
}),
], ],
}; };
}; };
export default main; export default webpackLensMain;

View File

@ -5,31 +5,16 @@
import path from "path"; import path from "path";
import type webpack from "webpack"; import type webpack from "webpack";
import HtmlWebpackPlugin from "html-webpack-plugin";
import MiniCssExtractPlugin from "mini-css-extract-plugin"; import MiniCssExtractPlugin from "mini-css-extract-plugin";
import ForkTsCheckerPlugin from "fork-ts-checker-webpack-plugin";
import MonacoWebpackPlugin from "monaco-editor-webpack-plugin";
import CircularDependencyPlugin from "circular-dependency-plugin"; import CircularDependencyPlugin from "circular-dependency-plugin";
import ReactRefreshWebpackPlugin from "@pmmmwh/react-refresh-webpack-plugin"; import ForkTsCheckerPlugin from "fork-ts-checker-webpack-plugin";
import type { WebpackPluginInstance } from "webpack"; import type { WebpackPluginInstance } from "webpack";
import { DefinePlugin } from "webpack"; import { optimize, DefinePlugin } from "webpack";
import { getTypescriptLoader } from "./get-typescript-loader"; import nodeExternals from "webpack-node-externals";
import { assetsFolderName, isDevelopment, rendererDir, buildDir, appName, htmlTemplate, publicPath, sassCommonVars, additionalExternals } from "./vars"; import { isDevelopment, buildDir, sassCommonVars } from "./vars";
import { platform } from "process"; import { platform } from "process";
export function webpackLensRenderer({ showVars = true } = {}): webpack.Configuration { export function webpackLensRenderer(): webpack.Configuration {
if (showVars) {
console.info("WEBPACK:renderer", {
assetsFolderName,
isDevelopment,
rendererDir,
buildDir,
appName,
htmlTemplate,
publicPath,
});
}
return { return {
target: "electron-renderer", target: "electron-renderer",
name: "lens-app-renderer", name: "lens-app-renderer",
@ -38,16 +23,13 @@ export function webpackLensRenderer({ showVars = true } = {}): webpack.Configura
devtool: isDevelopment ? "cheap-module-source-map" : "source-map", devtool: isDevelopment ? "cheap-module-source-map" : "source-map",
cache: isDevelopment ? { type: "filesystem" } : false, cache: isDevelopment ? { type: "filesystem" } : false,
entry: { entry: {
[appName]: path.resolve(rendererDir, "index.ts"), renderer: path.resolve(__dirname, "..", "src", "renderer", "library.ts"),
}, },
output: { output: {
libraryTarget: "global", library: {
globalObject: "this", type: "commonjs2",
publicPath, },
path: buildDir, path: path.resolve(buildDir, "library"),
filename: "[name].js",
chunkFilename: "chunks/[name].js",
assetModuleFilename: `${assetsFolderName}/[name][ext][query]`,
}, },
watchOptions: { watchOptions: {
ignored: /node_modules/, // https://webpack.js.org/configuration/watch/ ignored: /node_modules/, // https://webpack.js.org/configuration/watch/
@ -64,10 +46,7 @@ export function webpackLensRenderer({ showVars = true } = {}): webpack.Configura
], ],
}, },
externals: [ externals: [
{ nodeExternals(),
"win-ca": "commonjs win-ca",
},
...additionalExternals,
], ],
optimization: { optimization: {
minimize: false, minimize: false,
@ -83,11 +62,20 @@ export function webpackLensRenderer({ showVars = true } = {}): webpack.Configura
test: /\.node$/, test: /\.node$/,
use: "node-loader", use: "node-loader",
}, },
getTypescriptLoader({ {
getCustomTransformers: () => ({ test: /\.tsx?$/,
before: isDevelopment ? [require("react-refresh-typescript")()] : [], exclude: /node_modules/,
}), use: {
}), loader: "ts-loader",
options: {
transpileOnly: true,
compilerOptions: {
declaration: true,
sourceMap: false,
},
},
},
},
cssModulesWebpackRule(), cssModulesWebpackRule(),
...iconsAndImagesWebpackRules(), ...iconsAndImagesWebpackRules(),
...fontsLoaderWebpackRules(), ...fontsLoaderWebpackRules(),
@ -99,25 +87,7 @@ export function webpackLensRenderer({ showVars = true } = {}): webpack.Configura
CONTEXT_MATCHER_FOR_NON_FEATURES: `/\\.injectable(\\.${platform})?\\.tsx?$/`, CONTEXT_MATCHER_FOR_NON_FEATURES: `/\\.injectable(\\.${platform})?\\.tsx?$/`,
CONTEXT_MATCHER_FOR_FEATURES: `/\\/(renderer|common)\\/.+\\.injectable(\\.${platform})?\\.tsx?$/`, CONTEXT_MATCHER_FOR_FEATURES: `/\\/(renderer|common)\\/.+\\.injectable(\\.${platform})?\\.tsx?$/`,
}), }),
new ForkTsCheckerPlugin(), new ForkTsCheckerPlugin({}),
// see also: https://github.com/Microsoft/monaco-editor-webpack-plugin#options
new MonacoWebpackPlugin({
// publicPath: "/",
// filename: "[name].worker.js",
languages: ["json", "yaml"],
globalAPI: isDevelopment,
}),
new HtmlWebpackPlugin({
filename: "index.html",
template: htmlTemplate,
inject: true,
hash: true,
templateParameters: {
assetPath: `${publicPath}${assetsFolderName}`,
},
}),
new CircularDependencyPlugin({ new CircularDependencyPlugin({
cwd: __dirname, cwd: __dirname,
@ -129,11 +99,9 @@ export function webpackLensRenderer({ showVars = true } = {}): webpack.Configura
filename: "[name].css", filename: "[name].css",
}), }),
...( new optimize.LimitChunkCountPlugin({
isDevelopment maxChunks: 1,
? [new ReactRefreshWebpackPlugin({ overlay: false })] }),
: []
),
], ],
}; };
} }

View File

@ -21,8 +21,5 @@ export const htmlTemplate = path.resolve(rendererDir, "template.html");
export const publicPath = "/build/"; export const publicPath = "/build/";
export const sassCommonVars = path.resolve(rendererDir, "components/vars.scss"); export const sassCommonVars = path.resolve(rendererDir, "components/vars.scss");
export const webpackDevServerPort = Number(process.env.WEBPACK_DEV_SERVER_PORT) || 9191; export const webpackDevServerPort = Number(process.env.WEBPACK_DEV_SERVER_PORT) || 9191;
export const additionalExternals = (process.env.LENS_BUILD_ADDITIONAL_WEBPACK_EXTERNALS || "")
.split(",")
.map(s => s.trim());
assert(Number.isInteger(webpackDevServerPort), "WEBPACK_DEV_SERVER_PORT environment variable must only be an integer"); assert(Number.isInteger(webpackDevServerPort), "WEBPACK_DEV_SERVER_PORT environment variable must only be an integer");

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "@k8slens/ensure-binaries", "name": "@k8slens/ensure-binaries",
"version": "6.4.0-beta.10", "version": "6.4.0-beta.13",
"description": "CLI for downloading configured versions of the bundled versions of CLIs", "description": "CLI for downloading configured versions of the bundled versions of CLIs",
"main": "dist/index.js", "main": "dist/index.js",
"license": "MIT", "license": "MIT",
@ -31,8 +31,8 @@
"zod": "^3.20.2" "zod": "^3.20.2"
}, },
"devDependencies": { "devDependencies": {
"@swc/cli": "^0.1.59", "@swc/cli": "^0.1.61",
"@swc/core": "^1.3.31", "@swc/core": "^1.3.35",
"@types/cli-progress": "^3.11.0", "@types/cli-progress": "^3.11.0",
"@types/gunzip-maybe": "^1.4.0", "@types/gunzip-maybe": "^1.4.0",
"@types/node": "^16.18.11", "@types/node": "^16.18.11",

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@
"name": "@k8slens/extensions", "name": "@k8slens/extensions",
"productName": "OpenLens extensions", "productName": "OpenLens extensions",
"description": "OpenLens - Open Source Kubernetes IDE: extensions", "description": "OpenLens - Open Source Kubernetes IDE: extensions",
"version": "6.4.0-beta.10", "version": "6.4.0-beta.13",
"copyright": "© 2022 OpenLens Authors", "copyright": "© 2022 OpenLens Authors",
"license": "MIT", "license": "MIT",
"main": "dist/extension-api.js", "main": "dist/extension-api.js",
@ -26,7 +26,7 @@
"prepare:dev": "yarn run build" "prepare:dev": "yarn run build"
}, },
"dependencies": { "dependencies": {
"@k8slens/core": "^6.4.0-beta.10" "@k8slens/core": "^6.4.0-beta.13"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^16.18.6", "@types/node": "^16.18.6",

View File

@ -3,6 +3,5 @@
* Licensed under MIT License. See LICENSE in root directory for more information. * Licensed under MIT License. See LICENSE in root directory for more information.
*/ */
export { extensionApi as Main } from "@k8slens/core/main"; export { mainExtensionApi as Main, commonExtensionApi as Common } from "@k8slens/core/main";
export { extensionApi as Renderer } from "@k8slens/core/renderer"; export { rendererExtensionApi as Renderer } from "@k8slens/core/renderer";
export { extensionApi as Common } from "@k8slens/core/common";

View File

@ -1,6 +1,6 @@
{ {
"name": "@k8slens/generate-tray-icons", "name": "@k8slens/generate-tray-icons",
"version": "6.4.0-beta.10", "version": "6.4.0-beta.13",
"description": "CLI generating tray icons for building a lens-like application", "description": "CLI generating tray icons for building a lens-like application",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
@ -22,8 +22,8 @@
"registry": "https://registry.npmjs.org/" "registry": "https://registry.npmjs.org/"
}, },
"devDependencies": { "devDependencies": {
"@swc/cli": "^0.1.59", "@swc/cli": "^0.1.61",
"@swc/core": "^1.3.30", "@swc/core": "^1.3.35",
"@types/jsdom": "^20.0.1", "@types/jsdom": "^20.0.1",
"@types/node": "^18.11.18", "@types/node": "^18.11.18",
"@types/sharp": "^0.31.1", "@types/sharp": "^0.31.1",

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "@k8slens/node-fetch", "name": "@k8slens/node-fetch",
"version": "6.4.0-beta.10", "version": "6.4.0-beta.13",
"description": "Node fetch for Lens", "description": "Node fetch for Lens",
"license": "MIT", "license": "MIT",
"private": false, "private": false,

View File

@ -4,7 +4,7 @@
"productName": "OpenLens", "productName": "OpenLens",
"description": "OpenLens - Open Source IDE for Kubernetes", "description": "OpenLens - Open Source IDE for Kubernetes",
"homepage": "https://github.com/lensapp/lens", "homepage": "https://github.com/lensapp/lens",
"version": "6.4.0-beta.10", "version": "6.4.0-beta.13",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/lensapp/lens.git" "url": "git+https://github.com/lensapp/lens.git"
@ -192,9 +192,9 @@
} }
}, },
"dependencies": { "dependencies": {
"@k8slens/core": "^6.4.0-beta.10", "@k8slens/core": "^6.4.0-beta.13",
"@k8slens/ensure-binaries": "^6.4.0-beta.10", "@k8slens/ensure-binaries": "^6.4.0-beta.13",
"@k8slens/generate-tray-icons": "^6.4.0-beta.10", "@k8slens/generate-tray-icons": "^6.4.0-beta.13",
"@ogre-tools/fp": "^12.0.1", "@ogre-tools/fp": "^12.0.1",
"@ogre-tools/injectable": "^12.0.1", "@ogre-tools/injectable": "^12.0.1",
"@ogre-tools/injectable-extension-for-auto-registration": "^12.0.1", "@ogre-tools/injectable-extension-for-auto-registration": "^12.0.1",
@ -204,10 +204,10 @@
"rimraf": "^4.1.2" "rimraf": "^4.1.2"
}, },
"devDependencies": { "devDependencies": {
"@k8slens/node-fetch": "^6.4.0-beta.10", "@k8slens/node-fetch": "^6.4.0-beta.13",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.10", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
"@swc/cli": "^0.1.59", "@swc/cli": "^0.1.61",
"@swc/core": "^1.3.31", "@swc/core": "^1.3.35",
"@swc/jest": "^0.2.24", "@swc/jest": "^0.2.24",
"@types/byline": "^4.2.33", "@types/byline": "^4.2.33",
"@types/chart.js": "^2.9.36", "@types/chart.js": "^2.9.36",
@ -233,6 +233,7 @@
"autoprefixer": "^10.4.13", "autoprefixer": "^10.4.13",
"circular-dependency-plugin": "^5.2.2", "circular-dependency-plugin": "^5.2.2",
"concurrently": "^7.6.0", "concurrently": "^7.6.0",
"copy-webpack-plugin": "^11.0.0",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"css-loader": "^6.7.2", "css-loader": "^6.7.2",
"electron": "^19.1.9", "electron": "^19.1.9",

View File

@ -1,8 +1,7 @@
import { createContainer } from "@ogre-tools/injectable"; import { createContainer } from "@ogre-tools/injectable";
import { autoRegister } from "@ogre-tools/injectable-extension-for-auto-registration"; import { autoRegister } from "@ogre-tools/injectable-extension-for-auto-registration";
import { runInAction } from "mobx"; import { runInAction } from "mobx";
import { createApp, extensionApi as Main } from "@k8slens/core/main"; import { createApp, mainExtensionApi as Main, commonExtensionApi as Common } from "@k8slens/core/main";
import { extensionApi as Common } from "@k8slens/core/common";
const di = createContainer("main"); const di = createContainer("main");
const app = createApp({ const app = createApp({

View File

@ -1,8 +1,7 @@
import "@k8slens/core/styles"; import "@k8slens/core/styles";
import { createContainer } from "@ogre-tools/injectable"; import { createContainer } from "@ogre-tools/injectable";
import { runInAction } from "mobx"; import { runInAction } from "mobx";
import { createApp, extensionApi as Renderer } from "@k8slens/core/renderer"; import { createApp, rendererExtensionApi as Renderer, commonExtensionApi as Common } from "@k8slens/core/renderer";
import { extensionApi as Common } from "@k8slens/core/common";
import { autoRegister } from "@ogre-tools/injectable-extension-for-auto-registration"; import { autoRegister } from "@ogre-tools/injectable-extension-for-auto-registration";
const di = createContainer("renderer"); const di = createContainer("renderer");

View File

@ -11,6 +11,7 @@
import MonacoWebpackPlugin from "monaco-editor-webpack-plugin"; import MonacoWebpackPlugin from "monaco-editor-webpack-plugin";
import CircularDependencyPlugin from "circular-dependency-plugin"; import CircularDependencyPlugin from "circular-dependency-plugin";
import ReactRefreshWebpackPlugin from "@pmmmwh/react-refresh-webpack-plugin"; import ReactRefreshWebpackPlugin from "@pmmmwh/react-refresh-webpack-plugin";
import CopyPlugin from "copy-webpack-plugin";
import type { WebpackPluginInstance } from "webpack"; import type { WebpackPluginInstance } from "webpack";
import { DefinePlugin } from "webpack"; import { DefinePlugin } from "webpack";
import { assetsFolderName, isDevelopment, rendererDir, buildDir, htmlTemplate, publicPath, sassCommonVars } from "./vars"; import { assetsFolderName, isDevelopment, rendererDir, buildDir, htmlTemplate, publicPath, sassCommonVars } from "./vars";
@ -113,6 +114,15 @@
filename: "[name].css", filename: "[name].css",
}), }),
new CopyPlugin({
patterns: [
{
from: "node_modules/@k8slens/core/static/build/library/*.ttf",
to: "[name][ext]",
},
],
}),
...( ...(
isDevelopment isDevelopment
? [new ReactRefreshWebpackPlugin()] ? [new ReactRefreshWebpackPlugin()]

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "@k8slens/release-tool", "name": "@k8slens/release-tool",
"version": "6.4.0-beta.10", "version": "6.4.0-beta.13",
"description": "Release tool for lens monorepo", "description": "Release tool for lens monorepo",
"main": "dist/index.mjs", "main": "dist/index.mjs",
"license": "MIT", "license": "MIT",
@ -14,8 +14,8 @@
}, },
"type": "module", "type": "module",
"devDependencies": { "devDependencies": {
"@swc/cli": "^0.1.59", "@swc/cli": "^0.1.61",
"@swc/core": "^1.3.31", "@swc/core": "^1.3.35",
"@types/command-line-args": "^5.2.0", "@types/command-line-args": "^5.2.0",
"@types/fs-extra": "^11.0.1", "@types/fs-extra": "^11.0.1",
"@types/node": "^16.18.11", "@types/node": "^16.18.11",

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "@k8slens/semver", "name": "@k8slens/semver",
"version": "6.4.0-beta.10", "version": "6.4.0-beta.13",
"description": "CLI over semver package for picking parts of a version", "description": "CLI over semver package for picking parts of a version",
"license": "MIT", "license": "MIT",
"private": true, "private": true,
@ -15,8 +15,8 @@
"semver": "^7.3.8" "semver": "^7.3.8"
}, },
"devDependencies": { "devDependencies": {
"@swc/cli": "^0.1.59", "@swc/cli": "^0.1.61",
"@swc/core": "^1.3.31", "@swc/core": "^1.3.35",
"@types/command-line-args": "^5.2.0", "@types/command-line-args": "^5.2.0",
"@types/node": "^16.18.11", "@types/node": "^16.18.11",
"@types/semver": "^7.3.13", "@types/semver": "^7.3.13",

File diff suppressed because it is too large Load Diff

1377
yarn.lock

File diff suppressed because it is too large Load Diff