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

chore: Turn on @typescript-eslint/strict-boolean-expressions

- Fix some bugs about bad conditions

Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
Sebastian Malton 2023-05-04 13:15:30 -04:00
parent d3f45bfe49
commit 1675c56e59
165 changed files with 853 additions and 1141 deletions

599
package-lock.json generated
View File

@ -7539,7 +7539,6 @@
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", "resolved": "https://registry.npmjs.org/@types/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz",
"integrity": "sha512-0VLab/pcLTLcfbxi6THSIMVYcw9hEUBGvjwwaGpW77mMgRXfGF+a76t7BxTGyLh1y68tBvrffp8UWnqvm76+yg==", "integrity": "sha512-0VLab/pcLTLcfbxi6THSIMVYcw9hEUBGvjwwaGpW77mMgRXfGF+a76t7BxTGyLh1y68tBvrffp8UWnqvm76+yg==",
"dev": true,
"dependencies": { "dependencies": {
"postcss": "^8.0.0" "postcss": "^8.0.0"
} }
@ -7548,7 +7547,6 @@
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/@types/postcss-modules-scope/-/postcss-modules-scope-3.0.1.tgz", "resolved": "https://registry.npmjs.org/@types/postcss-modules-scope/-/postcss-modules-scope-3.0.1.tgz",
"integrity": "sha512-LNkp3c4ML9EQj2dgslp4i80Jxj72YK3HjYzrTn6ftUVylW1zaKFGqrMlNIyqBmPWmIhZ/Y5r0Y4T49Hk1IuDUg==", "integrity": "sha512-LNkp3c4ML9EQj2dgslp4i80Jxj72YK3HjYzrTn6ftUVylW1zaKFGqrMlNIyqBmPWmIhZ/Y5r0Y4T49Hk1IuDUg==",
"dev": true,
"dependencies": { "dependencies": {
"postcss": "^8.0.0" "postcss": "^8.0.0"
} }
@ -11757,6 +11755,7 @@
"version": "0.2.2", "version": "0.2.2",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
"dev": true,
"engines": { "engines": {
"node": ">=0.10" "node": ">=0.10"
} }
@ -34819,10 +34818,11 @@
"version": "6.5.0", "version": "6.5.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"typescript": "^4.9.3" "typescript": "^4.9.3",
"typescript-plugin-css-modules": "^5.0.1"
}, },
"devDependencies": { "devDependencies": {
"typescript-plugin-css-modules": "^5.0.1" "@k8slens/eslint-config": "^6.5.0-alpha.3"
} }
}, },
"packages/infrastructure/webpack": { "packages/infrastructure/webpack": {
@ -38893,7 +38893,7 @@
"typedoc": "^0.24.6", "typedoc": "^0.24.6",
"typedoc-plugin-markdown": "^3.15.1", "typedoc-plugin-markdown": "^3.15.1",
"typescript": "^4.9.5", "typescript": "^4.9.5",
"typescript-plugin-css-modules": "^3.4.0", "typescript-plugin-css-modules": "^5.0.1",
"url-parse": "^1.5.10", "url-parse": "^1.5.10",
"uuid": "^8.3.2", "uuid": "^8.3.2",
"webpack": "^5.81.0", "webpack": "^5.81.0",
@ -38910,6 +38910,78 @@
"zod": "^3.21.4" "zod": "^3.21.4"
}, },
"dependencies": { "dependencies": {
"dotenv": {
"version": "16.0.3",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
"integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==",
"dev": true
},
"strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
"dev": true
},
"stylus": {
"version": "0.59.0",
"resolved": "https://registry.npmjs.org/stylus/-/stylus-0.59.0.tgz",
"integrity": "sha512-lQ9w/XIOH5ZHVNuNbWW8D822r+/wBSO/d6XvtyHLF7LW4KaCIDeVbvn5DF8fGCJAUCwVhVi/h6J0NUcnylUEjg==",
"dev": true,
"requires": {
"@adobe/css-tools": "^4.0.1",
"debug": "^4.3.2",
"glob": "^7.1.6",
"sax": "~1.2.4",
"source-map": "^0.7.3"
}
},
"tsconfig-paths": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz",
"integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==",
"dev": true,
"requires": {
"json5": "^2.2.2",
"minimist": "^1.2.6",
"strip-bom": "^3.0.0"
}
},
"typescript-plugin-css-modules": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/typescript-plugin-css-modules/-/typescript-plugin-css-modules-5.0.1.tgz",
"integrity": "sha512-hKXObfwfjx2/myRq4JeQ8D3xIWYTFqusi0hS/Aka7RFX1xQEoEkdOGDWyXNb8LmObawsUzbI30gQnZvqYXCrkA==",
"dev": true,
"requires": {
"@types/postcss-modules-local-by-default": "^4.0.0",
"@types/postcss-modules-scope": "^3.0.1",
"dotenv": "^16.0.3",
"icss-utils": "^5.1.0",
"less": "^4.1.3",
"lodash.camelcase": "^4.3.0",
"postcss": "^8.4.21",
"postcss-load-config": "^3.1.4",
"postcss-modules-extract-imports": "^3.0.0",
"postcss-modules-local-by-default": "^4.0.0",
"postcss-modules-scope": "^3.0.0",
"reserved-words": "^0.1.2",
"sass": "^1.58.3",
"source-map-js": "^1.0.2",
"stylus": "^0.59.0",
"tsconfig-paths": "^4.1.2"
},
"dependencies": {
"postcss-load-config": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz",
"integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==",
"dev": true,
"requires": {
"lilconfig": "^2.0.5",
"yaml": "^1.10.2"
}
}
}
},
"xterm": { "xterm": {
"version": "4.19.0", "version": "4.19.0",
"resolved": "https://registry.npmjs.org/xterm/-/xterm-4.19.0.tgz", "resolved": "https://registry.npmjs.org/xterm/-/xterm-4.19.0.tgz",
@ -38993,7 +39065,7 @@
"typedoc": "^0.24.6", "typedoc": "^0.24.6",
"typedoc-plugin-markdown": "^3.15.1", "typedoc-plugin-markdown": "^3.15.1",
"typescript": "^4.9.5", "typescript": "^4.9.5",
"typescript-plugin-css-modules": "^4.1.1", "typescript-plugin-css-modules": "^5.0.1",
"webpack": "^5.81.0", "webpack": "^5.81.0",
"webpack-cli": "^5.0.1" "webpack-cli": "^5.0.1"
}, },
@ -39086,9 +39158,9 @@
} }
}, },
"typescript-plugin-css-modules": { "typescript-plugin-css-modules": {
"version": "4.2.3", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/typescript-plugin-css-modules/-/typescript-plugin-css-modules-4.2.3.tgz", "resolved": "https://registry.npmjs.org/typescript-plugin-css-modules/-/typescript-plugin-css-modules-5.0.1.tgz",
"integrity": "sha512-jEEP2oUPOqR89QGgvPK2HSTZLkrCeKZQ9EwiNxm9VUcufUbNY1Tv053fPKRq6c13PMQjlBU3WrQjKN8u0j5Y6w==", "integrity": "sha512-hKXObfwfjx2/myRq4JeQ8D3xIWYTFqusi0hS/Aka7RFX1xQEoEkdOGDWyXNb8LmObawsUzbI30gQnZvqYXCrkA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/postcss-modules-local-by-default": "^4.0.0", "@types/postcss-modules-local-by-default": "^4.0.0",
@ -39642,7 +39714,75 @@
"requires": { "requires": {
"@k8slens/eslint-config": "^6.5.0-alpha.2", "@k8slens/eslint-config": "^6.5.0-alpha.2",
"typescript": "^4.9.3", "typescript": "^4.9.3",
"typescript-plugin-css-modules": "^3.4.0" "typescript-plugin-css-modules": "^5.0.1"
},
"dependencies": {
"dotenv": {
"version": "16.0.3",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
"integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ=="
},
"strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA=="
},
"stylus": {
"version": "0.59.0",
"resolved": "https://registry.npmjs.org/stylus/-/stylus-0.59.0.tgz",
"integrity": "sha512-lQ9w/XIOH5ZHVNuNbWW8D822r+/wBSO/d6XvtyHLF7LW4KaCIDeVbvn5DF8fGCJAUCwVhVi/h6J0NUcnylUEjg==",
"requires": {
"@adobe/css-tools": "^4.0.1",
"debug": "^4.3.2",
"glob": "^7.1.6",
"sax": "~1.2.4",
"source-map": "^0.7.3"
}
},
"tsconfig-paths": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz",
"integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==",
"requires": {
"json5": "^2.2.2",
"minimist": "^1.2.6",
"strip-bom": "^3.0.0"
}
},
"typescript-plugin-css-modules": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/typescript-plugin-css-modules/-/typescript-plugin-css-modules-5.0.1.tgz",
"integrity": "sha512-hKXObfwfjx2/myRq4JeQ8D3xIWYTFqusi0hS/Aka7RFX1xQEoEkdOGDWyXNb8LmObawsUzbI30gQnZvqYXCrkA==",
"requires": {
"@types/postcss-modules-local-by-default": "^4.0.0",
"@types/postcss-modules-scope": "^3.0.1",
"dotenv": "^16.0.3",
"icss-utils": "^5.1.0",
"less": "^4.1.3",
"lodash.camelcase": "^4.3.0",
"postcss": "^8.4.21",
"postcss-load-config": "^3.1.4",
"postcss-modules-extract-imports": "^3.0.0",
"postcss-modules-local-by-default": "^4.0.0",
"postcss-modules-scope": "^3.0.0",
"reserved-words": "^0.1.2",
"sass": "^1.58.3",
"source-map-js": "^1.0.2",
"stylus": "^0.59.0",
"tsconfig-paths": "^4.1.2"
},
"dependencies": {
"postcss-load-config": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz",
"integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==",
"requires": {
"lilconfig": "^2.0.5",
"yaml": "^1.10.2"
}
}
}
}
} }
}, },
"@k8slens/utilities": { "@k8slens/utilities": {
@ -43423,7 +43563,6 @@
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", "resolved": "https://registry.npmjs.org/@types/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz",
"integrity": "sha512-0VLab/pcLTLcfbxi6THSIMVYcw9hEUBGvjwwaGpW77mMgRXfGF+a76t7BxTGyLh1y68tBvrffp8UWnqvm76+yg==", "integrity": "sha512-0VLab/pcLTLcfbxi6THSIMVYcw9hEUBGvjwwaGpW77mMgRXfGF+a76t7BxTGyLh1y68tBvrffp8UWnqvm76+yg==",
"dev": true,
"requires": { "requires": {
"postcss": "^8.0.0" "postcss": "^8.0.0"
} }
@ -43432,7 +43571,6 @@
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/@types/postcss-modules-scope/-/postcss-modules-scope-3.0.1.tgz", "resolved": "https://registry.npmjs.org/@types/postcss-modules-scope/-/postcss-modules-scope-3.0.1.tgz",
"integrity": "sha512-LNkp3c4ML9EQj2dgslp4i80Jxj72YK3HjYzrTn6ftUVylW1zaKFGqrMlNIyqBmPWmIhZ/Y5r0Y4T49Hk1IuDUg==", "integrity": "sha512-LNkp3c4ML9EQj2dgslp4i80Jxj72YK3HjYzrTn6ftUVylW1zaKFGqrMlNIyqBmPWmIhZ/Y5r0Y4T49Hk1IuDUg==",
"dev": true,
"requires": { "requires": {
"postcss": "^8.0.0" "postcss": "^8.0.0"
} }
@ -44785,11 +44923,6 @@
"resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
"integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg=="
}, },
"atob": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
},
"atomically": { "atomically": {
"version": "1.7.0", "version": "1.7.0",
"resolved": "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz", "resolved": "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz",
@ -46526,24 +46659,6 @@
"resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
"integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA=="
}, },
"css": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz",
"integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==",
"requires": {
"inherits": "^2.0.3",
"source-map": "^0.6.1",
"source-map-resolve": "^0.5.2",
"urix": "^0.1.0"
},
"dependencies": {
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
}
}
},
"css-box-model": { "css-box-model": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz",
@ -46568,14 +46683,6 @@
"semver": "^7.3.8" "semver": "^7.3.8"
} }
}, },
"css-parse": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/css-parse/-/css-parse-2.0.0.tgz",
"integrity": "sha512-UNIFik2RgSbiTwIW1IsFwXWn6vs+bYdq83LKTSOsx7NJR7WII9dxewkHLltfTLVppoUApHV0118a4RZRI9FLwA==",
"requires": {
"css": "^2.0.0"
}
},
"css-select": { "css-select": {
"version": "4.3.0", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
@ -46589,15 +46696,6 @@
"nth-check": "^2.0.1" "nth-check": "^2.0.1"
} }
}, },
"css-selector-tokenizer": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz",
"integrity": "sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==",
"requires": {
"cssesc": "^3.0.0",
"fastparse": "^1.1.2"
}
},
"css-vendor": { "css-vendor": {
"version": "2.0.8", "version": "2.0.8",
"resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz",
@ -46742,7 +46840,8 @@
"decode-uri-component": { "decode-uri-component": {
"version": "0.2.2", "version": "0.2.2",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==" "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
"dev": true
}, },
"decompress-response": { "decompress-response": {
"version": "6.0.0", "version": "6.0.0",
@ -47239,7 +47338,8 @@
"dotenv": { "dotenv": {
"version": "10.0.0", "version": "10.0.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
"integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==" "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==",
"dev": true
}, },
"dotenv-expand": { "dotenv-expand": {
"version": "5.1.0", "version": "5.1.0",
@ -48986,11 +49086,6 @@
"resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
"integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==" "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg=="
}, },
"fastparse": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz",
"integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ=="
},
"fastq": { "fastq": {
"version": "1.15.0", "version": "1.15.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
@ -49496,42 +49591,6 @@
"wide-align": "^1.1.5" "wide-align": "^1.1.5"
} }
}, },
"generic-names": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/generic-names/-/generic-names-1.0.3.tgz",
"integrity": "sha512-b6OHfQuKasIKM9b6YPkX+KUj/TLBTx3B/1aT1T5F12FEuEqyFMdr59OMS53aoaSw8eVtapdqieX6lbg5opaOhA==",
"requires": {
"loader-utils": "^0.2.16"
},
"dependencies": {
"big.js": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz",
"integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q=="
},
"emojis-list": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
"integrity": "sha512-knHEZMgs8BB+MInokmNTg/OyPlAddghe1YBgNwJBc5zsJi/uyIcXoSDsL/W9ymOsBoBGdPIHXYJ9+qKFwRwDng=="
},
"json5": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
"integrity": "sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw=="
},
"loader-utils": {
"version": "0.2.17",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz",
"integrity": "sha512-tiv66G0SmiOx+pLWMtGEkfSEejxvb6N6uRrQjfWJIT79W9GMpgKeCAmm9aVBKtd4WEgntciI8CsGqjpDoCWJug==",
"requires": {
"big.js": "^3.1.3",
"emojis-list": "^2.0.0",
"json5": "^0.5.0",
"object-assign": "^4.0.1"
}
}
}
},
"gensync": { "gensync": {
"version": "1.0.0-beta.2", "version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@ -57682,7 +57741,7 @@
"type-fest": "^2.14.0", "type-fest": "^2.14.0",
"typed-emitter": "^1.4.0", "typed-emitter": "^1.4.0",
"typescript": "^4.9.5", "typescript": "^4.9.5",
"typescript-plugin-css-modules": "^4.1.1", "typescript-plugin-css-modules": "^5.0.1",
"webpack": "^5.81.0", "webpack": "^5.81.0",
"webpack-cli": "^4.9.2", "webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.13.3", "webpack-dev-server": "^4.13.3",
@ -57727,9 +57786,9 @@
} }
}, },
"typescript-plugin-css-modules": { "typescript-plugin-css-modules": {
"version": "4.2.3", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/typescript-plugin-css-modules/-/typescript-plugin-css-modules-4.2.3.tgz", "resolved": "https://registry.npmjs.org/typescript-plugin-css-modules/-/typescript-plugin-css-modules-5.0.1.tgz",
"integrity": "sha512-jEEP2oUPOqR89QGgvPK2HSTZLkrCeKZQ9EwiNxm9VUcufUbNY1Tv053fPKRq6c13PMQjlBU3WrQjKN8u0j5Y6w==", "integrity": "sha512-hKXObfwfjx2/myRq4JeQ8D3xIWYTFqusi0hS/Aka7RFX1xQEoEkdOGDWyXNb8LmObawsUzbI30gQnZvqYXCrkA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/postcss-modules-local-by-default": "^4.0.0", "@types/postcss-modules-local-by-default": "^4.0.0",
@ -58669,255 +58728,6 @@
"source-map-js": "^1.0.2" "source-map-js": "^1.0.2"
} }
}, },
"postcss-filter-plugins": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-3.0.1.tgz",
"integrity": "sha512-tRKbW4wWBEkSSFuJtamV2wkiV9rj6Yy7P3Y13+zaynlPEEZt8EgYKn3y/RBpMeIhNmHXFlSdzofml65hD5OafA==",
"requires": {
"postcss": "^6.0.14"
},
"dependencies": {
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"requires": {
"color-convert": "^1.9.0"
}
},
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="
},
"postcss": {
"version": "6.0.23",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz",
"integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==",
"requires": {
"chalk": "^2.4.1",
"source-map": "^0.6.1",
"supports-color": "^5.4.0"
}
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"requires": {
"has-flag": "^3.0.0"
}
}
}
},
"postcss-icss-keyframes": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/postcss-icss-keyframes/-/postcss-icss-keyframes-0.2.1.tgz",
"integrity": "sha512-4m+hLY5TVqoTM198KKnzdNudyu1OvtqwD+8kVZ9PNiEO4+IfHYoyVvEXsOHjV8nZ1k6xowf+nY4HlUfZhOFvvw==",
"requires": {
"icss-utils": "^3.0.1",
"postcss": "^6.0.2",
"postcss-value-parser": "^3.3.0"
},
"dependencies": {
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"requires": {
"color-convert": "^1.9.0"
}
},
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="
},
"icss-utils": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-3.0.1.tgz",
"integrity": "sha512-ANhVLoEfe0KoC9+z4yiTaXOneB49K6JIXdS+yAgH0NERELpdIT7kkj2XxUPuHafeHnn8umXnECSpsfk1RTaUew==",
"requires": {
"postcss": "^6.0.2"
}
},
"postcss": {
"version": "6.0.23",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz",
"integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==",
"requires": {
"chalk": "^2.4.1",
"source-map": "^0.6.1",
"supports-color": "^5.4.0"
}
},
"postcss-value-parser": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
"integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ=="
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"requires": {
"has-flag": "^3.0.0"
}
}
}
},
"postcss-icss-selectors": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/postcss-icss-selectors/-/postcss-icss-selectors-2.0.3.tgz",
"integrity": "sha512-dxFtq+wscbU9faJaH8kIi98vvCPDbt+qg1g9GoG0os1PY3UvgY1Y2G06iZrZb1iVC9cyFfafwSY1IS+IQpRQ4w==",
"requires": {
"css-selector-tokenizer": "^0.7.0",
"generic-names": "^1.0.2",
"icss-utils": "^3.0.1",
"lodash": "^4.17.4",
"postcss": "^6.0.2"
},
"dependencies": {
"ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"requires": {
"color-convert": "^1.9.0"
}
},
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"requires": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
}
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"requires": {
"color-name": "1.1.3"
}
},
"color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="
},
"icss-utils": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-3.0.1.tgz",
"integrity": "sha512-ANhVLoEfe0KoC9+z4yiTaXOneB49K6JIXdS+yAgH0NERELpdIT7kkj2XxUPuHafeHnn8umXnECSpsfk1RTaUew==",
"requires": {
"postcss": "^6.0.2"
}
},
"postcss": {
"version": "6.0.23",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz",
"integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==",
"requires": {
"chalk": "^2.4.1",
"source-map": "^0.6.1",
"supports-color": "^5.4.0"
}
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"requires": {
"has-flag": "^3.0.0"
}
}
}
},
"postcss-import": { "postcss-import": {
"version": "15.1.0", "version": "15.1.0",
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
@ -60191,11 +60001,6 @@
"resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz",
"integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng=="
}, },
"resolve-url": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
"integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg=="
},
"resolve.exports": { "resolve.exports": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz",
@ -60966,18 +60771,6 @@
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
}, },
"source-map-resolve": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
"integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
"requires": {
"atob": "^2.1.2",
"decode-uri-component": "^0.2.0",
"resolve-url": "^0.2.1",
"source-map-url": "^0.4.0",
"urix": "^0.1.0"
}
},
"source-map-support": { "source-map-support": {
"version": "0.5.21", "version": "0.5.21",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
@ -60994,11 +60787,6 @@
} }
} }
}, },
"source-map-url": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz",
"integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw=="
},
"spawn-command": { "spawn-command": {
"version": "0.0.2-1", "version": "0.0.2-1",
"resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz", "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz",
@ -61494,41 +61282,6 @@
"integrity": "sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA==", "integrity": "sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA==",
"dev": true "dev": true
}, },
"stylus": {
"version": "0.54.8",
"resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.8.tgz",
"integrity": "sha512-vr54Or4BZ7pJafo2mpf0ZcwA74rpuYCZbxrHBsH8kbcXOwSfvBFwsRfpGO5OD5fhG5HDCFW737PKaawI7OqEAg==",
"requires": {
"css-parse": "~2.0.0",
"debug": "~3.1.0",
"glob": "^7.1.6",
"mkdirp": "~1.0.4",
"safer-buffer": "^2.1.2",
"sax": "~1.2.4",
"semver": "^6.3.0",
"source-map": "^0.7.3"
},
"dependencies": {
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
},
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
}
}
},
"sucrase": { "sucrase": {
"version": "3.32.0", "version": "3.32.0",
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz",
@ -62629,37 +62382,6 @@
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==" "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g=="
}, },
"typescript-plugin-css-modules": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/typescript-plugin-css-modules/-/typescript-plugin-css-modules-3.4.0.tgz",
"integrity": "sha512-2MdjfSg4MGex1csCWRUwKD+MpgnvcvLLr9bSAMemU/QYGqBsXdez0cc06H/fFhLtRoKJjXg6PSTur3Gy1Umhpw==",
"requires": {
"dotenv": "^10.0.0",
"icss-utils": "^5.1.0",
"less": "^4.1.1",
"lodash.camelcase": "^4.3.0",
"postcss": "^8.3.0",
"postcss-filter-plugins": "^3.0.1",
"postcss-icss-keyframes": "^0.2.1",
"postcss-icss-selectors": "^2.0.3",
"postcss-load-config": "^3.0.1",
"reserved-words": "^0.1.2",
"sass": "^1.32.13",
"stylus": "^0.54.8",
"tsconfig-paths": "^3.9.0"
},
"dependencies": {
"postcss-load-config": {
"version": "3.1.4",
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.4.tgz",
"integrity": "sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==",
"requires": {
"lilconfig": "^2.0.5",
"yaml": "^1.10.2"
}
}
}
},
"typical": { "typical": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz",
@ -62815,11 +62537,6 @@
"punycode": "^2.1.0" "punycode": "^2.1.0"
} }
}, },
"urix": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
"integrity": "sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg=="
},
"url-parse": { "url-parse": {
"version": "1.5.10", "version": "1.5.10",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",

View File

@ -125,6 +125,7 @@ module.exports = {
"no-constant-condition": ["error", { "no-constant-condition": ["error", {
"checkLoops": false, "checkLoops": false,
}], }],
"no-extra-boolean-cast": "off",
"header/header": [2, "../../license-header"], "header/header": [2, "../../license-header"],
"react/prop-types": "off", "react/prop-types": "off",
"no-invalid-this": "off", "no-invalid-this": "off",
@ -136,6 +137,11 @@ module.exports = {
"@typescript-eslint/explicit-module-boundary-types": "off", "@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-empty-function": "off", "@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-unnecessary-type-assertion": "off", "@typescript-eslint/no-unnecessary-type-assertion": "off",
"@typescript-eslint/strict-boolean-expressions": ["error", {
"allowNullableString": true,
"allowNullableBoolean": true,
"allowNullableNumber": true,
}],
"no-restricted-imports": ["error", { "no-restricted-imports": ["error", {
"paths": [ "paths": [
{ {

View File

@ -3,27 +3,24 @@
* 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 assert from "assert";
import type { AppPaths } from "./app-path-injection-token"; import type { AppPaths } from "./app-path-injection-token";
const appPathsStateInjectable = getInjectable({ const appPathsStateInjectable = getInjectable({
id: "app-paths-state", id: "app-paths-state",
instantiate: () => { instantiate: () => {
let state: AppPaths; let state: AppPaths | undefined;
return { return {
get: () =>{ get: () =>{
if (!state) { assert(state, "Tried to get app paths before initialization");
throw new Error("Tried to get app paths before state is setupped.");
}
return state; return state;
}, },
set: (newState: AppPaths) => { set: (newState: AppPaths) => {
if (state) { assert(!state, "Tried to overwrite existing state of app paths");
throw new Error("Tried to overwrite existing state of app paths.");
}
state = newState; state = newState;
}, },

View File

@ -8,7 +8,7 @@ import type TypedEmitter from "typed-emitter";
import { observable, makeObservable } from "mobx"; import { observable, makeObservable } from "mobx";
import { once } from "lodash"; import { once } from "lodash";
import type { Disposer, StrictReactNode } from "@k8slens/utilities"; import type { Disposer, StrictReactNode } from "@k8slens/utilities";
import { iter } from "@k8slens/utilities"; import { isObject, iter } from "@k8slens/utilities";
import type { CategoryColumnRegistration, TitleCellProps } from "../../renderer/components/catalog/custom-category-columns"; import type { CategoryColumnRegistration, TitleCellProps } from "../../renderer/components/catalog/custom-category-columns";
import type { Literal, Metadata } from "../cluster-types"; import type { Literal, Metadata } from "../cluster-types";
@ -364,15 +364,15 @@ export abstract class CatalogEntity<
constructor({ metadata, status, spec }: CatalogEntityData<Metadata, Status, Spec>) { constructor({ metadata, status, spec }: CatalogEntityData<Metadata, Status, Spec>) {
makeObservable(this); makeObservable(this);
if (!metadata || typeof metadata !== "object") { if (!isObject(metadata)) {
throw new TypeError("CatalogEntity's metadata must be a defined object"); throw new TypeError("CatalogEntity's metadata must be a defined object");
} }
if (!status || typeof status !== "object") { if (!isObject(status)) {
throw new TypeError("CatalogEntity's status must be a defined object"); throw new TypeError("CatalogEntity's status must be a defined object");
} }
if (!spec || typeof spec !== "object") { if (!isObject(spec)) {
throw new TypeError("CatalogEntity's spec must be a defined object"); throw new TypeError("CatalogEntity's spec must be a defined object");
} }

View File

@ -3,24 +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.
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import type { SelfSignedCert } from "selfsigned"; import type { SelfSignedCert } from "selfsigned";
const lensProxyCertificateInjectable = getInjectable({ const lensProxyCertificateInjectable = getInjectable({
id: "lens-proxy-certificate", id: "lens-proxy-certificate",
instantiate: () => { instantiate: () => {
let certState: SelfSignedCert; let certState: SelfSignedCert | undefined;
const cert = { const cert = {
get: () => { get: () => {
if (!certState) { assert(certState, "certificate has not been set");
throw "certificate has not been set";
}
return certState; return certState;
}, },
set: (certificate: SelfSignedCert) => { set: (certificate: SelfSignedCert) => {
if (certState) { assert(!certState, "certificate has already been set");
throw "certificate has already been set";
}
certState = certificate; certState = certificate;
}, },

View File

@ -30,12 +30,14 @@ export function ipcMainHandle(channel: string, listener: (event: Electron.IpcMai
} }
export async function broadcastMessage(channel: string, ...args: unknown[]): Promise<void> { export async function broadcastMessage(channel: string, ...args: unknown[]): Promise<void> {
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (ipcRenderer) { if (ipcRenderer) {
await ipcRenderer.invoke(broadcastMainChannel, channel, ...args.map(toJS)); await ipcRenderer.invoke(broadcastMainChannel, channel, ...args.map(toJS));
return; return;
} }
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (!webContents) { if (!webContents) {
return; return;
} }
@ -56,9 +58,9 @@ export async function broadcastMessage(channel: string, ...args: unknown[]): Pro
); );
}); });
const views = webContents.getAllWebContents(); const views = webContents.getAllWebContents() ?? [];
if (!views || !isArray(views) || views.length === 0) return; if (!isArray(views) || views.length === 0) return;
args = args.map(toJS); args = args.map(toJS);

View File

@ -10,7 +10,7 @@ import { autorun, action, observable } from "mobx";
import type { KubeApi } from "@k8slens/kube-api"; import type { KubeApi } from "@k8slens/kube-api";
import type { KubeObject, ObjectReference } from "@k8slens/kube-object"; import type { KubeObject, ObjectReference } from "@k8slens/kube-object";
import { parseKubeApi, createKubeApiURL } from "@k8slens/kube-api"; import { parseKubeApi, createKubeApiURL } from "@k8slens/kube-api";
import { getOrInsertWith, iter } from "@k8slens/utilities"; import { getOrInsertWith, isDefined, iter } from "@k8slens/utilities";
import type { CreateCustomResourceStore } from "./create-custom-resource-store.injectable"; import type { CreateCustomResourceStore } from "./create-custom-resource-store.injectable";
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
@ -142,7 +142,7 @@ export class ApiManager {
*/ */
getStore<Store extends KubeObjectStore>(api: string | KubeApi): Store | undefined ; getStore<Store extends KubeObjectStore>(api: string | KubeApi): Store | undefined ;
getStore(apiOrBase: string | KubeApi | undefined): KubeObjectStore | undefined { getStore(apiOrBase: string | KubeApi | undefined): KubeObjectStore | undefined {
if (!apiOrBase) { if (!isDefined(apiOrBase) || apiOrBase === "") {
return undefined; return undefined;
} }
@ -152,7 +152,7 @@ export class ApiManager {
const api = apiBase && this.getApi(apiBase); const api = apiBase && this.getApi(apiBase);
if (!api) { if (!isDefined(api) || api === "") {
return undefined; return undefined;
} }

View File

@ -12,6 +12,7 @@ import createKubeJsonApiInjectable from "./create-kube-json-api.injectable";
import type { KubeApiOptions } from "@k8slens/kube-api"; import type { KubeApiOptions } from "@k8slens/kube-api";
import { KubeApi } from "@k8slens/kube-api"; import { KubeApi } from "@k8slens/kube-api";
import type { KubeJsonApiDataFor, KubeObject, KubeObjectConstructor } from "@k8slens/kube-object"; import type { KubeJsonApiDataFor, KubeObject, KubeObjectConstructor } from "@k8slens/kube-object";
import { isDefined } from "@k8slens/utilities";
export interface CreateKubeApiForRemoteClusterConfig { export interface CreateKubeApiForRemoteClusterConfig {
cluster: { cluster: {
@ -92,7 +93,7 @@ const createKubeApiForRemoteClusterInjectable = getInjectable({
serverAddress: config.cluster.server, serverAddress: config.cluster.server,
apiBase: "", apiBase: "",
debug: isDevelopment, debug: isDevelopment,
...(token ? { ...(isDefined(token) ? {
getRequestOptions: async () => ({ getRequestOptions: async () => ({
headers: { headers: {
"Authorization": `Bearer ${typeof token === "function" ? await token() : token}`, "Authorization": `Bearer ${typeof token === "function" ? await token() : token}`,

View File

@ -49,10 +49,10 @@ export function normalizeMetrics(metrics: MetricData | undefined | null, frames
if (frames > 0) { if (frames > 0) {
// fill the gaps // fill the gaps
result.forEach(res => { result.forEach(res => {
if (!res.values || !res.values.length) return; if (!res.values.length) return;
let now = moment().startOf("minute").subtract(1, "minute").unix(); let now = moment().startOf("minute").subtract(1, "minute").unix();
let timestamp = res.values[0][0]; let timestamp = res.values[0]?.[0] ?? 0;
while (timestamp <= now) { while (timestamp <= now) {
timestamp = moment.unix(timestamp).add(1, "minute").unix(); timestamp = moment.unix(timestamp).add(1, "minute").unix();
@ -63,7 +63,7 @@ export function normalizeMetrics(metrics: MetricData | undefined | null, frames
} }
while (res.values.length < frames) { while (res.values.length < frames) {
const timestamp = moment.unix(res.values[0][0]).subtract(1, "minute").unix(); const timestamp = moment.unix(res.values[0]?.[0] ?? 0).subtract(1, "minute").unix();
if (!res.values.find((value) => value[0] === timestamp)) { if (!res.values.find((value) => value[0] === timestamp)) {
res.values.unshift([timestamp, "0"]); res.values.unshift([timestamp, "0"]);
@ -101,9 +101,11 @@ export function getItemMetrics<Keys extends string>(metrics: Partial<Record<Keys
} }
const results = metrics[metric]?.data.result; const results = metrics[metric]?.data.result;
const result = results?.find(res => Object.values(res.metric)[0] == itemName); const result = results?.find(res => Object.values(res.metric)[0] == itemName);
const item = itemMetrics[metric];
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion if (item) {
itemMetrics[metric]!.data.result = result ? [result] : []; item.data.result = result ? [result] : [];
}
} }
return itemMetrics; return itemMetrics;
@ -114,7 +116,9 @@ export function getMetricLastPoints<Keys extends string>(metrics: Partial<Record
object.entries(metrics) object.entries(metrics)
.map(([metricName, metric]) => { .map(([metricName, metric]) => {
try { try {
return [metricName, +metric.data.result[0].values.slice(-1)[0][1]] as const; const [value = [0, "0"]] = metric.data.result[0]?.values.slice(-1) ?? [];
return [metricName, +value[1]] as const;
} catch { } catch {
return undefined; return undefined;
} }

View File

@ -48,14 +48,8 @@ const requestMetricsInjectable = getInjectable({
function requestMetrics<Keys extends string>(query: Record<Keys, Partial<Record<string, string>>>, params?: RequestMetricsParams): Promise<Record<Keys, MetricData>>; function requestMetrics<Keys extends string>(query: Record<Keys, Partial<Record<string, string>>>, params?: RequestMetricsParams): Promise<Record<Keys, MetricData>>;
async function requestMetrics(metricsQuery: string | string[] | Partial<Record<string, Partial<Record<string, string>>>>, params: RequestMetricsParams = {}): Promise<MetricData | MetricData[] | Partial<Record<string, MetricData>>> { async function requestMetrics(metricsQuery: string | string[] | Partial<Record<string, Partial<Record<string, string>>>>, params: RequestMetricsParams = {}): Promise<MetricData | MetricData[] | Partial<Record<string, MetricData>>> {
const { range = 3600, step = 60, namespace } = params; const { range = 3600, step = 60, namespace } = params;
let { start, end } = params; const now = getSecondsFromUnixEpoch();
const { start = now - range, end = now } = params;
if (!start && !end) {
const now = getSecondsFromUnixEpoch();
start = now - range;
end = now;
}
return apiBase.post("/metrics", { return apiBase.post("/metrics", {
data: metricsQuery, data: metricsQuery,

View File

@ -306,7 +306,7 @@ export class KubeObjectStore<
} }
protected resetOnError(error: unknown) { protected resetOnError(error: unknown) {
if (error) this.reset(); if (Boolean(error)) this.reset();
} }
protected async loadItem(params: { name: string; namespace?: string }): Promise<K | null> { protected async loadItem(params: { name: string; namespace?: string }): Promise<K | null> {

View File

@ -122,7 +122,7 @@ export abstract class LensProtocolRouter {
return -1; return -1;
} }
return countBy(b.path)["/"] - countBy(a.path)["/"]; return (countBy(b.path)["/"] ?? 0) - (countBy(a.path)["/"] ?? 0);
})[0] ?? null; })[0] ?? null;
} }
@ -204,6 +204,7 @@ export abstract class LensProtocolRouter {
const extension = extensionLoader.getInstanceByName(name) as LensExtension | undefined; const extension = extensionLoader.getInstanceByName(name) as LensExtension | undefined;
if (!extension) { if (!extension) {
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
this.dependencies.logger.info(`${LensProtocolRouter.LoggingPrefix}: Extension ${name} matched, but does not have a class for ${ipcRenderer ? "renderer" : "main"}`); this.dependencies.logger.info(`${LensProtocolRouter.LoggingPrefix}: Extension ${name} matched, but does not have a class for ${ipcRenderer ? "renderer" : "main"}`);
return name; return name;

View File

@ -3,13 +3,7 @@
* 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 KubeResource = export type KubeResource = keyof typeof apiResourceRecord;
"namespaces" | "nodes" | "events" | "resourcequotas" | "services" | "limitranges" | "leases" |
"secrets" | "configmaps" | "ingresses" | "ingressclasses" | "networkpolicies" | "persistentvolumeclaims" | "persistentvolumes" | "storageclasses" |
"pods" | "daemonsets" | "deployments" | "statefulsets" | "replicasets" | "replicationcontrollers" | "jobs" | "cronjobs" |
"endpoints" | "customresourcedefinitions" | "horizontalpodautoscalers" | "verticalpodautoscalers" | "podsecuritypolicies" | "poddisruptionbudgets" |
"priorityclasses" | "runtimeclasses" |
"roles" | "clusterroles" | "rolebindings" | "clusterrolebindings" | "serviceaccounts" | "mutatingwebhookconfigurations" | "validatingwebhookconfigurations";
export interface KubeApiResource { export interface KubeApiResource {
kind: string; kind: string;
@ -35,7 +29,7 @@ export interface KubeApiResourceData {
namespaced: boolean; namespaced: boolean;
} }
export const apiResourceRecord: Record<KubeResource, KubeApiResourceData> = { export const apiResourceRecord = {
clusterroles: { clusterroles: {
kind: "ClusterRole", kind: "ClusterRole",
group: "rbac.authorization.k8s.io", group: "rbac.authorization.k8s.io",

View File

@ -20,15 +20,16 @@ export function reactiveNow(interval?: number | "frame") {
// Note: This is the kludge until https://github.com/mobxjs/mobx-utils/issues/306 is fixed // Note: This is the kludge until https://github.com/mobxjs/mobx-utils/issues/306 is fixed
const synchronizationIsEnabled = !process.env.JEST_WORKER_ID; const synchronizationIsEnabled = !process.env.JEST_WORKER_ID;
let ticker = tickers[interval];
if (!tickers[interval] || !synchronizationIsEnabled) { if (!ticker || !synchronizationIsEnabled) {
if (typeof interval === "number") if (typeof interval === "number")
tickers[interval] = createIntervalTicker(interval); ticker = tickers[interval] = createIntervalTicker(interval);
else else
tickers[interval] = createAnimationFrameTicker(); ticker = tickers[interval] = createAnimationFrameTicker();
} }
return tickers[interval].current(); return ticker.current();
} }
function createIntervalTicker(interval: number) { function createIntervalTicker(interval: number) {

View File

@ -109,6 +109,7 @@ export class ExtensionDiscovery {
* Initializes the class and setups the file watcher for added/removed local extensions. * Initializes the class and setups the file watcher for added/removed local extensions.
*/ */
async init() { async init() {
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (ipcRenderer) { if (ipcRenderer) {
await this.initRenderer(); await this.initRenderer();
} else { } else {

View File

@ -101,6 +101,7 @@ export class ExtensionLoader {
)); ));
async init() { async init() {
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (ipcMain) { if (ipcMain) {
await this.initMain(); await this.initMain();
} else { } else {
@ -211,6 +212,7 @@ export class ExtensionLoader {
} }
broadcastExtensions() { broadcastExtensions() {
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
const channel = ipcRenderer const channel = ipcRenderer
? extensionLoaderFromRendererChannel ? extensionLoaderFromRendererChannel
: extensionLoaderFromMainChannel; : extensionLoaderFromMainChannel;

View File

@ -145,7 +145,9 @@ export class LensRendererExtension extends LensExtension {
*/ */
// eslint-disable-next-line @typescript-eslint/ban-types // eslint-disable-next-line @typescript-eslint/ban-types
async isEnabledForCluster(cluster: KubernetesCluster): Promise<Boolean> { async isEnabledForCluster(cluster: KubernetesCluster): Promise<Boolean> {
return Promise.resolve((void cluster) || true); void cluster;
return Promise.resolve(true);
} }
/** /**

View File

@ -146,10 +146,13 @@ export const logTabStore = Object.assign(
createWorkloadTab: asLegacyGlobalFunctionForExtensionApi(createWorkloadLogsTabInjectable), createWorkloadTab: asLegacyGlobalFunctionForExtensionApi(createWorkloadLogsTabInjectable),
renameTab: (tabId: string): void => { renameTab: (tabId: string): void => {
const { selectedPodId } = logTabStore.getData(tabId) ?? {}; const { selectedPodId } = logTabStore.getData(tabId) ?? {};
const pod = selectedPodId && podStore.getById(selectedPodId);
if (pod) { if (selectedPodId) {
renameTab(tabId, `Pod ${pod.getName()}`); const pod = podStore.getById(selectedPodId);
if (pod) {
renameTab(tabId, `Pod ${pod.getName()}`);
}
} }
}, },
tabs: undefined, tabs: undefined,

View File

@ -38,12 +38,13 @@ import roleBindingApiInjectable from "../../common/k8s-api/endpoints/role-bindin
import customResourceDefinitionApiInjectable from "../../common/k8s-api/endpoints/custom-resource-definition.api.injectable"; import customResourceDefinitionApiInjectable from "../../common/k8s-api/endpoints/custom-resource-definition.api.injectable";
import { shouldShowResourceInjectionToken } from "../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token"; import { shouldShowResourceInjectionToken } from "../../features/cluster/showing-kube-resources/common/allowed-resources-injection-token";
import requestMetricsInjectable from "../../common/k8s-api/endpoints/metrics.api/request-metrics.injectable"; import requestMetricsInjectable from "../../common/k8s-api/endpoints/metrics.api/request-metrics.injectable";
import { maybeGet } from "@k8slens/utilities";
export function isAllowedResource(resources: KubeResource | KubeResource[]) { export function isAllowedResource(resources: KubeResource | KubeResource[]) {
const di = getLegacyGlobalDiForExtensionApi(); const di = getLegacyGlobalDiForExtensionApi();
return [resources].flat().every((resourceName) => { return [resources].flat().every((resourceName: string) => {
const resource = apiResourceRecord[resourceName]; const resource = maybeGet(apiResourceRecord, resourceName);
if (!resource) { if (!resource) {
return true; return true;

View File

@ -81,11 +81,12 @@ const toRecursedInjectablesFor = (logError: (errorMessage: string) => void) => {
injectionToken: applicationMenuItemInjectionToken, injectionToken: applicationMenuItemInjectionToken,
}), }),
...((registration.submenu as MenuRegistration[]) ...(
? (registration.submenu as MenuRegistration[]).flatMap( registration.submenu
toRecursedInjectables(currentIdPath), ? (registration.submenu as MenuRegistration[])
) .flatMap(toRecursedInjectables(currentIdPath))
: []), : []
),
]; ];
}; };

View File

@ -40,10 +40,11 @@ const NonInjectedUpdateButton = observer(({ warningLevel, update, id }: UpdateBu
data-testid="update-button" data-testid="update-button"
data-warning-level={level} data-warning-level={level}
id={buttonId} id={buttonId}
className={cssNames(styles.updateButton, { className={cssNames(
[styles.warningHigh]: level === "high", styles.updateButton,
[styles.warningMedium]: level === "medium", level === "high" && styles.warningHigh,
})} level === "medium" && styles.warningMedium,
)}
> >
Update Update
<Icon material="arrow_drop_down" className={styles.icon}/> <Icon material="arrow_drop_down" className={styles.icon}/>

View File

@ -26,7 +26,7 @@ const selectedUpdateChannelInjectable = getInjectable({
setValue: action((channelId) => { setValue: action((channelId) => {
const targetUpdateChannel = const targetUpdateChannel =
channelId && updateChannels[channelId] channelId && (channelId in updateChannels)
? updateChannels[channelId] ? updateChannels[channelId]
: defaultUpdateChannel; : defaultUpdateChannel;

View File

@ -3,6 +3,7 @@
* 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 { isTruthy } from "@k8slens/utilities";
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { globalAgent } from "https"; import { globalAgent } from "https";
import { requestSystemCAsInjectionToken } from "./request-system-cas-token"; import { requestSystemCAsInjectionToken } from "./request-system-cas-token";
@ -34,7 +35,7 @@ const injectSystemCAsInjectable = getInjectable({
return globalAgent.options.ca; return globalAgent.options.ca;
} }
if (globalAgent.options.ca) { if (isTruthy(globalAgent.options.ca)) {
return [globalAgent.options.ca]; return [globalAgent.options.ca];
} }

View File

@ -73,13 +73,17 @@ const v500Beta10HotbarStoreMigrationInjectable = getInjectable({
{ {
// grab the default named hotbar or the first. // grab the default named hotbar or the first.
const defaultHotbarIndex = Math.max(0, hotbars.findIndex(hotbar => hotbar.name === "default")); const defaultHotbarIndex = Math.max(0, hotbars.findIndex(hotbar => hotbar.name === "default"));
const [{ name, id, items }] = hotbars.splice(defaultHotbarIndex, 1); const [hotbar] = hotbars.splice(defaultHotbarIndex, 1);
workspaceHotbars.set("default", { if (hotbar) {
name, const { name, id, items } = hotbar;
id,
items: items.filter(isDefined), workspaceHotbars.set("default", {
}); name,
id,
items: items.filter(isDefined),
});
}
} }
for (const cluster of clusters) { for (const cluster of clusters) {
@ -130,7 +134,8 @@ const v500Beta10HotbarStoreMigrationInjectable = getInjectable({
const defaultHotbarIndex = hotbars.findIndex(hotbar => hotbar.name === "default"); const defaultHotbarIndex = hotbars.findIndex(hotbar => hotbar.name === "default");
if (defaultHotbarIndex >= 0) { if (defaultHotbarIndex >= 0) {
const defaultHotbar = createHotbar(hotbars[defaultHotbarIndex]); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const defaultHotbar = createHotbar(hotbars[defaultHotbarIndex]!);
if (defaultHotbar.isFull()) { if (defaultHotbar.isFull()) {
// making a new hotbar is less destructive if the first hotbar // making a new hotbar is less destructive if the first hotbar

View File

@ -113,11 +113,10 @@ const registratorForPreferenceItemsInjectable = getInjectable({
const items = extension.appPreferences.map((registration, i) => { const items = extension.appPreferences.map((registration, i) => {
const itemId = `${commonId}-item-${registration.id ?? i}`; const itemId = `${commonId}-item-${registration.id ?? i}`;
const itemIsInSpecialTab = const itemIsInSpecialTab = (
registration.showInPreferencesTab && registration.showInPreferencesTab === "telemetry"
["telemetry", "application"].includes( || registration.showInPreferencesTab === "application"
registration.showInPreferencesTab, );
);
return getInjectable({ return getInjectable({
id: itemId, id: itemId,

View File

@ -9,6 +9,7 @@ import type { LensRendererExtension } from "../../../../extensions/lens-renderer
import { pipeline } from "@ogre-tools/fp"; import { pipeline } from "@ogre-tools/fp";
import { topBarItemOnRightSideInjectionToken } from "../../../../renderer/components/layout/top-bar/top-bar-items/top-bar-item-injection-token"; import { topBarItemOnRightSideInjectionToken } from "../../../../renderer/components/layout/top-bar/top-bar-items/top-bar-item-injection-token";
import { computed } from "mobx"; import { computed } from "mobx";
import type { PartialDeep } from "type-fest";
const legacyExtensionApiRegistratorForTopBarItemsInjectable = getInjectable({ const legacyExtensionApiRegistratorForTopBarItemsInjectable = getInjectable({
id: "legacy-extension-api-registrator-for-top-bar-items", id: "legacy-extension-api-registrator-for-top-bar-items",
@ -19,7 +20,7 @@ const legacyExtensionApiRegistratorForTopBarItemsInjectable = getInjectable({
return pipeline( return pipeline(
extension.topBarItems, extension.topBarItems,
reject((registration) => !registration?.components?.Item), reject((registration) => !(registration as PartialDeep<typeof registration>)?.components?.Item),
(validTopBarRegistrations) => (validTopBarRegistrations) =>
validTopBarRegistrations.map((registration, index) => { validTopBarRegistrations.map((registration, index) => {

View File

@ -64,7 +64,7 @@ jest.mock("@k8slens/tooltip", () => ({
void tooltipOverrideDisabled; void tooltipOverrideDisabled;
const ResolvedTarget = Target as React.FunctionComponent<TargetProps>; const ResolvedTarget = Target as React.FunctionComponent<TargetProps>;
if (tooltip) { if (Boolean(tooltip)) {
const testId = props["data-testid"] as string | undefined; const testId = props["data-testid"] as string | undefined;
return ( return (

View File

@ -111,9 +111,7 @@ export const createClusterPrometheusHandler = (...args: [Dependencies, Cluster])
break; break;
case "fulfilled": case "fulfilled":
if (res.value) { return res.value;
return res.value;
}
} }
} }
@ -130,6 +128,10 @@ export const createClusterPrometheusHandler = (...args: [Dependencies, Cluster])
const prometheusPath = ensurePrometheusPath(service); const prometheusPath = ensurePrometheusPath(service);
const provider = ensurePrometheusProvider(service); const provider = ensurePrometheusProvider(service);
if (!provider) {
return undefined;
}
return { prometheusPath, provider }; return { prometheusPath, provider };
}; };

View File

@ -29,7 +29,7 @@ const clusterStoreSnapMigrationInjectable = getInjectable({
} }
logger.info("Migrating embedded kubeconfig paths"); logger.info("Migrating embedded kubeconfig paths");
const storedClusters = (store.get("clusters") || []) as ClusterModel[]; const storedClusters = (store.get("clusters") as (ClusterModel[] | undefined) ?? []);
if (!storedClusters.length) return; if (!storedClusters.length) return;

View File

@ -4,7 +4,7 @@
*/ */
import * as yaml from "js-yaml"; import * as yaml from "js-yaml";
import { iter, put, sortBySemverVersion } from "@k8slens/utilities"; import { isObject, iter, put, sortBySemverVersion } from "@k8slens/utilities";
import type { HelmRepo } from "../../common/helm/helm-repo"; import type { HelmRepo } from "../../common/helm/helm-repo";
import type { HelmChartManagerCache } from "./helm-chart-manager-cache.injectable"; import type { HelmChartManagerCache } from "./helm-chart-manager-cache.injectable";
import type { Logger } from "@k8slens/logger"; import type { Logger } from "@k8slens/logger";
@ -71,11 +71,11 @@ export class HelmChartManager {
const cacheFileStats = await this.dependencies.stat(this.repo.cacheFilePath); const cacheFileStats = await this.dependencies.stat(this.repo.cacheFilePath);
const data = yaml.load(cacheFile) as string | number | HelmCacheFile; const data = yaml.load(cacheFile) as string | number | HelmCacheFile;
if (!data || typeof data !== "object" || typeof data.entries !== "object") { if (!isObject(data) || !isObject(data.entries)) {
throw Object.assign(new TypeError("Helm Cache file does not parse correctly"), { file: this.repo.cacheFilePath, data }); throw Object.assign(new TypeError("Helm Cache file does not parse correctly"), { file: this.repo.cacheFilePath, data });
} }
const normalized = normalizeHelmCharts(this.repo.name, data.entries); const normalized = normalizeHelmCharts(this.repo.name, data.entries as RepoHelmChartList);
return put( return put(
this.dependencies.cache, this.dependencies.cache,

View File

@ -21,14 +21,17 @@ const getClusterForRequestInjectable = getInjectable({
// lens-server is connecting to 127.0.0.1:<port>/<uid> // lens-server is connecting to 127.0.0.1:<port>/<uid>
if (req.url && req.headers.host.startsWith("127.0.0.1")) { if (req.url && req.headers.host.startsWith("127.0.0.1")) {
const clusterId = req.url.split("/")[1]; const clusterId = req.url.split("/")[1];
const cluster = getClusterById(clusterId);
if (cluster) { if (clusterId) {
// we need to swap path prefix so that request is proxied to kube api const cluster = getClusterById(clusterId);
req.url = req.url.replace(`/${clusterId}`, apiKubePrefix);
if (cluster) {
// we need to swap path prefix so that request is proxied to kube api
req.url = req.url.replace(`/${clusterId}`, apiKubePrefix);
}
return cluster;
} }
return cluster;
} }
const clusterId = getClusterIdFromHost(req.headers.host); const clusterId = getClusterIdFromHost(req.headers.host);

View File

@ -200,7 +200,7 @@ export class LensProxy {
this.dependencies.logger.error(`[LENS-PROXY]: http proxy errored for cluster: ${String(error)}`, { url: req.url }); this.dependencies.logger.error(`[LENS-PROXY]: http proxy errored for cluster: ${String(error)}`, { url: req.url });
if (target) { if (target !== undefined) {
this.dependencies.logger.debug(`Failed proxy to target: ${JSON.stringify(target, null, 2)}`); this.dependencies.logger.debug(`Failed proxy to target: ${JSON.stringify(target, null, 2)}`);
if (req.method === "GET" && (!res.statusCode || res.statusCode >= 500)) { if (req.method === "GET" && (!res.statusCode || res.statusCode >= 500)) {
@ -245,12 +245,10 @@ export class LensProxy {
const kubeAuthProxyServer = this.dependencies.getKubeAuthProxyServer(cluster); const kubeAuthProxyServer = this.dependencies.getKubeAuthProxyServer(cluster);
const proxyTarget = await kubeAuthProxyServer.getApiTarget(isLongRunningRequest(req.url)); const proxyTarget = await kubeAuthProxyServer.getApiTarget(isLongRunningRequest(req.url));
if (proxyTarget) { this.dependencies.proxy.web(req, res, proxyTarget);
return this.dependencies.proxy.web(req, res, proxyTarget); } else {
} res.setHeader("Content-Security-Policy", this.dependencies.contentSecurityPolicy);
await this.dependencies.router.route(cluster, req, res);
} }
res.setHeader("Content-Security-Policy", this.dependencies.contentSecurityPolicy);
await this.dependencies.router.route(cluster, req, res);
} }
} }

View File

@ -3,7 +3,6 @@
* 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 { chunk } from "lodash";
import type { ConnectionOptions } from "tls"; import type { ConnectionOptions } from "tls";
import { connect } from "tls"; import { connect } from "tls";
import url from "url"; import url from "url";
@ -13,6 +12,7 @@ import type { LensProxyApiRequest } from "../lens-proxy";
import kubeAuthProxyServerInjectable from "../../cluster/kube-auth-proxy-server.injectable"; import kubeAuthProxyServerInjectable from "../../cluster/kube-auth-proxy-server.injectable";
import kubeAuthProxyCertificateInjectable from "../../kube-auth-proxy/kube-auth-proxy-certificate.injectable"; import kubeAuthProxyCertificateInjectable from "../../kube-auth-proxy/kube-auth-proxy-certificate.injectable";
import clusterApiUrlInjectable from "../../../features/cluster/connections/main/api-url.injectable"; import clusterApiUrlInjectable from "../../../features/cluster/connections/main/api-url.injectable";
import { iter } from "@k8slens/utilities";
const skipRawHeaders = new Set(["Host", "Authorization"]); const skipRawHeaders = new Set(["Host", "Authorization"]);
@ -35,7 +35,7 @@ const kubeApiUpgradeRequestInjectable = getInjectable({
proxySocket.write(`${req.method} ${pUrl.path ?? ""} HTTP/1.1\r\n`); proxySocket.write(`${req.method} ${pUrl.path ?? ""} HTTP/1.1\r\n`);
proxySocket.write(`Host: ${clusterApiUrl.host}\r\n`); proxySocket.write(`Host: ${clusterApiUrl.host}\r\n`);
for (const [key, value] of chunk(req.rawHeaders, 2)) { for (const [key, value] of iter.chunks(req.rawHeaders, 2)) {
if (skipRawHeaders.has(key)) { if (skipRawHeaders.has(key)) {
continue; continue;
} }

View File

@ -9,6 +9,7 @@ import type { InspectOptions } from "util";
import { inspect } from "util"; import { inspect } from "util";
import { omit } from "lodash"; import { omit } from "lodash";
import type { Format, TransformableInfo } from "logform"; import type { Format, TransformableInfo } from "logform";
import { isObject, isTruthy } from "@k8slens/utilities";
// The following license was copied from https://github.com/duccio/winston-console-format/blob/master/LICENSE // The following license was copied from https://github.com/duccio/winston-console-format/blob/master/LICENSE
// This was modified to support formatting causes // This was modified to support formatting causes
@ -116,9 +117,15 @@ export class ConsoleFormat implements Format {
private _cause(source: unknown): string[] { private _cause(source: unknown): string[] {
const messages: string[] = []; const messages: string[] = [];
if (source instanceof Error && source.cause) { if (source instanceof Error && isTruthy(source.cause)) {
messages.push("Cause:"); messages.push("Cause:");
messages.push(...this.getLines(omit(source.cause, "response")).map(l => ` ${l}`));
if (isObject(source.cause)) {
messages.push(...this.getLines(omit(source.cause, "response")).map(l => ` ${l}`));
} else {
messages.push(...this.getLines(source.cause).map(l => ` ${l}`));
}
messages.push(...this._cause(source.cause)); messages.push(...this._cause(source.cause));
} }

View File

@ -7,22 +7,14 @@ import { matches } from "lodash/fp";
import type { PrometheusProvider } from "./provider"; import type { PrometheusProvider } from "./provider";
import prometheusProvidersInjectable from "./providers.injectable"; import prometheusProvidersInjectable from "./providers.injectable";
export type GetPrometheusProviderByKind = (kind: string) => PrometheusProvider; export type GetPrometheusProviderByKind = (kind: string) => PrometheusProvider | undefined;
const getPrometheusProviderByKindInjectable = getInjectable({ const getPrometheusProviderByKindInjectable = getInjectable({
id: "get-prometheus-provider-by-kind", id: "get-prometheus-provider-by-kind",
instantiate: (di): GetPrometheusProviderByKind => { instantiate: (di): GetPrometheusProviderByKind => {
const providers = di.inject(prometheusProvidersInjectable); const providers = di.inject(prometheusProvidersInjectable);
return (kind) => { return (kind) => providers.get().find(matches({ kind }));
const provider = providers.get().find(matches({ kind }));
if (!provider) {
throw new Error(`Provider of kind "${kind}" does not exist`);
}
return provider;
};
}, },
}); });

View File

@ -6,11 +6,14 @@
import type { PrometheusProvider } from "./provider"; import type { PrometheusProvider } from "./provider";
import { createPrometheusProvider, bytesSent, findFirstNamespacedService, prometheusProviderInjectionToken } from "./provider"; import { createPrometheusProvider, bytesSent, findFirstNamespacedService, prometheusProviderInjectionToken } from "./provider";
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
export const getHelmLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string }): PrometheusProvider["getQuery"] => ( export const getHelmLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string }): PrometheusProvider["getQuery"] => (
(opts, queryName) => { (opts, queryName) => {
switch(opts.category) { switch(opts.category) {
case "cluster": case "cluster":
assert(opts.nodes, 'Missing "nodes" option when "category" option is "cluster"');
switch (queryName) { switch (queryName) {
case "memoryUsage": case "memoryUsage":
return `sum(node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)) by (component)`.replace(/_bytes/g, `_bytes{node=~"${opts.nodes}"}`); return `sum(node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)) by (component)`.replace(/_bytes/g, `_bytes{node=~"${opts.nodes}"}`);
@ -69,6 +72,10 @@ export const getHelmLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string }):
} }
break; break;
case "pods": case "pods":
assert(opts.pods, 'Missing "pods" option when "category" option is "pods"');
assert(opts.namespace, 'Missing "namespace" option when "category" option is "pods"');
assert(opts.selector, 'Missing "selector" option when "category" option is "pods"');
switch (queryName) { switch (queryName) {
case "cpuUsage": case "cpuUsage":
return `sum(rate(container_cpu_usage_seconds_total{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`; return `sum(rate(container_cpu_usage_seconds_total{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
@ -95,6 +102,9 @@ export const getHelmLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string }):
} }
break; break;
case "pvc": case "pvc":
assert(opts.pvc, 'Missing "pvc" option when "category" option is "pvc"');
assert(opts.namespace, 'Missing "namespace" option when "category" option is "pvc"');
switch (queryName) { switch (queryName) {
case "diskUsage": case "diskUsage":
return `sum(kubelet_volume_stats_used_bytes{persistentvolumeclaim="${opts.pvc}",namespace="${opts.namespace}"}) by (persistentvolumeclaim, namespace)`; return `sum(kubelet_volume_stats_used_bytes{persistentvolumeclaim="${opts.pvc}",namespace="${opts.namespace}"}) by (persistentvolumeclaim, namespace)`;
@ -103,6 +113,9 @@ export const getHelmLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string }):
} }
break; break;
case "ingress": case "ingress":
assert(opts.ingress, 'Missing "ingress" option when "category" option is "ingress"');
assert(opts.namespace, 'Missing "namespace" option when "category" option is "ingress"');
switch (queryName) { switch (queryName) {
case "bytesSentSuccess": case "bytesSentSuccess":
return bytesSent({ return bytesSent({
@ -126,7 +139,7 @@ export const getHelmLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string }):
break; break;
} }
throw new Error(`Unknown queryName="${queryName}" for category="${opts.category}"`); throw new Error(`Unknown queryName="${queryName}" for category="${String(opts.category)}"`);
} }
); );

View File

@ -6,11 +6,14 @@
import { bytesSent, prometheusProviderInjectionToken, findNamespacedService, createPrometheusProvider } from "./provider"; import { bytesSent, prometheusProviderInjectionToken, findNamespacedService, createPrometheusProvider } from "./provider";
import type { PrometheusProvider } from "./provider"; import type { PrometheusProvider } from "./provider";
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
export const getLensLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string }): PrometheusProvider["getQuery"] => ( export const getLensLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string }): PrometheusProvider["getQuery"] => (
(opts, queryName) => { (opts, queryName) => {
switch(opts.category) { switch(opts.category) {
case "cluster": case "cluster":
assert(opts.nodes, 'Missing "nodes" option when "category" option is "cluster"');
switch (queryName) { switch (queryName) {
case "memoryUsage": case "memoryUsage":
return `sum(node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)) by (kubernetes_name)`.replace(/_bytes/g, `_bytes{kubernetes_node=~"${opts.nodes}"}`); return `sum(node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)) by (kubernetes_name)`.replace(/_bytes/g, `_bytes{kubernetes_node=~"${opts.nodes}"}`);
@ -69,6 +72,10 @@ export const getLensLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string }):
} }
break; break;
case "pods": case "pods":
assert(opts.pods, 'Missing "pods" option when "category" option is "pods"');
assert(opts.namespace, 'Missing "namespace" option when "category" option is "pods"');
assert(opts.selector, 'Missing "selector" option when "category" option is "pods"');
switch (queryName) { switch (queryName) {
case "cpuUsage": case "cpuUsage":
return `sum(rate(container_cpu_usage_seconds_total{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`; return `sum(rate(container_cpu_usage_seconds_total{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
@ -95,6 +102,9 @@ export const getLensLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string }):
} }
break; break;
case "pvc": case "pvc":
assert(opts.pvc, 'Missing "pvc" option when "category" option is "pvc"');
assert(opts.namespace, 'Missing "namespace" option when "category" option is "pvc"');
switch (queryName) { switch (queryName) {
case "diskUsage": case "diskUsage":
return `sum(kubelet_volume_stats_used_bytes{persistentvolumeclaim="${opts.pvc}",namespace="${opts.namespace}"}) by (persistentvolumeclaim, namespace)`; return `sum(kubelet_volume_stats_used_bytes{persistentvolumeclaim="${opts.pvc}",namespace="${opts.namespace}"}) by (persistentvolumeclaim, namespace)`;
@ -103,6 +113,9 @@ export const getLensLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string }):
} }
break; break;
case "ingress": case "ingress":
assert(opts.ingress, 'Missing "ingress" option when "category" option is "ingress"');
assert(opts.namespace, 'Missing "namespace" option when "category" option is "ingress"');
switch (queryName) { switch (queryName) {
case "bytesSentSuccess": case "bytesSentSuccess":
return bytesSent({ return bytesSent({
@ -126,7 +139,7 @@ export const getLensLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string }):
break; break;
} }
throw new Error(`Unknown queryName="${queryName}" for category="${opts.category}"`); throw new Error(`Unknown queryName="${queryName}" for category="${String(opts.category)}"`);
} }
); );

View File

@ -6,11 +6,14 @@
import type { PrometheusProvider } from "./provider"; import type { PrometheusProvider } from "./provider";
import { bytesSent, createPrometheusProvider, findFirstNamespacedService, prometheusProviderInjectionToken } from "./provider"; import { bytesSent, createPrometheusProvider, findFirstNamespacedService, prometheusProviderInjectionToken } from "./provider";
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
export const getOperatorLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string }): PrometheusProvider["getQuery"] => ( export const getOperatorLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string }): PrometheusProvider["getQuery"] => (
(opts, queryName) => { (opts, queryName) => {
switch(opts.category) { switch(opts.category) {
case "cluster": case "cluster":
assert(opts.nodes, 'Missing "nodes" option when "category" option is "cluster"');
switch (queryName) { switch (queryName) {
case "memoryUsage": case "memoryUsage":
return `sum(node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes))`.replace(/_bytes/g, `_bytes * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"}`); return `sum(node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes))`.replace(/_bytes/g, `_bytes * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"}`);
@ -69,6 +72,10 @@ export const getOperatorLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string
} }
break; break;
case "pods": case "pods":
assert(opts.pods, 'Missing "pods" option when "category" option is "pods"');
assert(opts.namespace, 'Missing "namespace" option when "category" option is "pods"');
assert(opts.selector, 'Missing "selector" option when "category" option is "pods"');
switch (queryName) { switch (queryName) {
case "cpuUsage": case "cpuUsage":
return `sum(rate(container_cpu_usage_seconds_total{pod=~"${opts.pods}", namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`; return `sum(rate(container_cpu_usage_seconds_total{pod=~"${opts.pods}", namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
@ -95,6 +102,9 @@ export const getOperatorLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string
} }
break; break;
case "pvc": case "pvc":
assert(opts.pvc, 'Missing "pvc" option when "category" option is "pvc"');
assert(opts.namespace, 'Missing "namespace" option when "category" option is "pvc"');
switch (queryName) { switch (queryName) {
case "diskUsage": case "diskUsage":
return `sum(kubelet_volume_stats_used_bytes{persistentvolumeclaim="${opts.pvc}", namespace="${opts.namespace}"}) by (persistentvolumeclaim, namespace)`; return `sum(kubelet_volume_stats_used_bytes{persistentvolumeclaim="${opts.pvc}", namespace="${opts.namespace}"}) by (persistentvolumeclaim, namespace)`;
@ -103,6 +113,9 @@ export const getOperatorLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string
} }
break; break;
case "ingress": case "ingress":
assert(opts.ingress, 'Missing "ingress" option when "category" option is "ingress"');
assert(opts.namespace, 'Missing "namespace" option when "category" option is "ingress"');
switch (queryName) { switch (queryName) {
case "bytesSentSuccess": case "bytesSentSuccess":
return bytesSent({ return bytesSent({
@ -126,7 +139,7 @@ export const getOperatorLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string
break; break;
} }
throw new Error(`Unknown queryName="${queryName}" for category="${opts.category}"`); throw new Error(`Unknown queryName="${queryName}" for category="${String(opts.category)}"`);
} }
); );

View File

@ -6,11 +6,14 @@
import type { PrometheusProvider } from "./provider"; import type { PrometheusProvider } from "./provider";
import { bytesSent, createPrometheusProvider, findFirstNamespacedService, prometheusProviderInjectionToken } from "./provider"; import { bytesSent, createPrometheusProvider, findFirstNamespacedService, prometheusProviderInjectionToken } from "./provider";
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
export const getStacklightLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string }): PrometheusProvider["getQuery"] => ( export const getStacklightLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string }): PrometheusProvider["getQuery"] => (
(opts, queryName) => { (opts, queryName) => {
switch(opts.category) { switch(opts.category) {
case "cluster": case "cluster":
assert(opts.nodes, 'Missing "nodes" option when "category" option is "cluster"');
switch (queryName) { switch (queryName) {
case "memoryUsage": case "memoryUsage":
return `sum(node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)) by (kubernetes_name)`.replace(/_bytes/g, `_bytes{node=~"${opts.nodes}"}`); return `sum(node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)) by (kubernetes_name)`.replace(/_bytes/g, `_bytes{node=~"${opts.nodes}"}`);
@ -69,6 +72,10 @@ export const getStacklightLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: stri
} }
break; break;
case "pods": case "pods":
assert(opts.pods, 'Missing "pods" option when "category" option is "pods"');
assert(opts.namespace, 'Missing "namespace" option when "category" option is "pods"');
assert(opts.selector, 'Missing "selector" option when "category" option is "pods"');
switch (queryName) { switch (queryName) {
case "cpuUsage": case "cpuUsage":
return `sum(rate(container_cpu_usage_seconds_total{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`; return `sum(rate(container_cpu_usage_seconds_total{container!="POD",container!="",pod=~"${opts.pods}",namespace="${opts.namespace}"}[${rateAccuracy}])) by (${opts.selector})`;
@ -95,6 +102,9 @@ export const getStacklightLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: stri
} }
break; break;
case "pvc": case "pvc":
assert(opts.pvc, 'Missing "pvc" option when "category" option is "pvc"');
assert(opts.namespace, 'Missing "namespace" option when "category" option is "pvc"');
switch (queryName) { switch (queryName) {
case "diskUsage": case "diskUsage":
return `sum(kubelet_volume_stats_used_bytes{persistentvolumeclaim="${opts.pvc}",namespace="${opts.namespace}"}) by (persistentvolumeclaim, namespace)`; return `sum(kubelet_volume_stats_used_bytes{persistentvolumeclaim="${opts.pvc}",namespace="${opts.namespace}"}) by (persistentvolumeclaim, namespace)`;
@ -103,6 +113,9 @@ export const getStacklightLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: stri
} }
break; break;
case "ingress": case "ingress":
assert(opts.ingress, 'Missing "ingress" option when "category" option is "ingress"');
assert(opts.namespace, 'Missing "namespace" option when "category" option is "ingress"');
switch (queryName) { switch (queryName) {
case "bytesSentSuccess": case "bytesSentSuccess":
return bytesSent({ return bytesSent({
@ -126,7 +139,7 @@ export const getStacklightLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: stri
break; break;
} }
throw new Error(`Unknown queryName="${queryName}" for category="${opts.category}"`); throw new Error(`Unknown queryName="${queryName}" for category="${String(opts.category)}"`);
} }
); );

View File

@ -9,7 +9,7 @@ import type { LensExtension } from "../../../extensions/lens-extension";
import { observable, when } from "mobx"; import { observable, when } from "mobx";
import type { LensProtocolRouterDependencies, RouteAttempt } from "../../../common/protocol-handler"; import type { LensProtocolRouterDependencies, RouteAttempt } from "../../../common/protocol-handler";
import { ProtocolHandlerInvalid } from "../../../common/protocol-handler"; import { ProtocolHandlerInvalid } from "../../../common/protocol-handler";
import { disposer, noop } from "@k8slens/utilities"; import { disposer, isTruthy, noop } from "@k8slens/utilities";
import type { BroadcastMessage } from "../../../common/ipc/broadcast-message.injectable"; import type { BroadcastMessage } from "../../../common/ipc/broadcast-message.injectable";
export interface FallbackHandler { export interface FallbackHandler {
@ -79,7 +79,7 @@ export class LensProtocolRouterMain extends proto.LensProtocolRouter {
await this._routeToExtension(url); await this._routeToExtension(url);
} }
} catch (error) { } catch (error) {
void this.dependencies.broadcastMessage(ProtocolHandlerInvalid, error ? String(error) : "unknown error", rawUrl); void this.dependencies.broadcastMessage(ProtocolHandlerInvalid, isTruthy(error) ? String(error) : "unknown error", rawUrl);
if (error instanceof proto.RoutingError) { if (error instanceof proto.RoutingError) {
this.dependencies.logger.error(`${proto.LensProtocolRouter.LoggingPrefix}: ${String(error)}`, { url: error.url }); this.dependencies.logger.error(`${proto.LensProtocolRouter.LoggingPrefix}: ${String(error)}`, { url: error.url });

View File

@ -5,7 +5,7 @@
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import type { ServerResponse } from "http"; import type { ServerResponse } from "http";
import { loggerInjectionToken } from "@k8slens/logger"; import { loggerInjectionToken } from "@k8slens/logger";
import { object } from "@k8slens/utilities"; import { isDefined, object } from "@k8slens/utilities";
import type { LensApiRequest, Route } from "./route"; import type { LensApiRequest, Route } from "./route";
import { contentTypes } from "./router-content-types"; import { contentTypes } from "./router-content-types";
@ -34,7 +34,7 @@ const writeServerResponseFor = (serverResponse: ServerResponse) => ({
if (content instanceof Buffer) { if (content instanceof Buffer) {
serverResponse.write(content); serverResponse.write(content);
serverResponse.end(); serverResponse.end();
} else if (content) { } else if (isDefined(content)) {
serverResponse.end(content); serverResponse.end(content);
} else { } else {
serverResponse.end(); serverResponse.end();
@ -67,7 +67,7 @@ const createHandlerForRouteInjectable = getInjectable({
} catch(error) { } catch(error) {
const mappedResult = contentTypes.txt.resultMapper({ const mappedResult = contentTypes.txt.resultMapper({
statusCode: 500, statusCode: 500,
error: error ? String(error) : "unknown error", error: isDefined(error) ? String(error) : "unknown error",
}); });
logger.error(`[ROUTER]: route ${route.path}, called with ${request.path}, threw an error`, error); logger.error(`[ROUTER]: route ${route.path}, called with ${request.path}, threw an error`, error);

View File

@ -54,7 +54,7 @@ export interface ClusterLensApiRequest<Path extends string> extends LensApiReque
export interface LensApiResult<Response> { export interface LensApiResult<Response> {
statusCode?: number; statusCode?: number;
response?: Response; response?: Response;
error?: unknown; error?: string | Error;
contentType?: LensApiResultContentType; contentType?: LensApiResultContentType;
headers?: Partial<Record<string, string>>; headers?: Partial<Record<string, string>>;
proxy?: httpProxy; proxy?: httpProxy;

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 { isDefined } from "@k8slens/utilities";
import type { LensApiResult } from "./route"; import type { LensApiResult } from "./route";
export interface LensApiResultContentType { export interface LensApiResultContentType {
@ -14,19 +15,15 @@ export interface LensApiResultContentType {
const resultMapperFor = const resultMapperFor =
(contentType: string): LensApiResultContentType["resultMapper"] => (contentType: string): LensApiResultContentType["resultMapper"] =>
({ response, error, statusCode= error ? 400 : 200, headers = {}}) => ({ ({ response, error, statusCode = isDefined(error) ? 200 : 400, headers = {}}) => ({
statusCode, statusCode,
content: error || response, content: isDefined(error) ? error : response,
headers: { ...headers, "Content-Type": contentType }, headers: { ...headers, "Content-Type": contentType },
}); });
export type SupportedFileExtension = "json" | "txt" | "html" | "css" | "gif" | "jpg" | "png" | "svg" | "js" | "woff2" | "ttf"; export type SupportedFileExtension = "json" | "txt" | "html" | "css" | "gif" | "jpg" | "png" | "svg" | "js" | "woff2" | "ttf";
export interface ContentTypes extends Record<SupportedFileExtension, LensApiResultContentType> { export const contentTypes: Record<SupportedFileExtension, LensApiResultContentType> = {
[key: string]: LensApiResultContentType | undefined;
}
export const contentTypes: ContentTypes = {
json: { json: {
resultMapper: (result: LensApiResult<unknown>) => { resultMapper: (result: LensApiResult<unknown>) => {
const resultMapper = resultMapperFor("application/json"); const resultMapper = resultMapperFor("application/json");

View File

@ -10,6 +10,7 @@ import type { LensApiRequest } from "../../router/route";
import path from "path"; import path from "path";
import { contentTypes } from "../../router/router-content-types"; import { contentTypes } from "../../router/router-content-types";
import { prefixedLoggerInjectable } from "@k8slens/logger"; import { prefixedLoggerInjectable } from "@k8slens/logger";
import { maybeGet } from "@k8slens/utilities";
const prodStaticFileRouteHandlerInjectable = getInjectable({ const prodStaticFileRouteHandlerInjectable = getInjectable({
id: "prod-static-file-route-handler", id: "prod-static-file-route-handler",
@ -33,7 +34,7 @@ const prodStaticFileRouteHandlerInjectable = getInjectable({
} }
const fileExtension = path.extname(assetFilePath).slice(1); const fileExtension = path.extname(assetFilePath).slice(1);
const contentType = contentTypes[fileExtension] || contentTypes.txt; const contentType = maybeGet(contentTypes, fileExtension) || contentTypes.txt;
try { try {
return { response: await readFileBuffer(assetFilePath), contentType }; return { response: await readFileBuffer(assetFilePath), contentType };

View File

@ -16,6 +16,7 @@ import type { CreateKubeApi } from "../../../common/k8s-api/create-kube-api.inje
import { initialNodeShellImage } from "../../../common/cluster-types"; import { initialNodeShellImage } from "../../../common/cluster-types";
import type { LoadProxyKubeconfig } from "../../cluster/load-proxy-kubeconfig.injectable"; import type { LoadProxyKubeconfig } from "../../cluster/load-proxy-kubeconfig.injectable";
import type { Pod } from "@k8slens/kube-object"; import type { Pod } from "@k8slens/kube-object";
import { isDefined } from "@k8slens/utilities";
export interface NodeShellSessionArgs extends ShellSessionArgs { export interface NodeShellSessionArgs extends ShellSessionArgs {
nodeName: string; nodeName: string;
@ -67,7 +68,7 @@ export class NodeShellSession extends ShellSession {
this.send({ this.send({
type: TerminalChannels.STDOUT, type: TerminalChannels.STDOUT,
data: `Error occurred: ${get(error, "response.body.message", error ? String(error) : "unknown error")}`, data: `Error occurred: ${get(error, "response.body.message", isDefined(error) ? String(error) : "unknown error")}`,
}); });
throw new ShellOpenError( throw new ShellOpenError(

View File

@ -271,7 +271,7 @@ export abstract class ShellSession {
} }
}) })
.once("close", code => { .once("close", code => {
this.dependencies.logger.info(`[SHELL-SESSION]: websocket for ${this.terminalId} closed with code=${WebSocketCloseEvent[code]}(${code})`, { cluster: this.cluster.getMeta() }); this.dependencies.logger.info(`[SHELL-SESSION]: websocket for ${this.terminalId} closed with code=${String(WebSocketCloseEvent[code])}(${code})`, { cluster: this.cluster.getMeta() });
const stopShellSession = this.running const stopShellSession = this.running
&& ( && (

View File

@ -55,15 +55,11 @@ const toItemInjectablesFor = (extension: LensMainExtension, withErrorLoggingFor:
separator: registration.type === "separator", separator: registration.type === "separator",
label: computed(() => { label: computed(() => {
if (!registration.label) {
return "";
}
if (isString(registration.label)) { if (isString(registration.label)) {
return registration.label; return registration.label;
} }
return registration.label.get(); return registration.label?.get() ?? "";
}), }),
tooltip: registration.toolTip, tooltip: registration.toolTip,

View File

@ -7,7 +7,7 @@ import { computed, observable, makeObservable, action, runInAction } from "mobx"
import { ipcRendererOn } from "../../../../common/ipc"; import { ipcRendererOn } from "../../../../common/ipc";
import type { CatalogCategory, CatalogEntity, CatalogEntityData, CatalogCategoryRegistry, CatalogEntityKindData } from "../../../../common/catalog"; import type { CatalogCategory, CatalogEntity, CatalogEntityData, CatalogCategoryRegistry, CatalogEntityKindData } from "../../../../common/catalog";
import type { Disposer } from "@k8slens/utilities"; import type { Disposer } from "@k8slens/utilities";
import { iter } from "@k8slens/utilities"; import { isDefined, isString, iter } from "@k8slens/utilities";
import { once } from "lodash"; import { once } from "lodash";
import { CatalogRunEvent } from "../../../../common/catalog/catalog-run-event"; import { CatalogRunEvent } from "../../../../common/catalog/catalog-run-event";
import { ipcRenderer } from "electron"; import { ipcRenderer } from "electron";
@ -60,7 +60,7 @@ export class CatalogEntityRegistry {
// If the entity was not found but there are rawEntities to be processed, // If the entity was not found but there are rawEntities to be processed,
// try to process them and return the entity. // try to process them and return the entity.
// This might happen if an extension registered a new Catalog category. // This might happen if an extension registered a new Catalog category.
if (this.activeEntityId && !entity && this.rawEntities.length > 0) { if (this.activeEntityId.get() && !entity && this.rawEntities.length > 0) {
this.processRawEntities(); this.processRawEntities();
return this.getActiveEntityById(); return this.getActiveEntityById();
@ -70,8 +70,8 @@ export class CatalogEntityRegistry {
} }
set activeEntity(raw: CatalogEntity | string | undefined) { set activeEntity(raw: CatalogEntity | string | undefined) {
if (raw) { if (isDefined(raw)) {
const id = typeof raw === "string" const id = isString(raw)
? raw ? raw
: raw.getId(); : raw.getId();

View File

@ -9,7 +9,7 @@ import type { ImgHTMLAttributes, MouseEventHandler } from "react";
import React from "react"; import React from "react";
import randomColor from "randomcolor"; import randomColor from "randomcolor";
import type { StrictReactNode } from "@k8slens/utilities"; import type { StrictReactNode } from "@k8slens/utilities";
import { cssNames } from "@k8slens/utilities"; import { isTruthy, cssNames } from "@k8slens/utilities";
import { computeDefaultShortName } from "../../../common/catalog/helpers"; import { computeDefaultShortName } from "../../../common/catalog/helpers";
export interface AvatarProps { export interface AvatarProps {
@ -44,11 +44,13 @@ export const Avatar = ({
"data-testid": dataTestId, "data-testid": dataTestId,
}: AvatarProps) => ( }: AvatarProps) => (
<div <div
className={cssNames(styles.Avatar, { className={cssNames(
[styles.circle]: variant == "circle", styles.Avatar,
[styles.rounded]: variant == "rounded", variant == "circle" && styles.circle,
[styles.disabled]: disabled, variant == "rounded" && styles.rounded,
}, className)} disabled && styles.disabled,
className,
)}
style={{ style={{
width: `${size}px`, width: `${size}px`,
height: `${size}px`, height: `${size}px`,
@ -62,14 +64,18 @@ export const Avatar = ({
onClick={onClick} onClick={onClick}
data-testid={dataTestId} data-testid={dataTestId}
> >
{src {
? ( src
<img ? (
src={src} <img
{...imgProps} src={src}
alt={title} {...imgProps}
/> alt={title}
) />
: children || computeDefaultShortName(title)} )
: isTruthy(children)
? children
: computeDefaultShortName(title)
}
</div> </div>
); );

View File

@ -63,15 +63,17 @@ export const Badge = withTooltip(observer(({
return ( return (
<div <div
{...elemProps} {...elemProps}
className={cssNames(styles.badge, className, { className={cssNames(
[styles.small]: small, styles.badge,
[styles.flat]: flat, className,
[styles.clickable]: Boolean(elemProps.onClick) || isExpandable, small && styles.small,
[styles.interactive]: isExpandable, flat && styles.flat,
[styles.isExpanded]: isExpanded, (Boolean(elemProps.onClick) || isExpandable) && styles.clickable,
[styles.disabled]: disabled, isExpandable && styles.interactive,
[styles.scrollable]: scrollable, isExpanded && styles.isExpanded,
})} disabled && styles.disabled,
scrollable && styles.scrollable,
)}
onMouseUp={onMouseUp} onMouseUp={onMouseUp}
ref={elem} ref={elem}
> >

View File

@ -11,9 +11,7 @@ import { observer } from "mobx-react";
import { observable, action } from "mobx"; import { observable, action } from "mobx";
import type { CatalogCategory, CatalogEntityAddMenu } from "../../api/catalog-entity"; import type { CatalogCategory, CatalogEntityAddMenu } from "../../api/catalog-entity";
import { EventEmitter } from "events"; import { EventEmitter } from "events";
import type { CatalogCategoryRegistry } from "../../../common/catalog";
import { withInjectables } from "@ogre-tools/injectable-react"; import { withInjectables } from "@ogre-tools/injectable-react";
import catalogCategoryRegistryInjectable from "../../../common/catalog/category-registry.injectable";
import type { Navigate } from "../../navigation/navigate.injectable"; import type { Navigate } from "../../navigation/navigate.injectable";
import navigateInjectable from "../../navigation/navigate.injectable"; import navigateInjectable from "../../navigation/navigate.injectable";
@ -24,7 +22,6 @@ export interface CatalogAddButtonProps {
type CategoryId = string; type CategoryId = string;
interface Dependencies { interface Dependencies {
catalogCategoryRegistry: CatalogCategoryRegistry;
navigate: Navigate; navigate: Navigate;
} }
@ -43,19 +40,9 @@ class NonInjectedCatalogAddButton extends React.Component<CatalogAddButtonProps
} }
} }
get categories() {
return this.props.catalogCategoryRegistry.filteredItems;
}
updateMenuItems = action(() => { updateMenuItems = action(() => {
this.menuItems.clear(); this.menuItems.clear();
this.updateCategoryItems(this.props.category);
if (this.props.category) {
this.updateCategoryItems(this.props.category);
} else {
// Show menu items from all categories
this.categories.forEach(this.updateCategoryItems);
}
}); });
updateCategoryItems = action((category: CatalogCategory) => { updateCategoryItems = action((category: CatalogCategory) => {
@ -84,16 +71,13 @@ class NonInjectedCatalogAddButton extends React.Component<CatalogAddButtonProps
onButtonClick = () => { onButtonClick = () => {
const defaultAction = this.items.find(item => item.defaultAction)?.onClick; const defaultAction = this.items.find(item => item.defaultAction)?.onClick;
const clickAction = defaultAction || (this.items.length === 1 ? this.items[0].onClick : null); const clickAction = defaultAction ?? this.items[0]?.onClick;
void clickAction?.(); void clickAction?.();
}; };
get items() { get items() {
const { category } = this.props; return this.getCategoryFilteredItems(this.props.category);
return category ? this.getCategoryFilteredItems(category) :
this.categories.map(this.getCategoryFilteredItems).flat();
} }
render() { render() {
@ -136,7 +120,6 @@ class NonInjectedCatalogAddButton extends React.Component<CatalogAddButtonProps
export const CatalogAddButton = withInjectables<Dependencies, CatalogAddButtonProps>(NonInjectedCatalogAddButton, { export const CatalogAddButton = withInjectables<Dependencies, CatalogAddButtonProps>(NonInjectedCatalogAddButton, {
getProps: (di, props) => ({ getProps: (di, props) => ({
...props, ...props,
catalogCategoryRegistry: di.inject(catalogCategoryRegistryInjectable),
navigate: di.inject(navigateInjectable), navigate: di.inject(navigateInjectable),
}), }),
}); });

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 { isTruthy } from "@k8slens/utilities";
import React from "react"; import React from "react";
import type { CatalogCategory } from "../../../common/catalog/catalog-entity"; import type { CatalogCategory } from "../../../common/catalog/catalog-entity";
@ -18,7 +19,7 @@ export const CatalogCategoryLabel = ({ category }: CatalogCategoryLabelProps) =>
return ( return (
<div className="flex"> <div className="flex">
<div>{category.metadata.name}</div> <div>{category.metadata.name}</div>
{badge ? (<div className="flex items-center">{badge}</div>) : null} {isTruthy(badge) ? (<div className="flex items-center">{badge}</div>) : null}
</div> </div>
); );
}; };

View File

@ -43,10 +43,6 @@ class NonInjectedCatalogEntityDrawerMenu<T extends CatalogEntity> extends React.
} }
getMenuItems(entity: T): React.ReactChild[] { getMenuItems(entity: T): React.ReactChild[] {
if (!entity) {
return [];
}
const items = this.menuItems const items = this.menuItems
.map(this.props.normalizeMenuItem) .map(this.props.normalizeMenuItem)
.filter(hasDefiniteField("icon")) .filter(hasDefiniteField("icon"))

View File

@ -15,22 +15,26 @@ const kubernetesClusterDetailsItemInjectable = getInjectable({
kind: KubernetesCluster.kind, kind: KubernetesCluster.kind,
orderNumber: 40, orderNumber: 40,
components: { components: {
Details: ({ entity }) => ( Details: ({ entity }) => {
<> const kube = entity as KubernetesCluster;
<DrawerTitle>Kubernetes Information</DrawerTitle>
<div className="box grow EntityMetadata"> return (
<DrawerItem <>
name="Distribution" <DrawerTitle>Kubernetes Information</DrawerTitle>
data-testid={`kubernetes-distro-for-${entity.getId()}`} <div className="box grow EntityMetadata">
> <DrawerItem
{entity.metadata.distro || "unknown"} name="Distribution"
</DrawerItem> data-testid={`kubernetes-distro-for-${entity.getId()}`}
<DrawerItem name="Kubelet Version"> >
{entity.metadata.kubeVersion || "unknown"} {kube.metadata.distro || "unknown"}
</DrawerItem> </DrawerItem>
</div> <DrawerItem name="Kubelet Version">
</> {kube.metadata.kubeVersion || "unknown"}
), </DrawerItem>
</div>
</>
);
},
}, },
}), }),
injectionToken: catalogEntityDetailItemInjectionToken, injectionToken: catalogEntityDetailItemInjectionToken,

View File

@ -137,7 +137,7 @@ const NonInjectedBarChart = observer(({
typeof datasetIndex === "number" typeof datasetIndex === "number"
? { ? {
borderColor: "darkgray", borderColor: "darkgray",
backgroundColor: datasets[datasetIndex].borderColor as string, backgroundColor: datasets[datasetIndex]?.borderColor as string,
} }
: { : {
borderColor: "darkgray", borderColor: "darkgray",
@ -181,7 +181,7 @@ const tooltipCallbackWith = (precision: number): ChartTooltipCallback["label"] =
return ""; return "";
} }
const { label, data } = datasets[datasetIndex]; const { label, data } = datasets[datasetIndex] ?? {};
if (!label || !data) { if (!label || !data) {
return "<unknown>"; return "<unknown>";

View File

@ -36,7 +36,7 @@ const memoryLikeOptions: ChartOptions = {
return ""; return "";
} }
const { label = "", data } = datasets[datasetIndex]; const { label = "", data } = datasets[datasetIndex] ?? {};
if (!data) { if (!data) {
return label; return label;
@ -78,7 +78,7 @@ export const metricTabOptions: Record<MetricsTab, ChartOptions> = {
return ""; return "";
} }
const { label = "", data } = datasets[datasetIndex]; const { label = "", data } = datasets[datasetIndex] ?? {};
if (!data) { if (!data) {
return label; return label;
@ -106,7 +106,7 @@ export const metricTabOptions: Record<MetricsTab, ChartOptions> = {
return ""; return "";
} }
const { label = "", data } = datasets[datasetIndex]; const { label = "", data } = datasets[datasetIndex] ?? {};
if (!data) { if (!data) {
return label; return label;
@ -134,7 +134,7 @@ export const metricTabOptions: Record<MetricsTab, ChartOptions> = {
return ""; return "";
} }
const { label = "", data } = datasets[datasetIndex]; const { label = "", data } = datasets[datasetIndex] ?? {};
if (!data) { if (!data) {
return label; return label;

View File

@ -80,7 +80,7 @@ const NonInjectedPieChart = observer(({
return false; return false;
} }
const { data = [] } = datasets[datasetIndex]; const { data = [] } = datasets[datasetIndex] ?? {};
if (datasets.length === 1) return true; if (datasets.length === 1) return true;

View File

@ -6,7 +6,7 @@
import "./checkbox.scss"; import "./checkbox.scss";
import React from "react"; import React from "react";
import type { StrictReactNode } from "@k8slens/utilities"; import type { StrictReactNode } from "@k8slens/utilities";
import { cssNames, noop } from "@k8slens/utilities"; import { isTruthy, cssNames, noop } from "@k8slens/utilities";
export interface CheckboxProps { export interface CheckboxProps {
className?: string; className?: string;
@ -35,7 +35,7 @@ export function Checkbox({ label, inline, className, value, children, onChange =
onChange={event => onChange(event.target.checked, event)} onChange={event => onChange(event.target.checked, event)}
/> />
<i className="box flex align-center"/> <i className="box flex align-center"/>
{label ? <span className="label">{label}</span> : null} {isTruthy(label) ? <span className="label">{label}</span> : null}
{children} {children}
</label> </label>
); );

View File

@ -16,7 +16,7 @@ import { TopBar } from "../layout/top-bar/top-bar";
import type { IComputedValue } from "mobx"; import type { IComputedValue } from "mobx";
import currentRouteComponentInjectable from "../../routes/current-route-component.injectable"; import currentRouteComponentInjectable from "../../routes/current-route-component.injectable";
import welcomeRouteInjectable from "../../../common/front-end-routing/routes/welcome/welcome-route.injectable"; import welcomeRouteInjectable from "../../../common/front-end-routing/routes/welcome/welcome-route.injectable";
import { buildURL } from "@k8slens/utilities"; import { buildURL, isDefined } from "@k8slens/utilities";
import type { WatchForGeneralEntityNavigation } from "../../api/helpers/watch-for-general-entity-navigation.injectable"; import type { WatchForGeneralEntityNavigation } from "../../api/helpers/watch-for-general-entity-navigation.injectable";
import watchForGeneralEntityNavigationInjectable from "../../api/helpers/watch-for-general-entity-navigation.injectable"; import watchForGeneralEntityNavigationInjectable from "../../api/helpers/watch-for-general-entity-navigation.injectable";
import currentPathInjectable from "../../routes/current-path.injectable"; import currentPathInjectable from "../../routes/current-path.injectable";
@ -39,7 +39,7 @@ class NonInjectedClusterManager extends React.Component<Dependencies> {
renderMainComponent() { renderMainComponent() {
const Component = this.props.currentRouteComponent.get(); const Component = this.props.currentRouteComponent.get();
if (Component) { if (isDefined(Component)) {
return <Component />; return <Component />;
} }

View File

@ -59,7 +59,7 @@ class NonInjectedClusterPrometheusSetting extends React.Component<ClusterPrometh
} }
@computed get canEditPrometheusPath(): boolean { @computed get canEditPrometheusPath(): boolean {
if (!this.selectedOption || this.selectedOption === autoDetectPrometheus) { if (this.selectedOption === autoDetectPrometheus) {
return false; return false;
} }
@ -96,7 +96,7 @@ class NonInjectedClusterPrometheusSetting extends React.Component<ClusterPrometh
} }
parsePrometheusPath = () => { parsePrometheusPath = () => {
if (!this.selectedOption || !this.path) { if (!this.path) {
return undefined; return undefined;
} }
const parsed = this.path.split(/\/|:/, 3); const parsed = this.path.split(/\/|:/, 3);

View File

@ -14,7 +14,7 @@ import navigateToEntitySettingsInjectable from "../../../common/front-end-routin
import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable"; import hostedClusterInjectable from "../../cluster-frame-context/hosted-cluster.injectable";
export interface ClusterNoMetricsProps { export interface ClusterNoMetricsProps {
className: string; className?: string;
} }
interface Dependencies { interface Dependencies {

View File

@ -212,9 +212,7 @@ function getInternalCommands(dependencies: Dependencies): CommandRegistration[]
id: "entity.viewSettings", id: "entity.viewSettings",
title: ({ entity }) => `${entity.kind}/${entity.getName()}: View Settings`, title: ({ entity }) => `${entity.kind}/${entity.getName()}: View Settings`,
action: ({ entity }) => dependencies.navigateToEntitySettings(entity.getId()), action: ({ entity }) => dependencies.navigateToEntitySettings(entity.getId()),
isActive: ({ entity }) => ( isActive: ({ entity }) => dependencies.hasCatalogEntitySettingItems(entity),
entity && dependencies.hasCatalogEntitySettingItems(entity)
),
}, },
{ {
id: "cluster.openTerminal", id: "cluster.openTerminal",

View File

@ -54,7 +54,7 @@ class NonInjectedHorizontalPodAutoscalerDetails extends React.Component<KubeObje
} }
renderMetrics(hpa: HorizontalPodAutoscaler) { renderMetrics(hpa: HorizontalPodAutoscaler) {
const renderName = (metric: HorizontalPodAutoscalerMetricSpec) => { const renderName = (metric: HorizontalPodAutoscalerMetricSpec | undefined) => {
const metricName = getMetricName(metric); const metricName = getMetricName(metric);
switch (metric?.type) { switch (metric?.type) {
@ -95,7 +95,7 @@ class NonInjectedHorizontalPodAutoscalerDetails extends React.Component<KubeObje
this.props.getMetrics(hpa) this.props.getMetrics(hpa)
.map((metrics, index) => ( .map((metrics, index) => (
<TableRow key={index}> <TableRow key={index}>
<TableCell className="name">{renderName(hpa.getMetrics()[index])}</TableCell> <TableCell className="name">{renderName(hpa.getMetrics()?.[index])}</TableCell>
<TableCell className="metrics">{metrics}</TableCell> <TableCell className="metrics">{metrics}</TableCell>
</TableRow> </TableRow>
)) ))
@ -107,10 +107,6 @@ class NonInjectedHorizontalPodAutoscalerDetails extends React.Component<KubeObje
render() { render() {
const { object: hpa, apiManager, getDetailsUrl, logger } = this.props; const { object: hpa, apiManager, getDetailsUrl, logger } = this.props;
if (!hpa) {
return null;
}
if (!(hpa instanceof HorizontalPodAutoscaler)) { if (!(hpa instanceof HorizontalPodAutoscaler)) {
logger.error("[HpaDetails]: passed object that is not an instanceof HorizontalPodAutoscaler", hpa); logger.error("[HpaDetails]: passed object that is not an instanceof HorizontalPodAutoscaler", hpa);

View File

@ -59,10 +59,6 @@ class NonInjectedLimitRangeDetails extends React.Component<KubeObjectDetailsProp
render() { render() {
const { object: limitRange, logger } = this.props; const { object: limitRange, logger } = this.props;
if (!limitRange) {
return null;
}
if (!(limitRange instanceof LimitRange)) { if (!(limitRange instanceof LimitRange)) {
logger.error("[LimitRangeDetails]: passed object that is not an instanceof LimitRange", limitRange); logger.error("[LimitRangeDetails]: passed object that is not an instanceof LimitRange", limitRange);

View File

@ -44,9 +44,7 @@ class NonInjectedConfigMapDetails extends React.Component<KubeObjectDetailsProps
autorun(() => { autorun(() => {
const configMap = this.props.object as ConfigMap; const configMap = this.props.object as ConfigMap;
if (configMap) { this.data.replace(configMap.data); // refresh
this.data.replace(configMap.data); // refresh
}
}), }),
]); ]);
} }
@ -79,10 +77,6 @@ class NonInjectedConfigMapDetails extends React.Component<KubeObjectDetailsProps
render() { render() {
const { object: configMap, logger } = this.props; const { object: configMap, logger } = this.props;
if (!configMap) {
return null;
}
if (!(configMap instanceof ConfigMap)) { if (!(configMap instanceof ConfigMap)) {
logger.error("[ConfigMapDetails]: passed object that is not an instanceof ConfigMap", configMap); logger.error("[ConfigMapDetails]: passed object that is not an instanceof ConfigMap", configMap);

View File

@ -144,13 +144,11 @@ export const WebhookConfig: React.FC<WebhookProps> = ({ webhook }) => {
{" "} {" "}
{rule.operations.join(", ")} {rule.operations.join(", ")}
</div> </div>
{rule.resources && ( <div>
<div> Resources:
Resources: {" "}
{" "} {rule.resources?.join(", ")}
{rule.resources.join(", ")} </div>
</div>
)}
{rule.scope && ( {rule.scope && (
<div> <div>
Scope: Scope:

View File

@ -83,10 +83,6 @@ class NonInjectedResourceQuotaDetails extends React.Component<KubeObjectDetailsP
render() { render() {
const { object: quota } = this.props; const { object: quota } = this.props;
if (!quota) {
return null;
}
if (!(quota instanceof ResourceQuota)) { if (!(quota instanceof ResourceQuota)) {
this.props.logger.error("[ResourceQuotaDetails]: passed object that is not an instanceof ResourceQuota", quota); this.props.logger.error("[ResourceQuotaDetails]: passed object that is not an instanceof ResourceQuota", quota);

View File

@ -48,10 +48,8 @@ class NonInjectedSecretDetails extends React.Component<KubeObjectDetailsProps &
autorun(() => { autorun(() => {
const secret = this.props.object as Secret; const secret = this.props.object as Secret;
if (secret) { this.data = secret.data;
this.data = secret.data; this.revealSecret.clear();
this.revealSecret.clear();
}
}), }),
]); ]);
} }
@ -145,10 +143,6 @@ class NonInjectedSecretDetails extends React.Component<KubeObjectDetailsProps &
render() { render() {
const { object: secret, logger } = this.props; const { object: secret, logger } = this.props;
if (!secret) {
return null;
}
if (!(secret instanceof Secret)) { if (!(secret instanceof Secret)) {
logger.error("[SecretDetails]: passed object that is not an instanceof Secret", secret); logger.error("[SecretDetails]: passed object that is not an instanceof Secret", secret);

View File

@ -161,10 +161,6 @@ class NonInjectedVpaDetails extends React.Component<KubeObjectDetailsProps & Dep
render() { render() {
const { object: vpa, apiManager, getDetailsUrl, logger } = this.props; const { object: vpa, apiManager, getDetailsUrl, logger } = this.props;
if (!vpa) {
return null;
}
if (!(vpa instanceof VerticalPodAutoscaler)) { if (!(vpa instanceof VerticalPodAutoscaler)) {
logger.error("[VpaDetails]: passed object that is not an instanceof VerticalPodAutoscaler", vpa); logger.error("[VpaDetails]: passed object that is not an instanceof VerticalPodAutoscaler", vpa);
@ -186,10 +182,7 @@ class NonInjectedVpaDetails extends React.Component<KubeObjectDetailsProps & Dep
</DrawerItem> </DrawerItem>
<DrawerItem name="Recommender"> <DrawerItem name="Recommender">
{ {recommenders?.[0]?.name ?? "default"}
/* according to the spec there can be 0 or 1 recommenders, only */
recommenders?.length ? recommenders[0].name : "default"
}
</DrawerItem> </DrawerItem>
{vpa.status && this.renderStatus(vpa, vpa.status)} {vpa.status && this.renderStatus(vpa, vpa.status)}

View File

@ -28,10 +28,6 @@ class NonInjectedCustomResourceDefinitionDetails extends React.Component<KubeObj
render() { render() {
const { object: crd } = this.props; const { object: crd } = this.props;
if (!crd) {
return null;
}
if (!(crd instanceof CustomResourceDefinition)) { if (!(crd instanceof CustomResourceDefinition)) {
this.props.logger.error("[CustomResourceDefinitionDetails]: passed object that is not an instanceof CustomResourceDefinition", crd); this.props.logger.error("[CustomResourceDefinitionDetails]: passed object that is not an instanceof CustomResourceDefinition", crd);

View File

@ -110,9 +110,9 @@ class NonInjectedCustomResourceDetails extends React.Component<CustomResourceDet
} }
render() { render() {
const { props: { object, crd, logger }} = this; const { object, crd, logger } = this.props;
if (!object || !crd) { if (!crd) {
return null; return null;
} }

View File

@ -9,6 +9,7 @@ import type { CreateStorage } from "../../../utils/create-storage/create-storage
import type { TabId } from "../dock/store"; import type { TabId } from "../dock/store";
import autoBind from "auto-bind"; import autoBind from "auto-bind";
import { toJS } from "../../../../common/utils"; import { toJS } from "../../../../common/utils";
import { isTruthy } from "@k8slens/utilities";
export interface DockTabStoreOptions { export interface DockTabStoreOptions {
autoInit?: boolean; // load data from storage when `storageKey` is provided and bind events, default: true autoInit?: boolean; // load data from storage when `storageKey` is provided and bind events, default: true
@ -59,7 +60,7 @@ export class DockTabStore<T> {
findTabIdFromData(inspector: (val: T) => unknown): TabId | undefined { findTabIdFromData(inspector: (val: T) => unknown): TabId | undefined {
for (const [tabId, data] of this.data) { for (const [tabId, data] of this.data) {
if (inspector(data)) { if (isTruthy(inspector(data))) {
return tabId; return tabId;
} }
} }

View File

@ -78,10 +78,6 @@ class NonInjectedDockTab extends React.Component<DockTabProps & Dependencies> {
render() { render() {
const { className, moreActions, dockStore, active, isMac, ...tabProps } = this.props; const { className, moreActions, dockStore, active, isMac, ...tabProps } = this.props;
if (!tabProps.value) {
return;
}
void dockStore; void dockStore;
void active; void active;
void isMac; void isMac;
@ -94,9 +90,11 @@ class NonInjectedDockTab extends React.Component<DockTabProps & Dependencies> {
<Tab <Tab
{...tabProps} {...tabProps}
id={`tab-${id}`} id={`tab-${id}`}
className={cssNames(styles.DockTab, className, { className={cssNames(
[styles.pinned]: pinned, styles.DockTab,
})} className,
pinned && styles.pinned,
)}
onContextMenu={() => this.menuVisible.set(true)} onContextMenu={() => this.menuVisible.set(true)}
label={( label={(
<div className="flex align-center" onAuxClick={isMiddleClick(close)}> <div className="flex align-center" onAuxClick={isMiddleClick(close)}>

View File

@ -93,11 +93,11 @@ class NonInjectedDock extends React.Component<DockProps & Dependencies> {
const currentIndex = tabs.indexOf(selectedTab); const currentIndex = tabs.indexOf(selectedTab);
const nextIndex = currentIndex + direction; const nextIndex = currentIndex + direction;
// check if moving to the next or previous tab is possible.
if (nextIndex >= tabs.length || nextIndex < 0) return;
const nextElement = tabs[nextIndex]; const nextElement = tabs[nextIndex];
// check if moving to the next or previous tab is possible.
if (!nextElement) return;
this.onChangeTab(nextElement); this.onChangeTab(nextElement);
}; };

View File

@ -9,7 +9,7 @@ import React, { Component } from "react";
import { computed, observable, reaction, makeObservable } from "mobx"; import { computed, observable, reaction, makeObservable } from "mobx";
import { disposeOnUnmount, observer } from "mobx-react"; import { disposeOnUnmount, observer } from "mobx-react";
import type { StrictReactNode } from "@k8slens/utilities"; import type { StrictReactNode } from "@k8slens/utilities";
import { cssNames } from "@k8slens/utilities"; import { cssNames, isTruthy } from "@k8slens/utilities";
import { Button } from "@k8slens/button"; import { Button } from "@k8slens/button";
import { Icon } from "@k8slens/icon"; import { Icon } from "@k8slens/icon";
import { Spinner } from "../spinner"; import { Spinner } from "../spinner";
@ -90,7 +90,7 @@ class NonInjectedInfoPanel extends Component<InfoPanelProps & Dependencies> {
try { try {
const result = await this.props.submit?.(); const result = await this.props.submit?.();
if (showNotifications && result) { if (showNotifications && isTruthy(result)) {
this.props.showSuccessNotification(result); this.props.showSuccessNotification(result);
} }
@ -109,7 +109,7 @@ class NonInjectedInfoPanel extends Component<InfoPanelProps & Dependencies> {
submitAndClose = async () => { submitAndClose = async () => {
const result = await this.submit(); const result = await this.submit();
if (result) { if (isTruthy(result)) {
this.close(); this.close();
} }
}; };

View File

@ -32,7 +32,7 @@ export const LogSearch = observer((props: PodLogSearchProps) => {
const logs = tabData.showTimestamps const logs = tabData.showTimestamps
? model.logs.get() ? model.logs.get()
: model.logsWithoutTimestamps.get(); : model.logsWithoutTimestamps.get();
const jumpDisabled = !searchQuery || !occurrences.length; const jumpDisabled = !searchQuery.get() || !occurrences.length;
const setSearch = (query: string) => { const setSearch = (query: string) => {
searchStore.onSearch(logs, query); searchStore.onSearch(logs, query);

View File

@ -4,7 +4,6 @@
*/ */
import { getInjectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable";
import { when } from "mobx"; import { when } from "mobx";
import { loggerInjectionToken } from "@k8slens/logger";
import { TerminalChannels } from "../../../../common/terminal/channels"; import { TerminalChannels } from "../../../../common/terminal/channels";
import { waitUntilDefined, noop } from "@k8slens/utilities"; import { waitUntilDefined, noop } from "@k8slens/utilities";
import showSuccessNotificationInjectable from "../../notifications/show-success-notification.injectable"; import showSuccessNotificationInjectable from "../../notifications/show-success-notification.injectable";
@ -40,7 +39,6 @@ const sendCommandInjectable = getInjectable({
const selectTab = di.inject(selectDockTabInjectable); const selectTab = di.inject(selectDockTabInjectable);
const getTerminalApi = di.inject(getTerminalApiInjectable); const getTerminalApi = di.inject(getTerminalApiInjectable);
const showSuccessNotification = di.inject(showSuccessNotificationInjectable); const showSuccessNotification = di.inject(showSuccessNotificationInjectable);
const logger = di.inject(loggerInjectionToken);
return async (command: string, options: SendCommandOptions = {}): Promise<void> => { return async (command: string, options: SendCommandOptions = {}): Promise<void> => {
let tabId: string | undefined = options.tabId; let tabId: string | undefined = options.tabId;
@ -70,21 +68,14 @@ const sendCommandInjectable = getInjectable({
await shellIsReady.catch(noop); await shellIsReady.catch(noop);
clearTimeout(notifyVeryLong); clearTimeout(notifyVeryLong);
if (terminalApi) { if (options.enter) {
if (options.enter) { command += "\r";
command += "\r";
}
terminalApi.sendMessage({
type: TerminalChannels.STDIN,
data: command,
});
} else {
logger.warn(
"The selected tab is does not have a connection. Cannot send command.",
{ tabId, command },
);
} }
terminalApi.sendMessage({
type: TerminalChannels.STDIN,
data: command,
});
}; };
}, },
}); });

View File

@ -44,13 +44,11 @@ export class Terminal {
protected readonly api: TerminalApi; protected readonly api: TerminalApi;
private get elem() { private get elem() {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion return this.xterm.element;
return this.xterm.element!;
} }
private get viewport() { private get viewport() {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion return this.elem?.querySelector(".xterm-viewport");
return this.elem.querySelector(".xterm-viewport")!;
} }
attachTo(parentElem: HTMLElement) { attachTo(parentElem: HTMLElement) {
@ -99,8 +97,10 @@ export class Terminal {
const onDataHandler = this.xterm.onData(this.onData); const onDataHandler = this.xterm.onData(this.onData);
const clearOnce = once(this.onClear); const clearOnce = once(this.onClear);
this.viewport.addEventListener("scroll", this.onScroll); const { viewport, elem } = this;
this.elem.addEventListener("contextmenu", this.onContextMenu);
viewport?.addEventListener("scroll", this.onScroll);
elem?.addEventListener("contextmenu", this.onContextMenu);
this.api.once("ready", clearOnce); this.api.once("ready", clearOnce);
this.api.once("connected", clearOnce); this.api.once("connected", clearOnce);
this.api.on("data", this.onApiData); this.api.on("data", this.onApiData);
@ -128,7 +128,8 @@ export class Terminal {
() => this.fitAddon.dispose(), () => this.fitAddon.dispose(),
() => this.api.removeAllListeners(), () => this.api.removeAllListeners(),
() => window.removeEventListener("resize", this.onResize), () => window.removeEventListener("resize", this.onResize),
() => this.elem.removeEventListener("contextmenu", this.onContextMenu), () => elem?.removeEventListener("contextmenu", this.onContextMenu),
() => viewport?.removeEventListener("scroll", this.onScroll),
this.xterm.onResize(({ cols, rows }) => { this.xterm.onResize(({ cols, rows }) => {
this.api.sendTerminalSize(cols, rows); this.api.sendTerminalSize(cols, rows);
}), }),
@ -161,7 +162,7 @@ export class Terminal {
}; };
onScroll = () => { onScroll = () => {
this.scrollPos = this.viewport.scrollTop; this.scrollPos = this.viewport?.scrollTop ?? 0;
}; };
onClear = () => { onClear = () => {
@ -176,7 +177,12 @@ export class Terminal {
onActivate = () => { onActivate = () => {
this.fit(); this.fit();
setTimeout(() => this.focus(), 250); // delay used to prevent focus on active tab setTimeout(() => this.focus(), 250); // delay used to prevent focus on active tab
this.viewport.scrollTop = this.scrollPos; // restore last scroll position
const { viewport } = this;
if (viewport) {
viewport.scrollTop = this.scrollPos; // restore last scroll position
}
}; };
onContextMenu = () => { onContextMenu = () => {

View File

@ -10,7 +10,7 @@ import { Badge } from "../badge";
import { KubeObject } from "@k8slens/kube-object"; import { KubeObject } from "@k8slens/kube-object";
export interface DrawerItemLabelsProps extends DrawerItemProps { export interface DrawerItemLabelsProps extends DrawerItemProps {
labels: string[] | Partial<Record<string, string>>; labels?: string[] | Partial<Record<string, string>>;
} }
export function DrawerItemLabels(props: DrawerItemLabelsProps) { export function DrawerItemLabels(props: DrawerItemLabelsProps) {

View File

@ -28,10 +28,12 @@ export interface DrawerTitleProps {
export function DrawerTitle({ className, children, size = "title" }: DrawerTitleProps) { export function DrawerTitle({ className, children, size = "title" }: DrawerTitleProps) {
return ( return (
<div <div
className={cssNames(styles.DrawerTitle, className, { className={cssNames(
[styles.title]: size === "title", styles.DrawerTitle,
[styles["sub-title"]]: size === "sub-title", className,
})} size === "title" && styles.title,
size === "sub-title" && styles["sub-title"],
)}
> >
{children} {children}
</div> </div>

View File

@ -36,10 +36,6 @@ const NonInjectedEventDetails = observer(({
className, className,
logger, logger,
}: Dependencies & KubeObjectDetailsProps) => { }: Dependencies & KubeObjectDetailsProps) => {
if (!event) {
return null;
}
if (!(event instanceof KubeEvent)) { if (!(event instanceof KubeEvent)) {
logger.error("[EventDetails]: passed object that is not an instanceof KubeEvent", event); logger.error("[EventDetails]: passed object that is not an instanceof KubeEvent", event);

View File

@ -43,10 +43,6 @@ class NonInjectedKubeEventDetails extends React.Component<KubeEventDetailsProps
render() { render() {
const { object, eventStore } = this.props; const { object, eventStore } = this.props;
if (!object) {
return null;
}
if (!(object instanceof KubeObject)) { if (!(object instanceof KubeObject)) {
this.props.logger.error("[KubeEventDetails]: passed object that is not an instanceof KubeObject", object); this.props.logger.error("[KubeEventDetails]: passed object that is not an instanceof KubeObject", object);
@ -64,7 +60,7 @@ class NonInjectedKubeEventDetails extends React.Component<KubeEventDetailsProps
<div className={styles.KubeEventDetails}> <div className={styles.KubeEventDetails}>
{events.map(event => ( {events.map(event => (
<div className={styles.event} key={event.getId()}> <div className={styles.event} key={event.getId()}>
<div className={cssNames(styles.title, { [styles.warning]: event.isWarning() })}> <div className={cssNames(styles.title, event.isWarning() && styles.warning)}>
{event.message} {event.message}
</div> </div>
<DrawerItem name="Source"> <DrawerItem name="Source">

View File

@ -2,7 +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 { isObject } from "@k8slens/utilities"; import { isObject, isTruthy } from "@k8slens/utilities";
import React from "react"; import React from "react";
import { SemVer } from "semver"; import { SemVer } from "semver";
import URLParse from "url-parse"; import URLParse from "url-parse";
@ -76,8 +76,8 @@ const attemptInstallByInfoInjectable = getInjectable({
return disposer(); return disposer();
} }
if (result.value.error || !isObject(result.value.versions)) { if (isTruthy(result.value.error) || !isObject(result.value.versions)) {
const message = result.value.error ? `: ${String(result.value.error)}` : ""; const message = isTruthy(result.value.error) ? `: ${String(result.value.error)}` : "";
showErrorNotification(`Failed to get registry information for extension${message}`); showErrorNotification(`Failed to get registry information for extension${message}`);

View File

@ -3,10 +3,10 @@
* 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 { hasTypedProperty, isDefined } from "@k8slens/utilities"; import { hasTypedProperty, isDefined, isObject } from "@k8slens/utilities";
export function getMessageFromError(error: unknown): string { export function getMessageFromError(error: unknown): string {
if (!error || typeof error !== "object") { if (!isObject(error)) {
return "an error has occurred"; return "an error has occurred";
} }

View File

@ -51,7 +51,7 @@ const NonInjectedInstalledExtensions = observer(({
enableExtension, enableExtension,
disableExtension, disableExtension,
}: Dependencies) => { }: Dependencies) => {
if (!extensionDiscovery.isLoaded) { if (!extensionDiscovery.isLoaded.get()) {
return <div><Spinner center /></div>; return <div><Spinner center /></div>;
} }
@ -85,8 +85,8 @@ const NonInjectedInstalledExtensions = observer(({
accessor: "extension", accessor: "extension",
width: 200, width: 200,
sortType: (rowA, rowB) => { // Custom sorting for extension name sortType: (rowA, rowB) => { // Custom sorting for extension name
const nameA = extensions[rowA.index].manifest.name; const nameA = extensions[rowA.index]?.manifest.name ?? "";
const nameB = extensions[rowB.index].manifest.name; const nameB = extensions[rowB.index]?.manifest.name ?? "";
if (nameA > nameB) return -1; if (nameA > nameB) return -1;
if (nameB > nameA) return 1; if (nameB > nameA) return 1;
@ -126,7 +126,12 @@ const NonInjectedInstalledExtensions = observer(({
), ),
version, version,
status: ( status: (
<div className={cssNames({ [styles.enabled]: isEnabled, [styles.invalid]: !isCompatible })}> <div
className={cssNames(
isEnabled && styles.enabled,
!isCompatible && styles.invalid,
)}
>
{getStatus(extension)} {getStatus(extension)}
</div> </div>
), ),

View File

@ -25,7 +25,6 @@ import versionsOfSelectedHelmChartInjectable from "./details/versions-of-selecte
import type { HelmChartDetailsVersionSelection } from "./details/versions/helm-chart-details-version-selection.injectable"; import type { HelmChartDetailsVersionSelection } from "./details/versions/helm-chart-details-version-selection.injectable";
import helmChartDetailsVersionSelectionInjectable from "./details/versions/helm-chart-details-version-selection.injectable"; import helmChartDetailsVersionSelectionInjectable from "./details/versions/helm-chart-details-version-selection.injectable";
import assert from "assert"; import assert from "assert";
import autoBindReact from "auto-bind/react";
export interface HelmChartDetailsProps { export interface HelmChartDetailsProps {
hideDetails(): void; hideDetails(): void;
@ -47,15 +46,6 @@ interface Dependencies {
@observer @observer
class NonInjectedHelmChartDetails extends Component<HelmChartDetailsProps & Dependencies> { class NonInjectedHelmChartDetails extends Component<HelmChartDetailsProps & Dependencies> {
constructor(props: HelmChartDetailsProps & Dependencies) {
super(props);
autoBindReact(this);
}
get chart() {
return this.props.chart;
}
install = () => { install = () => {
const chart = this.props.versionSelection.value.get(); const chart = this.props.versionSelection.value.get();
@ -144,13 +134,13 @@ class NonInjectedHelmChartDetails extends Component<HelmChartDetailsProps & Depe
const readmeIsLoading = this.props.readme.pending.get(); const readmeIsLoading = this.props.readme.pending.get();
const versionsAreLoading = this.props.versions.pending.get(); const versionsAreLoading = this.props.versions.pending.get();
if (!this.chart || versionsAreLoading) { if (versionsAreLoading) {
return <Spinner center data-testid="spinner-for-chart-details" />; return <Spinner center data-testid="spinner-for-chart-details" />;
} }
return ( return (
<div className="box grow"> <div className="box grow">
{this.renderIntroduction(this.chart)} {this.renderIntroduction(this.props.chart)}
{readmeIsLoading ? ( {readmeIsLoading ? (
<Spinner center data-testid="spinner-for-chart-readme" /> <Spinner center data-testid="spinner-for-chart-readme" />
@ -162,12 +152,13 @@ class NonInjectedHelmChartDetails extends Component<HelmChartDetailsProps & Depe
} }
render() { render() {
return ( return (
<Drawer <Drawer
className="HelmChartDetails" className="HelmChartDetails"
usePortal={true} usePortal
open={!!this.chart} open
title={this.chart ? `Chart: ${this.chart.getFullName()}` : ""} title={`Chart: ${this.props.chart.getFullName()}`}
onClose={this.props.hideDetails} onClose={this.props.hideDetails}
> >
{this.renderContent()} {this.renderContent()}

View File

@ -31,7 +31,7 @@ export const HelmChartIcon = ({
return ( return (
<div <div
className={cssNames(styles.chartIcon, className, { [styles.imageNotLoaded]: !isImageLoaded })} className={cssNames(styles.chartIcon, className, !isImageLoaded && styles.imageNotLoaded)}
data-testid="image-container" data-testid="image-container"
style={{ style={{
backgroundImage: isImageLoaded ? backgroundImage : `url(${placeholderImageUrl})`, backgroundImage: isImageLoaded ? backgroundImage : `url(${placeholderImageUrl})`,

View File

@ -46,12 +46,9 @@ class NonInjectedHelmReleaseMenu extends React.Component<HelmReleaseMenuProps &
renderContent() { renderContent() {
const { release, toolbar } = this.props; const { release, toolbar } = this.props;
if (!release) return null;
const hasRollback = release && release.getRevision() > 1;
return ( return (
<> <>
{hasRollback && ( {(release.getRevision() > 1) && (
<MenuItem onClick={this.rollback}> <MenuItem onClick={this.rollback}>
<Icon <Icon
material="history" material="history"

View File

@ -69,10 +69,10 @@ class NonInjectedHotbarEntityIcon extends React.Component<HotbarEntityIconProps
return ( return (
<div <div
className={cssNames(styles.led, { className={cssNames(
// TODO: make it more generic styles.led,
[styles.online]: this.props.entity.status.phase === LensKubernetesClusterStatus.CONNECTED, (this.props.entity.status.phase === LensKubernetesClusterStatus.CONNECTED) && styles.online,
})} )}
/> />
); );
} }
@ -107,7 +107,6 @@ class NonInjectedHotbarEntityIcon extends React.Component<HotbarEntityIconProps
className={className} className={className}
active={this.isActive(entity)} active={this.isActive(entity)}
onMenuOpen={() => this.onMenuOpen()} onMenuOpen={() => this.onMenuOpen()}
disabled={!entity}
menuItems={this.menuItems} menuItems={this.menuItems}
tooltip={( tooltip={(
entity.metadata.source entity.metadata.source

View File

@ -50,7 +50,7 @@ const NonInjectedHotbarIcon = observer(({
}; };
return ( return (
<div className={cssNames(styles.HotbarIcon, className, { [styles.contextMenuAvailable]: menuItems.length > 0 })}> <div className={cssNames(styles.HotbarIcon, className, (menuItems.length > 0 && styles.contextMenuAvailable))}>
{tooltip && ( {tooltip && (
<Tooltip targetId={id}> <Tooltip targetId={id}>
{tooltip} {tooltip}
@ -61,7 +61,11 @@ const NonInjectedHotbarIcon = observer(({
id={id} id={id}
title={title} title={title}
colorHash={source ? `${title}-${source}` : title} colorHash={source ? `${title}-${source}` : title}
className={cssNames(styles.avatar, { [styles.active]: active, [styles.hasImage]: !!src })} className={cssNames(
styles.avatar,
active && styles.active,
!!src && styles.hasImag,
)}
disabled={disabled} disabled={disabled}
size={size} size={size}
src={src} src={src}

View File

@ -8,7 +8,7 @@ import "./input.scss";
import type { DOMAttributes, InputHTMLAttributes, TextareaHTMLAttributes } from "react"; import type { DOMAttributes, InputHTMLAttributes, TextareaHTMLAttributes } from "react";
import React from "react"; import React from "react";
import type { StrictReactNode, SingleOrMany } from "@k8slens/utilities"; import type { StrictReactNode, SingleOrMany } from "@k8slens/utilities";
import { isReactNode, isString, isFunction, debouncePromise, isPromiseSettledFulfilled, cssNames } from "@k8slens/utilities"; import { isReactNode, isFalsy, isTruthy, isString, isFunction, debouncePromise, isPromiseSettledFulfilled, cssNames } from "@k8slens/utilities";
import { Icon } from "@k8slens/icon"; import { Icon } from "@k8slens/icon";
import type { TooltipProps } from "@k8slens/tooltip"; import type { TooltipProps } from "@k8slens/tooltip";
import { Tooltip } from "@k8slens/tooltip"; import { Tooltip } from "@k8slens/tooltip";
@ -195,7 +195,13 @@ export class Input extends React.Component<InputProps, State> {
return undefined; return undefined;
} catch (error) { } catch (error) {
return this.getValidatorError(value, validator) || (error instanceof Error ? error.message : String(error)); const validationError = this.getValidatorError(value, validator);
return isTruthy(validationError)
? validationError
: error instanceof Error
? error.message
: String(error);
} }
})()); })());
} }
@ -327,7 +333,7 @@ export class Input extends React.Component<InputProps, State> {
get showMaxLenIndicator() { get showMaxLenIndicator() {
const { maxLength, multiLine } = this.props; const { maxLength, multiLine } = this.props;
return maxLength && multiLine; return Boolean(maxLength && multiLine);
} }
get isUncontrolled() { get isUncontrolled() {
@ -440,12 +446,12 @@ export class Input extends React.Component<InputProps, State> {
{errors.map((error, i) => <p key={i}>{error}</p>)} {errors.map((error, i) => <p key={i}>{error}</p>)}
</div> </div>
); );
const componentId = id || showErrorsAsTooltip const componentId = id || isTruthy(showErrorsAsTooltip)
? `input_tooltip_id_${uuid.v4()}` ? `input_tooltip_id_${uuid.v4()}`
: undefined; : undefined;
let tooltipError: StrictReactNode; let tooltipError: StrictReactNode;
if (showErrorsAsTooltip && showErrors) { if (isTruthy(showErrorsAsTooltip) && showErrors) {
const tooltipProps = typeof showErrorsAsTooltip === "object" ? showErrorsAsTooltip : {}; const tooltipProps = typeof showErrorsAsTooltip === "object" ? showErrorsAsTooltip : {};
tooltipProps.className = cssNames("InputTooltipError", tooltipProps.className); tooltipProps.className = cssNames("InputTooltipError", tooltipProps.className);
@ -473,7 +479,7 @@ export class Input extends React.Component<InputProps, State> {
{contentRight} {contentRight}
</label> </label>
<div className="input-info flex gaps"> <div className="input-info flex gaps">
{!showErrorsAsTooltip && showErrors && errorsInfo} {(isFalsy(showErrorsAsTooltip) && showErrors) && errorsInfo}
{this.showMaxLenIndicator && ( {this.showMaxLenIndicator && (
<div className="maxLengthIndicator box right"> <div className="maxLengthIndicator box right">
{this.getValue().length} {this.getValue().length}

View File

@ -59,6 +59,10 @@ export function isAsyncValidator(validator: InputValidator): validator is AsyncI
return typeof validator.debounce === "number"; return typeof validator.debounce === "number";
} }
export function isSyncValidator(validator: InputValidator): validator is SyncInputValidator {
return typeof validator.debounce !== "number";
}
/** /**
* A helper function to create an {@link AsyncInputValidator} * A helper function to create an {@link AsyncInputValidator}
*/ */
@ -106,7 +110,11 @@ export function unionInputValidatorsAsync(
debounce: longestDebounce, debounce: longestDebounce,
validate: async (value, props) => { validate: async (value, props) => {
for (const validator of validators) { for (const validator of validators) {
if (isAsyncValidator(validator)) { if (isSyncValidator(validator)) {
if (validator.validate(value, props)) {
return;
}
} else {
try { try {
await validator.validate(value, props); await validator.validate(value, props);
@ -114,10 +122,6 @@ export function unionInputValidatorsAsync(
} catch { } catch {
// Do nothing // Do nothing
} }
} else {
if (validator.validate(value, props)) {
return;
}
} }
} }

View File

@ -7,7 +7,7 @@ import "./search-input.scss";
import React, { createRef } from "react"; import React, { createRef } from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import { addWindowEventListener, cssNames, disposer } from "@k8slens/utilities"; import { addWindowEventListener, cssNames, disposer, isTruthy } from "@k8slens/utilities";
import { Icon } from "@k8slens/icon"; import { Icon } from "@k8slens/icon";
import type { InputProps } from "./input"; import type { InputProps } from "./input";
import { Input } from "./input"; import { Input } from "./input";
@ -86,7 +86,7 @@ class NonInjectedSearchInput extends React.Component<SearchInputProps & Dependen
void bindGlobalFocusHotkey; void bindGlobalFocusHotkey;
void isMac; void isMac;
if (showClearIcon && value) { if (showClearIcon && isTruthy(value)) {
rightIcon = ( rightIcon = (
<Icon <Icon
small small

View File

@ -210,7 +210,7 @@ class NonInjectedItemListLayoutContent<
const onConfirm = isFunction(removeItems) const onConfirm = isFunction(removeItems)
? () => removeItems.apply(store, [selectedItems]) ? () => removeItems.apply(store, [selectedItems])
: isFunction(removeSelectedItems) : isFunction(removeSelectedItems)
? removeSelectedItems.bind(store) ? () => removeSelectedItems.apply(store)
: noop; : noop;
openConfirmDialog({ openConfirmDialog({
@ -328,7 +328,7 @@ class NonInjectedItemListLayoutContent<
{() => ( {() => (
<AddRemoveButtons <AddRemoveButtons
onRemove={ onRemove={
(store.removeItems || store.removeSelectedItems) && selectedItems.length > 0 (Boolean(store.removeItems) || Boolean(store.removeSelectedItems)) && selectedItems.length > 0
? () => this.removeItemsDialog(selectedItems) ? () => this.removeItemsDialog(selectedItems)
: undefined : undefined
} }

View File

@ -8,7 +8,7 @@ import "./item-list-layout.scss";
import React from "react"; import React from "react";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import type { IClassName, StrictReactNode } from "@k8slens/utilities"; import type { IClassName, StrictReactNode } from "@k8slens/utilities";
import { isFunction, cssNames, isDefined } from "@k8slens/utilities"; import { isTruthy, isFunction, cssNames, isDefined } from "@k8slens/utilities";
import type { ItemObject } from "@k8slens/list-layout"; import type { ItemObject } from "@k8slens/list-layout";
import type { Filter } from "./page-filters/store"; import type { Filter } from "./page-filters/store";
import type { HeaderCustomizer, HeaderPlaceholders, ItemListStore, SearchFilter } from "./list-layout"; import type { HeaderCustomizer, HeaderPlaceholders, ItemListStore, SearchFilter } from "./list-layout";
@ -91,7 +91,7 @@ export class ItemListLayoutHeader<I extends ItemObject, PreLoadStores extends bo
<div className={cssNames("header flex gaps align-center", headerClassName)}> <div className={cssNames("header flex gaps align-center", headerClassName)}>
{title} {title}
{ {
info && ( isTruthy(info) && (
<div className="info-panel box grow"> <div className="info-panel box grow">
{info} {info}
</div> </div>

View File

@ -346,7 +346,7 @@ export const ItemListLayout = withInjectables<Dependencies, ItemListLayoutProps<
}) as <I extends ItemObject, PreLoadStores extends boolean = true>(props: ItemListLayoutProps<I, PreLoadStores>) => React.ReactElement; }) as <I extends ItemObject, PreLoadStores extends boolean = true>(props: ItemListLayoutProps<I, PreLoadStores>) => React.ReactElement;
function applyFilters<I extends ItemObject>(filters: ItemsFilter<I>[], items: I[]): I[] { function applyFilters<I extends ItemObject>(filters: ItemsFilter<I>[], items: I[]): I[] {
if (!filters || !filters.length) { if (!filters.length) {
return items; return items;
} }

View File

@ -76,7 +76,7 @@ const matchesApiFor = (api: SubscribableStore["api"]) => (column: GeneralKubeObj
const getLoadErrorMessage = (error: unknown): string => { const getLoadErrorMessage = (error: unknown): string => {
if (error instanceof Error) { if (error instanceof Error) {
if (error.cause) { if (Boolean(error.cause)) {
return `${error.message}: ${getLoadErrorMessage(error.cause)}`; return `${error.message}: ${getLoadErrorMessage(error.cause)}`;
} }
@ -166,7 +166,19 @@ class NonInjectedKubeObjectListLayout<
sortingCallbacks = {}, sortingCallbacks = {},
...layoutProps ...layoutProps
} = this.props; } = this.props;
const resourceName = this.props.resourceName || ResourceNames[ResourceKindMap[store.api.kind]] || store.api.kind; const resourceName = (() => {
if (this.props.resourceName) {
return this.props.resourceName;
}
const kubeResource = ResourceKindMap[store.api.kind];
if (kubeResource) {
return ResourceNames[kubeResource];
}
return store.api.kind;
})();
const targetColumns = [ const targetColumns = [
...(columns ?? []), ...(columns ?? []),
...generalColumns.filter(matchesApiFor(store.api)), ...generalColumns.filter(matchesApiFor(store.api)),

View File

@ -51,7 +51,7 @@ class NonInjectedKubeObjectMenu<Kube extends KubeObject> extends React.Component
private menuItems = observable.array<KubeObjectContextMenuItem>(); private menuItems = observable.array<KubeObjectContextMenuItem>();
componentDidUpdate(prevProps: Readonly<KubeObjectMenuProps<Kube> & Dependencies>): void { componentDidUpdate(prevProps: Readonly<KubeObjectMenuProps<Kube> & Dependencies>): void {
if (prevProps.object !== this.props.object && this.props.object) { if (prevProps.object !== this.props.object) {
this.emitOnContextMenuOpen(this.props.object); this.emitOnContextMenuOpen(this.props.object);
} }
} }
@ -199,11 +199,11 @@ class NonInjectedKubeObjectMenu<Kube extends KubeObject> extends React.Component
id={`menu-actions-for-kube-object-menu-for-${object?.getId()}`} id={`menu-actions-for-kube-object-menu-for-${object?.getId()}`}
data-testid={`menu-actions-for-kube-object-menu-for-${object?.getId()}`} data-testid={`menu-actions-for-kube-object-menu-for-${object?.getId()}`}
className={cssNames("KubeObjectMenu", className)} className={cssNames("KubeObjectMenu", className)}
onOpen={object ? () => this.emitOnContextMenuOpen(object) : undefined} onOpen={() => this.emitOnContextMenuOpen(object)}
{...menuProps} {...menuProps}
> >
{this.renderMenuItems()} {this.renderMenuItems()}
{object && this.renderContextMenuItems(object)} {this.renderContextMenuItems(object)}
</MenuActions> </MenuActions>
); );
} }

View File

@ -48,10 +48,6 @@ const NonInjectedKubeObjectMeta = observer((props : Dependencies & KubeObjectMet
namespaceApi, namespaceApi,
} = props; } = props;
if (!object) {
return null;
}
if (!(object instanceof KubeObject)) { if (!(object instanceof KubeObject)) {
logger.error("[KubeObjectMeta]: passed object that is not an instanceof KubeObject", object); logger.error("[KubeObjectMeta]: passed object that is not an instanceof KubeObject", object);

View File

@ -8,7 +8,7 @@ import React from "react";
import type { IObservableValue } from "mobx"; import type { IObservableValue } from "mobx";
import { observer } from "mobx-react"; import { observer } from "mobx-react";
import type { StrictReactNode } from "@k8slens/utilities"; import type { StrictReactNode } from "@k8slens/utilities";
import { cssNames } from "@k8slens/utilities"; import { cssNames, isTruthy } from "@k8slens/utilities";
import { Button } from "@k8slens/button"; import { Button } from "@k8slens/button";
import type { DialogProps } from "../dialog"; import type { DialogProps } from "../dialog";
import { Dialog } from "../dialog"; import { Dialog } from "../dialog";
@ -54,7 +54,7 @@ class NonInjectedKubeConfigDialog extends React.Component<KubeConfigDialogProps
}; };
renderContents = (data: KubeconfigDialogData) => ( renderContents = (data: KubeconfigDialogData) => (
<Wizard header={<h5>{ data.title || "Kubeconfig File" }</h5>}> <Wizard header={<h5>{isTruthy(data.title) ? data.title : "Kubeconfig File" }</h5>}>
<WizardStep <WizardStep
customButtons={ ( customButtons={ (
<div className="actions flex gaps"> <div className="actions flex gaps">

Some files were not shown because too many files have changed in this diff Show More