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",
"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==",
"dev": true,
"dependencies": {
"postcss": "^8.0.0"
}
@ -7548,7 +7547,6 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@types/postcss-modules-scope/-/postcss-modules-scope-3.0.1.tgz",
"integrity": "sha512-LNkp3c4ML9EQj2dgslp4i80Jxj72YK3HjYzrTn6ftUVylW1zaKFGqrMlNIyqBmPWmIhZ/Y5r0Y4T49Hk1IuDUg==",
"dev": true,
"dependencies": {
"postcss": "^8.0.0"
}
@ -11757,6 +11755,7 @@
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz",
"integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==",
"dev": true,
"engines": {
"node": ">=0.10"
}
@ -34819,10 +34818,11 @@
"version": "6.5.0",
"license": "MIT",
"dependencies": {
"typescript": "^4.9.3"
"typescript": "^4.9.3",
"typescript-plugin-css-modules": "^5.0.1"
},
"devDependencies": {
"typescript-plugin-css-modules": "^5.0.1"
"@k8slens/eslint-config": "^6.5.0-alpha.3"
}
},
"packages/infrastructure/webpack": {
@ -38893,7 +38893,7 @@
"typedoc": "^0.24.6",
"typedoc-plugin-markdown": "^3.15.1",
"typescript": "^4.9.5",
"typescript-plugin-css-modules": "^3.4.0",
"typescript-plugin-css-modules": "^5.0.1",
"url-parse": "^1.5.10",
"uuid": "^8.3.2",
"webpack": "^5.81.0",
@ -38910,6 +38910,78 @@
"zod": "^3.21.4"
},
"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": {
"version": "4.19.0",
"resolved": "https://registry.npmjs.org/xterm/-/xterm-4.19.0.tgz",
@ -38993,7 +39065,7 @@
"typedoc": "^0.24.6",
"typedoc-plugin-markdown": "^3.15.1",
"typescript": "^4.9.5",
"typescript-plugin-css-modules": "^4.1.1",
"typescript-plugin-css-modules": "^5.0.1",
"webpack": "^5.81.0",
"webpack-cli": "^5.0.1"
},
@ -39086,9 +39158,9 @@
}
},
"typescript-plugin-css-modules": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/typescript-plugin-css-modules/-/typescript-plugin-css-modules-4.2.3.tgz",
"integrity": "sha512-jEEP2oUPOqR89QGgvPK2HSTZLkrCeKZQ9EwiNxm9VUcufUbNY1Tv053fPKRq6c13PMQjlBU3WrQjKN8u0j5Y6w==",
"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",
@ -39642,7 +39714,75 @@
"requires": {
"@k8slens/eslint-config": "^6.5.0-alpha.2",
"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": {
@ -43423,7 +43563,6 @@
"version": "4.0.0",
"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==",
"dev": true,
"requires": {
"postcss": "^8.0.0"
}
@ -43432,7 +43571,6 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@types/postcss-modules-scope/-/postcss-modules-scope-3.0.1.tgz",
"integrity": "sha512-LNkp3c4ML9EQj2dgslp4i80Jxj72YK3HjYzrTn6ftUVylW1zaKFGqrMlNIyqBmPWmIhZ/Y5r0Y4T49Hk1IuDUg==",
"dev": true,
"requires": {
"postcss": "^8.0.0"
}
@ -44785,11 +44923,6 @@
"resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
"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": {
"version": "1.7.0",
"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",
"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": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz",
@ -46568,14 +46683,6 @@
"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": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
@ -46589,15 +46696,6 @@
"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": {
"version": "2.0.8",
"resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz",
@ -46742,7 +46840,8 @@
"decode-uri-component": {
"version": "0.2.2",
"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": {
"version": "6.0.0",
@ -47239,7 +47338,8 @@
"dotenv": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
"integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q=="
"integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==",
"dev": true
},
"dotenv-expand": {
"version": "5.1.0",
@ -48986,11 +49086,6 @@
"resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
"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": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
@ -49496,42 +49591,6 @@
"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": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
@ -57682,7 +57741,7 @@
"type-fest": "^2.14.0",
"typed-emitter": "^1.4.0",
"typescript": "^4.9.5",
"typescript-plugin-css-modules": "^4.1.1",
"typescript-plugin-css-modules": "^5.0.1",
"webpack": "^5.81.0",
"webpack-cli": "^4.9.2",
"webpack-dev-server": "^4.13.3",
@ -57727,9 +57786,9 @@
}
},
"typescript-plugin-css-modules": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/typescript-plugin-css-modules/-/typescript-plugin-css-modules-4.2.3.tgz",
"integrity": "sha512-jEEP2oUPOqR89QGgvPK2HSTZLkrCeKZQ9EwiNxm9VUcufUbNY1Tv053fPKRq6c13PMQjlBU3WrQjKN8u0j5Y6w==",
"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",
@ -58669,255 +58728,6 @@
"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": {
"version": "15.1.0",
"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",
"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": {
"version": "2.0.2",
"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",
"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": {
"version": "0.5.21",
"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": {
"version": "0.0.2-1",
"resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2-1.tgz",
@ -61494,41 +61282,6 @@
"integrity": "sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA==",
"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": {
"version": "3.32.0",
"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",
"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": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz",
@ -62815,11 +62537,6 @@
"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": {
"version": "1.5.10",
"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", {
"checkLoops": false,
}],
"no-extra-boolean-cast": "off",
"header/header": [2, "../../license-header"],
"react/prop-types": "off",
"no-invalid-this": "off",
@ -136,6 +137,11 @@ module.exports = {
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-unnecessary-type-assertion": "off",
"@typescript-eslint/strict-boolean-expressions": ["error", {
"allowNullableString": true,
"allowNullableBoolean": true,
"allowNullableNumber": true,
}],
"no-restricted-imports": ["error", {
"paths": [
{

View File

@ -3,27 +3,24 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import type { AppPaths } from "./app-path-injection-token";
const appPathsStateInjectable = getInjectable({
id: "app-paths-state",
instantiate: () => {
let state: AppPaths;
let state: AppPaths | undefined;
return {
get: () =>{
if (!state) {
throw new Error("Tried to get app paths before state is setupped.");
}
assert(state, "Tried to get app paths before initialization");
return state;
},
set: (newState: AppPaths) => {
if (state) {
throw new Error("Tried to overwrite existing state of app paths.");
}
assert(!state, "Tried to overwrite existing state of app paths");
state = newState;
},

View File

@ -8,7 +8,7 @@ import type TypedEmitter from "typed-emitter";
import { observable, makeObservable } from "mobx";
import { once } from "lodash";
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 { Literal, Metadata } from "../cluster-types";
@ -364,15 +364,15 @@ export abstract class CatalogEntity<
constructor({ metadata, status, spec }: CatalogEntityData<Metadata, Status, Spec>) {
makeObservable(this);
if (!metadata || typeof metadata !== "object") {
if (!isObject(metadata)) {
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");
}
if (!spec || typeof spec !== "object") {
if (!isObject(spec)) {
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.
*/
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
import type { SelfSignedCert } from "selfsigned";
const lensProxyCertificateInjectable = getInjectable({
id: "lens-proxy-certificate",
instantiate: () => {
let certState: SelfSignedCert;
let certState: SelfSignedCert | undefined;
const cert = {
get: () => {
if (!certState) {
throw "certificate has not been set";
}
assert(certState, "certificate has not been set");
return certState;
},
set: (certificate: SelfSignedCert) => {
if (certState) {
throw "certificate has already been set";
}
assert(!certState, "certificate has already been set");
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> {
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (ipcRenderer) {
await ipcRenderer.invoke(broadcastMainChannel, channel, ...args.map(toJS));
return;
}
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (!webContents) {
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);

View File

@ -10,7 +10,7 @@ import { autorun, action, observable } from "mobx";
import type { KubeApi } from "@k8slens/kube-api";
import type { KubeObject, ObjectReference } from "@k8slens/kube-object";
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";
// 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(apiOrBase: string | KubeApi | undefined): KubeObjectStore | undefined {
if (!apiOrBase) {
if (!isDefined(apiOrBase) || apiOrBase === "") {
return undefined;
}
@ -152,7 +152,7 @@ export class ApiManager {
const api = apiBase && this.getApi(apiBase);
if (!api) {
if (!isDefined(api) || api === "") {
return undefined;
}

View File

@ -12,6 +12,7 @@ import createKubeJsonApiInjectable from "./create-kube-json-api.injectable";
import type { KubeApiOptions } from "@k8slens/kube-api";
import { KubeApi } from "@k8slens/kube-api";
import type { KubeJsonApiDataFor, KubeObject, KubeObjectConstructor } from "@k8slens/kube-object";
import { isDefined } from "@k8slens/utilities";
export interface CreateKubeApiForRemoteClusterConfig {
cluster: {
@ -92,7 +93,7 @@ const createKubeApiForRemoteClusterInjectable = getInjectable({
serverAddress: config.cluster.server,
apiBase: "",
debug: isDevelopment,
...(token ? {
...(isDefined(token) ? {
getRequestOptions: async () => ({
headers: {
"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) {
// fill the gaps
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 timestamp = res.values[0][0];
let timestamp = res.values[0]?.[0] ?? 0;
while (timestamp <= now) {
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) {
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)) {
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 result = results?.find(res => Object.values(res.metric)[0] == itemName);
const item = itemMetrics[metric];
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
itemMetrics[metric]!.data.result = result ? [result] : [];
if (item) {
item.data.result = result ? [result] : [];
}
}
return itemMetrics;
@ -114,7 +116,9 @@ export function getMetricLastPoints<Keys extends string>(metrics: Partial<Record
object.entries(metrics)
.map(([metricName, metric]) => {
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 {
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>>;
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;
let { start, end } = params;
if (!start && !end) {
const now = getSecondsFromUnixEpoch();
start = now - range;
end = now;
}
const now = getSecondsFromUnixEpoch();
const { start = now - range, end = now } = params;
return apiBase.post("/metrics", {
data: metricsQuery,

View File

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

View File

@ -122,7 +122,7 @@ export abstract class LensProtocolRouter {
return -1;
}
return countBy(b.path)["/"] - countBy(a.path)["/"];
return (countBy(b.path)["/"] ?? 0) - (countBy(a.path)["/"] ?? 0);
})[0] ?? null;
}
@ -204,6 +204,7 @@ export abstract class LensProtocolRouter {
const extension = extensionLoader.getInstanceByName(name) as LensExtension | undefined;
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"}`);
return name;

View File

@ -3,13 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
export type KubeResource =
"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 type KubeResource = keyof typeof apiResourceRecord;
export interface KubeApiResource {
kind: string;
@ -35,7 +29,7 @@ export interface KubeApiResourceData {
namespaced: boolean;
}
export const apiResourceRecord: Record<KubeResource, KubeApiResourceData> = {
export const apiResourceRecord = {
clusterroles: {
kind: "ClusterRole",
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
const synchronizationIsEnabled = !process.env.JEST_WORKER_ID;
let ticker = tickers[interval];
if (!tickers[interval] || !synchronizationIsEnabled) {
if (!ticker || !synchronizationIsEnabled) {
if (typeof interval === "number")
tickers[interval] = createIntervalTicker(interval);
ticker = tickers[interval] = createIntervalTicker(interval);
else
tickers[interval] = createAnimationFrameTicker();
ticker = tickers[interval] = createAnimationFrameTicker();
}
return tickers[interval].current();
return ticker.current();
}
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.
*/
async init() {
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (ipcRenderer) {
await this.initRenderer();
} else {

View File

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

View File

@ -145,7 +145,9 @@ export class LensRendererExtension extends LensExtension {
*/
// eslint-disable-next-line @typescript-eslint/ban-types
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),
renameTab: (tabId: string): void => {
const { selectedPodId } = logTabStore.getData(tabId) ?? {};
const pod = selectedPodId && podStore.getById(selectedPodId);
if (pod) {
renameTab(tabId, `Pod ${pod.getName()}`);
if (selectedPodId) {
const pod = podStore.getById(selectedPodId);
if (pod) {
renameTab(tabId, `Pod ${pod.getName()}`);
}
}
},
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 { 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 { maybeGet } from "@k8slens/utilities";
export function isAllowedResource(resources: KubeResource | KubeResource[]) {
const di = getLegacyGlobalDiForExtensionApi();
return [resources].flat().every((resourceName) => {
const resource = apiResourceRecord[resourceName];
return [resources].flat().every((resourceName: string) => {
const resource = maybeGet(apiResourceRecord, resourceName);
if (!resource) {
return true;

View File

@ -81,11 +81,12 @@ const toRecursedInjectablesFor = (logError: (errorMessage: string) => void) => {
injectionToken: applicationMenuItemInjectionToken,
}),
...((registration.submenu as MenuRegistration[])
? (registration.submenu as MenuRegistration[]).flatMap(
toRecursedInjectables(currentIdPath),
)
: []),
...(
registration.submenu
? (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-warning-level={level}
id={buttonId}
className={cssNames(styles.updateButton, {
[styles.warningHigh]: level === "high",
[styles.warningMedium]: level === "medium",
})}
className={cssNames(
styles.updateButton,
level === "high" && styles.warningHigh,
level === "medium" && styles.warningMedium,
)}
>
Update
<Icon material="arrow_drop_down" className={styles.icon}/>

View File

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

View File

@ -3,6 +3,7 @@
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { isTruthy } from "@k8slens/utilities";
import { getInjectable } from "@ogre-tools/injectable";
import { globalAgent } from "https";
import { requestSystemCAsInjectionToken } from "./request-system-cas-token";
@ -34,7 +35,7 @@ const injectSystemCAsInjectable = getInjectable({
return globalAgent.options.ca;
}
if (globalAgent.options.ca) {
if (isTruthy(globalAgent.options.ca)) {
return [globalAgent.options.ca];
}

View File

@ -73,13 +73,17 @@ const v500Beta10HotbarStoreMigrationInjectable = getInjectable({
{
// grab the default named hotbar or the first.
const defaultHotbarIndex = Math.max(0, hotbars.findIndex(hotbar => hotbar.name === "default"));
const [{ name, id, items }] = hotbars.splice(defaultHotbarIndex, 1);
const [hotbar] = hotbars.splice(defaultHotbarIndex, 1);
workspaceHotbars.set("default", {
name,
id,
items: items.filter(isDefined),
});
if (hotbar) {
const { name, id, items } = hotbar;
workspaceHotbars.set("default", {
name,
id,
items: items.filter(isDefined),
});
}
}
for (const cluster of clusters) {
@ -130,7 +134,8 @@ const v500Beta10HotbarStoreMigrationInjectable = getInjectable({
const defaultHotbarIndex = hotbars.findIndex(hotbar => hotbar.name === "default");
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()) {
// 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 itemId = `${commonId}-item-${registration.id ?? i}`;
const itemIsInSpecialTab =
registration.showInPreferencesTab &&
["telemetry", "application"].includes(
registration.showInPreferencesTab,
);
const itemIsInSpecialTab = (
registration.showInPreferencesTab === "telemetry"
|| registration.showInPreferencesTab === "application"
);
return getInjectable({
id: itemId,

View File

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

View File

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

View File

@ -111,9 +111,7 @@ export const createClusterPrometheusHandler = (...args: [Dependencies, Cluster])
break;
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 provider = ensurePrometheusProvider(service);
if (!provider) {
return undefined;
}
return { prometheusPath, provider };
};

View File

@ -29,7 +29,7 @@ const clusterStoreSnapMigrationInjectable = getInjectable({
}
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;

View File

@ -4,7 +4,7 @@
*/
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 { HelmChartManagerCache } from "./helm-chart-manager-cache.injectable";
import type { Logger } from "@k8slens/logger";
@ -71,11 +71,11 @@ export class HelmChartManager {
const cacheFileStats = await this.dependencies.stat(this.repo.cacheFilePath);
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 });
}
const normalized = normalizeHelmCharts(this.repo.name, data.entries);
const normalized = normalizeHelmCharts(this.repo.name, data.entries as RepoHelmChartList);
return put(
this.dependencies.cache,

View File

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

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 });
if (target) {
if (target !== undefined) {
this.dependencies.logger.debug(`Failed proxy to target: ${JSON.stringify(target, null, 2)}`);
if (req.method === "GET" && (!res.statusCode || res.statusCode >= 500)) {
@ -245,12 +245,10 @@ export class LensProxy {
const kubeAuthProxyServer = this.dependencies.getKubeAuthProxyServer(cluster);
const proxyTarget = await kubeAuthProxyServer.getApiTarget(isLongRunningRequest(req.url));
if (proxyTarget) {
return this.dependencies.proxy.web(req, res, proxyTarget);
}
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.
*/
import { chunk } from "lodash";
import type { ConnectionOptions } from "tls";
import { connect } from "tls";
import url from "url";
@ -13,6 +12,7 @@ import type { LensProxyApiRequest } from "../lens-proxy";
import kubeAuthProxyServerInjectable from "../../cluster/kube-auth-proxy-server.injectable";
import kubeAuthProxyCertificateInjectable from "../../kube-auth-proxy/kube-auth-proxy-certificate.injectable";
import clusterApiUrlInjectable from "../../../features/cluster/connections/main/api-url.injectable";
import { iter } from "@k8slens/utilities";
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(`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)) {
continue;
}

View File

@ -9,6 +9,7 @@ import type { InspectOptions } from "util";
import { inspect } from "util";
import { omit } from "lodash";
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
// This was modified to support formatting causes
@ -116,9 +117,15 @@ export class ConsoleFormat implements Format {
private _cause(source: unknown): string[] {
const messages: string[] = [];
if (source instanceof Error && source.cause) {
if (source instanceof Error && isTruthy(source.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));
}

View File

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

View File

@ -6,11 +6,14 @@
import type { PrometheusProvider } from "./provider";
import { createPrometheusProvider, bytesSent, findFirstNamespacedService, prometheusProviderInjectionToken } from "./provider";
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
export const getHelmLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string }): PrometheusProvider["getQuery"] => (
(opts, queryName) => {
switch(opts.category) {
case "cluster":
assert(opts.nodes, 'Missing "nodes" option when "category" option is "cluster"');
switch (queryName) {
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}"}`);
@ -69,6 +72,10 @@ export const getHelmLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string }):
}
break;
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) {
case "cpuUsage":
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;
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) {
case "diskUsage":
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;
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) {
case "bytesSentSuccess":
return bytesSent({
@ -126,7 +139,7 @@ export const getHelmLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string }):
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 type { PrometheusProvider } from "./provider";
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
export const getLensLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string }): PrometheusProvider["getQuery"] => (
(opts, queryName) => {
switch(opts.category) {
case "cluster":
assert(opts.nodes, 'Missing "nodes" option when "category" option is "cluster"');
switch (queryName) {
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}"}`);
@ -69,6 +72,10 @@ export const getLensLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string }):
}
break;
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) {
case "cpuUsage":
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;
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) {
case "diskUsage":
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;
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) {
case "bytesSentSuccess":
return bytesSent({
@ -126,7 +139,7 @@ export const getLensLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string }):
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 { bytesSent, createPrometheusProvider, findFirstNamespacedService, prometheusProviderInjectionToken } from "./provider";
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
export const getOperatorLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string }): PrometheusProvider["getQuery"] => (
(opts, queryName) => {
switch(opts.category) {
case "cluster":
assert(opts.nodes, 'Missing "nodes" option when "category" option is "cluster"');
switch (queryName) {
case "memoryUsage":
return `sum(node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes))`.replace(/_bytes/g, `_bytes * on (pod,namespace) group_left(node) kube_pod_info{node=~"${opts.nodes}"}`);
@ -69,6 +72,10 @@ export const getOperatorLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string
}
break;
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) {
case "cpuUsage":
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;
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) {
case "diskUsage":
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;
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) {
case "bytesSentSuccess":
return bytesSent({
@ -126,7 +139,7 @@ export const getOperatorLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string
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 { bytesSent, createPrometheusProvider, findFirstNamespacedService, prometheusProviderInjectionToken } from "./provider";
import { getInjectable } from "@ogre-tools/injectable";
import assert from "assert";
export const getStacklightLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: string }): PrometheusProvider["getQuery"] => (
(opts, queryName) => {
switch(opts.category) {
case "cluster":
assert(opts.nodes, 'Missing "nodes" option when "category" option is "cluster"');
switch (queryName) {
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}"}`);
@ -69,6 +72,10 @@ export const getStacklightLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: stri
}
break;
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) {
case "cpuUsage":
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;
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) {
case "diskUsage":
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;
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) {
case "bytesSentSuccess":
return bytesSent({
@ -126,7 +139,7 @@ export const getStacklightLikeQueryFor = ({ rateAccuracy }: { rateAccuracy: stri
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 type { LensProtocolRouterDependencies, RouteAttempt } 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";
export interface FallbackHandler {
@ -79,7 +79,7 @@ export class LensProtocolRouterMain extends proto.LensProtocolRouter {
await this._routeToExtension(url);
}
} 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) {
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 type { ServerResponse } from "http";
import { loggerInjectionToken } from "@k8slens/logger";
import { object } from "@k8slens/utilities";
import { isDefined, object } from "@k8slens/utilities";
import type { LensApiRequest, Route } from "./route";
import { contentTypes } from "./router-content-types";
@ -34,7 +34,7 @@ const writeServerResponseFor = (serverResponse: ServerResponse) => ({
if (content instanceof Buffer) {
serverResponse.write(content);
serverResponse.end();
} else if (content) {
} else if (isDefined(content)) {
serverResponse.end(content);
} else {
serverResponse.end();
@ -67,7 +67,7 @@ const createHandlerForRouteInjectable = getInjectable({
} catch(error) {
const mappedResult = contentTypes.txt.resultMapper({
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);

View File

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

View File

@ -2,6 +2,7 @@
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { isDefined } from "@k8slens/utilities";
import type { LensApiResult } from "./route";
export interface LensApiResultContentType {
@ -14,19 +15,15 @@ export interface LensApiResultContentType {
const resultMapperFor =
(contentType: string): LensApiResultContentType["resultMapper"] =>
({ response, error, statusCode= error ? 400 : 200, headers = {}}) => ({
({ response, error, statusCode = isDefined(error) ? 200 : 400, headers = {}}) => ({
statusCode,
content: error || response,
content: isDefined(error) ? error : response,
headers: { ...headers, "Content-Type": contentType },
});
export type SupportedFileExtension = "json" | "txt" | "html" | "css" | "gif" | "jpg" | "png" | "svg" | "js" | "woff2" | "ttf";
export interface ContentTypes extends Record<SupportedFileExtension, LensApiResultContentType> {
[key: string]: LensApiResultContentType | undefined;
}
export const contentTypes: ContentTypes = {
export const contentTypes: Record<SupportedFileExtension, LensApiResultContentType> = {
json: {
resultMapper: (result: LensApiResult<unknown>) => {
const resultMapper = resultMapperFor("application/json");

View File

@ -10,6 +10,7 @@ import type { LensApiRequest } from "../../router/route";
import path from "path";
import { contentTypes } from "../../router/router-content-types";
import { prefixedLoggerInjectable } from "@k8slens/logger";
import { maybeGet } from "@k8slens/utilities";
const prodStaticFileRouteHandlerInjectable = getInjectable({
id: "prod-static-file-route-handler",
@ -33,7 +34,7 @@ const prodStaticFileRouteHandlerInjectable = getInjectable({
}
const fileExtension = path.extname(assetFilePath).slice(1);
const contentType = contentTypes[fileExtension] || contentTypes.txt;
const contentType = maybeGet(contentTypes, fileExtension) || contentTypes.txt;
try {
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 type { LoadProxyKubeconfig } from "../../cluster/load-proxy-kubeconfig.injectable";
import type { Pod } from "@k8slens/kube-object";
import { isDefined } from "@k8slens/utilities";
export interface NodeShellSessionArgs extends ShellSessionArgs {
nodeName: string;
@ -67,7 +68,7 @@ export class NodeShellSession extends ShellSession {
this.send({
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(

View File

@ -271,7 +271,7 @@ export abstract class ShellSession {
}
})
.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
&& (

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,6 +2,7 @@
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { isTruthy } from "@k8slens/utilities";
import React from "react";
import type { CatalogCategory } from "../../../common/catalog/catalog-entity";
@ -18,7 +19,7 @@ export const CatalogCategoryLabel = ({ category }: CatalogCategoryLabelProps) =>
return (
<div className="flex">
<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>
);
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -59,7 +59,7 @@ class NonInjectedClusterPrometheusSetting extends React.Component<ClusterPrometh
}
@computed get canEditPrometheusPath(): boolean {
if (!this.selectedOption || this.selectedOption === autoDetectPrometheus) {
if (this.selectedOption === autoDetectPrometheus) {
return false;
}
@ -96,7 +96,7 @@ class NonInjectedClusterPrometheusSetting extends React.Component<ClusterPrometh
}
parsePrometheusPath = () => {
if (!this.selectedOption || !this.path) {
if (!this.path) {
return undefined;
}
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";
export interface ClusterNoMetricsProps {
className: string;
className?: string;
}
interface Dependencies {

View File

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

View File

@ -54,7 +54,7 @@ class NonInjectedHorizontalPodAutoscalerDetails extends React.Component<KubeObje
}
renderMetrics(hpa: HorizontalPodAutoscaler) {
const renderName = (metric: HorizontalPodAutoscalerMetricSpec) => {
const renderName = (metric: HorizontalPodAutoscalerMetricSpec | undefined) => {
const metricName = getMetricName(metric);
switch (metric?.type) {
@ -95,7 +95,7 @@ class NonInjectedHorizontalPodAutoscalerDetails extends React.Component<KubeObje
this.props.getMetrics(hpa)
.map((metrics, 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>
</TableRow>
))
@ -107,10 +107,6 @@ class NonInjectedHorizontalPodAutoscalerDetails extends React.Component<KubeObje
render() {
const { object: hpa, apiManager, getDetailsUrl, logger } = this.props;
if (!hpa) {
return null;
}
if (!(hpa instanceof HorizontalPodAutoscaler)) {
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() {
const { object: limitRange, logger } = this.props;
if (!limitRange) {
return null;
}
if (!(limitRange instanceof 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(() => {
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() {
const { object: configMap, logger } = this.props;
if (!configMap) {
return null;
}
if (!(configMap instanceof 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(", ")}
</div>
{rule.resources && (
<div>
Resources:
{" "}
{rule.resources.join(", ")}
</div>
)}
<div>
Resources:
{" "}
{rule.resources?.join(", ")}
</div>
{rule.scope && (
<div>
Scope:

View File

@ -83,10 +83,6 @@ class NonInjectedResourceQuotaDetails extends React.Component<KubeObjectDetailsP
render() {
const { object: quota } = this.props;
if (!quota) {
return null;
}
if (!(quota instanceof ResourceQuota)) {
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(() => {
const secret = this.props.object as Secret;
if (secret) {
this.data = secret.data;
this.revealSecret.clear();
}
this.data = secret.data;
this.revealSecret.clear();
}),
]);
}
@ -145,10 +143,6 @@ class NonInjectedSecretDetails extends React.Component<KubeObjectDetailsProps &
render() {
const { object: secret, logger } = this.props;
if (!secret) {
return null;
}
if (!(secret instanceof 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() {
const { object: vpa, apiManager, getDetailsUrl, logger } = this.props;
if (!vpa) {
return null;
}
if (!(vpa instanceof VerticalPodAutoscaler)) {
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 name="Recommender">
{
/* according to the spec there can be 0 or 1 recommenders, only */
recommenders?.length ? recommenders[0].name : "default"
}
{recommenders?.[0]?.name ?? "default"}
</DrawerItem>
{vpa.status && this.renderStatus(vpa, vpa.status)}

View File

@ -28,10 +28,6 @@ class NonInjectedCustomResourceDefinitionDetails extends React.Component<KubeObj
render() {
const { object: crd } = this.props;
if (!crd) {
return null;
}
if (!(crd instanceof CustomResourceDefinition)) {
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() {
const { props: { object, crd, logger }} = this;
const { object, crd, logger } = this.props;
if (!object || !crd) {
if (!crd) {
return null;
}

View File

@ -9,6 +9,7 @@ import type { CreateStorage } from "../../../utils/create-storage/create-storage
import type { TabId } from "../dock/store";
import autoBind from "auto-bind";
import { toJS } from "../../../../common/utils";
import { isTruthy } from "@k8slens/utilities";
export interface DockTabStoreOptions {
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 {
for (const [tabId, data] of this.data) {
if (inspector(data)) {
if (isTruthy(inspector(data))) {
return tabId;
}
}

View File

@ -78,10 +78,6 @@ class NonInjectedDockTab extends React.Component<DockTabProps & Dependencies> {
render() {
const { className, moreActions, dockStore, active, isMac, ...tabProps } = this.props;
if (!tabProps.value) {
return;
}
void dockStore;
void active;
void isMac;
@ -94,9 +90,11 @@ class NonInjectedDockTab extends React.Component<DockTabProps & Dependencies> {
<Tab
{...tabProps}
id={`tab-${id}`}
className={cssNames(styles.DockTab, className, {
[styles.pinned]: pinned,
})}
className={cssNames(
styles.DockTab,
className,
pinned && styles.pinned,
)}
onContextMenu={() => this.menuVisible.set(true)}
label={(
<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 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];
// check if moving to the next or previous tab is possible.
if (!nextElement) return;
this.onChangeTab(nextElement);
};

View File

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

View File

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

View File

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

View File

@ -44,13 +44,11 @@ export class Terminal {
protected readonly api: TerminalApi;
private get elem() {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return this.xterm.element!;
return this.xterm.element;
}
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) {
@ -99,8 +97,10 @@ export class Terminal {
const onDataHandler = this.xterm.onData(this.onData);
const clearOnce = once(this.onClear);
this.viewport.addEventListener("scroll", this.onScroll);
this.elem.addEventListener("contextmenu", this.onContextMenu);
const { viewport, elem } = this;
viewport?.addEventListener("scroll", this.onScroll);
elem?.addEventListener("contextmenu", this.onContextMenu);
this.api.once("ready", clearOnce);
this.api.once("connected", clearOnce);
this.api.on("data", this.onApiData);
@ -128,7 +128,8 @@ export class Terminal {
() => this.fitAddon.dispose(),
() => this.api.removeAllListeners(),
() => 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.api.sendTerminalSize(cols, rows);
}),
@ -161,7 +162,7 @@ export class Terminal {
};
onScroll = () => {
this.scrollPos = this.viewport.scrollTop;
this.scrollPos = this.viewport?.scrollTop ?? 0;
};
onClear = () => {
@ -176,7 +177,12 @@ export class Terminal {
onActivate = () => {
this.fit();
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 = () => {

View File

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

View File

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

View File

@ -36,10 +36,6 @@ const NonInjectedEventDetails = observer(({
className,
logger,
}: Dependencies & KubeObjectDetailsProps) => {
if (!event) {
return null;
}
if (!(event instanceof KubeEvent)) {
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() {
const { object, eventStore } = this.props;
if (!object) {
return null;
}
if (!(object instanceof KubeObject)) {
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}>
{events.map(event => (
<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}
</div>
<DrawerItem name="Source">

View File

@ -2,7 +2,7 @@
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { isObject } from "@k8slens/utilities";
import { isObject, isTruthy } from "@k8slens/utilities";
import React from "react";
import { SemVer } from "semver";
import URLParse from "url-parse";
@ -76,8 +76,8 @@ const attemptInstallByInfoInjectable = getInjectable({
return disposer();
}
if (result.value.error || !isObject(result.value.versions)) {
const message = result.value.error ? `: ${String(result.value.error)}` : "";
if (isTruthy(result.value.error) || !isObject(result.value.versions)) {
const message = isTruthy(result.value.error) ? `: ${String(result.value.error)}` : "";
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.
*/
import { hasTypedProperty, isDefined } from "@k8slens/utilities";
import { hasTypedProperty, isDefined, isObject } from "@k8slens/utilities";
export function getMessageFromError(error: unknown): string {
if (!error || typeof error !== "object") {
if (!isObject(error)) {
return "an error has occurred";
}

View File

@ -51,7 +51,7 @@ const NonInjectedInstalledExtensions = observer(({
enableExtension,
disableExtension,
}: Dependencies) => {
if (!extensionDiscovery.isLoaded) {
if (!extensionDiscovery.isLoaded.get()) {
return <div><Spinner center /></div>;
}
@ -85,8 +85,8 @@ const NonInjectedInstalledExtensions = observer(({
accessor: "extension",
width: 200,
sortType: (rowA, rowB) => { // Custom sorting for extension name
const nameA = extensions[rowA.index].manifest.name;
const nameB = extensions[rowB.index].manifest.name;
const nameA = extensions[rowA.index]?.manifest.name ?? "";
const nameB = extensions[rowB.index]?.manifest.name ?? "";
if (nameA > nameB) return -1;
if (nameB > nameA) return 1;
@ -126,7 +126,12 @@ const NonInjectedInstalledExtensions = observer(({
),
version,
status: (
<div className={cssNames({ [styles.enabled]: isEnabled, [styles.invalid]: !isCompatible })}>
<div
className={cssNames(
isEnabled && styles.enabled,
!isCompatible && styles.invalid,
)}
>
{getStatus(extension)}
</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 helmChartDetailsVersionSelectionInjectable from "./details/versions/helm-chart-details-version-selection.injectable";
import assert from "assert";
import autoBindReact from "auto-bind/react";
export interface HelmChartDetailsProps {
hideDetails(): void;
@ -47,15 +46,6 @@ interface Dependencies {
@observer
class NonInjectedHelmChartDetails extends Component<HelmChartDetailsProps & Dependencies> {
constructor(props: HelmChartDetailsProps & Dependencies) {
super(props);
autoBindReact(this);
}
get chart() {
return this.props.chart;
}
install = () => {
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 versionsAreLoading = this.props.versions.pending.get();
if (!this.chart || versionsAreLoading) {
if (versionsAreLoading) {
return <Spinner center data-testid="spinner-for-chart-details" />;
}
return (
<div className="box grow">
{this.renderIntroduction(this.chart)}
{this.renderIntroduction(this.props.chart)}
{readmeIsLoading ? (
<Spinner center data-testid="spinner-for-chart-readme" />
@ -162,12 +152,13 @@ class NonInjectedHelmChartDetails extends Component<HelmChartDetailsProps & Depe
}
render() {
return (
<Drawer
className="HelmChartDetails"
usePortal={true}
open={!!this.chart}
title={this.chart ? `Chart: ${this.chart.getFullName()}` : ""}
usePortal
open
title={`Chart: ${this.props.chart.getFullName()}`}
onClose={this.props.hideDetails}
>
{this.renderContent()}

View File

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

View File

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

View File

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

View File

@ -50,7 +50,7 @@ const NonInjectedHotbarIcon = observer(({
};
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 targetId={id}>
{tooltip}
@ -61,7 +61,11 @@ const NonInjectedHotbarIcon = observer(({
id={id}
title={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}
size={size}
src={src}

View File

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

View File

@ -59,6 +59,10 @@ export function isAsyncValidator(validator: InputValidator): validator is AsyncI
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}
*/
@ -106,7 +110,11 @@ export function unionInputValidatorsAsync(
debounce: longestDebounce,
validate: async (value, props) => {
for (const validator of validators) {
if (isAsyncValidator(validator)) {
if (isSyncValidator(validator)) {
if (validator.validate(value, props)) {
return;
}
} else {
try {
await validator.validate(value, props);
@ -114,10 +122,6 @@ export function unionInputValidatorsAsync(
} catch {
// 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 { 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 type { InputProps } from "./input";
import { Input } from "./input";
@ -86,7 +86,7 @@ class NonInjectedSearchInput extends React.Component<SearchInputProps & Dependen
void bindGlobalFocusHotkey;
void isMac;
if (showClearIcon && value) {
if (showClearIcon && isTruthy(value)) {
rightIcon = (
<Icon
small

View File

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

View File

@ -8,7 +8,7 @@ import "./item-list-layout.scss";
import React from "react";
import { observer } from "mobx-react";
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 { Filter } from "./page-filters/store";
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)}>
{title}
{
info && (
isTruthy(info) && (
<div className="info-panel box grow">
{info}
</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;
function applyFilters<I extends ItemObject>(filters: ItemsFilter<I>[], items: I[]): I[] {
if (!filters || !filters.length) {
if (!filters.length) {
return items;
}

View File

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

View File

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

View File

@ -48,10 +48,6 @@ const NonInjectedKubeObjectMeta = observer((props : Dependencies & KubeObjectMet
namespaceApi,
} = props;
if (!object) {
return null;
}
if (!(object instanceof KubeObject)) {
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 { observer } from "mobx-react";
import type { StrictReactNode } from "@k8slens/utilities";
import { cssNames } from "@k8slens/utilities";
import { cssNames, isTruthy } from "@k8slens/utilities";
import { Button } from "@k8slens/button";
import type { DialogProps } from "../dialog";
import { Dialog } from "../dialog";
@ -54,7 +54,7 @@ class NonInjectedKubeConfigDialog extends React.Component<KubeConfigDialogProps
};
renderContents = (data: KubeconfigDialogData) => (
<Wizard header={<h5>{ data.title || "Kubeconfig File" }</h5>}>
<Wizard header={<h5>{isTruthy(data.title) ? data.title : "Kubeconfig File" }</h5>}>
<WizardStep
customButtons={ (
<div className="actions flex gaps">

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