mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Merge branch 'load_resources_per_namespaces' into fix-1898/watch-api-streaming
This commit is contained in:
commit
195f911342
@ -57,7 +57,10 @@ jobs:
|
||||
displayName: Run tests
|
||||
- script: make test-extensions
|
||||
displayName: Run In-tree Extension tests
|
||||
- script: make integration-win
|
||||
- bash: |
|
||||
rm -rf extensions/telemetry
|
||||
make integration-win
|
||||
git checkout extensions/telemetry
|
||||
displayName: Run integration tests
|
||||
- script: make build
|
||||
condition: "and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'))"
|
||||
@ -98,7 +101,10 @@ jobs:
|
||||
displayName: Run tests
|
||||
- script: make test-extensions
|
||||
displayName: Run In-tree Extension tests
|
||||
- script: make integration-mac
|
||||
- bash: |
|
||||
rm -rf extensions/telemetry
|
||||
make integration-mac
|
||||
git checkout extensions/telemetry
|
||||
displayName: Run integration tests
|
||||
- script: make test-extensions
|
||||
displayName: Run In-tree Extension tests
|
||||
@ -152,7 +158,10 @@ jobs:
|
||||
# Although the kube and minikube config files are in placed $HOME they are owned by root
|
||||
sudo chown -R $USER $HOME/.kube $HOME/.minikube
|
||||
displayName: Install integration test dependencies
|
||||
- script: xvfb-run --auto-servernum --server-args='-screen 0, 1600x900x24' make integration-linux
|
||||
- bash: |
|
||||
rm -rf extensions/telemetry
|
||||
xvfb-run --auto-servernum --server-args='-screen 0, 1600x900x24' make integration-linux
|
||||
git checkout extensions/telemetry
|
||||
displayName: Run integration tests
|
||||
- bash: |
|
||||
sudo chown root:root /
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -17,3 +17,4 @@ types/extension-renderer-api.d.ts
|
||||
extensions/*/dist
|
||||
docs/extensions/api
|
||||
site/
|
||||
.vscode/
|
||||
|
||||
18
Makefile
18
Makefile
@ -105,34 +105,18 @@ docs:
|
||||
|
||||
.PHONY: clean-extensions
|
||||
clean-extensions:
|
||||
ifeq "$(DETECTED_OS)" "Windows"
|
||||
$(foreach dir, $(wildcard $(EXTENSIONS_DIR)/*), if exist $(dir)\dist del /s /q $(dir)\dist)
|
||||
$(foreach dir, $(wildcard $(EXTENSIONS_DIR)/*), if exist $(dir)\node_modules del /s /q $(dir)\node_modules)
|
||||
else
|
||||
$(foreach dir, $(wildcard $(EXTENSIONS_DIR)/*), rm -rf $(dir)/dist)
|
||||
$(foreach dir, $(wildcard $(EXTENSIONS_DIR)/*), rm -rf $(dir)/node_modules)
|
||||
endif
|
||||
|
||||
.PHONY: clean-npm
|
||||
clean-npm:
|
||||
ifeq "$(DETECTED_OS)" "Windows"
|
||||
if exist src\extensions\npm\extensions\dist del /s /q src\extensions\npm\extensions\dist
|
||||
if exist src\extensions\npm\extensions\__mocks__ del /s /q src\extensions\npm\extensions\__mocks__
|
||||
if exist src\extensions\npm\extensions\node_modules del /s /q src\extensions\npm\extensions\node_modules
|
||||
else
|
||||
rm -rf src/extensions/npm/extensions/dist
|
||||
rm -rf src/extensions/npm/extensions/__mocks__
|
||||
rm -rf src/extensions/npm/extensions/node_modules
|
||||
endif
|
||||
|
||||
.PHONY: clean
|
||||
clean: clean-npm clean-extensions
|
||||
ifeq "$(DETECTED_OS)" "Windows"
|
||||
if exist binaries\client del /s /q binaries\client
|
||||
if exist dist del /s /q dist\*.*
|
||||
if exist static\build del /s /q static\build\*.*
|
||||
else
|
||||
rm -rf binaries/client
|
||||
rm -rf dist/*
|
||||
rm -rf static/build/*
|
||||
endif
|
||||
rm -rf node_modules/
|
||||
|
||||
@ -19,6 +19,9 @@ When contributing to this repository, please consider first discussing the chang
|
||||
* `make dev` - builds and starts the app
|
||||
* `make clean` - cleanup local environment build artifacts
|
||||
|
||||
### Developing on Windows
|
||||
|
||||
On Windows we only support [Git Bash](https://gitforwindows.org/) (or similar shell) for running commands.
|
||||
|
||||
## Github Workflow
|
||||
|
||||
|
||||
@ -224,7 +224,7 @@
|
||||
"tar": "^6.0.5",
|
||||
"tcp-port-used": "^1.0.1",
|
||||
"tempy": "^0.5.0",
|
||||
"uuid": "^8.1.0",
|
||||
"uuid": "^8.3.2",
|
||||
"win-ca": "^3.2.0",
|
||||
"winston": "^3.2.1",
|
||||
"winston-transport-browserconsole": "^1.0.5",
|
||||
@ -279,18 +279,18 @@
|
||||
"@types/tempy": "^0.3.0",
|
||||
"@types/terser-webpack-plugin": "^3.0.0",
|
||||
"@types/universal-analytics": "^0.4.4",
|
||||
"@types/uuid": "^8.0.0",
|
||||
"@types/uuid": "^8.3.0",
|
||||
"@types/webdriverio": "^4.13.0",
|
||||
"@types/webpack": "^4.41.17",
|
||||
"@types/webpack-dev-server": "^3.11.1",
|
||||
"@types/webpack-env": "^1.15.2",
|
||||
"@types/webpack-node-externals": "^1.7.1",
|
||||
"@typescript-eslint/eslint-plugin": "^4.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.12.0",
|
||||
"@typescript-eslint/parser": "^4.0.0",
|
||||
"ace-builds": "^1.4.11",
|
||||
"ansi_up": "^4.0.4",
|
||||
"chart.js": "^2.9.3",
|
||||
"circular-dependency-plugin": "^5.2.0",
|
||||
"circular-dependency-plugin": "^5.2.2",
|
||||
"color": "^3.1.2",
|
||||
"concurrently": "^5.2.0",
|
||||
"css-element-queries": "^1.2.3",
|
||||
|
||||
@ -7,38 +7,49 @@ export type KubeResource =
|
||||
"endpoints" | "customresourcedefinitions" | "horizontalpodautoscalers" | "podsecuritypolicies" | "poddisruptionbudgets";
|
||||
|
||||
export interface KubeApiResource {
|
||||
kind: string; // resource type
|
||||
resource: KubeResource; // valid resource name
|
||||
group?: string; // api-group
|
||||
}
|
||||
|
||||
// TODO: auto-populate all resources dynamically (see: kubectl api-resources -o=wide -v=7)
|
||||
export const apiResources: KubeApiResource[] = [
|
||||
{ resource: "configmaps" },
|
||||
{ resource: "cronjobs", group: "batch" },
|
||||
{ resource: "customresourcedefinitions", group: "apiextensions.k8s.io" },
|
||||
{ resource: "daemonsets", group: "apps" },
|
||||
{ resource: "deployments", group: "apps" },
|
||||
{ resource: "endpoints" },
|
||||
{ resource: "events" },
|
||||
{ resource: "horizontalpodautoscalers" },
|
||||
{ resource: "ingresses", group: "networking.k8s.io" },
|
||||
{ resource: "jobs", group: "batch" },
|
||||
{ resource: "namespaces" },
|
||||
{ resource: "networkpolicies", group: "networking.k8s.io" },
|
||||
{ resource: "nodes" },
|
||||
{ resource: "persistentvolumes" },
|
||||
{ resource: "persistentvolumeclaims" },
|
||||
{ resource: "pods" },
|
||||
{ resource: "poddisruptionbudgets" },
|
||||
{ resource: "podsecuritypolicies" },
|
||||
{ resource: "resourcequotas" },
|
||||
{ resource: "replicasets", group: "apps" },
|
||||
{ resource: "secrets" },
|
||||
{ resource: "services" },
|
||||
{ resource: "statefulsets", group: "apps" },
|
||||
{ resource: "storageclasses", group: "storage.k8s.io" },
|
||||
{ kind: "ConfigMap", resource: "configmaps" },
|
||||
{ kind: "CronJob", resource: "cronjobs", group: "batch" },
|
||||
{ kind: "CustomResourceDefinition", resource: "customresourcedefinitions", group: "apiextensions.k8s.io" },
|
||||
{ kind: "DaemonSet", resource: "daemonsets", group: "apps" },
|
||||
{ kind: "Deployment", resource: "deployments", group: "apps" },
|
||||
{ kind: "Endpoint", resource: "endpoints" },
|
||||
{ kind: "Event", resource: "events" },
|
||||
{ kind: "HorizontalPodAutoscaler", resource: "horizontalpodautoscalers" },
|
||||
{ kind: "Ingress", resource: "ingresses", group: "networking.k8s.io" },
|
||||
{ kind: "Job", resource: "jobs", group: "batch" },
|
||||
{ kind: "Namespace", resource: "namespaces" },
|
||||
{ kind: "NetworkPolicy", resource: "networkpolicies", group: "networking.k8s.io" },
|
||||
{ kind: "Node", resource: "nodes" },
|
||||
{ kind: "PersistentVolume", resource: "persistentvolumes" },
|
||||
{ kind: "PersistentVolumeClaim", resource: "persistentvolumeclaims" },
|
||||
{ kind: "Pod", resource: "pods" },
|
||||
{ kind: "PodDisruptionBudget", resource: "poddisruptionbudgets" },
|
||||
{ kind: "PodSecurityPolicy", resource: "podsecuritypolicies" },
|
||||
{ kind: "ResourceQuota", resource: "resourcequotas" },
|
||||
{ kind: "ReplicaSet", resource: "replicasets", group: "apps" },
|
||||
{ kind: "Secret", resource: "secrets" },
|
||||
{ kind: "Service", resource: "services" },
|
||||
{ kind: "StatefulSet", resource: "statefulsets", group: "apps" },
|
||||
{ kind: "StorageClass", resource: "storageclasses", group: "storage.k8s.io" },
|
||||
];
|
||||
|
||||
export function isAllowedResourceType(kind: string): boolean {
|
||||
const apiResource = apiResources.find(resource => resource.kind === kind);
|
||||
|
||||
if (apiResource) {
|
||||
return getHostedCluster().allowedResources.includes(apiResource.resource);
|
||||
}
|
||||
|
||||
return true; // allowed by default for other resources
|
||||
}
|
||||
|
||||
export function isAllowedResource(resources: KubeResource | KubeResource[]) {
|
||||
if (!Array.isArray(resources)) {
|
||||
resources = [resources];
|
||||
|
||||
@ -28,6 +28,7 @@ export interface UserPreferences {
|
||||
downloadBinariesPath?: string;
|
||||
kubectlBinariesPath?: string;
|
||||
openAtLogin?: boolean;
|
||||
hiddenTableColumns?: Record<string, string[]>
|
||||
}
|
||||
|
||||
export class UserStore extends BaseStore<UserStoreModel> {
|
||||
@ -54,6 +55,7 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
||||
downloadMirror: "default",
|
||||
downloadKubectlBinaries: true, // Download kubectl binaries matching cluster version
|
||||
openAtLogin: false,
|
||||
hiddenTableColumns: {},
|
||||
};
|
||||
|
||||
protected async handleOnLoad() {
|
||||
|
||||
@ -10,7 +10,7 @@ import { workspaceStore } from "../common/workspace-store";
|
||||
import { preferencesURL } from "../renderer/components/+preferences/preferences.route";
|
||||
import { clusterViewURL } from "../renderer/components/cluster-manager/cluster-view.route";
|
||||
import logger from "./logger";
|
||||
import { isDevelopment } from "../common/vars";
|
||||
import { isDevelopment, isWindows } from "../common/vars";
|
||||
import { exitApp } from "./exit-app";
|
||||
|
||||
// note: instance of Tray should be saved somewhere, otherwise it disappears
|
||||
@ -29,7 +29,7 @@ export function initTray(windowManager: WindowManager) {
|
||||
try {
|
||||
const menu = createTrayMenu(windowManager);
|
||||
|
||||
buildTray(getTrayIcon(), menu);
|
||||
buildTray(getTrayIcon(), menu, windowManager);
|
||||
} catch (err) {
|
||||
logger.error(`[TRAY]: building failed: ${err}`);
|
||||
}
|
||||
@ -42,20 +42,25 @@ export function initTray(windowManager: WindowManager) {
|
||||
};
|
||||
}
|
||||
|
||||
export function buildTray(icon: string | NativeImage, menu: Menu) {
|
||||
function buildTray(icon: string | NativeImage, menu: Menu, windowManager: WindowManager) {
|
||||
if (!tray) {
|
||||
tray = new Tray(icon);
|
||||
tray.setToolTip(packageInfo.description);
|
||||
tray.setIgnoreDoubleClickEvents(true);
|
||||
}
|
||||
tray.setImage(icon);
|
||||
tray.setContextMenu(menu);
|
||||
|
||||
tray.setImage(icon);
|
||||
tray.setContextMenu(menu);
|
||||
if (isWindows) {
|
||||
tray.on("click", () => {
|
||||
windowManager.ensureMainWindow();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return tray;
|
||||
}
|
||||
|
||||
export function createTrayMenu(windowManager: WindowManager): Menu {
|
||||
function createTrayMenu(windowManager: WindowManager): Menu {
|
||||
return Menu.buildFromTemplate([
|
||||
{
|
||||
label: "About Lens",
|
||||
|
||||
@ -78,10 +78,24 @@ export function normalizeMetrics(metrics: IMetrics, frames = 60): IMetrics {
|
||||
result.forEach(res => {
|
||||
if (!res.values || !res.values.length) return;
|
||||
|
||||
let now = moment().startOf("minute").subtract(1, "minute").unix();
|
||||
let timestamp = res.values[0][0];
|
||||
|
||||
while (timestamp <= now) {
|
||||
timestamp = moment.unix(timestamp).add(1, "minute").unix();
|
||||
|
||||
if (!res.values.find((value) => value[0] === timestamp)) {
|
||||
res.values.push([timestamp, "0"]);
|
||||
}
|
||||
}
|
||||
|
||||
while (res.values.length < frames) {
|
||||
const timestamp = moment.unix(res.values[0][0]).subtract(1, "minute").unix();
|
||||
|
||||
res.values.unshift([timestamp, "0"]);
|
||||
if (!res.values.find((value) => value[0] === timestamp)) {
|
||||
res.values.unshift([timestamp, "0"]);
|
||||
}
|
||||
now = timestamp;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ export const resourceApplierApi = {
|
||||
.post<KubeJsonApiData[]>("/stack", { data: resource })
|
||||
.then(data => {
|
||||
const items = data.map(obj => {
|
||||
const api = apiManager.getApi(obj.metadata.selfLink);
|
||||
const api = apiManager.getApiByKind(obj.kind, obj.apiVersion);
|
||||
|
||||
if (api) {
|
||||
return new api.objectConstructor(obj);
|
||||
|
||||
@ -4,7 +4,7 @@ import React, { Fragment } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { TabLayout } from "../layout/tab-layout";
|
||||
import { eventStore } from "./event.store";
|
||||
import { KubeObjectListLayout, KubeObjectListLayoutProps, getDetailsUrl } from "../kube-object";
|
||||
import { getDetailsUrl, KubeObjectListLayout, KubeObjectListLayoutProps } from "../kube-object";
|
||||
import { KubeEvent } from "../../api/endpoints/events.api";
|
||||
import { Tooltip } from "../tooltip";
|
||||
import { Link } from "react-router-dom";
|
||||
@ -65,7 +65,7 @@ export class Events extends React.Component<Props> {
|
||||
small
|
||||
material="help_outline"
|
||||
className="help-icon"
|
||||
tooltip="Limited to {eventStore.limit}"
|
||||
tooltip={`Limited to ${eventStore.limit}`}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { debounce } from "lodash";
|
||||
import { action, comparer, IReactionDisposer, IReactionOptions, observable, reaction, toJS, when } from "mobx";
|
||||
import { autobind, createStorage } from "../../utils";
|
||||
import { KubeObjectStore, KubeObjectStoreLoadingParams } from "../../kube-object.store";
|
||||
@ -7,14 +8,14 @@ import { apiManager } from "../../api/api-manager";
|
||||
import { isAllowedResource } from "../../../common/rbac";
|
||||
import { clusterStore, getHostedCluster } from "../../../common/cluster-store";
|
||||
|
||||
const storage = createStorage<string[]>("context_namespaces", []);
|
||||
const storage = createStorage<string[]>("context_namespaces");
|
||||
|
||||
export const namespaceUrlParam = createPageParam<string[]>({
|
||||
name: "namespaces",
|
||||
isSystem: true,
|
||||
multiValues: true,
|
||||
get defaultValue() {
|
||||
return storage.get(); // initial namespaces coming from URL or local-storage (default)
|
||||
return storage.get() ?? []; // initial namespaces coming from URL or local-storage (default)
|
||||
}
|
||||
});
|
||||
|
||||
@ -51,9 +52,9 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
|
||||
await getHostedCluster().whenReady; // wait for cluster-state from main
|
||||
this.isReady = true;
|
||||
|
||||
this.setContext(this.initNamespaces);
|
||||
this.onSelectedNamespacesChange();
|
||||
this.onAllowedNamespacesChange();
|
||||
this.setContext(this.initialNamespaces);
|
||||
this.autoLoadAllowedNamespaces();
|
||||
this.autoUpdateUrlAndLocalStorage();
|
||||
}
|
||||
|
||||
public onContextChange(callback: (contextNamespaces: string[]) => void, opts: IReactionOptions = {}): IReactionDisposer {
|
||||
@ -63,7 +64,7 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
|
||||
});
|
||||
}
|
||||
|
||||
private onSelectedNamespacesChange(): IReactionDisposer {
|
||||
private autoUpdateUrlAndLocalStorage(): IReactionDisposer {
|
||||
return this.onContextChange(namespaces => {
|
||||
storage.set(namespaces); // save to local-storage
|
||||
namespaceUrlParam.set(namespaces, { replaceHistory: true }); // update url
|
||||
@ -73,7 +74,7 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
|
||||
});
|
||||
}
|
||||
|
||||
private onAllowedNamespacesChange(): IReactionDisposer {
|
||||
private autoLoadAllowedNamespaces(): IReactionDisposer {
|
||||
return reaction(() => this.allowedNamespaces, () => this.loadAll(), {
|
||||
fireImmediately: true,
|
||||
equals: comparer.identity,
|
||||
@ -84,28 +85,19 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
|
||||
return toJS(getHostedCluster().allowedNamespaces);
|
||||
}
|
||||
|
||||
get initNamespaces() {
|
||||
const allowedNamespaces = new Set(this.allowedNamespaces);
|
||||
const lastUsedNamespaces = new Set(storage.get());
|
||||
private get initialNamespaces(): string[] {
|
||||
const allowed = new Set(this.allowedNamespaces);
|
||||
const prevSelected = storage.get();
|
||||
|
||||
// remove previously saved, but currently disallowed namespaces
|
||||
lastUsedNamespaces.forEach(namespace => {
|
||||
if (!allowedNamespaces.has(namespace)) {
|
||||
lastUsedNamespaces.delete(namespace);
|
||||
}
|
||||
});
|
||||
|
||||
// return previously saved and currently allowed namespaces
|
||||
if (lastUsedNamespaces.size) {
|
||||
return Array.from(lastUsedNamespaces);
|
||||
if (Array.isArray(prevSelected)) {
|
||||
return prevSelected.filter(namespace => allowed.has(namespace));
|
||||
}
|
||||
|
||||
// otherwise select "default" or first allowed namespace
|
||||
else {
|
||||
if (allowedNamespaces.has("default")) {
|
||||
return ["default"];
|
||||
} else if (allowedNamespaces.size) {
|
||||
return [Array.from(allowedNamespaces)[0]];
|
||||
}
|
||||
if (allowed.has("default")) {
|
||||
return ["default"];
|
||||
} else if (allowed.size) {
|
||||
return [Array.from(allowed)[0]];
|
||||
}
|
||||
|
||||
return [];
|
||||
@ -132,17 +124,18 @@ export class NamespaceStore extends KubeObjectStore<Namespace> {
|
||||
return super.subscribe(apis);
|
||||
}
|
||||
|
||||
async loadAll() {
|
||||
return super.loadAll({
|
||||
// prevent multiple loading from different sources (e.g. items-list-layout, namespace-select)
|
||||
private loadAllLazy = debounce(() => {
|
||||
super.loadAll({
|
||||
namespaces: this.allowedNamespaces,
|
||||
});
|
||||
}, 250);
|
||||
|
||||
async loadAll() {
|
||||
this.loadAllLazy();
|
||||
}
|
||||
|
||||
protected async loadItems({ isAdmin, namespaces }: KubeObjectStoreLoadingParams) {
|
||||
if (isAdmin) {
|
||||
return this.api.list();
|
||||
}
|
||||
|
||||
protected async loadItems({ namespaces }: KubeObjectStoreLoadingParams) {
|
||||
if (!isAllowedResource("namespaces")) {
|
||||
return namespaces.map(getDummyNamespace);
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import { IResourceMetricsValue, ResourceMetricsContext } from "../resource-metri
|
||||
import { observer } from "mobx-react";
|
||||
import { ChartOptions, ChartPoint } from "chart.js";
|
||||
import { themeStore } from "../../theme.store";
|
||||
import { mapValues } from "lodash";
|
||||
|
||||
type IContext = IResourceMetricsValue<Node, { metrics: IClusterMetrics }>;
|
||||
|
||||
@ -15,26 +16,26 @@ export const NodeCharts = observer(() => {
|
||||
const id = object.getId();
|
||||
const { chartCapacityColor } = themeStore.activeTheme.colors;
|
||||
|
||||
if (!metrics) return null;
|
||||
if (isMetricsEmpty(metrics)) return <NoMetrics/>;
|
||||
if (!metrics) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const values = Object.values(metrics).map(metric =>
|
||||
normalizeMetrics(metric).data.result[0].values
|
||||
);
|
||||
const [
|
||||
if (isMetricsEmpty(metrics)) {
|
||||
return <NoMetrics />;
|
||||
}
|
||||
|
||||
const {
|
||||
memoryUsage,
|
||||
memoryRequests,
|
||||
_memoryLimits, // eslint-disable-line unused-imports/no-unused-vars-ts
|
||||
memoryCapacity,
|
||||
cpuUsage,
|
||||
cpuRequests,
|
||||
_cpuLimits, // eslint-disable-line unused-imports/no-unused-vars-ts
|
||||
cpuCapacity,
|
||||
podUsage,
|
||||
podCapacity,
|
||||
fsSize,
|
||||
fsUsage
|
||||
] = values;
|
||||
} = mapValues(metrics, metric => normalizeMetrics(metric).data.result[0].values);
|
||||
|
||||
const datasets = [
|
||||
// CPU
|
||||
|
||||
@ -26,10 +26,10 @@ export class RoleBindingsStore extends KubeObjectStore<RoleBinding> {
|
||||
return clusterRoleBindingApi.get(params);
|
||||
}
|
||||
|
||||
protected async loadItems({ isAdmin, namespaces }: KubeObjectStoreLoadingParams): Promise<RoleBinding[]> {
|
||||
protected async loadItems(params: KubeObjectStoreLoadingParams): Promise<RoleBinding[]> {
|
||||
const items = await Promise.all([
|
||||
super.loadItems({ isAdmin, namespaces, api: clusterRoleBindingApi }),
|
||||
super.loadItems({ isAdmin, namespaces, api: roleBindingApi }),
|
||||
super.loadItems({ ...params, api: clusterRoleBindingApi }),
|
||||
super.loadItems({ ...params, api: roleBindingApi }),
|
||||
]);
|
||||
|
||||
return items.flat();
|
||||
|
||||
@ -24,10 +24,10 @@ export class RolesStore extends KubeObjectStore<Role> {
|
||||
return clusterRoleApi.get(params);
|
||||
}
|
||||
|
||||
protected async loadItems({ isAdmin, namespaces }: KubeObjectStoreLoadingParams): Promise<Role[]> {
|
||||
protected async loadItems(params: KubeObjectStoreLoadingParams): Promise<Role[]> {
|
||||
const items = await Promise.all([
|
||||
super.loadItems({ isAdmin, namespaces, api: clusterRoleApi }),
|
||||
super.loadItems({ isAdmin, namespaces, api: roleApi }),
|
||||
super.loadItems({ ...params, api: clusterRoleApi }),
|
||||
super.loadItems({ ...params, api: roleApi }),
|
||||
]);
|
||||
|
||||
return items.flat();
|
||||
|
||||
@ -48,7 +48,6 @@ export class WorkloadsOverview extends React.Component<Props> {
|
||||
if (this.isUnmounting) break;
|
||||
|
||||
try {
|
||||
store.reset();
|
||||
await store.loadAll();
|
||||
unsubscribeMap.get(store)?.(); // unsubscribe previous watcher
|
||||
unsubscribeMap.set(store, store.subscribe());
|
||||
|
||||
@ -74,6 +74,8 @@ export class Pods extends React.Component<Props> {
|
||||
<KubeObjectListLayout
|
||||
className="Pods" store={podsStore}
|
||||
dependentStores={[volumeClaimStore, eventStore]}
|
||||
tableId = "workloads_pods"
|
||||
isConfigurable
|
||||
sortingCallbacks={{
|
||||
[sortBy.name]: (pod: Pod) => pod.getName(),
|
||||
[sortBy.namespace]: (pod: Pod) => pod.getNs(),
|
||||
@ -94,7 +96,7 @@ export class Pods extends React.Component<Props> {
|
||||
renderHeaderTitle="Pods"
|
||||
renderTableHeader={[
|
||||
{ title: "Name", className: "name", sortBy: sortBy.name },
|
||||
{ className: "warning" },
|
||||
{ className: "warning", showWithColumn: "name" },
|
||||
{ title: "Namespace", className: "namespace", sortBy: sortBy.namespace },
|
||||
{ title: "Containers", className: "containers", sortBy: sortBy.containers },
|
||||
{ title: "Restarts", className: "restarts", sortBy: sortBy.restarts },
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
height: 18px; // Must be equal to lineHeight variable in pod-log-list.tsx
|
||||
font-family: var(--font-monospace);
|
||||
font-size: smaller;
|
||||
white-space: pre;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover {
|
||||
background: var(--logRowHoverBackground);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import "./item-list-layout.scss";
|
||||
import "./table-menu.scss";
|
||||
import groupBy from "lodash/groupBy";
|
||||
|
||||
import React, { ReactNode } from "react";
|
||||
@ -18,6 +19,11 @@ import { PageFiltersList } from "./page-filters-list";
|
||||
import { PageFiltersSelect } from "./page-filters-select";
|
||||
import { NamespaceSelectFilter } from "../+namespaces/namespace-select";
|
||||
import { themeStore } from "../../theme.store";
|
||||
import { MenuActions} from "../menu/menu-actions";
|
||||
import { MenuItem } from "../menu";
|
||||
import { Checkbox } from "../checkbox";
|
||||
import { userStore } from "../../../common/user-store";
|
||||
import logger from "../../../main/logger";
|
||||
|
||||
// todo: refactor, split to small re-usable components
|
||||
|
||||
@ -32,6 +38,7 @@ interface IHeaderPlaceholders {
|
||||
}
|
||||
|
||||
export interface ItemListLayoutProps<T extends ItemObject = ItemObject> {
|
||||
tableId?: string;
|
||||
className: IClassName;
|
||||
store: ItemStore<T>;
|
||||
dependentStores?: ItemStore[];
|
||||
@ -50,6 +57,7 @@ export interface ItemListLayoutProps<T extends ItemObject = ItemObject> {
|
||||
isReady?: boolean; // show loading indicator while not ready
|
||||
isSelectable?: boolean; // show checkbox in rows for selecting items
|
||||
isSearchable?: boolean; // apply search-filter & add search-input
|
||||
isConfigurable?: boolean;
|
||||
copyClassNameFromHeadCells?: boolean;
|
||||
sortingCallbacks?: { [sortBy: string]: TableSortCallback };
|
||||
tableProps?: Partial<TableProps>; // low-level table configuration
|
||||
@ -74,6 +82,7 @@ const defaultProps: Partial<ItemListLayoutProps> = {
|
||||
showHeader: true,
|
||||
isSearchable: true,
|
||||
isSelectable: true,
|
||||
isConfigurable: false,
|
||||
copyClassNameFromHeadCells: true,
|
||||
dependentStores: [],
|
||||
filterItems: [],
|
||||
@ -92,6 +101,7 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
|
||||
|
||||
private watchDisposers: IReactionDisposer[] = [];
|
||||
|
||||
@observable hiddenColumnNames = new Set<string>();
|
||||
@observable isUnmounting = false;
|
||||
|
||||
@observable userSettings: ItemListLayoutUserSettings = {
|
||||
@ -112,11 +122,11 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
this.loadStores();
|
||||
|
||||
if (!this.props.isClusterScoped) {
|
||||
disposeOnUnmount(this, [
|
||||
namespaceStore.onContextChange(() => this.loadStores(), {
|
||||
fireImmediately: true,
|
||||
})
|
||||
namespaceStore.onContextChange(() => this.loadStores())
|
||||
]);
|
||||
}
|
||||
}
|
||||
@ -127,7 +137,10 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
|
||||
}
|
||||
|
||||
@computed get stores() {
|
||||
const { store, dependentStores, isClusterScoped } = this.props;
|
||||
const { store, dependentStores, isClusterScoped, tableId } = this.props;
|
||||
|
||||
if (this.canBeConfigured) this.hiddenColumnNames = new Set(userStore.preferences?.hiddenTableColumns?.[tableId]);
|
||||
|
||||
const stores = new Set([store, ...dependentStores]);
|
||||
|
||||
if (!isClusterScoped) {
|
||||
@ -148,7 +161,6 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
|
||||
}
|
||||
|
||||
try {
|
||||
store.reset();
|
||||
await store.loadAll();
|
||||
this.watchDisposers.push(store.subscribe());
|
||||
} catch (error) {
|
||||
@ -195,9 +207,7 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
|
||||
};
|
||||
|
||||
@computed get isReady() {
|
||||
const { isReady, store } = this.props;
|
||||
|
||||
return typeof isReady == "boolean" ? isReady : store.isLoaded;
|
||||
return this.props.isReady ?? this.props.store.isLoaded;
|
||||
}
|
||||
|
||||
@computed get filters() {
|
||||
@ -243,6 +253,42 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
|
||||
return this.applyFilters(filterItems, allItems);
|
||||
}
|
||||
|
||||
updateColumnFilter(checkboxValue: boolean, columnName: string) {
|
||||
if (checkboxValue){
|
||||
this.hiddenColumnNames.delete(columnName);
|
||||
} else {
|
||||
this.hiddenColumnNames.add(columnName);
|
||||
}
|
||||
|
||||
if (this.canBeConfigured) {
|
||||
userStore.preferences.hiddenTableColumns[this.props.tableId] = Array.from(this.hiddenColumnNames);
|
||||
}
|
||||
}
|
||||
|
||||
columnIsVisible(index: number): boolean {
|
||||
const {renderTableHeader} = this.props;
|
||||
|
||||
if (!this.canBeConfigured) return true;
|
||||
|
||||
return !this.hiddenColumnNames.has(renderTableHeader[index].showWithColumn ?? renderTableHeader[index].className);
|
||||
}
|
||||
|
||||
get canBeConfigured(): boolean {
|
||||
const { isConfigurable, tableId, renderTableHeader } = this.props;
|
||||
|
||||
if (!isConfigurable || !tableId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!renderTableHeader?.every(({ className }) => className)) {
|
||||
logger.warning("[ItemObjectList]: cannot configure an object list without all headers being identifiable");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@autobind()
|
||||
getRow(uid: string) {
|
||||
const {
|
||||
@ -286,7 +332,7 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
|
||||
}
|
||||
}
|
||||
|
||||
return <TableCell key={index} {...cellProps} />;
|
||||
return this.columnIsVisible(index) ? <TableCell key={index} {...cellProps} /> : null;
|
||||
})
|
||||
}
|
||||
{renderItemMenu && (
|
||||
@ -330,12 +376,7 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
|
||||
}
|
||||
|
||||
renderNoItems() {
|
||||
const { allItems, items, filters } = this;
|
||||
const allItemsCount = allItems.length;
|
||||
const itemsCount = items.length;
|
||||
const isFiltered = filters.length > 0 && allItemsCount > itemsCount;
|
||||
|
||||
if (isFiltered) {
|
||||
if (this.filters.length > 0) {
|
||||
return (
|
||||
<NoItems>
|
||||
No items found.
|
||||
@ -458,14 +499,18 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
|
||||
onClick={prevDefault(() => store.toggleSelectionAll(items))}
|
||||
/>
|
||||
)}
|
||||
{renderTableHeader.map((cellProps, index) => <TableCell key={index} {...cellProps} />)}
|
||||
{renderItemMenu && <TableCell className="menu"/>}
|
||||
{renderTableHeader.map((cellProps, index) => this.columnIsVisible(index) ? <TableCell key={index} {...cellProps} /> : null)}
|
||||
{renderItemMenu && <TableCell className="menu">
|
||||
{this.canBeConfigured && this.renderColumnMenu()}
|
||||
</TableCell>
|
||||
}
|
||||
</TableHead>
|
||||
)}
|
||||
{
|
||||
!virtual && items.map(item => this.getRow(item.getId()))
|
||||
}
|
||||
</Table>
|
||||
|
||||
)}
|
||||
<AddRemoveButtons
|
||||
onRemove={selectedItems.length ? removeItemsDialog : null}
|
||||
@ -476,6 +521,29 @@ export class ItemListLayout extends React.Component<ItemListLayoutProps> {
|
||||
);
|
||||
}
|
||||
|
||||
renderColumnMenu() {
|
||||
const { renderTableHeader} = this.props;
|
||||
|
||||
return (
|
||||
<MenuActions
|
||||
toolbar = {false}
|
||||
autoCloseOnSelect = {false}
|
||||
className={cssNames("KubeObjectMenu")}
|
||||
>
|
||||
{renderTableHeader.map((cellProps, index) => (
|
||||
!cellProps.showWithColumn &&
|
||||
<MenuItem key={index} className="input">
|
||||
<Checkbox label = {cellProps.title ?? `<${cellProps.className}>`}
|
||||
className = "MenuCheckbox"
|
||||
value ={!this.hiddenColumnNames.has(cellProps.className)}
|
||||
onChange = {(v) => this.updateColumnFilter(v, cellProps.className)}
|
||||
/>
|
||||
</MenuItem>
|
||||
))}
|
||||
</MenuActions>
|
||||
);
|
||||
}
|
||||
|
||||
renderFooter() {
|
||||
if (this.props.renderFooter) {
|
||||
return this.props.renderFooter(this);
|
||||
|
||||
4
src/renderer/components/item-object-list/table-menu.scss
Normal file
4
src/renderer/components/item-object-list/table-menu.scss
Normal file
@ -0,0 +1,4 @@
|
||||
.MenuCheckbox {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
@ -13,6 +13,7 @@ import isString from "lodash/isString";
|
||||
export interface MenuActionsProps extends Partial<MenuProps> {
|
||||
className?: string;
|
||||
toolbar?: boolean; // display menu as toolbar with icons
|
||||
autoCloseOnSelect?: boolean;
|
||||
triggerIcon?: string | IconProps | React.ReactNode;
|
||||
removeConfirmationMessage?: React.ReactNode | (() => React.ReactNode);
|
||||
updateAction?(): void;
|
||||
@ -80,7 +81,7 @@ export class MenuActions extends React.Component<MenuActionsProps> {
|
||||
|
||||
render() {
|
||||
const {
|
||||
className, toolbar, children, updateAction, removeAction, triggerIcon, removeConfirmationMessage,
|
||||
className, toolbar, autoCloseOnSelect, children, updateAction, removeAction, triggerIcon, removeConfirmationMessage,
|
||||
...menuProps
|
||||
} = this.props;
|
||||
const menuClassName = cssNames("MenuActions flex", className, {
|
||||
@ -98,7 +99,7 @@ export class MenuActions extends React.Component<MenuActionsProps> {
|
||||
className={menuClassName}
|
||||
usePortal={autoClose}
|
||||
closeOnScroll={autoClose}
|
||||
closeOnClickItem={autoClose}
|
||||
closeOnClickItem={autoCloseOnSelect ?? autoClose }
|
||||
closeOnClickOutside={autoClose}
|
||||
{...menuProps}
|
||||
>
|
||||
|
||||
@ -15,6 +15,7 @@ export interface TableCellProps extends React.DOMAttributes<HTMLDivElement> {
|
||||
isChecked?: boolean; // mark checkbox as checked or not
|
||||
renderBoolean?: boolean; // show "true" or "false" for all of the children elements are "typeof boolean"
|
||||
sortBy?: TableSortBy; // column name, must be same as key in sortable object <Table sortable={}/>
|
||||
showWithColumn?: string // className of another column, if it is not empty the current column is not shown in the filter menu, visibility of this one is the same as a specified column, applicable to headers only
|
||||
_sorting?: Partial<TableSortParams>; // <Table> sorting state, don't use this prop outside (!)
|
||||
_sort?(sortBy: TableSortBy): void; // <Table> sort function, don't use this prop outside (!)
|
||||
_nowrap?: boolean; // indicator, might come from parent <TableHead>, don't use this prop outside (!)
|
||||
@ -63,7 +64,7 @@ export class TableCell extends React.Component<TableCellProps> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { className, checkbox, isChecked, sortBy, _sort, _sorting, _nowrap, children, title, renderBoolean: displayBoolean, ...cellProps } = this.props;
|
||||
const { className, checkbox, isChecked, sortBy, _sort, _sorting, _nowrap, children, title, renderBoolean: displayBoolean, showWithColumn, ...cellProps } = this.props;
|
||||
const classNames = cssNames("TableCell", className, {
|
||||
checkbox,
|
||||
nowrap: _nowrap,
|
||||
|
||||
@ -9,7 +9,7 @@ export interface ItemObject {
|
||||
|
||||
@autobind()
|
||||
export abstract class ItemStore<T extends ItemObject = ItemObject> {
|
||||
abstract loadAll(...args: any[]): Promise<any>;
|
||||
abstract loadAll(...args: any[]): Promise<void>;
|
||||
|
||||
protected defaultSorting = (item: T) => item.getName();
|
||||
|
||||
|
||||
@ -6,10 +6,9 @@ import { ItemStore } from "./item.store";
|
||||
import { apiManager } from "./api/api-manager";
|
||||
import { IKubeApiQueryParams, KubeApi } from "./api/kube-api";
|
||||
import { KubeJsonApiData } from "./api/kube-json-api";
|
||||
import { getHostedCluster } from "../common/cluster-store";
|
||||
import { isAllowedResourceType } from "../common/rbac";
|
||||
|
||||
export interface KubeObjectStoreLoadingParams {
|
||||
isAdmin: boolean;
|
||||
namespaces: string[];
|
||||
api?: KubeApi;
|
||||
}
|
||||
@ -76,16 +75,18 @@ export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemSt
|
||||
}
|
||||
}
|
||||
|
||||
protected async loadItems({ isAdmin, namespaces, api }: KubeObjectStoreLoadingParams): Promise<T[]> {
|
||||
if (!api.isNamespaced) {
|
||||
if (isAdmin) return api.list({}, this.query);
|
||||
protected async loadItems({ namespaces, api }: KubeObjectStoreLoadingParams): Promise<T[]> {
|
||||
if (isAllowedResourceType(api.kind)) {
|
||||
if (api.isNamespaced) {
|
||||
return Promise
|
||||
.all(namespaces.map(namespace => api.list({ namespace })))
|
||||
.then(items => items.flat());
|
||||
}
|
||||
|
||||
return [];
|
||||
return api.list({}, this.query);
|
||||
}
|
||||
|
||||
return Promise
|
||||
.all(namespaces.map(namespace => api.list({ namespace })))
|
||||
.then(items => items.flat());
|
||||
return [];
|
||||
}
|
||||
|
||||
protected filterItemsOnLoad(items: T[]) {
|
||||
@ -93,28 +94,21 @@ export abstract class KubeObjectStore<T extends KubeObject = any> extends ItemSt
|
||||
}
|
||||
|
||||
@action
|
||||
async loadAll(params: { namespaces?: string[] } = {}) {
|
||||
async loadAll({ namespaces: contextNamespaces }: { namespaces?: string[] } = {}) {
|
||||
this.isLoading = true;
|
||||
let items: T[];
|
||||
|
||||
try {
|
||||
let contextNamespaces = params.namespaces;
|
||||
|
||||
if (!params.namespaces) {
|
||||
if (!contextNamespaces) {
|
||||
const { namespaceStore } = await import("./components/+namespaces/namespace.store");
|
||||
|
||||
await namespaceStore.whenReady;
|
||||
contextNamespaces = namespaceStore.getContextNamespaces();
|
||||
}
|
||||
|
||||
items = await this.loadItems({
|
||||
isAdmin: getHostedCluster().isAdmin,
|
||||
namespaces: contextNamespaces,
|
||||
api: this.api,
|
||||
});
|
||||
let items = await this.loadItems({ namespaces: contextNamespaces, api: this.api });
|
||||
|
||||
items = this.filterItemsOnLoad(items);
|
||||
items = this.sortItems(items);
|
||||
|
||||
this.items.replace(items);
|
||||
this.isLoaded = true;
|
||||
} catch (error) {
|
||||
|
||||
133
yarn.lock
133
yarn.lock
@ -1710,10 +1710,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/universal-analytics/-/universal-analytics-0.4.4.tgz#496a52b92b599a0112bec7c12414062de6ea8449"
|
||||
integrity sha512-9g3F0SGxVr4UDd6y07bWtFnkpSSX1Ake7U7AGHgSFrwM6pF53/fV85bfxT2JLWS/3sjLCcyzoYzQlCxpkVo7wA==
|
||||
|
||||
"@types/uuid@^8.0.0":
|
||||
version "8.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.0.0.tgz#165aae4819ad2174a17476dbe66feebd549556c0"
|
||||
integrity sha512-xSQfNcvOiE5f9dyd4Kzxbof1aTrLobL278pGLKOZI6esGfZ7ts9Ka16CzIN6Y8hFHE1C7jIBZokULhK1bOgjRw==
|
||||
"@types/uuid@^8.3.0":
|
||||
version "8.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.0.tgz#215c231dff736d5ba92410e6d602050cce7e273f"
|
||||
integrity sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==
|
||||
|
||||
"@types/webdriverio@^4.13.0", "@types/webdriverio@^4.8.0":
|
||||
version "4.13.3"
|
||||
@ -1792,53 +1792,28 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@typescript-eslint/eslint-plugin@^4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.0.0.tgz#99349a501447fed91de18346705c0c65cf603bee"
|
||||
integrity sha512-5e6q1TR7gS2P+8W2xndCu7gBh3BzmYEo70OyIdsmCmknHha/yNbz2vdevl+tP1uoaMOcrzg4gyrAijuV3DDBHA==
|
||||
"@typescript-eslint/eslint-plugin@^4.12.0", "@typescript-eslint/eslint-plugin@^4.5.0":
|
||||
version "4.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.12.0.tgz#00d1b23b40b58031e6d7c04a5bc6c1a30a2e834a"
|
||||
integrity sha512-wHKj6q8s70sO5i39H2g1gtpCXCvjVszzj6FFygneNFyIAxRvNSVz9GML7XpqrB9t7hNutXw+MHnLN/Ih6uyB8Q==
|
||||
dependencies:
|
||||
"@typescript-eslint/experimental-utils" "4.0.0"
|
||||
"@typescript-eslint/scope-manager" "4.0.0"
|
||||
"@typescript-eslint/experimental-utils" "4.12.0"
|
||||
"@typescript-eslint/scope-manager" "4.12.0"
|
||||
debug "^4.1.1"
|
||||
functional-red-black-tree "^1.0.1"
|
||||
regexpp "^3.0.0"
|
||||
semver "^7.3.2"
|
||||
tsutils "^3.17.1"
|
||||
|
||||
"@typescript-eslint/eslint-plugin@^4.5.0":
|
||||
version "4.8.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.8.2.tgz#cf9102ec800391caa574f589ffe0623cca1d9308"
|
||||
integrity sha512-gQ06QLV5l1DtvYtqOyFLXD9PdcILYqlrJj2l+CGDlPtmgLUzc1GpqciJFIRvyfvgLALpnxYINFuw+n9AZhPBKQ==
|
||||
dependencies:
|
||||
"@typescript-eslint/experimental-utils" "4.8.2"
|
||||
"@typescript-eslint/scope-manager" "4.8.2"
|
||||
debug "^4.1.1"
|
||||
functional-red-black-tree "^1.0.1"
|
||||
regexpp "^3.0.0"
|
||||
semver "^7.3.2"
|
||||
tsutils "^3.17.1"
|
||||
|
||||
"@typescript-eslint/experimental-utils@4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.0.0.tgz#fbec21a3b5ab59127edb6ce2e139ed378cc50eb5"
|
||||
integrity sha512-hbX6zR+a/vcpFVNJYN/Nbd7gmaMosDTxHEKcvmhWeWcq/0UDifrqmCfkkodbAKL46Fn4ekSBMTyq2zlNDzcQxw==
|
||||
"@typescript-eslint/experimental-utils@4.12.0":
|
||||
version "4.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.12.0.tgz#372838e76db76c9a56959217b768a19f7129546b"
|
||||
integrity sha512-MpXZXUAvHt99c9ScXijx7i061o5HEjXltO+sbYfZAAHxv3XankQkPaNi5myy0Yh0Tyea3Hdq1pi7Vsh0GJb0fA==
|
||||
dependencies:
|
||||
"@types/json-schema" "^7.0.3"
|
||||
"@typescript-eslint/scope-manager" "4.0.0"
|
||||
"@typescript-eslint/types" "4.0.0"
|
||||
"@typescript-eslint/typescript-estree" "4.0.0"
|
||||
eslint-scope "^5.0.0"
|
||||
eslint-utils "^2.0.0"
|
||||
|
||||
"@typescript-eslint/experimental-utils@4.8.2":
|
||||
version "4.8.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.8.2.tgz#8909a5732f19329cf5ef0c39766170476bff5e50"
|
||||
integrity sha512-hpTw6o6IhBZEsQsjuw/4RWmceRyESfAiEzAEnXHKG1X7S5DXFaZ4IO1JO7CW1aQ604leQBzjZmuMI9QBCAJX8Q==
|
||||
dependencies:
|
||||
"@types/json-schema" "^7.0.3"
|
||||
"@typescript-eslint/scope-manager" "4.8.2"
|
||||
"@typescript-eslint/types" "4.8.2"
|
||||
"@typescript-eslint/typescript-estree" "4.8.2"
|
||||
"@typescript-eslint/scope-manager" "4.12.0"
|
||||
"@typescript-eslint/types" "4.12.0"
|
||||
"@typescript-eslint/typescript-estree" "4.12.0"
|
||||
eslint-scope "^5.0.0"
|
||||
eslint-utils "^2.0.0"
|
||||
|
||||
@ -1852,13 +1827,13 @@
|
||||
"@typescript-eslint/typescript-estree" "4.8.2"
|
||||
debug "^4.1.1"
|
||||
|
||||
"@typescript-eslint/scope-manager@4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.0.0.tgz#8c9e3b3b8cdf5a1fbe671d9fad73ff67bc027ea8"
|
||||
integrity sha512-9gcWUPoWo7gk/+ZQPg7L1ySRmR5HLIy3Vu6/LfhQbuzIkGm6v2CGIjpVRISoDLFRovNRDImd4aP/sa8O4yIEBg==
|
||||
"@typescript-eslint/scope-manager@4.12.0":
|
||||
version "4.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.12.0.tgz#beeb8beca895a07b10c593185a5612f1085ef279"
|
||||
integrity sha512-QVf9oCSVLte/8jvOsxmgBdOaoe2J0wtEmBr13Yz0rkBNkl5D8bfnf6G4Vhox9qqMIoG7QQoVwd2eG9DM/ge4Qg==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "4.0.0"
|
||||
"@typescript-eslint/visitor-keys" "4.0.0"
|
||||
"@typescript-eslint/types" "4.12.0"
|
||||
"@typescript-eslint/visitor-keys" "4.12.0"
|
||||
|
||||
"@typescript-eslint/scope-manager@4.8.2":
|
||||
version "4.8.2"
|
||||
@ -1868,23 +1843,23 @@
|
||||
"@typescript-eslint/types" "4.8.2"
|
||||
"@typescript-eslint/visitor-keys" "4.8.2"
|
||||
|
||||
"@typescript-eslint/types@4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.0.0.tgz#ec1f9fc06b8558a1d5afa6e337182d08beece7f5"
|
||||
integrity sha512-bK+c2VLzznX2fUWLK6pFDv3cXGTp7nHIuBMq1B9klA+QCsqLHOOqe5TQReAQDl7DN2RfH+neweo0oC5hYlG7Rg==
|
||||
"@typescript-eslint/types@4.12.0":
|
||||
version "4.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.12.0.tgz#fb891fe7ccc9ea8b2bbd2780e36da45d0dc055e5"
|
||||
integrity sha512-N2RhGeheVLGtyy+CxRmxdsniB7sMSCfsnbh8K/+RUIXYYq3Ub5+sukRCjVE80QerrUBvuEvs4fDhz5AW/pcL6g==
|
||||
|
||||
"@typescript-eslint/types@4.8.2":
|
||||
version "4.8.2"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.8.2.tgz#c862dd0e569d9478eb82d6aee662ea53f5661a36"
|
||||
integrity sha512-z1/AVcVF8ju5ObaHe2fOpZYEQrwHyZ7PTOlmjd3EoFeX9sv7UekQhfrCmgUO7PruLNfSHrJGQvrW3Q7xQ8EoAw==
|
||||
|
||||
"@typescript-eslint/typescript-estree@4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.0.0.tgz#2244c63de2f2190bc5718eb0fb3fd2c437d42097"
|
||||
integrity sha512-ewFMPi2pMLDNIXGMPdf8r7El2oPSZw9PEYB0j+WcpKd7AX2ARmajGa7RUHTukllWX2bj4vWX6JLE1Oih2BMokA==
|
||||
"@typescript-eslint/typescript-estree@4.12.0":
|
||||
version "4.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.12.0.tgz#3963418c850f564bdab3882ae23795d115d6d32e"
|
||||
integrity sha512-gZkFcmmp/CnzqD2RKMich2/FjBTsYopjiwJCroxqHZIY11IIoN0l5lKqcgoAPKHt33H2mAkSfvzj8i44Jm7F4w==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "4.0.0"
|
||||
"@typescript-eslint/visitor-keys" "4.0.0"
|
||||
"@typescript-eslint/types" "4.12.0"
|
||||
"@typescript-eslint/visitor-keys" "4.12.0"
|
||||
debug "^4.1.1"
|
||||
globby "^11.0.1"
|
||||
is-glob "^4.0.1"
|
||||
@ -1906,12 +1881,12 @@
|
||||
semver "^7.3.2"
|
||||
tsutils "^3.17.1"
|
||||
|
||||
"@typescript-eslint/visitor-keys@4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.0.0.tgz#e2bbb69d98076d6a3f06abcb2048225a74362c33"
|
||||
integrity sha512-sTouJbv6rjVJeTE4lpSBVYXq/u5K3gbB6LKt7ccFEZPTZB/VeQ0ssUz9q5Hx++sCqBbdF8PzrrgvEnicXAR6NQ==
|
||||
"@typescript-eslint/visitor-keys@4.12.0":
|
||||
version "4.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.12.0.tgz#a470a79be6958075fa91c725371a83baf428a67a"
|
||||
integrity sha512-hVpsLARbDh4B9TKYz5cLbcdMIOAoBYgFPCSP9FFS/liSF+b33gVNq8JHY3QGhHNVz85hObvL7BEYLlgx553WCw==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "4.0.0"
|
||||
"@typescript-eslint/types" "4.12.0"
|
||||
eslint-visitor-keys "^2.0.0"
|
||||
|
||||
"@typescript-eslint/visitor-keys@4.8.2":
|
||||
@ -3463,10 +3438,10 @@ cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
|
||||
inherits "^2.0.1"
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
circular-dependency-plugin@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/circular-dependency-plugin/-/circular-dependency-plugin-5.2.0.tgz#e09dbc2dd3e2928442403e2d45b41cea06bc0a93"
|
||||
integrity sha512-7p4Kn/gffhQaavNfyDFg7LS5S/UT1JAjyGd4UqR2+jzoYF02eDkj0Ec3+48TsIa4zghjLY87nQHIh/ecK9qLdw==
|
||||
circular-dependency-plugin@^5.2.2:
|
||||
version "5.2.2"
|
||||
resolved "https://registry.yarnpkg.com/circular-dependency-plugin/-/circular-dependency-plugin-5.2.2.tgz#39e836079db1d3cf2f988dc48c5188a44058b600"
|
||||
integrity sha512-g38K9Cm5WRwlaH6g03B9OEz/0qRizI+2I7n+Gz+L5DxXJAPAiWQvwlYNm1V1jkdpUv95bOe/ASm2vfi/G560jQ==
|
||||
|
||||
class-utils@^0.3.5:
|
||||
version "0.3.6"
|
||||
@ -4254,27 +4229,13 @@ debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6:
|
||||
dependencies:
|
||||
ms "^2.1.1"
|
||||
|
||||
debug@^4.0.1, debug@^4.1.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
|
||||
integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
|
||||
dependencies:
|
||||
ms "^2.1.1"
|
||||
|
||||
debug@^4.1.1:
|
||||
debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0:
|
||||
version "4.3.1"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
|
||||
integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
|
||||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
debug@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1"
|
||||
integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==
|
||||
dependencies:
|
||||
ms "2.1.2"
|
||||
|
||||
debuglog@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
|
||||
@ -13803,10 +13764,10 @@ uuid@^7.0.3:
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b"
|
||||
integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==
|
||||
|
||||
uuid@^8.1.0:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.1.0.tgz#6f1536eb43249f473abc6bd58ff983da1ca30d8d"
|
||||
integrity sha512-CI18flHDznR0lq54xBycOVmphdCYnQLKn8abKn7PXUiKUGdEd+/l9LWNJmugXel4hXq7S+RMNl34ecyC9TntWg==
|
||||
uuid@^8.3.2:
|
||||
version "8.3.2"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
|
||||
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
||||
|
||||
v8-compile-cache@2.0.3:
|
||||
version "2.0.3"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user