mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Merge remote-tracking branch 'origin/master' into feature/extensions_api
# Conflicts: # package.json # src/renderer/components/cluster-manager/clusters-menu.tsx
This commit is contained in:
commit
b1783d6150
1
.gitignore
vendored
1
.gitignore
vendored
@ -11,3 +11,4 @@ binaries/server/
|
|||||||
src/extensions/*/*.js
|
src/extensions/*/*.js
|
||||||
src/extensions/*/*.d.ts
|
src/extensions/*/*.d.ts
|
||||||
locales/**/**.js
|
locales/**/**.js
|
||||||
|
lens.log
|
||||||
|
|||||||
6
Makefile
6
Makefile
@ -6,7 +6,7 @@ endif
|
|||||||
|
|
||||||
.PHONY: init dev build test clean
|
.PHONY: init dev build test clean
|
||||||
|
|
||||||
init: download-bins install-deps compile-dev
|
init: install-deps download-bins compile-dev
|
||||||
echo "Init done"
|
echo "Init done"
|
||||||
|
|
||||||
download-bins:
|
download-bins:
|
||||||
@ -28,7 +28,7 @@ endif
|
|||||||
lint:
|
lint:
|
||||||
yarn lint
|
yarn lint
|
||||||
|
|
||||||
test:
|
test: download-bins
|
||||||
yarn test
|
yarn test
|
||||||
|
|
||||||
integration-linux:
|
integration-linux:
|
||||||
@ -46,7 +46,7 @@ integration-win:
|
|||||||
test-app:
|
test-app:
|
||||||
yarn test
|
yarn test
|
||||||
|
|
||||||
build: install-deps
|
build: install-deps download-bins
|
||||||
yarn install
|
yarn install
|
||||||
ifeq "$(DETECTED_OS)" "Windows"
|
ifeq "$(DETECTED_OS)" "Windows"
|
||||||
yarn dist:win
|
yarn dist:win
|
||||||
|
|||||||
10
README.md
10
README.md
@ -10,9 +10,9 @@ Lens is the only IDE you’ll ever need to take control of your Kubernetes clust
|
|||||||
|
|
||||||
## What makes Lens special?
|
## What makes Lens special?
|
||||||
|
|
||||||
* Amazing usability and end user experience
|
* Amazing usability and end-user experience
|
||||||
* Multi cluster management; Support for hundreds of clusters
|
* Multi cluster management: support for hundreds of clusters
|
||||||
* Standalone application; No need to install anything in-cluster
|
* Standalone application: no need to install anything in-cluster
|
||||||
* Real-time cluster state visualization
|
* Real-time cluster state visualization
|
||||||
* Resource utilization charts and trends with history powered by built-in Prometheus
|
* Resource utilization charts and trends with history powered by built-in Prometheus
|
||||||
* Terminal access to nodes and containers
|
* Terminal access to nodes and containers
|
||||||
@ -38,7 +38,7 @@ brew cask install lens
|
|||||||
|
|
||||||
## Development (advanced)
|
## Development (advanced)
|
||||||
|
|
||||||
Allows faster separately re-run some of involved processes:
|
Allows for faster separate re-runs of some of the more involved processes:
|
||||||
|
|
||||||
1. `yarn dev:main` compiles electron's main process part and start watching files
|
1. `yarn dev:main` compiles electron's main process part and start watching files
|
||||||
1. `yarn dev:renderer` compiles electron's renderer part and start watching files
|
1. `yarn dev:renderer` compiles electron's renderer part and start watching files
|
||||||
@ -54,4 +54,4 @@ Allows faster separately re-run some of involved processes:
|
|||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Bug reports and pull requests are welcome on GitHub at https://github.com/lensapp/lens.
|
Bug reports and pull requests are welcome on GitHub at https://github.com/lensapp/lens.
|
||||||
16
package.json
16
package.json
@ -2,7 +2,7 @@
|
|||||||
"name": "kontena-lens",
|
"name": "kontena-lens",
|
||||||
"productName": "Lens",
|
"productName": "Lens",
|
||||||
"description": "Lens - The Kubernetes IDE",
|
"description": "Lens - The Kubernetes IDE",
|
||||||
"version": "3.6.0-beta.1",
|
"version": "3.6.0-beta.2",
|
||||||
"main": "static/build/main.js",
|
"main": "static/build/main.js",
|
||||||
"copyright": "© 2020, Mirantis, Inc.",
|
"copyright": "© 2020, Mirantis, Inc.",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -38,8 +38,8 @@
|
|||||||
"rebuild-pty": "yarn run electron-rebuild -f -w node-pty"
|
"rebuild-pty": "yarn run electron-rebuild -f -w node-pty"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"bundledKubectlVersion": "1.17.4",
|
"bundledKubectlVersion": "1.17.11",
|
||||||
"bundledHelmVersion": "3.2.4"
|
"bundledHelmVersion": "3.3.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12.0 <13.0"
|
"node": ">=12.0 <13.0"
|
||||||
@ -172,7 +172,9 @@
|
|||||||
"@types/module-alias": "^2.0.0",
|
"@types/module-alias": "^2.0.0",
|
||||||
"@types/node": "^12.12.45",
|
"@types/node": "^12.12.45",
|
||||||
"@types/proper-lockfile": "^4.1.1",
|
"@types/proper-lockfile": "^4.1.1",
|
||||||
|
"@types/react-beautiful-dnd": "^13.0.0",
|
||||||
"@types/tar": "^4.0.3",
|
"@types/tar": "^4.0.3",
|
||||||
|
"array-move": "^3.0.0",
|
||||||
"chalk": "^4.1.0",
|
"chalk": "^4.1.0",
|
||||||
"conf": "^7.0.1",
|
"conf": "^7.0.1",
|
||||||
"crypto-js": "^4.0.0",
|
"crypto-js": "^4.0.0",
|
||||||
@ -200,6 +202,7 @@
|
|||||||
"openid-client": "^3.15.2",
|
"openid-client": "^3.15.2",
|
||||||
"path-to-regexp": "^6.1.0",
|
"path-to-regexp": "^6.1.0",
|
||||||
"proper-lockfile": "^4.1.1",
|
"proper-lockfile": "^4.1.1",
|
||||||
|
"react-beautiful-dnd": "^13.0.0",
|
||||||
"react-router": "^5.2.0",
|
"react-router": "^5.2.0",
|
||||||
"request": "^2.88.2",
|
"request": "^2.88.2",
|
||||||
"request-promise-native": "^1.0.8",
|
"request-promise-native": "^1.0.8",
|
||||||
@ -258,8 +261,8 @@
|
|||||||
"@types/webpack": "^4.41.17",
|
"@types/webpack": "^4.41.17",
|
||||||
"@types/webpack-env": "^1.15.2",
|
"@types/webpack-env": "^1.15.2",
|
||||||
"@types/webpack-node-externals": "^1.7.1",
|
"@types/webpack-node-externals": "^1.7.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^3.4.0",
|
"@typescript-eslint/eslint-plugin": "^4.0.0",
|
||||||
"@typescript-eslint/parser": "^3.4.0",
|
"@typescript-eslint/parser": "^4.0.0",
|
||||||
"ace-builds": "^1.4.11",
|
"ace-builds": "^1.4.11",
|
||||||
"ansi_up": "^4.0.4",
|
"ansi_up": "^4.0.4",
|
||||||
"babel-core": "^7.0.0-beta.3",
|
"babel-core": "^7.0.0-beta.3",
|
||||||
@ -270,7 +273,6 @@
|
|||||||
"circular-dependency-plugin": "^5.2.0",
|
"circular-dependency-plugin": "^5.2.0",
|
||||||
"color": "^3.1.2",
|
"color": "^3.1.2",
|
||||||
"concurrently": "^5.2.0",
|
"concurrently": "^5.2.0",
|
||||||
"cross-env": "^7.0.2",
|
|
||||||
"css-element-queries": "^1.2.3",
|
"css-element-queries": "^1.2.3",
|
||||||
"css-loader": "^3.5.3",
|
"css-loader": "^3.5.3",
|
||||||
"dompurify": "^2.0.11",
|
"dompurify": "^2.0.11",
|
||||||
@ -278,7 +280,7 @@
|
|||||||
"electron-builder": "^22.7.0",
|
"electron-builder": "^22.7.0",
|
||||||
"electron-notarize": "^0.3.0",
|
"electron-notarize": "^0.3.0",
|
||||||
"electron-rebuild": "^1.11.0",
|
"electron-rebuild": "^1.11.0",
|
||||||
"eslint": "^7.3.1",
|
"eslint": "^7.7.0",
|
||||||
"file-loader": "^6.0.0",
|
"file-loader": "^6.0.0",
|
||||||
"flex.box": "^3.4.4",
|
"flex.box": "^3.4.4",
|
||||||
"fork-ts-checker-webpack-plugin": "^5.0.0",
|
"fork-ts-checker-webpack-plugin": "^5.0.0",
|
||||||
|
|||||||
@ -13,6 +13,7 @@ export const clusterIpc = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
activate: createIpcChannel({
|
activate: createIpcChannel({
|
||||||
channel: "cluster:activate",
|
channel: "cluster:activate",
|
||||||
handle: (clusterId: ClusterId) => {
|
handle: (clusterId: ClusterId) => {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import type { WorkspaceId } from "./workspace-store";
|
import { WorkspaceId, workspaceStore } from "./workspace-store";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { app, ipcRenderer, remote } from "electron";
|
import { app, ipcRenderer, remote } from "electron";
|
||||||
import { unlink } from "fs-extra";
|
import { unlink } from "fs-extra";
|
||||||
@ -11,6 +11,9 @@ import { tracker } from "./tracker";
|
|||||||
import { dumpConfigYaml } from "./kube-helpers";
|
import { dumpConfigYaml } from "./kube-helpers";
|
||||||
import { saveToAppFiles } from "./utils/saveToAppFiles";
|
import { saveToAppFiles } from "./utils/saveToAppFiles";
|
||||||
import { KubeConfig } from "@kubernetes/client-node";
|
import { KubeConfig } from "@kubernetes/client-node";
|
||||||
|
import _ from "lodash";
|
||||||
|
import move from "array-move";
|
||||||
|
import { is } from "immer/dist/internal";
|
||||||
|
|
||||||
export interface ClusterIconUpload {
|
export interface ClusterIconUpload {
|
||||||
clusterId: string;
|
clusterId: string;
|
||||||
@ -48,6 +51,7 @@ export interface ClusterPreferences {
|
|||||||
prometheusProvider?: {
|
prometheusProvider?: {
|
||||||
type: string;
|
type: string;
|
||||||
};
|
};
|
||||||
|
iconOrder?: number;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
httpsProxy?: string;
|
httpsProxy?: string;
|
||||||
}
|
}
|
||||||
@ -60,7 +64,7 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
|||||||
static embedCustomKubeConfig(clusterId: ClusterId, kubeConfig: KubeConfig | string): string {
|
static embedCustomKubeConfig(clusterId: ClusterId, kubeConfig: KubeConfig | string): string {
|
||||||
const filePath = ClusterStore.getCustomKubeConfigPath(clusterId);
|
const filePath = ClusterStore.getCustomKubeConfigPath(clusterId);
|
||||||
const fileContents = typeof kubeConfig == "string" ? kubeConfig : dumpConfigYaml(kubeConfig);
|
const fileContents = typeof kubeConfig == "string" ? kubeConfig : dumpConfigYaml(kubeConfig);
|
||||||
saveToAppFiles(filePath, fileContents);
|
saveToAppFiles(filePath, fileContents, { mode: 0o600});
|
||||||
return filePath;
|
return filePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,6 +105,20 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
|||||||
this.activeClusterId = id;
|
this.activeClusterId = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@action
|
||||||
|
swapIconOrders(workspace: WorkspaceId, from: number, to: number) {
|
||||||
|
const clusters = this.getByWorkspaceId(workspace);
|
||||||
|
if (from < 0 || to < 0 || from >= clusters.length || to >= clusters.length || isNaN(from) || isNaN(to)) {
|
||||||
|
throw new Error(`invalid from<->to arguments`)
|
||||||
|
}
|
||||||
|
|
||||||
|
move.mutate(clusters, from, to);
|
||||||
|
for (const i in clusters) {
|
||||||
|
// This resets the iconOrder to the current display order
|
||||||
|
clusters[i].preferences.iconOrder = +i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
hasClusters() {
|
hasClusters() {
|
||||||
return this.clusters.size > 0;
|
return this.clusters.size > 0;
|
||||||
}
|
}
|
||||||
@ -114,7 +132,9 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getByWorkspaceId(workspaceId: string): Cluster[] {
|
getByWorkspaceId(workspaceId: string): Cluster[] {
|
||||||
return this.clustersList.filter(cluster => cluster.workspace === workspaceId)
|
const clusters = Array.from(this.clusters.values())
|
||||||
|
.filter(cluster => cluster.workspace === workspaceId);
|
||||||
|
return _.sortBy(clusters, cluster => cluster.preferences.iconOrder)
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
@ -156,7 +176,7 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
|||||||
const removedClusters = new Map<ClusterId, Cluster>();
|
const removedClusters = new Map<ClusterId, Cluster>();
|
||||||
|
|
||||||
// update new clusters
|
// update new clusters
|
||||||
clusters.forEach(clusterModel => {
|
for (const clusterModel of clusters) {
|
||||||
let cluster = currentClusters.get(clusterModel.id);
|
let cluster = currentClusters.get(clusterModel.id);
|
||||||
if (cluster) {
|
if (cluster) {
|
||||||
cluster.updateModel(clusterModel);
|
cluster.updateModel(clusterModel);
|
||||||
@ -164,7 +184,7 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
|
|||||||
cluster = new Cluster(clusterModel);
|
cluster = new Cluster(clusterModel);
|
||||||
}
|
}
|
||||||
newClusters.set(clusterModel.id, cluster);
|
newClusters.set(clusterModel.id, cluster);
|
||||||
});
|
}
|
||||||
|
|
||||||
// update removed clusters
|
// update removed clusters
|
||||||
currentClusters.forEach(cluster => {
|
currentClusters.forEach(cluster => {
|
||||||
|
|||||||
@ -93,6 +93,34 @@ describe("empty config", () => {
|
|||||||
expect(fs.readFileSync(file, "utf8")).toBe("kubeconfig");
|
expect(fs.readFileSync(file, "utf8")).toBe("kubeconfig");
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("check if reorderring works for same from and to", () => {
|
||||||
|
clusterStore.swapIconOrders("workstation", 1, 1)
|
||||||
|
|
||||||
|
const clusters = clusterStore.getByWorkspaceId("workstation");
|
||||||
|
expect(clusters[0].id).toBe("prod")
|
||||||
|
expect(clusters[0].preferences.iconOrder).toBe(0)
|
||||||
|
expect(clusters[1].id).toBe("dev")
|
||||||
|
expect(clusters[1].preferences.iconOrder).toBe(1)
|
||||||
|
});
|
||||||
|
|
||||||
|
it("check if reorderring works for different from and to", () => {
|
||||||
|
clusterStore.swapIconOrders("workstation", 0, 1)
|
||||||
|
|
||||||
|
const clusters = clusterStore.getByWorkspaceId("workstation");
|
||||||
|
expect(clusters[0].id).toBe("dev")
|
||||||
|
expect(clusters[0].preferences.iconOrder).toBe(0)
|
||||||
|
expect(clusters[1].id).toBe("prod")
|
||||||
|
expect(clusters[1].preferences.iconOrder).toBe(1)
|
||||||
|
});
|
||||||
|
|
||||||
|
it("check if after icon reordering, changing workspaces still works", () => {
|
||||||
|
clusterStore.swapIconOrders("workstation", 1, 1)
|
||||||
|
clusterStore.getById("prod").workspace = "default"
|
||||||
|
|
||||||
|
expect(clusterStore.getByWorkspaceId("workstation").length).toBe(1);
|
||||||
|
expect(clusterStore.getByWorkspaceId("default").length).toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
it("removes cluster from store", async () => {
|
it("removes cluster from store", async () => {
|
||||||
await clusterStore.removeById("foo");
|
await clusterStore.removeById("foo");
|
||||||
expect(clusterStore.getById("foo")).toBeUndefined();
|
expect(clusterStore.getById("foo")).toBeUndefined();
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import type { ThemeId } from "../renderer/theme.store";
|
import type { ThemeId } from "../renderer/theme.store";
|
||||||
|
import { app, remote } from 'electron';
|
||||||
import semver from "semver"
|
import semver from "semver"
|
||||||
import { readFile } from "fs-extra"
|
import { readFile } from "fs-extra"
|
||||||
import { action, observable, reaction, toJS } from "mobx";
|
import { action, observable, reaction, toJS } from "mobx";
|
||||||
@ -8,6 +9,7 @@ import { getAppVersion } from "./utils/app-version";
|
|||||||
import { kubeConfigDefaultPath, loadConfig } from "./kube-helpers";
|
import { kubeConfigDefaultPath, loadConfig } from "./kube-helpers";
|
||||||
import { tracker } from "./tracker";
|
import { tracker } from "./tracker";
|
||||||
import logger from "../main/logger";
|
import logger from "../main/logger";
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
export interface UserStoreModel {
|
export interface UserStoreModel {
|
||||||
kubeConfigPath: string;
|
kubeConfigPath: string;
|
||||||
@ -22,6 +24,9 @@ export interface UserPreferences {
|
|||||||
allowUntrustedCAs?: boolean;
|
allowUntrustedCAs?: boolean;
|
||||||
allowTelemetry?: boolean;
|
allowTelemetry?: boolean;
|
||||||
downloadMirror?: string | "default";
|
downloadMirror?: string | "default";
|
||||||
|
downloadKubectlBinaries?: boolean;
|
||||||
|
downloadBinariesPath?: string;
|
||||||
|
kubectlBinariesPath?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UserStore extends BaseStore<UserStoreModel> {
|
export class UserStore extends BaseStore<UserStoreModel> {
|
||||||
@ -53,6 +58,9 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
|||||||
allowUntrustedCAs: false,
|
allowUntrustedCAs: false,
|
||||||
colorTheme: UserStore.defaultTheme,
|
colorTheme: UserStore.defaultTheme,
|
||||||
downloadMirror: "default",
|
downloadMirror: "default",
|
||||||
|
downloadKubectlBinaries: true, // Download kubectl binaries matching cluster version
|
||||||
|
downloadBinariesPath: this.getDefaultKubectlPath(),
|
||||||
|
kubectlBinariesPath: ""
|
||||||
};
|
};
|
||||||
|
|
||||||
get isNewVersion() {
|
get isNewVersion() {
|
||||||
@ -98,6 +106,14 @@ export class UserStore extends BaseStore<UserStoreModel> {
|
|||||||
this.newContexts.clear();
|
this.newContexts.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getting default directory to download kubectl binaries
|
||||||
|
* @returns string
|
||||||
|
*/
|
||||||
|
getDefaultKubectlPath(): string {
|
||||||
|
return path.join((app || remote.app).getPath("userData"), "binaries")
|
||||||
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
protected async fromStore(data: Partial<UserStoreModel> = {}) {
|
protected async fromStore(data: Partial<UserStoreModel> = {}) {
|
||||||
const { lastSeenAppVersion, seenContexts = [], preferences, kubeConfigPath } = data
|
const { lastSeenAppVersion, seenContexts = [], preferences, kubeConfigPath } = data
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
// Save file to electron app directory (e.g. "/Users/$USER/Library/Application Support/Lens" for MacOS)
|
// Save file to electron app directory (e.g. "/Users/$USER/Library/Application Support/Lens" for MacOS)
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { app, remote } from "electron";
|
import { app, remote } from "electron";
|
||||||
import { ensureDirSync, writeFileSync } from "fs-extra";
|
import { ensureDirSync, writeFileSync, WriteFileOptions } from "fs-extra";
|
||||||
|
|
||||||
export function saveToAppFiles(filePath: string, contents: any): string {
|
export function saveToAppFiles(filePath: string, contents: any, options?: WriteFileOptions): string {
|
||||||
const absPath = path.resolve((app || remote.app).getPath("userData"), filePath);
|
const absPath = path.resolve((app || remote.app).getPath("userData"), filePath);
|
||||||
ensureDirSync(path.dirname(absPath));
|
ensureDirSync(path.dirname(absPath));
|
||||||
writeFileSync(absPath, contents);
|
writeFileSync(absPath, contents, options);
|
||||||
return absPath;
|
return absPath;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,8 +8,8 @@ export const isMac = process.platform === "darwin"
|
|||||||
export const isWindows = process.platform === "win32"
|
export const isWindows = process.platform === "win32"
|
||||||
export const isDebugging = process.env.DEBUG === "true";
|
export const isDebugging = process.env.DEBUG === "true";
|
||||||
export const isProduction = process.env.NODE_ENV === "production"
|
export const isProduction = process.env.NODE_ENV === "production"
|
||||||
export const isDevelopment = isDebugging || !isProduction;
|
|
||||||
export const isTestEnv = !!process.env.JEST_WORKER_ID;
|
export const isTestEnv = !!process.env.JEST_WORKER_ID;
|
||||||
|
export const isDevelopment = !isTestEnv && !isProduction;
|
||||||
|
|
||||||
export const appName = `${packageInfo.productName}${isDevelopment ? "Dev" : ""}`
|
export const appName = `${packageInfo.productName}${isDevelopment ? "Dev" : ""}`
|
||||||
export const publicPath = "/build/"
|
export const publicPath = "/build/"
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import type { ClusterId, ClusterModel, ClusterPreferences } from "../common/clus
|
|||||||
import type { IMetricsReqParams } from "../renderer/api/endpoints/metrics.api";
|
import type { IMetricsReqParams } from "../renderer/api/endpoints/metrics.api";
|
||||||
import type { WorkspaceId } from "../common/workspace-store";
|
import type { WorkspaceId } from "../common/workspace-store";
|
||||||
import type { FeatureStatusMap } from "./feature"
|
import type { FeatureStatusMap } from "./feature"
|
||||||
import { action, computed, observable, reaction, toJS, when } from "mobx";
|
import { action, computed, intercept, observable, reaction, toJS, when } from "mobx";
|
||||||
import { apiKubePrefix } from "../common/vars";
|
import { apiKubePrefix } from "../common/vars";
|
||||||
import { broadcastIpc } from "../common/ipc";
|
import { broadcastIpc } from "../common/ipc";
|
||||||
import { ContextHandler } from "./context-handler"
|
import { ContextHandler } from "./context-handler"
|
||||||
@ -130,7 +130,7 @@ export class Cluster implements ClusterModel {
|
|||||||
if (!this.eventDisposers.length) {
|
if (!this.eventDisposers.length) {
|
||||||
this.bindEvents();
|
this.bindEvents();
|
||||||
}
|
}
|
||||||
if (this.disconnected) {
|
if (this.disconnected || !this.accessible) {
|
||||||
await this.reconnect();
|
await this.reconnect();
|
||||||
}
|
}
|
||||||
await this.refresh();
|
await this.refresh();
|
||||||
|
|||||||
@ -46,6 +46,7 @@ export class HelmRepoManager extends Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
|
helmCli.setLogger(logger)
|
||||||
await helmCli.ensureBinary();
|
await helmCli.ensureBinary();
|
||||||
if (!this.initialized) {
|
if (!this.initialized) {
|
||||||
this.helmEnv = await this.parseHelmEnv()
|
this.helmEnv = await this.parseHelmEnv()
|
||||||
|
|||||||
@ -21,7 +21,7 @@ import logger from "./logger"
|
|||||||
|
|
||||||
const workingDir = path.join(app.getPath("appData"), appName);
|
const workingDir = path.join(app.getPath("appData"), appName);
|
||||||
app.setName(appName);
|
app.setName(appName);
|
||||||
if(!process.env.CICD) {
|
if (!process.env.CICD) {
|
||||||
app.setPath("userData", workingDir);
|
app.setPath("userData", workingDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,7 +49,8 @@ async function main() {
|
|||||||
try {
|
try {
|
||||||
proxyPort = await getFreePort()
|
proxyPort = await getFreePort()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await dialog.showErrorBox("Lens Error", "Could not find a free port for the cluster proxy")
|
logger.error(error)
|
||||||
|
dialog.showErrorBox("Lens Error", "Could not find a free port for the cluster proxy")
|
||||||
app.quit();
|
app.quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +69,7 @@ async function main() {
|
|||||||
proxyServer = LensProxy.create(proxyPort, clusterManager);
|
proxyServer = LensProxy.create(proxyPort, clusterManager);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`Could not start proxy (127.0.0:${proxyPort}): ${error.message}`)
|
logger.error(`Could not start proxy (127.0.0:${proxyPort}): ${error.message}`)
|
||||||
await dialog.showErrorBox("Lens Error", `Could not start proxy (127.0.0:${proxyPort}): ${error.message || "unknown error"}`)
|
dialog.showErrorBox("Lens Error", `Could not start proxy (127.0.0:${proxyPort}): ${error.message || "unknown error"}`)
|
||||||
app.quit();
|
app.quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -44,6 +44,10 @@ export class KubeAuthProxy {
|
|||||||
}
|
}
|
||||||
logger.debug(`spawning kubectl proxy with args: ${args}`)
|
logger.debug(`spawning kubectl proxy with args: ${args}`)
|
||||||
this.proxyProcess = spawn(proxyBin, args, { env: this.env, })
|
this.proxyProcess = spawn(proxyBin, args, { env: this.env, })
|
||||||
|
this.proxyProcess.on("error", (error) => {
|
||||||
|
this.sendIpcLogMessage({ data: error.message, error: true })
|
||||||
|
this.exit()
|
||||||
|
})
|
||||||
|
|
||||||
this.proxyProcess.on("exit", (code) => {
|
this.proxyProcess.on("exit", (code) => {
|
||||||
this.sendIpcLogMessage({ data: `proxy exited with code: ${code}`, error: code > 0 })
|
this.sendIpcLogMessage({ data: `proxy exited with code: ${code}`, error: code > 0 })
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import { helmCli } from "./helm/helm-cli"
|
|||||||
import { userStore } from "../common/user-store"
|
import { userStore } from "../common/user-store"
|
||||||
import { customRequest } from "../common/request";
|
import { customRequest } from "../common/request";
|
||||||
import { getBundledKubectlVersion } from "../common/utils/app-version"
|
import { getBundledKubectlVersion } from "../common/utils/app-version"
|
||||||
import { isDevelopment, isWindows } from "../common/vars";
|
import { isDevelopment, isWindows, isTestEnv } from "../common/vars";
|
||||||
|
|
||||||
const bundledVersion = getBundledKubectlVersion()
|
const bundledVersion = getBundledKubectlVersion()
|
||||||
const kubectlMap: Map<string, string> = new Map([
|
const kubectlMap: Map<string, string> = new Map([
|
||||||
@ -22,9 +22,10 @@ const kubectlMap: Map<string, string> = new Map([
|
|||||||
["1.13", "1.13.12"],
|
["1.13", "1.13.12"],
|
||||||
["1.14", "1.14.10"],
|
["1.14", "1.14.10"],
|
||||||
["1.15", "1.15.11"],
|
["1.15", "1.15.11"],
|
||||||
["1.16", "1.16.8"],
|
["1.16", "1.16.14"],
|
||||||
["1.17", bundledVersion],
|
["1.17", bundledVersion],
|
||||||
["1.18", "1.18.0"]
|
["1.18", "1.18.8"],
|
||||||
|
["1.19", "1.19.0"]
|
||||||
])
|
])
|
||||||
|
|
||||||
const packageMirrors: Map<string, string> = new Map([
|
const packageMirrors: Map<string, string> = new Map([
|
||||||
@ -35,8 +36,9 @@ const packageMirrors: Map<string, string> = new Map([
|
|||||||
let bundledPath: string
|
let bundledPath: string
|
||||||
const initScriptVersionString = "# lens-initscript v3\n"
|
const initScriptVersionString = "# lens-initscript v3\n"
|
||||||
|
|
||||||
if (isDevelopment) {
|
if (isDevelopment || isTestEnv) {
|
||||||
bundledPath = path.join(process.cwd(), "binaries", "client", process.platform, process.arch, "kubectl")
|
const platformName = isWindows ? "windows" : process.platform
|
||||||
|
bundledPath = path.join(process.cwd(), "binaries", "client", platformName, process.arch, "kubectl")
|
||||||
} else {
|
} else {
|
||||||
bundledPath = path.join(process.resourcesPath, process.arch, "kubectl")
|
bundledPath = path.join(process.resourcesPath, process.arch, "kubectl")
|
||||||
}
|
}
|
||||||
@ -58,6 +60,7 @@ export class Kubectl {
|
|||||||
|
|
||||||
public static readonly bundledKubectlPath = bundledPath
|
public static readonly bundledKubectlPath = bundledPath
|
||||||
public static readonly bundledKubectlVersion: string = bundledVersion
|
public static readonly bundledKubectlVersion: string = bundledVersion
|
||||||
|
public static invalidBundle = false
|
||||||
private static bundledInstance: Kubectl;
|
private static bundledInstance: Kubectl;
|
||||||
|
|
||||||
// Returns the single bundled Kubectl instance
|
// Returns the single bundled Kubectl instance
|
||||||
@ -94,13 +97,38 @@ export class Kubectl {
|
|||||||
|
|
||||||
this.url = `${this.getDownloadMirror()}/v${this.kubectlVersion}/bin/${platformName}/${arch}/${binaryName}`
|
this.url = `${this.getDownloadMirror()}/v${this.kubectlVersion}/bin/${platformName}/${arch}/${binaryName}`
|
||||||
|
|
||||||
this.dirname = path.normalize(path.join(Kubectl.kubectlDir, this.kubectlVersion))
|
this.dirname = path.normalize(path.join(this.getDownloadDir(), this.kubectlVersion))
|
||||||
this.path = path.join(this.dirname, binaryName)
|
this.path = path.join(this.dirname, binaryName)
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getPath(): Promise<string> {
|
public getBundledPath() {
|
||||||
|
return Kubectl.bundledKubectlPath
|
||||||
|
}
|
||||||
|
|
||||||
|
public getPathFromPreferences() {
|
||||||
|
return userStore.preferences?.kubectlBinariesPath || this.getBundledPath()
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getDownloadDir() {
|
||||||
|
return userStore.preferences?.downloadBinariesPath || Kubectl.kubectlDir
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getPath(bundled = false): Promise<string> {
|
||||||
|
if (userStore.preferences?.downloadKubectlBinaries === false) {
|
||||||
|
return this.getPathFromPreferences()
|
||||||
|
}
|
||||||
|
|
||||||
|
// return binary name if bundled path is not functional
|
||||||
|
if (!await this.checkBinary(this.getBundledPath(), false)) {
|
||||||
|
Kubectl.invalidBundle = true
|
||||||
|
return path.basename(bundledPath)
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.ensureKubectl()
|
if (!await this.ensureKubectl()) {
|
||||||
|
logger.error("Failed to ensure kubectl, fallback to the bundled version")
|
||||||
|
return Kubectl.bundledKubectlPath
|
||||||
|
}
|
||||||
return this.path
|
return this.path
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error("Failed to ensure kubectl, fallback to the bundled version")
|
logger.error("Failed to ensure kubectl, fallback to the bundled version")
|
||||||
@ -112,6 +140,7 @@ export class Kubectl {
|
|||||||
public async binDir() {
|
public async binDir() {
|
||||||
try {
|
try {
|
||||||
await this.ensureKubectl()
|
await this.ensureKubectl()
|
||||||
|
await this.writeInitScripts()
|
||||||
return this.dirname
|
return this.dirname
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(err)
|
logger.error(err)
|
||||||
@ -119,16 +148,15 @@ export class Kubectl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async checkBinary(checkVersion = true) {
|
public async checkBinary(path: string, checkVersion = true) {
|
||||||
const exists = await pathExists(this.path)
|
const exists = await pathExists(path)
|
||||||
if (exists) {
|
if (exists) {
|
||||||
if (!checkVersion) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { stdout } = await promiseExec(`"${this.path}" version --client=true -o json`)
|
const { stdout } = await promiseExec(`"${path}" version --client=true -o json`)
|
||||||
const output = JSON.parse(stdout)
|
const output = JSON.parse(stdout)
|
||||||
|
if (!checkVersion) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
let version: string = output.clientVersion.gitVersion
|
let version: string = output.clientVersion.gitVersion
|
||||||
if (version[0] === 'v') {
|
if (version[0] === 'v') {
|
||||||
version = version.slice(1)
|
version = version.slice(1)
|
||||||
@ -165,20 +193,32 @@ export class Kubectl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async ensureKubectl(): Promise<boolean> {
|
public async ensureKubectl(): Promise<boolean> {
|
||||||
|
if (userStore.preferences?.downloadKubectlBinaries === false) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (Kubectl.invalidBundle) {
|
||||||
|
logger.error(`Detected invalid bundle binary, returning ...`)
|
||||||
|
return false
|
||||||
|
}
|
||||||
await ensureDir(this.dirname, 0o755)
|
await ensureDir(this.dirname, 0o755)
|
||||||
return lockFile.lock(this.dirname).then(async (release) => {
|
return lockFile.lock(this.dirname).then(async (release) => {
|
||||||
logger.debug(`Acquired a lock for ${this.kubectlVersion}`)
|
logger.debug(`Acquired a lock for ${this.kubectlVersion}`)
|
||||||
const bundled = await this.checkBundled()
|
const bundled = await this.checkBundled()
|
||||||
const isValid = await this.checkBinary(!bundled)
|
let isValid = await this.checkBinary(this.path, !bundled)
|
||||||
if (!isValid) {
|
if (!isValid && !bundled) {
|
||||||
await this.downloadKubectl().catch((error) => {
|
await this.downloadKubectl().catch((error) => {
|
||||||
logger.error(error)
|
logger.error(error)
|
||||||
|
logger.debug(`Releasing lock for ${this.kubectlVersion}`)
|
||||||
|
release()
|
||||||
|
return false
|
||||||
});
|
});
|
||||||
|
isValid = !await this.checkBinary(this.path, false)
|
||||||
|
}
|
||||||
|
if(!isValid) {
|
||||||
|
logger.debug(`Releasing lock for ${this.kubectlVersion}`)
|
||||||
|
release()
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
await this.writeInitScripts().catch((error) => {
|
|
||||||
logger.error("Failed to write init scripts");
|
|
||||||
logger.error(error)
|
|
||||||
})
|
|
||||||
logger.debug(`Releasing lock for ${this.kubectlVersion}`)
|
logger.debug(`Releasing lock for ${this.kubectlVersion}`)
|
||||||
release()
|
release()
|
||||||
return true
|
return true
|
||||||
@ -221,71 +261,52 @@ export class Kubectl {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async scriptIsLatest(scriptPath: string) {
|
|
||||||
const scriptExists = await pathExists(scriptPath)
|
|
||||||
if (!scriptExists) return false
|
|
||||||
|
|
||||||
try {
|
|
||||||
const filehandle = await fs.promises.open(scriptPath, 'r')
|
|
||||||
const buffer = Buffer.alloc(40)
|
|
||||||
await filehandle.read(buffer, 0, 40, 0)
|
|
||||||
await filehandle.close()
|
|
||||||
return buffer.toString().startsWith(initScriptVersionString)
|
|
||||||
} catch (err) {
|
|
||||||
logger.error(err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async writeInitScripts() {
|
protected async writeInitScripts() {
|
||||||
|
const kubectlPath = userStore.preferences?.downloadKubectlBinaries ? this.dirname : path.dirname(this.getPathFromPreferences())
|
||||||
const helmPath = helmCli.getBinaryDir()
|
const helmPath = helmCli.getBinaryDir()
|
||||||
const fsPromises = fs.promises;
|
const fsPromises = fs.promises;
|
||||||
const bashScriptPath = path.join(this.dirname, '.bash_set_path')
|
const bashScriptPath = path.join(this.dirname, '.bash_set_path')
|
||||||
const bashScriptIsLatest = await this.scriptIsLatest(bashScriptPath)
|
|
||||||
if (!bashScriptIsLatest) {
|
let bashScript = "" + initScriptVersionString
|
||||||
let bashScript = "" + initScriptVersionString
|
bashScript += "tempkubeconfig=\"$KUBECONFIG\"\n"
|
||||||
bashScript += "tempkubeconfig=\"$KUBECONFIG\"\n"
|
bashScript += "test -f \"/etc/profile\" && . \"/etc/profile\"\n"
|
||||||
bashScript += "test -f \"/etc/profile\" && . \"/etc/profile\"\n"
|
bashScript += "if test -f \"$HOME/.bash_profile\"; then\n"
|
||||||
bashScript += "if test -f \"$HOME/.bash_profile\"; then\n"
|
bashScript += " . \"$HOME/.bash_profile\"\n"
|
||||||
bashScript += " . \"$HOME/.bash_profile\"\n"
|
bashScript += "elif test -f \"$HOME/.bash_login\"; then\n"
|
||||||
bashScript += "elif test -f \"$HOME/.bash_login\"; then\n"
|
bashScript += " . \"$HOME/.bash_login\"\n"
|
||||||
bashScript += " . \"$HOME/.bash_login\"\n"
|
bashScript += "elif test -f \"$HOME/.profile\"; then\n"
|
||||||
bashScript += "elif test -f \"$HOME/.profile\"; then\n"
|
bashScript += " . \"$HOME/.profile\"\n"
|
||||||
bashScript += " . \"$HOME/.profile\"\n"
|
bashScript += "fi\n"
|
||||||
bashScript += "fi\n"
|
bashScript += `export PATH="${helmPath}:${kubectlPath}:$PATH"\n`
|
||||||
bashScript += `export PATH="${this.dirname}:${helmPath}:$PATH"\n`
|
bashScript += "export KUBECONFIG=\"$tempkubeconfig\"\n"
|
||||||
bashScript += "export KUBECONFIG=\"$tempkubeconfig\"\n"
|
bashScript += "unset tempkubeconfig\n"
|
||||||
bashScript += "unset tempkubeconfig\n"
|
await fsPromises.writeFile(bashScriptPath, bashScript.toString(), { mode: 0o644 })
|
||||||
await fsPromises.writeFile(bashScriptPath, bashScript.toString(), { mode: 0o644 })
|
|
||||||
}
|
|
||||||
|
|
||||||
const zshScriptPath = path.join(this.dirname, '.zlogin')
|
const zshScriptPath = path.join(this.dirname, '.zlogin')
|
||||||
const zshScriptIsLatest = await this.scriptIsLatest(zshScriptPath)
|
|
||||||
if (!zshScriptIsLatest) {
|
|
||||||
let zshScript = "" + initScriptVersionString
|
|
||||||
|
|
||||||
zshScript += "tempkubeconfig=\"$KUBECONFIG\"\n"
|
let zshScript = "" + initScriptVersionString
|
||||||
// restore previous ZDOTDIR
|
|
||||||
zshScript += "export ZDOTDIR=\"$OLD_ZDOTDIR\"\n"
|
|
||||||
// source all the files
|
|
||||||
zshScript += "test -f \"$OLD_ZDOTDIR/.zshenv\" && . \"$OLD_ZDOTDIR/.zshenv\"\n"
|
|
||||||
zshScript += "test -f \"$OLD_ZDOTDIR/.zprofile\" && . \"$OLD_ZDOTDIR/.zprofile\"\n"
|
|
||||||
zshScript += "test -f \"$OLD_ZDOTDIR/.zlogin\" && . \"$OLD_ZDOTDIR/.zlogin\"\n"
|
|
||||||
zshScript += "test -f \"$OLD_ZDOTDIR/.zshrc\" && . \"$OLD_ZDOTDIR/.zshrc\"\n"
|
|
||||||
|
|
||||||
// voodoo to replace any previous occurrences of kubectl path in the PATH
|
zshScript += "tempkubeconfig=\"$KUBECONFIG\"\n"
|
||||||
zshScript += `kubectlpath=\"${this.dirname}"\n`
|
// restore previous ZDOTDIR
|
||||||
zshScript += `helmpath=\"${helmPath}"\n`
|
zshScript += "export ZDOTDIR=\"$OLD_ZDOTDIR\"\n"
|
||||||
zshScript += "p=\":$kubectlpath:\"\n"
|
// source all the files
|
||||||
zshScript += "d=\":$PATH:\"\n"
|
zshScript += "test -f \"$OLD_ZDOTDIR/.zshenv\" && . \"$OLD_ZDOTDIR/.zshenv\"\n"
|
||||||
zshScript += "d=${d//$p/:}\n"
|
zshScript += "test -f \"$OLD_ZDOTDIR/.zprofile\" && . \"$OLD_ZDOTDIR/.zprofile\"\n"
|
||||||
zshScript += "d=${d/#:/}\n"
|
zshScript += "test -f \"$OLD_ZDOTDIR/.zlogin\" && . \"$OLD_ZDOTDIR/.zlogin\"\n"
|
||||||
zshScript += "export PATH=\"$kubectlpath:$helmpath:${d/%:/}\"\n"
|
zshScript += "test -f \"$OLD_ZDOTDIR/.zshrc\" && . \"$OLD_ZDOTDIR/.zshrc\"\n"
|
||||||
zshScript += "export KUBECONFIG=\"$tempkubeconfig\"\n"
|
|
||||||
zshScript += "unset tempkubeconfig\n"
|
// voodoo to replace any previous occurrences of kubectl path in the PATH
|
||||||
zshScript += "unset OLD_ZDOTDIR\n"
|
zshScript += `kubectlpath=\"${kubectlPath}"\n`
|
||||||
await fsPromises.writeFile(zshScriptPath, zshScript.toString(), { mode: 0o644 })
|
zshScript += `helmpath=\"${helmPath}"\n`
|
||||||
}
|
zshScript += "p=\":$kubectlpath:\"\n"
|
||||||
|
zshScript += "d=\":$PATH:\"\n"
|
||||||
|
zshScript += "d=${d//$p/:}\n"
|
||||||
|
zshScript += "d=${d/#:/}\n"
|
||||||
|
zshScript += "export PATH=\"$helmpath:$kubectlpath:${d/%:/}\"\n"
|
||||||
|
zshScript += "export KUBECONFIG=\"$tempkubeconfig\"\n"
|
||||||
|
zshScript += "unset tempkubeconfig\n"
|
||||||
|
zshScript += "unset OLD_ZDOTDIR\n"
|
||||||
|
await fsPromises.writeFile(zshScriptPath, zshScript.toString(), { mode: 0o644 })
|
||||||
}
|
}
|
||||||
|
|
||||||
protected getDownloadMirror() {
|
protected getDownloadMirror() {
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
import packageInfo from "../../package.json"
|
import packageInfo from "../../package.json"
|
||||||
|
import path from "path"
|
||||||
import { bundledKubectl, Kubectl } from "../../src/main/kubectl";
|
import { bundledKubectl, Kubectl } from "../../src/main/kubectl";
|
||||||
|
import { isWindows } from "../common/vars";
|
||||||
|
|
||||||
jest.mock("../common/user-store");
|
jest.mock("../common/user-store");
|
||||||
|
|
||||||
@ -15,3 +17,29 @@ describe("kubectlVersion", () => {
|
|||||||
expect(kubectl.kubectlVersion).toBe(bundledKubectl.kubectlVersion)
|
expect(kubectl.kubectlVersion).toBe(bundledKubectl.kubectlVersion)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("getPath()", () => {
|
||||||
|
it("returns path to downloaded kubectl binary", async () => {
|
||||||
|
const { bundledKubectlVersion } = packageInfo.config;
|
||||||
|
const kubectl = new Kubectl(bundledKubectlVersion);
|
||||||
|
const kubectlPath = await kubectl.getPath()
|
||||||
|
let binaryName = "kubectl"
|
||||||
|
if (isWindows) {
|
||||||
|
binaryName += ".exe"
|
||||||
|
}
|
||||||
|
const expectedPath = path.join(Kubectl.kubectlDir, Kubectl.bundledKubectlVersion, binaryName)
|
||||||
|
expect(kubectlPath).toBe(expectedPath)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns plain binary name if bundled kubectl is non-functional", async () => {
|
||||||
|
const { bundledKubectlVersion } = packageInfo.config;
|
||||||
|
const kubectl = new Kubectl(bundledKubectlVersion);
|
||||||
|
jest.spyOn(kubectl, "getBundledPath").mockReturnValue("/invalid/path/kubectl")
|
||||||
|
const kubectlPath = await kubectl.getPath()
|
||||||
|
let binaryName = "kubectl"
|
||||||
|
if (isWindows) {
|
||||||
|
binaryName += ".exe"
|
||||||
|
}
|
||||||
|
expect(kubectlPath).toBe(binaryName)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import path from "path"
|
import path from "path"
|
||||||
import fs from "fs"
|
import fs from "fs"
|
||||||
import request from "request"
|
import request from "request"
|
||||||
import logger from "./logger"
|
|
||||||
import { ensureDir, pathExists } from "fs-extra"
|
import { ensureDir, pathExists } from "fs-extra"
|
||||||
import * as tar from "tar"
|
import * as tar from "tar"
|
||||||
import { isWindows } from "../common/vars";
|
import { isWindows } from "../common/vars";
|
||||||
|
import winston from "winston"
|
||||||
|
|
||||||
export type LensBinaryOpts = {
|
export type LensBinaryOpts = {
|
||||||
version: string;
|
version: string;
|
||||||
@ -27,6 +27,7 @@ export class LensBinary {
|
|||||||
protected arch: string
|
protected arch: string
|
||||||
protected originalBinaryName: string
|
protected originalBinaryName: string
|
||||||
protected requestOpts: request.Options
|
protected requestOpts: request.Options
|
||||||
|
protected logger: Console | winston.Logger
|
||||||
|
|
||||||
constructor(opts: LensBinaryOpts) {
|
constructor(opts: LensBinaryOpts) {
|
||||||
const baseDir = opts.baseDir
|
const baseDir = opts.baseDir
|
||||||
@ -34,7 +35,7 @@ export class LensBinary {
|
|||||||
this.binaryName = opts.newBinaryName || opts.originalBinaryName
|
this.binaryName = opts.newBinaryName || opts.originalBinaryName
|
||||||
this.binaryVersion = opts.version
|
this.binaryVersion = opts.version
|
||||||
this.requestOpts = opts.requestOpts
|
this.requestOpts = opts.requestOpts
|
||||||
|
this.logger = console
|
||||||
let arch = null
|
let arch = null
|
||||||
|
|
||||||
if (process.arch == "x64") {
|
if (process.arch == "x64") {
|
||||||
@ -59,6 +60,10 @@ export class LensBinary {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setLogger(logger: Console | winston.Logger) {
|
||||||
|
this.logger = logger
|
||||||
|
}
|
||||||
|
|
||||||
protected binaryDir() {
|
protected binaryDir() {
|
||||||
throw new Error("binaryDir not implemented")
|
throw new Error("binaryDir not implemented")
|
||||||
}
|
}
|
||||||
@ -93,7 +98,7 @@ export class LensBinary {
|
|||||||
await this.ensureBinary()
|
await this.ensureBinary()
|
||||||
return this.dirname
|
return this.dirname
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(err)
|
this.logger.error(err)
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -107,17 +112,17 @@ export class LensBinary {
|
|||||||
const isValid = await this.checkBinary()
|
const isValid = await this.checkBinary()
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
await this.downloadBinary().catch((error) => {
|
await this.downloadBinary().catch((error) => {
|
||||||
logger.error(error)
|
this.logger.error(error)
|
||||||
});
|
});
|
||||||
if (this.tarPath) await this.untarBinary()
|
if (this.tarPath) await this.untarBinary()
|
||||||
if (this.originalBinaryName != this.binaryName) await this.renameBinary()
|
if (this.originalBinaryName != this.binaryName) await this.renameBinary()
|
||||||
logger.info(`${this.originalBinaryName} has been downloaded to ${this.getBinaryPath()}`)
|
this.logger.info(`${this.originalBinaryName} has been downloaded to ${this.getBinaryPath()}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async untarBinary() {
|
protected async untarBinary() {
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
logger.debug(`Extracting ${this.originalBinaryName} binary`)
|
this.logger.debug(`Extracting ${this.originalBinaryName} binary`)
|
||||||
tar.x({
|
tar.x({
|
||||||
file: this.tarPath,
|
file: this.tarPath,
|
||||||
cwd: this.dirname
|
cwd: this.dirname
|
||||||
@ -129,7 +134,7 @@ export class LensBinary {
|
|||||||
|
|
||||||
protected async renameBinary() {
|
protected async renameBinary() {
|
||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
logger.debug(`Renaming ${this.originalBinaryName} binary to ${this.binaryName}`)
|
this.logger.debug(`Renaming ${this.originalBinaryName} binary to ${this.binaryName}`)
|
||||||
fs.rename(this.getOriginalBinaryPath(), this.getBinaryPath(), (err) => {
|
fs.rename(this.getOriginalBinaryPath(), this.getBinaryPath(), (err) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err)
|
reject(err)
|
||||||
@ -148,7 +153,7 @@ export class LensBinary {
|
|||||||
const file = fs.createWriteStream(binaryPath)
|
const file = fs.createWriteStream(binaryPath)
|
||||||
const url = this.getUrl()
|
const url = this.getUrl()
|
||||||
|
|
||||||
logger.info(`Downloading ${this.originalBinaryName} ${this.binaryVersion} from ${url} to ${binaryPath}`)
|
this.logger.info(`Downloading ${this.originalBinaryName} ${this.binaryVersion} from ${url} to ${binaryPath}`)
|
||||||
const requestOpts: request.UriOptions & request.CoreOptions = {
|
const requestOpts: request.UriOptions & request.CoreOptions = {
|
||||||
uri: url,
|
uri: url,
|
||||||
gzip: true,
|
gzip: true,
|
||||||
@ -158,12 +163,12 @@ export class LensBinary {
|
|||||||
const stream = request(requestOpts)
|
const stream = request(requestOpts)
|
||||||
|
|
||||||
stream.on("complete", () => {
|
stream.on("complete", () => {
|
||||||
logger.info(`Download of ${this.originalBinaryName} finished`)
|
this.logger.info(`Download of ${this.originalBinaryName} finished`)
|
||||||
file.end()
|
file.end()
|
||||||
})
|
})
|
||||||
|
|
||||||
stream.on("error", (error) => {
|
stream.on("error", (error) => {
|
||||||
logger.error(error)
|
this.logger.error(error)
|
||||||
fs.unlink(binaryPath, () => {
|
fs.unlink(binaryPath, () => {
|
||||||
// do nothing
|
// do nothing
|
||||||
})
|
})
|
||||||
@ -171,7 +176,7 @@ export class LensBinary {
|
|||||||
})
|
})
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
file.on("close", () => {
|
file.on("close", () => {
|
||||||
logger.debug(`${this.originalBinaryName} binary download closed`)
|
this.logger.debug(`${this.originalBinaryName} binary download closed`)
|
||||||
if (!this.tarPath) fs.chmod(binaryPath, 0o755, (err) => {
|
if (!this.tarPath) fs.chmod(binaryPath, 0o755, (err) => {
|
||||||
if (err) reject(err);
|
if (err) reject(err);
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,20 +1,30 @@
|
|||||||
|
import { app, remote } from "electron";
|
||||||
import winston from "winston"
|
import winston from "winston"
|
||||||
import { isDebugging } from "../common/vars";
|
import { isDebugging } from "../common/vars";
|
||||||
|
|
||||||
const options = {
|
const consoleOptions: winston.transports.ConsoleTransportOptions = {
|
||||||
colorize: true,
|
|
||||||
handleExceptions: false,
|
handleExceptions: false,
|
||||||
json: false,
|
|
||||||
level: isDebugging ? "debug" : "info",
|
level: isDebugging ? "debug" : "info",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fileOptions: winston.transports.FileTransportOptions = {
|
||||||
|
handleExceptions: false,
|
||||||
|
level: isDebugging ? "debug" : "info",
|
||||||
|
filename: "lens.log",
|
||||||
|
dirname: (app ?? remote?.app)?.getPath("logs"),
|
||||||
|
maxsize: 16 * 1024,
|
||||||
|
maxFiles: 16,
|
||||||
|
tailable: true,
|
||||||
|
}
|
||||||
|
|
||||||
const logger = winston.createLogger({
|
const logger = winston.createLogger({
|
||||||
format: winston.format.combine(
|
format: winston.format.combine(
|
||||||
winston.format.colorize(),
|
winston.format.colorize(),
|
||||||
winston.format.simple(),
|
winston.format.simple(),
|
||||||
),
|
),
|
||||||
transports: [
|
transports: [
|
||||||
new winston.transports.Console(options),
|
new winston.transports.Console(consoleOptions),
|
||||||
|
new winston.transports.File(fileOptions),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -43,7 +43,7 @@ export function buildMenu(windowManager: WindowManager) {
|
|||||||
`${appName}: ${app.getVersion()}`,
|
`${appName}: ${app.getVersion()}`,
|
||||||
`Electron: ${process.versions.electron}`,
|
`Electron: ${process.versions.electron}`,
|
||||||
`Chrome: ${process.versions.chrome}`,
|
`Chrome: ${process.versions.chrome}`,
|
||||||
`Copyright 2020 Copyright 2020 Mirantis, Inc.`,
|
`Copyright 2020 Mirantis, Inc.`,
|
||||||
]
|
]
|
||||||
dialog.showMessageBoxSync(browserWindow, {
|
dialog.showMessageBoxSync(browserWindow, {
|
||||||
title: `${isWindows ? " ".repeat(2) : ""}${appName}`,
|
title: `${isWindows ? " ".repeat(2) : ""}${appName}`,
|
||||||
@ -163,14 +163,14 @@ export function buildMenu(windowManager: WindowManager) {
|
|||||||
label: 'Forward',
|
label: 'Forward',
|
||||||
accelerator: 'CmdOrCtrl+]',
|
accelerator: 'CmdOrCtrl+]',
|
||||||
click() {
|
click() {
|
||||||
webContents.getFocusedWebContents()?.goForward();
|
webContents.getFocusedWebContents()?.goForward()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Reload',
|
label: 'Reload',
|
||||||
accelerator: 'CmdOrCtrl+R',
|
accelerator: 'CmdOrCtrl+R',
|
||||||
click() {
|
click() {
|
||||||
webContents.getFocusedWebContents()?.reload();
|
windowManager.reload({ channel: "menu:reload" });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ role: 'toggleDevTools' },
|
{ role: 'toggleDevTools' },
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { ClusterPreferences } from "../common/cluster-store";
|
|||||||
import { helmCli } from "./helm/helm-cli"
|
import { helmCli } from "./helm/helm-cli"
|
||||||
import { isWindows } from "../common/vars";
|
import { isWindows } from "../common/vars";
|
||||||
import { tracker } from "../common/tracker";
|
import { tracker } from "../common/tracker";
|
||||||
|
import { userStore } from "../common/user-store";
|
||||||
|
|
||||||
export class ShellSession extends EventEmitter {
|
export class ShellSession extends EventEmitter {
|
||||||
static shellEnvs: Map<string, any> = new Map()
|
static shellEnvs: Map<string, any> = new Map()
|
||||||
@ -20,6 +21,7 @@ export class ShellSession extends EventEmitter {
|
|||||||
protected nodeShellPod: string;
|
protected nodeShellPod: string;
|
||||||
protected kubectl: Kubectl;
|
protected kubectl: Kubectl;
|
||||||
protected kubectlBinDir: string;
|
protected kubectlBinDir: string;
|
||||||
|
protected kubectlPathDir: string;
|
||||||
protected helmBinDir: string;
|
protected helmBinDir: string;
|
||||||
protected preferences: ClusterPreferences;
|
protected preferences: ClusterPreferences;
|
||||||
protected running = false;
|
protected running = false;
|
||||||
@ -36,6 +38,8 @@ export class ShellSession extends EventEmitter {
|
|||||||
|
|
||||||
public async open() {
|
public async open() {
|
||||||
this.kubectlBinDir = await this.kubectl.binDir()
|
this.kubectlBinDir = await this.kubectl.binDir()
|
||||||
|
const pathFromPreferences = userStore.preferences.kubectlBinariesPath || Kubectl.bundledKubectlPath
|
||||||
|
this.kubectlPathDir = userStore.preferences.downloadKubectlBinaries ? await this.kubectl.binDir() : path.dirname(pathFromPreferences)
|
||||||
this.helmBinDir = helmCli.getBinaryDir()
|
this.helmBinDir = helmCli.getBinaryDir()
|
||||||
const env = await this.getCachedShellEnv()
|
const env = await this.getCachedShellEnv()
|
||||||
const shell = env.PTYSHELL
|
const shell = env.PTYSHELL
|
||||||
@ -67,11 +71,11 @@ export class ShellSession extends EventEmitter {
|
|||||||
protected async getShellArgs(shell: string): Promise<Array<string>> {
|
protected async getShellArgs(shell: string): Promise<Array<string>> {
|
||||||
switch(path.basename(shell)) {
|
switch(path.basename(shell)) {
|
||||||
case "powershell.exe":
|
case "powershell.exe":
|
||||||
return ["-NoExit", "-command", `& {Set-Location $Env:USERPROFILE; $Env:PATH="${this.kubectlBinDir};${this.helmBinDir};$Env:PATH"}`]
|
return ["-NoExit", "-command", `& {Set-Location $Env:USERPROFILE; $Env:PATH="${this.helmBinDir};${this.kubectlPathDir};$Env:PATH"}`]
|
||||||
case "bash":
|
case "bash":
|
||||||
return ["--init-file", path.join(this.kubectlBinDir, '.bash_set_path')]
|
return ["--init-file", path.join(this.kubectlBinDir, '.bash_set_path')]
|
||||||
case "fish":
|
case "fish":
|
||||||
return ["--login", "--init-command", `export PATH="${this.kubectlBinDir}:${this.helmBinDir}:$PATH"; export KUBECONFIG="${this.kubeconfigPath}"`]
|
return ["--login", "--init-command", `export PATH="${this.helmBinDir}:${this.kubectlPathDir}:$PATH"; export KUBECONFIG="${this.kubeconfigPath}"`]
|
||||||
case "zsh":
|
case "zsh":
|
||||||
return ["--login"]
|
return ["--login"]
|
||||||
default:
|
default:
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
import type { ClusterId } from "../common/cluster-store";
|
import { clusterStore } from "../common/cluster-store";
|
||||||
import { BrowserWindow, dialog, ipcMain, shell, WebContents, webContents } from "electron"
|
import { BrowserWindow, dialog, ipcMain, shell, webContents } from "electron"
|
||||||
import windowStateKeeper from "electron-window-state"
|
import windowStateKeeper from "electron-window-state"
|
||||||
import { observable } from "mobx";
|
import { observable } from "mobx";
|
||||||
import { initMenu } from "./menu";
|
import { initMenu } from "./menu";
|
||||||
|
import type { ClusterId } from "../common/cluster-store";
|
||||||
|
|
||||||
export class WindowManager {
|
export class WindowManager {
|
||||||
protected mainView: BrowserWindow;
|
protected mainView: BrowserWindow;
|
||||||
@ -58,6 +59,15 @@ export class WindowManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reload({ channel }: { channel: string }) {
|
||||||
|
const frameId = clusterStore.getById(this.activeClusterId)?.frameId;
|
||||||
|
if (frameId) {
|
||||||
|
this.mainView.webContents.sendToFrame(frameId, channel);
|
||||||
|
} else {
|
||||||
|
webContents.getFocusedWebContents()?.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async showMain() {
|
async showMain() {
|
||||||
try {
|
try {
|
||||||
await this.showSplash();
|
await this.showSplash();
|
||||||
|
|||||||
@ -1,13 +1,28 @@
|
|||||||
import { KubeObject } from "../kube-object";
|
import { KubeObject } from "../kube-object";
|
||||||
import { KubeApi } from "../kube-api";
|
import { VersionedKubeApi } from "../kube-api-versioned";
|
||||||
import { crdResourcesURL } from "../../components/+custom-resources/crd.route";
|
import { crdResourcesURL } from "../../components/+custom-resources/crd.route";
|
||||||
|
|
||||||
|
type AdditionalPrinterColumnsCommon = {
|
||||||
|
name: string;
|
||||||
|
type: "integer" | "number" | "string" | "boolean" | "date";
|
||||||
|
priority: number;
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdditionalPrinterColumnsV1 = AdditionalPrinterColumnsCommon & {
|
||||||
|
jsonPath: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdditionalPrinterColumnsV1Beta = AdditionalPrinterColumnsCommon & {
|
||||||
|
JSONPath: string;
|
||||||
|
}
|
||||||
|
|
||||||
export class CustomResourceDefinition extends KubeObject {
|
export class CustomResourceDefinition extends KubeObject {
|
||||||
static kind = "CustomResourceDefinition";
|
static kind = "CustomResourceDefinition";
|
||||||
|
|
||||||
spec: {
|
spec: {
|
||||||
group: string;
|
group: string;
|
||||||
version: string;
|
version?: string; // deprecated in v1 api
|
||||||
names: {
|
names: {
|
||||||
plural: string;
|
plural: string;
|
||||||
singular: string;
|
singular: string;
|
||||||
@ -20,18 +35,14 @@ export class CustomResourceDefinition extends KubeObject {
|
|||||||
name: string;
|
name: string;
|
||||||
served: boolean;
|
served: boolean;
|
||||||
storage: boolean;
|
storage: boolean;
|
||||||
|
schema?: unknown; // required in v1 but not present in v1beta
|
||||||
|
additionalPrinterColumns?: AdditionalPrinterColumnsV1[]
|
||||||
}[];
|
}[];
|
||||||
conversion: {
|
conversion: {
|
||||||
strategy?: string;
|
strategy?: string;
|
||||||
webhook?: any;
|
webhook?: any;
|
||||||
};
|
};
|
||||||
additionalPrinterColumns?: {
|
additionalPrinterColumns?: AdditionalPrinterColumnsV1Beta[]; // removed in v1
|
||||||
name: string;
|
|
||||||
type: "integer" | "number" | "string" | "boolean" | "date";
|
|
||||||
priority: number;
|
|
||||||
description: string;
|
|
||||||
JSONPath: string;
|
|
||||||
}[];
|
|
||||||
}
|
}
|
||||||
status: {
|
status: {
|
||||||
conditions: {
|
conditions: {
|
||||||
@ -61,8 +72,8 @@ export class CustomResourceDefinition extends KubeObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getResourceApiBase() {
|
getResourceApiBase() {
|
||||||
const { version, group } = this.spec;
|
const { group } = this.spec;
|
||||||
return `/apis/${group}/${version}/${this.getPluralName()}`
|
return `/apis/${group}/${this.getVersion()}/${this.getPluralName()}`
|
||||||
}
|
}
|
||||||
|
|
||||||
getPluralName() {
|
getPluralName() {
|
||||||
@ -87,7 +98,8 @@ export class CustomResourceDefinition extends KubeObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getVersion() {
|
getVersion() {
|
||||||
return this.spec.version;
|
// v1 has removed the spec.version property, if it is present it must match the first version
|
||||||
|
return this.spec.versions[0]?.name ?? this.spec.version;
|
||||||
}
|
}
|
||||||
|
|
||||||
isNamespaced() {
|
isNamespaced() {
|
||||||
@ -107,14 +119,16 @@ export class CustomResourceDefinition extends KubeObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getPrinterColumns(ignorePriority = true) {
|
getPrinterColumns(ignorePriority = true) {
|
||||||
const columns = this.spec.additionalPrinterColumns || [];
|
const columns = this.spec.versions.find(a => this.getVersion() == a.name)?.additionalPrinterColumns
|
||||||
|
?? this.spec.additionalPrinterColumns?.map(({JSONPath, ...rest}) => ({ ...rest, jsonPath: JSONPath })) // map to V1 shape
|
||||||
|
?? [];
|
||||||
return columns
|
return columns
|
||||||
.filter(column => column.name != "Age")
|
.filter(column => column.name != "Age")
|
||||||
.filter(column => ignorePriority ? true : !column.priority);
|
.filter(column => ignorePriority ? true : !column.priority);
|
||||||
}
|
}
|
||||||
|
|
||||||
getValidation() {
|
getValidation() {
|
||||||
return JSON.stringify(this.spec.validation, null, 2);
|
return JSON.stringify(this.spec.validation ?? this.spec.versions?.[0]?.schema, null, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
getConditions() {
|
getConditions() {
|
||||||
@ -130,16 +144,10 @@ export class CustomResourceDefinition extends KubeObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const crdBetaApi = new KubeApi<CustomResourceDefinition>({
|
export const crdApi = new VersionedKubeApi<CustomResourceDefinition>({
|
||||||
kind: CustomResourceDefinition.kind,
|
|
||||||
apiBase: "/apis/apiextensions.k8s.io/v1beta1/customresourcedefinitions",
|
|
||||||
isNamespaced: false,
|
|
||||||
objectConstructor: CustomResourceDefinition,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const crdApi = new KubeApi<CustomResourceDefinition>({
|
|
||||||
kind: CustomResourceDefinition.kind,
|
kind: CustomResourceDefinition.kind,
|
||||||
apiBase: "/apis/apiextensions.k8s.io/v1/customresourcedefinitions",
|
apiBase: "/apis/apiextensions.k8s.io/v1/customresourcedefinitions",
|
||||||
isNamespaced: false,
|
isNamespaced: false,
|
||||||
objectConstructor: CustomResourceDefinition,
|
objectConstructor: CustomResourceDefinition
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -121,6 +121,8 @@ export function lookupApiLink(ref: IKubeObjectRef, parentObject: KubeObject): st
|
|||||||
namespace = parentObject.getNs()
|
namespace = parentObject.getNs()
|
||||||
} = ref;
|
} = ref;
|
||||||
|
|
||||||
|
if (!kind) return "";
|
||||||
|
|
||||||
// search in registered apis by 'kind' & 'apiVersion'
|
// search in registered apis by 'kind' & 'apiVersion'
|
||||||
const api = apiManager.getApi(api => api.kind === kind && api.apiVersionWithGroup == apiVersion)
|
const api = apiManager.getApi(api => api.kind === kind && api.apiVersionWithGroup == apiVersion)
|
||||||
if (api) {
|
if (api) {
|
||||||
@ -129,7 +131,7 @@ export function lookupApiLink(ref: IKubeObjectRef, parentObject: KubeObject): st
|
|||||||
|
|
||||||
// lookup api by generated resource link
|
// lookup api by generated resource link
|
||||||
const apiPrefixes = ["/apis", "/api"];
|
const apiPrefixes = ["/apis", "/api"];
|
||||||
const resource = kind.toLowerCase() + kind.endsWith("s") ? "es" : "s";
|
const resource = kind.endsWith("s") ? `${kind.toLowerCase()}es` : `${kind.toLowerCase()}s`;
|
||||||
for (const apiPrefix of apiPrefixes) {
|
for (const apiPrefix of apiPrefixes) {
|
||||||
const apiLink = createKubeApiURL({ apiPrefix, apiVersion, name, namespace, resource });
|
const apiLink = createKubeApiURL({ apiPrefix, apiVersion, name, namespace, resource });
|
||||||
if (apiManager.getApi(apiLink)) {
|
if (apiManager.getApi(apiLink)) {
|
||||||
|
|||||||
56
src/renderer/api/kube-api-versioned.ts
Normal file
56
src/renderer/api/kube-api-versioned.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import { stringify } from "querystring";
|
||||||
|
import { KubeObject } from "./kube-object";
|
||||||
|
import { createKubeApiURL } from "./kube-api-parse";
|
||||||
|
import { KubeApi, IKubeApiQueryParams, IKubeApiOptions } from "./kube-api";
|
||||||
|
import { apiManager } from "./api-manager";
|
||||||
|
|
||||||
|
export class VersionedKubeApi<T extends KubeObject = any> extends KubeApi<T> {
|
||||||
|
private preferredVersion?: string;
|
||||||
|
|
||||||
|
constructor(opts: IKubeApiOptions<T>) {
|
||||||
|
super(opts);
|
||||||
|
|
||||||
|
this.getPreferredVersion().then(() => {
|
||||||
|
if (this.apiBase != opts.apiBase)
|
||||||
|
apiManager.registerApi(this.apiBase, this);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// override this property to make read-write
|
||||||
|
apiBase: string
|
||||||
|
|
||||||
|
async getPreferredVersion() {
|
||||||
|
if (this.preferredVersion) return;
|
||||||
|
|
||||||
|
const apiGroupVersion = await this.request.get<{ preferredVersion?: { version: string; }; }>(`${this.apiPrefix}/${this.apiGroup}`);
|
||||||
|
|
||||||
|
if (!apiGroupVersion?.preferredVersion) return;
|
||||||
|
|
||||||
|
this.preferredVersion = apiGroupVersion.preferredVersion.version;
|
||||||
|
|
||||||
|
// update apiBase
|
||||||
|
this.apiBase = this.getUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
async list({ namespace = "" } = {}, query?: IKubeApiQueryParams): Promise<T[]> {
|
||||||
|
await this.getPreferredVersion();
|
||||||
|
return await super.list({namespace}, query);
|
||||||
|
}
|
||||||
|
async get({ name = "", namespace = "default" } = {}, query?: IKubeApiQueryParams): Promise<T> {
|
||||||
|
await this.getPreferredVersion();
|
||||||
|
return super.get({ name, namespace }, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
getUrl({ name = "", namespace = "" } = {}, query?: Partial<IKubeApiQueryParams>) {
|
||||||
|
const { apiPrefix, apiGroup, apiVersion, apiResource, preferredVersion, isNamespaced } = this;
|
||||||
|
|
||||||
|
const resourcePath = createKubeApiURL({
|
||||||
|
apiPrefix: apiPrefix,
|
||||||
|
apiVersion: `${apiGroup}/${preferredVersion ?? apiVersion}`,
|
||||||
|
resource: apiResource,
|
||||||
|
namespace: isNamespaced ? namespace : undefined,
|
||||||
|
name: name,
|
||||||
|
});
|
||||||
|
return resourcePath + (query ? `?` + stringify(query) : "");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -169,12 +169,11 @@ export class AddCluster extends React.Component {
|
|||||||
<h2>Clusters associated with Lens</h2>
|
<h2>Clusters associated with Lens</h2>
|
||||||
<p>
|
<p>
|
||||||
Add clusters by clicking the <span className="text-primary">Add Cluster</span> button.
|
Add clusters by clicking the <span className="text-primary">Add Cluster</span> button.
|
||||||
You'll need to obtain a working kubeconfig for the cluster you want to add.
|
You'll need to obtain a working kubeconfig for the cluster you want to add. You can either browse it from the file system or paste it as a text from the clipboard.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Each <a href="https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/#context" target="_blank">cluster context</a> is added as a separate item in the
|
Selected <a href="https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/#context" target="_blank">cluster contexts</a> are added as a separate item in the
|
||||||
left-side cluster menu
|
left-side cluster menu to allow you to operate easily on multiple clusters and/or contexts.
|
||||||
to allow you to operate easily on multiple clusters and/or contexts.
|
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
For more information on kubeconfig see <a href="https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/" target="_blank">Kubernetes docs</a>.
|
For more information on kubeconfig see <a href="https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/" target="_blank">Kubernetes docs</a>.
|
||||||
@ -189,20 +188,6 @@ export class AddCluster extends React.Component {
|
|||||||
When connecting to a cluster, make sure you have a valid and working kubeconfig for the cluster. Following lists known "gotchas" in some authentication types used in kubeconfig with Lens
|
When connecting to a cluster, make sure you have a valid and working kubeconfig for the cluster. Following lists known "gotchas" in some authentication types used in kubeconfig with Lens
|
||||||
app.
|
app.
|
||||||
</p>
|
</p>
|
||||||
<a href="https://kubernetes.io/docs/reference/access-authn-authz/authentication/#option-1-oidc-authenticator" target="_blank">
|
|
||||||
<h3>OIDC (OpenID Connect)</h3>
|
|
||||||
</a>
|
|
||||||
<p>
|
|
||||||
When connecting Lens to OIDC enabled cluster, there's few things you as a user need to take into account.
|
|
||||||
</p>
|
|
||||||
<p><b>Dedicated refresh token</b></p>
|
|
||||||
<p>
|
|
||||||
As Lens app utilized kubeconfig is "disconnected" from your main kubeconfig Lens needs to have it's own refresh token it utilizes.
|
|
||||||
If you share the refresh token with e.g. <code>kubectl</code> who ever uses the token first will invalidate it for the next user.
|
|
||||||
One way to achieve this is with <a href="https://github.com/int128/kubelogin" target="_blank">kubelogin</a> tool by removing the tokens
|
|
||||||
(both <code>id_token</code> and <code>refresh_token</code>) from
|
|
||||||
the config and issuing <code>kubelogin</code> command. That'll take you through the login process and will result you having "dedicated" refresh token.
|
|
||||||
</p>
|
|
||||||
<h3>Exec auth plugins</h3>
|
<h3>Exec auth plugins</h3>
|
||||||
<p>
|
<p>
|
||||||
When using <a href="https://kubernetes.io/docs/reference/access-authn-authz/authentication/#configuration" target="_blank">exec auth</a> plugins make sure the paths that are used to call
|
When using <a href="https://kubernetes.io/docs/reference/access-authn-authz/authentication/#configuration" target="_blank">exec auth</a> plugins make sure the paths that are used to call
|
||||||
|
|||||||
@ -25,7 +25,6 @@
|
|||||||
|
|
||||||
.version {
|
.version {
|
||||||
.Select {
|
.Select {
|
||||||
width: 80px;
|
|
||||||
min-width: 80px;
|
min-width: 80px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import "./cluster-settings.scss";
|
import "./cluster-settings.scss";
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { Link } from "react-router-dom";
|
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { Features } from "./features";
|
import { Features } from "./features";
|
||||||
import { Removal } from "./removal";
|
import { Removal } from "./removal";
|
||||||
@ -15,6 +14,25 @@ import { navigate } from "../../navigation";
|
|||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class ClusterSettings extends React.Component {
|
export class ClusterSettings extends React.Component {
|
||||||
|
async componentDidMount() {
|
||||||
|
window.addEventListener('keydown', this.onEscapeKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
window.removeEventListener('keydown', this.onEscapeKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
onEscapeKey = (evt: KeyboardEvent) => {
|
||||||
|
if (evt.code === "Escape") {
|
||||||
|
evt.stopPropagation();
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
navigate("/");
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const cluster = getMatchedCluster();
|
const cluster = getMatchedCluster();
|
||||||
if (!cluster) return null;
|
if (!cluster) return null;
|
||||||
@ -26,7 +44,7 @@ export class ClusterSettings extends React.Component {
|
|||||||
showTooltip={false}
|
showTooltip={false}
|
||||||
/>
|
/>
|
||||||
<h2>{cluster.preferences.clusterName}</h2>
|
<h2>{cluster.preferences.clusterName}</h2>
|
||||||
<Icon material="close" onClick={() => navigate("/")} big/>
|
<Icon material="close" onClick={this.close} big/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -22,7 +22,7 @@ export class Status extends React.Component<Props> {
|
|||||||
const rows = [
|
const rows = [
|
||||||
["Online Status", cluster.online ? "online" : `offline (${cluster.failureReason || "unknown reason"})`],
|
["Online Status", cluster.online ? "online" : `offline (${cluster.failureReason || "unknown reason"})`],
|
||||||
["Distribution", cluster.distribution],
|
["Distribution", cluster.distribution],
|
||||||
["Kerbel Version", cluster.version],
|
["Kernel Version", cluster.version],
|
||||||
["API Address", cluster.apiUrl],
|
["API Address", cluster.apiUrl],
|
||||||
["Nodes Count", cluster.nodes || "0"]
|
["Nodes Count", cluster.nodes || "0"]
|
||||||
];
|
];
|
||||||
|
|||||||
@ -102,13 +102,13 @@ export class CRDDetails extends React.Component<Props> {
|
|||||||
</TableHead>
|
</TableHead>
|
||||||
{
|
{
|
||||||
printerColumns.map((column, index) => {
|
printerColumns.map((column, index) => {
|
||||||
const { name, type, JSONPath } = column;
|
const { name, type, jsonPath } = column;
|
||||||
return (
|
return (
|
||||||
<TableRow key={index}>
|
<TableRow key={index}>
|
||||||
<TableCell className="name">{name}</TableCell>
|
<TableCell className="name">{name}</TableCell>
|
||||||
<TableCell className="type">{type}</TableCell>
|
<TableCell className="type">{type}</TableCell>
|
||||||
<TableCell className="json-path">
|
<TableCell className="json-path">
|
||||||
<Badge label={JSONPath}/>
|
<Badge label={jsonPath}/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -57,7 +57,7 @@ export class CrdResourceDetails extends React.Component<Props> {
|
|||||||
<KubeObjectMeta object={object}/>
|
<KubeObjectMeta object={object}/>
|
||||||
{extraColumns.map(column => {
|
{extraColumns.map(column => {
|
||||||
const { name } = column;
|
const { name } = column;
|
||||||
const value = jsonPath.query(object, column.JSONPath.slice(1));
|
const value = jsonPath.query(object, (column.jsonPath).slice(1));
|
||||||
return (
|
return (
|
||||||
<DrawerItem key={name} name={name}>
|
<DrawerItem key={name} name={name}>
|
||||||
<CrdColumnValue value={value} />
|
<CrdColumnValue value={value} />
|
||||||
|
|||||||
@ -57,7 +57,7 @@ export class CrdResources extends React.Component<Props> {
|
|||||||
[sortBy.age]: (item: KubeObject) => item.metadata.creationTimestamp,
|
[sortBy.age]: (item: KubeObject) => item.metadata.creationTimestamp,
|
||||||
}
|
}
|
||||||
extraColumns.forEach(column => {
|
extraColumns.forEach(column => {
|
||||||
sortingCallbacks[column.name] = (item: KubeObject) => jsonPath.query(item, column.JSONPath.slice(1))
|
sortingCallbacks[column.name] = (item: KubeObject) => jsonPath.query(item, column.jsonPath.slice(1))
|
||||||
})
|
})
|
||||||
// todo: merge extra columns and other params to predefined view
|
// todo: merge extra columns and other params to predefined view
|
||||||
const { List } = apiManager.getViews(crd.getResourceApiBase());
|
const { List } = apiManager.getViews(crd.getResourceApiBase());
|
||||||
@ -88,9 +88,9 @@ export class CrdResources extends React.Component<Props> {
|
|||||||
renderTableContents={(crdInstance: KubeObject) => [
|
renderTableContents={(crdInstance: KubeObject) => [
|
||||||
crdInstance.getName(),
|
crdInstance.getName(),
|
||||||
isNamespaced && crdInstance.getNs(),
|
isNamespaced && crdInstance.getNs(),
|
||||||
...extraColumns.map(column =>
|
...extraColumns.map(column => {
|
||||||
jsonPath.query(crdInstance, column.JSONPath.slice(1))
|
return jsonPath.query(crdInstance, (column.jsonPath).slice(1))
|
||||||
),
|
}),
|
||||||
crdInstance.getAge(),
|
crdInstance.getAge(),
|
||||||
]}
|
]}
|
||||||
renderItemMenu={(item: KubeObject) => {
|
renderItemMenu={(item: KubeObject) => {
|
||||||
|
|||||||
83
src/renderer/components/+preferences/kubectl-binaries.tsx
Normal file
83
src/renderer/components/+preferences/kubectl-binaries.tsx
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Trans } from '@lingui/macro';
|
||||||
|
import { isPath } from '../input/input.validators';
|
||||||
|
import { Checkbox } from '../checkbox';
|
||||||
|
import { Input } from '../input';
|
||||||
|
import { SubTitle } from '../layout/sub-title';
|
||||||
|
import { UserPreferences, userStore } from '../../../common/user-store';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
import { Kubectl } from '../../../main/kubectl';
|
||||||
|
import { SelectOption, Select } from '../select';
|
||||||
|
|
||||||
|
export const KubectlBinaries = observer(({ preferences }: { preferences: UserPreferences }) => {
|
||||||
|
const [downloadPath, setDownloadPath] = useState(preferences.downloadBinariesPath || "");
|
||||||
|
const [binariesPath, setBinariesPath] = useState(preferences.kubectlBinariesPath || "");
|
||||||
|
|
||||||
|
const downloadMirrorOptions: SelectOption<string>[] = [
|
||||||
|
{ value: "default", label: "Default (Google)" },
|
||||||
|
{ value: "china", label: "China (Azure)" },
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
const save = () => {
|
||||||
|
preferences.downloadBinariesPath = downloadPath;
|
||||||
|
preferences.kubectlBinariesPath = binariesPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderPath = () => {
|
||||||
|
if (preferences.downloadKubectlBinaries) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SubTitle title="Path to Kubectl binary"/>
|
||||||
|
<Input
|
||||||
|
theme="round-black"
|
||||||
|
value={binariesPath}
|
||||||
|
validators={isPath}
|
||||||
|
onChange={setBinariesPath}
|
||||||
|
onBlur={save}
|
||||||
|
/>
|
||||||
|
<small className="hint">
|
||||||
|
<Trans>Default:</Trans>{" "}{Kubectl.bundledKubectlPath}
|
||||||
|
</small>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h2><Trans>Kubectl Binary</Trans></h2>
|
||||||
|
<small className="hint">
|
||||||
|
<Trans>Download kubectl binaries matching to Kubernetes cluster verison.</Trans>
|
||||||
|
</small>
|
||||||
|
<Checkbox
|
||||||
|
label={<Trans>Download kubectl binaries</Trans>}
|
||||||
|
value={preferences.downloadKubectlBinaries}
|
||||||
|
onChange={downloadKubectlBinaries => preferences.downloadKubectlBinaries = downloadKubectlBinaries}
|
||||||
|
/>
|
||||||
|
<SubTitle title="Download mirror" />
|
||||||
|
<Select
|
||||||
|
placeholder={<Trans>Download mirror for kubectl</Trans>}
|
||||||
|
options={downloadMirrorOptions}
|
||||||
|
value={preferences.downloadMirror}
|
||||||
|
onChange={({ value }: SelectOption) => preferences.downloadMirror = value}
|
||||||
|
disabled={!preferences.downloadKubectlBinaries}
|
||||||
|
/>
|
||||||
|
<SubTitle title="Directory for binaries"/>
|
||||||
|
<Input
|
||||||
|
theme="round-black"
|
||||||
|
value={downloadPath}
|
||||||
|
placeholder={`Directory to download binaries into`}
|
||||||
|
validators={isPath}
|
||||||
|
onChange={setDownloadPath}
|
||||||
|
onBlur={save}
|
||||||
|
disabled={!preferences.downloadKubectlBinaries}
|
||||||
|
/>
|
||||||
|
<small>
|
||||||
|
Default: {userStore.getDefaultKubectlPath()}
|
||||||
|
</small>
|
||||||
|
{renderPath()}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
@ -19,6 +19,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.SubTitle {
|
||||||
|
text-transform: none;
|
||||||
|
margin: 0!important;
|
||||||
|
}
|
||||||
|
|
||||||
.repos {
|
.repos {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
|||||||
@ -16,17 +16,14 @@ import { Badge } from "../badge";
|
|||||||
import { themeStore } from "../../theme.store";
|
import { themeStore } from "../../theme.store";
|
||||||
import { history } from "../../navigation";
|
import { history } from "../../navigation";
|
||||||
import { Tooltip } from "../tooltip";
|
import { Tooltip } from "../tooltip";
|
||||||
|
import { KubectlBinaries } from "./kubectl-binaries";
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
export class Preferences extends React.Component {
|
export class Preferences extends React.Component {
|
||||||
@observable helmLoading = false;
|
@observable helmLoading = false;
|
||||||
@observable helmRepos: HelmRepo[] = [];
|
@observable helmRepos: HelmRepo[] = [];
|
||||||
@observable helmAddedRepos = observable.map<string, HelmRepo>();
|
@observable helmAddedRepos = observable.map<string, HelmRepo>();
|
||||||
|
@observable httpProxy = userStore.preferences.httpsProxy || "";
|
||||||
@observable downloadMirrorOptions: SelectOption<string>[] = [
|
|
||||||
{ value: "default", label: "Default (Google)" },
|
|
||||||
{ value: "china", label: "China (Azure)" },
|
|
||||||
]
|
|
||||||
|
|
||||||
@computed get themeOptions(): SelectOption<string>[] {
|
@computed get themeOptions(): SelectOption<string>[] {
|
||||||
return themeStore.themes.map(theme => ({
|
return themeStore.themes.map(theme => ({
|
||||||
@ -43,9 +40,21 @@ export class Preferences extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
|
window.addEventListener('keydown', this.onEscapeKey);
|
||||||
await this.loadHelmRepos();
|
await this.loadHelmRepos();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
window.removeEventListener('keydown', this.onEscapeKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
onEscapeKey = (evt: KeyboardEvent) => {
|
||||||
|
if (evt.code === "Escape") {
|
||||||
|
evt.stopPropagation();
|
||||||
|
history.goBack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@action
|
@action
|
||||||
async loadHelmRepos() {
|
async loadHelmRepos() {
|
||||||
this.helmLoading = true;
|
this.helmLoading = true;
|
||||||
@ -121,13 +130,19 @@ export class Preferences extends React.Component {
|
|||||||
onChange={({ value }: SelectOption) => preferences.colorTheme = value}
|
onChange={({ value }: SelectOption) => preferences.colorTheme = value}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<h2><Trans>Download Mirror</Trans></h2>
|
<h2><Trans>HTTP Proxy</Trans></h2>
|
||||||
<Select
|
<Input
|
||||||
placeholder={<Trans>Download mirror for kubectl</Trans>}
|
theme="round-black"
|
||||||
options={this.downloadMirrorOptions}
|
placeholder={_i18n._(t`Type HTTP proxy url (example: http://proxy.acme.org:8080)`)}
|
||||||
value={preferences.downloadMirror}
|
value={this.httpProxy}
|
||||||
onChange={({ value }: SelectOption) => preferences.downloadMirror = value}
|
onChange={v => this.httpProxy = v}
|
||||||
|
onBlur={() => preferences.httpsProxy = this.httpProxy}
|
||||||
/>
|
/>
|
||||||
|
<small className="hint">
|
||||||
|
<Trans>Proxy is used only for non-cluster communication.</Trans>
|
||||||
|
</small>
|
||||||
|
|
||||||
|
<KubectlBinaries preferences={preferences} />
|
||||||
|
|
||||||
<h2><Trans>Helm</Trans></h2>
|
<h2><Trans>Helm</Trans></h2>
|
||||||
<Select
|
<Select
|
||||||
@ -158,17 +173,6 @@ export class Preferences extends React.Component {
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2><Trans>HTTP Proxy</Trans></h2>
|
|
||||||
<Input
|
|
||||||
theme="round-black"
|
|
||||||
placeholder={_i18n._(t`Type HTTP proxy url (example: http://proxy.acme.org:8080)`)}
|
|
||||||
value={preferences.httpsProxy || ""}
|
|
||||||
onChange={v => preferences.httpsProxy = v}
|
|
||||||
/>
|
|
||||||
<small className="hint">
|
|
||||||
<Trans>Proxy is used only for non-cluster communication.</Trans>
|
|
||||||
</small>
|
|
||||||
|
|
||||||
<h2><Trans>Certificate Trust</Trans></h2>
|
<h2><Trans>Certificate Trust</Trans></h2>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label={<Trans>Allow untrusted Certificate Authorities</Trans>}
|
label={<Trans>Allow untrusted Certificate Authorities</Trans>}
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
border-radius: $radius;
|
border-radius: $radius;
|
||||||
padding: $radius;
|
padding: $radius;
|
||||||
|
margin-bottom: $padding * 2;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
|||||||
@ -33,8 +33,8 @@ export function getMatchedCluster() {
|
|||||||
return clusterStore.getById(getMatchedClusterId())
|
return clusterStore.getById(getMatchedClusterId())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Refresh global menu depending on active route's type (common/cluster view)
|
|
||||||
if (ipcRenderer) {
|
if (ipcRenderer) {
|
||||||
|
// Refresh global menu depending on active route's type (common/cluster view)
|
||||||
const isMainView = !getHostedClusterId();
|
const isMainView = !getHostedClusterId();
|
||||||
if (isMainView) {
|
if (isMainView) {
|
||||||
reaction(() => getMatchedClusterId(), clusterId => {
|
reaction(() => getMatchedClusterId(), clusterId => {
|
||||||
@ -43,4 +43,9 @@ if (ipcRenderer) {
|
|||||||
fireImmediately: true
|
fireImmediately: true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reload dashboard
|
||||||
|
ipcRenderer.on("menu:reload", () => {
|
||||||
|
location.reload();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,10 @@
|
|||||||
padding: 0 $spacing; // extra spacing for cluster-icon's badge
|
padding: 0 $spacing; // extra spacing for cluster-icon's badge
|
||||||
margin-bottom: $spacing;
|
margin-bottom: $spacing;
|
||||||
|
|
||||||
|
> :last-child {
|
||||||
|
margin-bottom: $margin;
|
||||||
|
}
|
||||||
|
|
||||||
&:empty {
|
&:empty {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import { ClusterId, clusterStore } from "../../../common/cluster-store";
|
|||||||
import { workspaceStore } from "../../../common/workspace-store";
|
import { workspaceStore } from "../../../common/workspace-store";
|
||||||
import { ClusterIcon } from "../cluster-icon";
|
import { ClusterIcon } from "../cluster-icon";
|
||||||
import { Icon } from "../icon";
|
import { Icon } from "../icon";
|
||||||
import { cssNames, IClassName } from "../../utils";
|
import { cssNames, IClassName, autobind } from "../../utils";
|
||||||
import { Badge } from "../badge";
|
import { Badge } from "../badge";
|
||||||
import { navigate } from "../../navigation";
|
import { navigate } from "../../navigation";
|
||||||
import { addClusterURL } from "../+add-cluster";
|
import { addClusterURL } from "../+add-cluster";
|
||||||
@ -20,6 +20,7 @@ import { Tooltip } from "../tooltip";
|
|||||||
import { ConfirmDialog } from "../confirm-dialog";
|
import { ConfirmDialog } from "../confirm-dialog";
|
||||||
import { clusterIpc } from "../../../common/cluster-ipc";
|
import { clusterIpc } from "../../../common/cluster-ipc";
|
||||||
import { clusterViewURL, getMatchedClusterId } from "./cluster-view.route";
|
import { clusterViewURL, getMatchedClusterId } from "./cluster-view.route";
|
||||||
|
import { DragDropContext, Droppable, Draggable, DropResult, DroppableProvided, DraggableProvided } from "react-beautiful-dnd";
|
||||||
import { dynamicPages } from "./register-page";
|
import { dynamicPages } from "./register-page";
|
||||||
|
|
||||||
// fixme: allow to rearrange clusters with drag&drop
|
// fixme: allow to rearrange clusters with drag&drop
|
||||||
@ -88,6 +89,18 @@ export class ClustersMenu extends React.Component<Props> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@autobind()
|
||||||
|
swapClusterIconOrder(result: DropResult) {
|
||||||
|
if (result.reason === "DROP") {
|
||||||
|
const { currentWorkspaceId } = workspaceStore;
|
||||||
|
const {
|
||||||
|
source: { index: from },
|
||||||
|
destination: { index: to },
|
||||||
|
} = result
|
||||||
|
clusterStore.swapIconOrders(currentWorkspaceId, from, to)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { className } = this.props;
|
const { className } = this.props;
|
||||||
const { newContexts } = userStore;
|
const { newContexts } = userStore;
|
||||||
@ -95,26 +108,46 @@ export class ClustersMenu extends React.Component<Props> {
|
|||||||
return (
|
return (
|
||||||
<div className={cssNames("ClustersMenu flex column", className)}>
|
<div className={cssNames("ClustersMenu flex column", className)}>
|
||||||
<div className="clusters flex column gaps">
|
<div className="clusters flex column gaps">
|
||||||
{clusters.map(cluster => {
|
<DragDropContext onDragEnd={this.swapClusterIconOrder}>
|
||||||
return (
|
<Droppable droppableId="cluster-menu" type="CLUSTER">
|
||||||
<ClusterIcon
|
{(provided: DroppableProvided) => (
|
||||||
key={cluster.id}
|
<div
|
||||||
showErrors={true}
|
ref={provided.innerRef}
|
||||||
cluster={cluster}
|
{...provided.droppableProps}
|
||||||
isActive={cluster.id === getMatchedClusterId()}
|
>
|
||||||
onClick={() => this.showCluster(cluster.id)}
|
{clusters.map((cluster, index) => (
|
||||||
onContextMenu={() => this.showContextMenu(cluster)}
|
<Draggable draggableId={cluster.id} index={index} key={cluster.id}>
|
||||||
/>
|
{(provided: DraggableProvided) => (
|
||||||
)
|
<div
|
||||||
})}
|
ref={provided.innerRef}
|
||||||
|
{...provided.draggableProps}
|
||||||
|
{...provided.dragHandleProps}
|
||||||
|
>
|
||||||
|
<ClusterIcon
|
||||||
|
key={cluster.id}
|
||||||
|
showErrors={true}
|
||||||
|
cluster={cluster}
|
||||||
|
isActive={cluster.id === getMatchedClusterId()}
|
||||||
|
onClick={() => this.showCluster(cluster.id)}
|
||||||
|
onContextMenu={() => this.showContextMenu(cluster)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Draggable>
|
||||||
|
))}
|
||||||
|
{provided.placeholder}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Droppable>
|
||||||
|
</DragDropContext>
|
||||||
</div>
|
</div>
|
||||||
<div className="add-cluster" onClick={this.addCluster}>
|
<div className="add-cluster" onClick={this.addCluster}>
|
||||||
<Tooltip targetId="add-cluster-icon">
|
<Tooltip targetId="add-cluster-icon">
|
||||||
<Trans>Add Cluster</Trans>
|
<Trans>Add Cluster</Trans>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Icon big material="add" id="add-cluster-icon"/>
|
<Icon big material="add" id="add-cluster-icon" />
|
||||||
{newContexts.size > 0 && (
|
{newContexts.size > 0 && (
|
||||||
<Badge className="counter" label={newContexts.size} tooltip={<Trans>new</Trans>}/>
|
<Badge className="counter" label={newContexts.size} tooltip={<Trans>new</Trans>} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="dynamic-pages">
|
<div className="dynamic-pages">
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.chart-version {
|
&.chart-version {
|
||||||
min-width: 80px;
|
min-width: 130px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import type { InputProps } from "./input";
|
|||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
import { t } from "@lingui/macro";
|
import { t } from "@lingui/macro";
|
||||||
import { _i18n } from '../../i18n';
|
import { _i18n } from '../../i18n';
|
||||||
|
import fse from "fs-extra";
|
||||||
|
|
||||||
export interface Validator {
|
export interface Validator {
|
||||||
debounce?: number; // debounce for async validators in ms
|
debounce?: number; // debounce for async validators in ms
|
||||||
@ -41,6 +42,12 @@ export const isUrl: Validator = {
|
|||||||
validate: value => !!value.match(/^http(s)?:\/\/\w+(\.\w+)*(:[0-9]+)?\/?(\/[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]*)*$/),
|
validate: value => !!value.match(/^http(s)?:\/\/\w+(\.\w+)*(:[0-9]+)?\/?(\/[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]*)*$/),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isPath: Validator = {
|
||||||
|
condition: ({ type }) => type === "text",
|
||||||
|
message: () => _i18n._(t`This field must be a path to an existing file`),
|
||||||
|
validate: value => !value || fse.pathExistsSync(value),
|
||||||
|
}
|
||||||
|
|
||||||
export const minLength: Validator = {
|
export const minLength: Validator = {
|
||||||
condition: ({ minLength }) => !!minLength,
|
condition: ({ minLength }) => !!minLength,
|
||||||
message: (value, { minLength }) => _i18n._(t`Minimum length is ${minLength}`),
|
message: (value, { minLength }) => _i18n._(t`Minimum length is ${minLength}`),
|
||||||
|
|||||||
@ -70,11 +70,15 @@ html {
|
|||||||
&__menu {
|
&__menu {
|
||||||
background: var(--select-menu-bgc);
|
background: var(--select-menu-bgc);
|
||||||
box-shadow: inset 0 0 0 1px var(--select-menu-border-color);
|
box-shadow: inset 0 0 0 1px var(--select-menu-border-color);
|
||||||
|
width: max-content;
|
||||||
|
min-width: 100%;
|
||||||
|
|
||||||
&-list {
|
&-list {
|
||||||
@include custom-scrollbar;
|
@include custom-scrollbar;
|
||||||
padding-right: 1px;
|
padding-right: 1px;
|
||||||
padding-left: 1px;
|
padding-left: 1px;
|
||||||
|
width: max-content;
|
||||||
|
min-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-notice {
|
&-notice {
|
||||||
@ -83,6 +87,8 @@ html {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__option {
|
&__option {
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
background: $primary;
|
background: $primary;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -69,6 +69,7 @@ export function getSelectedDetails() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getDetailsUrl(details: string) {
|
export function getDetailsUrl(details: string) {
|
||||||
|
if (!details) return "";
|
||||||
return getQueryString({
|
return getQueryString({
|
||||||
details: details,
|
details: details,
|
||||||
selected: getSelectedDetails(),
|
selected: getSelectedDetails(),
|
||||||
|
|||||||
@ -2,7 +2,12 @@
|
|||||||
|
|
||||||
Here you can find description of changes we've built into each release. While we try our best to make each upgrade automatic and as smooth as possible, there may be some cases where you might need to do something to ensure the application works smoothly. So please read through the release highlights!
|
Here you can find description of changes we've built into each release. While we try our best to make each upgrade automatic and as smooth as possible, there may be some cases where you might need to do something to ensure the application works smoothly. So please read through the release highlights!
|
||||||
|
|
||||||
## 3.6.0-beta.1 (current version)
|
## 3.6.0-beta.2 (current version)
|
||||||
|
- Fix: too narrow sidebar without clusters
|
||||||
|
- Fix app crash when iterating Events without 'kind' property defined
|
||||||
|
- Detect non-functional bundled kubectl
|
||||||
|
|
||||||
|
## 3.6.0-beta.1
|
||||||
- Allow user to select Kubeconfig from filesystem
|
- Allow user to select Kubeconfig from filesystem
|
||||||
- Store reference to added Kubeconfig files
|
- Store reference to added Kubeconfig files
|
||||||
- Show the path of the cluster's Kubeconfig in cluster settings
|
- Show the path of the cluster's Kubeconfig in cluster settings
|
||||||
|
|||||||
350
yarn.lock
350
yarn.lock
@ -1350,6 +1350,17 @@
|
|||||||
"@types/yargs" "^15.0.0"
|
"@types/yargs" "^15.0.0"
|
||||||
chalk "^4.0.0"
|
chalk "^4.0.0"
|
||||||
|
|
||||||
|
"@jest/types@^26.3.0":
|
||||||
|
version "26.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.3.0.tgz#97627bf4bdb72c55346eef98e3b3f7ddc4941f71"
|
||||||
|
integrity sha512-BDPG23U0qDeAvU4f99haztXwdAg3hz4El95LkAM+tHAqqhiVzRpEGHHU8EDxT/AnxOrA65YjLBwDahdJ9pTLJQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/istanbul-lib-coverage" "^2.0.0"
|
||||||
|
"@types/istanbul-reports" "^3.0.0"
|
||||||
|
"@types/node" "*"
|
||||||
|
"@types/yargs" "^15.0.0"
|
||||||
|
chalk "^4.0.0"
|
||||||
|
|
||||||
"@kubernetes/client-node@^0.12.0":
|
"@kubernetes/client-node@^0.12.0":
|
||||||
version "0.12.0"
|
version "0.12.0"
|
||||||
resolved "https://registry.yarnpkg.com/@kubernetes/client-node/-/client-node-0.12.0.tgz#79120311bced206ac8fa36435fb4cc2c1828fff2"
|
resolved "https://registry.yarnpkg.com/@kubernetes/client-node/-/client-node-0.12.0.tgz#79120311bced206ac8fa36435fb4cc2c1828fff2"
|
||||||
@ -1545,6 +1556,27 @@
|
|||||||
prop-types "^15.7.2"
|
prop-types "^15.7.2"
|
||||||
react-is "^16.8.0"
|
react-is "^16.8.0"
|
||||||
|
|
||||||
|
"@nodelib/fs.scandir@2.1.3":
|
||||||
|
version "2.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b"
|
||||||
|
integrity sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==
|
||||||
|
dependencies:
|
||||||
|
"@nodelib/fs.stat" "2.0.3"
|
||||||
|
run-parallel "^1.1.9"
|
||||||
|
|
||||||
|
"@nodelib/fs.stat@2.0.3", "@nodelib/fs.stat@^2.0.2":
|
||||||
|
version "2.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz#34dc5f4cabbc720f4e60f75a747e7ecd6c175bd3"
|
||||||
|
integrity sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==
|
||||||
|
|
||||||
|
"@nodelib/fs.walk@^1.2.3":
|
||||||
|
version "1.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz#011b9202a70a6366e436ca5c065844528ab04976"
|
||||||
|
integrity sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==
|
||||||
|
dependencies:
|
||||||
|
"@nodelib/fs.scandir" "2.1.3"
|
||||||
|
fastq "^1.6.0"
|
||||||
|
|
||||||
"@npmcli/move-file@^1.0.1":
|
"@npmcli/move-file@^1.0.1":
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.0.1.tgz#de103070dac0f48ce49cf6693c23af59c0f70464"
|
resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.0.1.tgz#de103070dac0f48ce49cf6693c23af59c0f70464"
|
||||||
@ -1710,11 +1742,6 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
electron "*"
|
electron "*"
|
||||||
|
|
||||||
"@types/eslint-visitor-keys@^1.0.0":
|
|
||||||
version "1.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d"
|
|
||||||
integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==
|
|
||||||
|
|
||||||
"@types/fs-extra@^9.0.1":
|
"@types/fs-extra@^9.0.1":
|
||||||
version "9.0.1"
|
version "9.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.1.tgz#91c8fc4c51f6d5dbe44c2ca9ab09310bd00c7918"
|
resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.1.tgz#91c8fc4c51f6d5dbe44c2ca9ab09310bd00c7918"
|
||||||
@ -1830,6 +1857,21 @@
|
|||||||
"@types/istanbul-lib-coverage" "*"
|
"@types/istanbul-lib-coverage" "*"
|
||||||
"@types/istanbul-lib-report" "*"
|
"@types/istanbul-lib-report" "*"
|
||||||
|
|
||||||
|
"@types/istanbul-reports@^3.0.0":
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz#508b13aa344fa4976234e75dddcc34925737d821"
|
||||||
|
integrity sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==
|
||||||
|
dependencies:
|
||||||
|
"@types/istanbul-lib-report" "*"
|
||||||
|
|
||||||
|
"@types/jest@26.x":
|
||||||
|
version "26.0.13"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.13.tgz#5a7b9d5312f5dd521a38329c38ee9d3802a0b85e"
|
||||||
|
integrity sha512-sCzjKow4z9LILc6DhBvn5AkIfmQzDZkgtVVKmGwVrs5tuid38ws281D4l+7x1kP487+FlKDh5kfMZ8WSPAdmdA==
|
||||||
|
dependencies:
|
||||||
|
jest-diff "^25.2.1"
|
||||||
|
pretty-format "^25.2.1"
|
||||||
|
|
||||||
"@types/jest@^25.2.3":
|
"@types/jest@^25.2.3":
|
||||||
version "25.2.3"
|
version "25.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-25.2.3.tgz#33d27e4c4716caae4eced355097a47ad363fdcaf"
|
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-25.2.3.tgz#33d27e4c4716caae4eced355097a47ad363fdcaf"
|
||||||
@ -1988,6 +2030,13 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/react" "*"
|
"@types/react" "*"
|
||||||
|
|
||||||
|
"@types/react-beautiful-dnd@^13.0.0":
|
||||||
|
version "13.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/react-beautiful-dnd/-/react-beautiful-dnd-13.0.0.tgz#e60d3d965312fcf1516894af92dc3e9249587db4"
|
||||||
|
integrity sha512-by80tJ8aTTDXT256Gl+RfLRtFjYbUWOnZuEigJgNsJrSEGxvFe5eY6k3g4VIvf0M/6+xoLgfYWoWonlOo6Wqdg==
|
||||||
|
dependencies:
|
||||||
|
"@types/react" "*"
|
||||||
|
|
||||||
"@types/react-dom@*":
|
"@types/react-dom@*":
|
||||||
version "16.9.8"
|
version "16.9.8"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.8.tgz#fe4c1e11dfc67155733dfa6aa65108b4971cb423"
|
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.8.tgz#fe4c1e11dfc67155733dfa6aa65108b4971cb423"
|
||||||
@ -2233,51 +2282,76 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
"@typescript-eslint/eslint-plugin@^3.4.0":
|
"@typescript-eslint/eslint-plugin@^4.0.0":
|
||||||
version "3.4.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.4.0.tgz#8378062e6be8a1d049259bdbcf27ce5dfbeee62b"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.0.0.tgz#99349a501447fed91de18346705c0c65cf603bee"
|
||||||
integrity sha512-wfkpiqaEVhZIuQRmudDszc01jC/YR7gMSxa6ulhggAe/Hs0KVIuo9wzvFiDbG3JD5pRFQoqnf4m7REDsUvBnMQ==
|
integrity sha512-5e6q1TR7gS2P+8W2xndCu7gBh3BzmYEo70OyIdsmCmknHha/yNbz2vdevl+tP1uoaMOcrzg4gyrAijuV3DDBHA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@typescript-eslint/experimental-utils" "3.4.0"
|
"@typescript-eslint/experimental-utils" "4.0.0"
|
||||||
|
"@typescript-eslint/scope-manager" "4.0.0"
|
||||||
debug "^4.1.1"
|
debug "^4.1.1"
|
||||||
functional-red-black-tree "^1.0.1"
|
functional-red-black-tree "^1.0.1"
|
||||||
regexpp "^3.0.0"
|
regexpp "^3.0.0"
|
||||||
semver "^7.3.2"
|
semver "^7.3.2"
|
||||||
tsutils "^3.17.1"
|
tsutils "^3.17.1"
|
||||||
|
|
||||||
"@typescript-eslint/experimental-utils@3.4.0":
|
"@typescript-eslint/experimental-utils@4.0.0":
|
||||||
version "3.4.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-3.4.0.tgz#8a44dfc6fb7f1d071937b390fe27608ebda122b8"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.0.0.tgz#fbec21a3b5ab59127edb6ce2e139ed378cc50eb5"
|
||||||
integrity sha512-rHPOjL43lOH1Opte4+dhC0a/+ks+8gOBwxXnyrZ/K4OTAChpSjP76fbI8Cglj7V5GouwVAGaK+xVwzqTyE/TPw==
|
integrity sha512-hbX6zR+a/vcpFVNJYN/Nbd7gmaMosDTxHEKcvmhWeWcq/0UDifrqmCfkkodbAKL46Fn4ekSBMTyq2zlNDzcQxw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/json-schema" "^7.0.3"
|
"@types/json-schema" "^7.0.3"
|
||||||
"@typescript-eslint/typescript-estree" "3.4.0"
|
"@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-scope "^5.0.0"
|
||||||
eslint-utils "^2.0.0"
|
eslint-utils "^2.0.0"
|
||||||
|
|
||||||
"@typescript-eslint/parser@^3.4.0":
|
"@typescript-eslint/parser@^4.0.0":
|
||||||
version "3.4.0"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-3.4.0.tgz#fe52b68c5cb3bba3f5d875bd17adb70420d49d8d"
|
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.0.0.tgz#0b19c25ad404b617caf33121e05f7dad14a594aa"
|
||||||
integrity sha512-ZUGI/de44L5x87uX5zM14UYcbn79HSXUR+kzcqU42gH0AgpdB/TjuJy3m4ezI7Q/jk3wTQd755mxSDLhQP79KA==
|
integrity sha512-KuBwTUzc3G3dD5k9ybTjgqIQjjJPY6WPaEoxdNS5vN5XV/Tixp0itA14+NQlbeswTHvsELaKXZhynxD/O2wHFA==
|
||||||
dependencies:
|
|
||||||
"@types/eslint-visitor-keys" "^1.0.0"
|
|
||||||
"@typescript-eslint/experimental-utils" "3.4.0"
|
|
||||||
"@typescript-eslint/typescript-estree" "3.4.0"
|
|
||||||
eslint-visitor-keys "^1.1.0"
|
|
||||||
|
|
||||||
"@typescript-eslint/typescript-estree@3.4.0":
|
|
||||||
version "3.4.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-3.4.0.tgz#6a787eb70b48969e4cd1ea67b057083f96dfee29"
|
|
||||||
integrity sha512-zKwLiybtt4uJb4mkG5q2t6+W7BuYx2IISiDNV+IY68VfoGwErDx/RfVI7SWL4gnZ2t1A1ytQQwZ+YOJbHHJ2rw==
|
|
||||||
dependencies:
|
dependencies:
|
||||||
|
"@typescript-eslint/scope-manager" "4.0.0"
|
||||||
|
"@typescript-eslint/types" "4.0.0"
|
||||||
|
"@typescript-eslint/typescript-estree" "4.0.0"
|
||||||
debug "^4.1.1"
|
debug "^4.1.1"
|
||||||
eslint-visitor-keys "^1.1.0"
|
|
||||||
glob "^7.1.6"
|
"@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==
|
||||||
|
dependencies:
|
||||||
|
"@typescript-eslint/types" "4.0.0"
|
||||||
|
"@typescript-eslint/visitor-keys" "4.0.0"
|
||||||
|
|
||||||
|
"@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/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==
|
||||||
|
dependencies:
|
||||||
|
"@typescript-eslint/types" "4.0.0"
|
||||||
|
"@typescript-eslint/visitor-keys" "4.0.0"
|
||||||
|
debug "^4.1.1"
|
||||||
|
globby "^11.0.1"
|
||||||
is-glob "^4.0.1"
|
is-glob "^4.0.1"
|
||||||
lodash "^4.17.15"
|
lodash "^4.17.15"
|
||||||
semver "^7.3.2"
|
semver "^7.3.2"
|
||||||
tsutils "^3.17.1"
|
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==
|
||||||
|
dependencies:
|
||||||
|
"@typescript-eslint/types" "4.0.0"
|
||||||
|
eslint-visitor-keys "^2.0.0"
|
||||||
|
|
||||||
"@webassemblyjs/ast@1.9.0":
|
"@webassemblyjs/ast@1.9.0":
|
||||||
version "1.9.0"
|
version "1.9.0"
|
||||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964"
|
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964"
|
||||||
@ -2481,10 +2555,10 @@ acorn@^7.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.2.0.tgz#17ea7e40d7c8640ff54a694c889c26f31704effe"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.2.0.tgz#17ea7e40d7c8640ff54a694c889c26f31704effe"
|
||||||
integrity sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ==
|
integrity sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ==
|
||||||
|
|
||||||
acorn@^7.2.0:
|
acorn@^7.4.0:
|
||||||
version "7.3.1"
|
version "7.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.3.1.tgz#85010754db53c3fbaf3b9ea3e083aa5c5d147ffd"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.0.tgz#e1ad486e6c54501634c6c397c5c121daa383607c"
|
||||||
integrity sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==
|
integrity sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==
|
||||||
|
|
||||||
aggregate-error@^1.0.0:
|
aggregate-error@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
@ -2716,6 +2790,16 @@ array-find-index@^1.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1"
|
resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1"
|
||||||
integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=
|
integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=
|
||||||
|
|
||||||
|
array-move@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/array-move/-/array-move-3.0.0.tgz#b646a2f4980be78f04d28d7572a72036150d364e"
|
||||||
|
integrity sha512-kqK1ZKiAVfIdfiJjC3zpAGPg3OEkjeeKuOILwS1b+oh34dI6GTg9szgRT+oKWw48RuVF8RGjlWCSYkn6NU+Jvw==
|
||||||
|
|
||||||
|
array-union@^2.1.0:
|
||||||
|
version "2.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
|
||||||
|
integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
|
||||||
|
|
||||||
array-unique@^0.3.2:
|
array-unique@^0.3.2:
|
||||||
version "0.3.2"
|
version "0.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
|
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
|
||||||
@ -4007,13 +4091,6 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7:
|
|||||||
safe-buffer "^5.0.1"
|
safe-buffer "^5.0.1"
|
||||||
sha.js "^2.4.8"
|
sha.js "^2.4.8"
|
||||||
|
|
||||||
cross-env@^7.0.2:
|
|
||||||
version "7.0.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.2.tgz#bd5ed31339a93a3418ac4f3ca9ca3403082ae5f9"
|
|
||||||
integrity sha512-KZP/bMEOJEDCkDQAyRhu3RL2ZO/SUVrxQVI0G3YEQ+OLbRA3c6zgixe8Mq8a/z7+HKlNEjo8oiLUs8iRijY2Rw==
|
|
||||||
dependencies:
|
|
||||||
cross-spawn "^7.0.1"
|
|
||||||
|
|
||||||
cross-spawn@6.0.5, cross-spawn@^6.0.0, cross-spawn@^6.0.5:
|
cross-spawn@6.0.5, cross-spawn@^6.0.0, cross-spawn@^6.0.5:
|
||||||
version "6.0.5"
|
version "6.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
|
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
|
||||||
@ -4033,7 +4110,7 @@ cross-spawn@^3.0.0:
|
|||||||
lru-cache "^4.0.1"
|
lru-cache "^4.0.1"
|
||||||
which "^1.2.9"
|
which "^1.2.9"
|
||||||
|
|
||||||
cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2:
|
cross-spawn@^7.0.0, cross-spawn@^7.0.2:
|
||||||
version "7.0.3"
|
version "7.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
|
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
|
||||||
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
|
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
|
||||||
@ -4069,6 +4146,13 @@ crypto-random-string@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
|
resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
|
||||||
integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==
|
integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==
|
||||||
|
|
||||||
|
css-box-model@^1.2.0:
|
||||||
|
version "1.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/css-box-model/-/css-box-model-1.2.1.tgz#59951d3b81fd6b2074a62d49444415b0d2b4d7c1"
|
||||||
|
integrity sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==
|
||||||
|
dependencies:
|
||||||
|
tiny-invariant "^1.0.6"
|
||||||
|
|
||||||
css-element-queries@^1.2.3:
|
css-element-queries@^1.2.3:
|
||||||
version "1.2.3"
|
version "1.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/css-element-queries/-/css-element-queries-1.2.3.tgz#e14940b1fcd4bf0da60ea4145d05742d7172e516"
|
resolved "https://registry.yarnpkg.com/css-element-queries/-/css-element-queries-1.2.3.tgz#e14940b1fcd4bf0da60ea4145d05742d7172e516"
|
||||||
@ -4404,6 +4488,13 @@ diffie-hellman@^5.0.0:
|
|||||||
miller-rabin "^4.0.0"
|
miller-rabin "^4.0.0"
|
||||||
randombytes "^2.0.0"
|
randombytes "^2.0.0"
|
||||||
|
|
||||||
|
dir-glob@^3.0.1:
|
||||||
|
version "3.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
|
||||||
|
integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==
|
||||||
|
dependencies:
|
||||||
|
path-type "^4.0.0"
|
||||||
|
|
||||||
dmg-builder@22.7.0:
|
dmg-builder@22.7.0:
|
||||||
version "22.7.0"
|
version "22.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-22.7.0.tgz#ead7e7c046cbdc52d29d302a4455f6668cdf7d45"
|
resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-22.7.0.tgz#ead7e7c046cbdc52d29d302a4455f6668cdf7d45"
|
||||||
@ -4860,22 +4951,27 @@ eslint-scope@^5.0.0, eslint-scope@^5.1.0:
|
|||||||
esrecurse "^4.1.0"
|
esrecurse "^4.1.0"
|
||||||
estraverse "^4.1.1"
|
estraverse "^4.1.1"
|
||||||
|
|
||||||
eslint-utils@^2.0.0:
|
eslint-utils@^2.0.0, eslint-utils@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27"
|
resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27"
|
||||||
integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==
|
integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==
|
||||||
dependencies:
|
dependencies:
|
||||||
eslint-visitor-keys "^1.1.0"
|
eslint-visitor-keys "^1.1.0"
|
||||||
|
|
||||||
eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.2.0:
|
eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0:
|
||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e"
|
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e"
|
||||||
integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==
|
integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==
|
||||||
|
|
||||||
eslint@^7.3.1:
|
eslint-visitor-keys@^2.0.0:
|
||||||
version "7.3.1"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.3.1.tgz#76392bd7e44468d046149ba128d1566c59acbe19"
|
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8"
|
||||||
integrity sha512-cQC/xj9bhWUcyi/RuMbRtC3I0eW8MH0jhRELSvpKYkWep3C6YZ2OkvcvJVUeO6gcunABmzptbXBuDoXsjHmfTA==
|
integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==
|
||||||
|
|
||||||
|
eslint@^7.7.0:
|
||||||
|
version "7.7.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.7.0.tgz#18beba51411927c4b64da0a8ceadefe4030d6073"
|
||||||
|
integrity sha512-1KUxLzos0ZVsyL81PnRN335nDtQ8/vZUD6uMtWbF+5zDtjKcsklIi78XoE0MVL93QvWTu+E5y44VyyCsOMBrIg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/code-frame" "^7.0.0"
|
"@babel/code-frame" "^7.0.0"
|
||||||
ajv "^6.10.0"
|
ajv "^6.10.0"
|
||||||
@ -4885,9 +4981,9 @@ eslint@^7.3.1:
|
|||||||
doctrine "^3.0.0"
|
doctrine "^3.0.0"
|
||||||
enquirer "^2.3.5"
|
enquirer "^2.3.5"
|
||||||
eslint-scope "^5.1.0"
|
eslint-scope "^5.1.0"
|
||||||
eslint-utils "^2.0.0"
|
eslint-utils "^2.1.0"
|
||||||
eslint-visitor-keys "^1.2.0"
|
eslint-visitor-keys "^1.3.0"
|
||||||
espree "^7.1.0"
|
espree "^7.2.0"
|
||||||
esquery "^1.2.0"
|
esquery "^1.2.0"
|
||||||
esutils "^2.0.2"
|
esutils "^2.0.2"
|
||||||
file-entry-cache "^5.0.1"
|
file-entry-cache "^5.0.1"
|
||||||
@ -4901,7 +4997,7 @@ eslint@^7.3.1:
|
|||||||
js-yaml "^3.13.1"
|
js-yaml "^3.13.1"
|
||||||
json-stable-stringify-without-jsonify "^1.0.1"
|
json-stable-stringify-without-jsonify "^1.0.1"
|
||||||
levn "^0.4.1"
|
levn "^0.4.1"
|
||||||
lodash "^4.17.14"
|
lodash "^4.17.19"
|
||||||
minimatch "^3.0.4"
|
minimatch "^3.0.4"
|
||||||
natural-compare "^1.4.0"
|
natural-compare "^1.4.0"
|
||||||
optionator "^0.9.1"
|
optionator "^0.9.1"
|
||||||
@ -4914,14 +5010,14 @@ eslint@^7.3.1:
|
|||||||
text-table "^0.2.0"
|
text-table "^0.2.0"
|
||||||
v8-compile-cache "^2.0.3"
|
v8-compile-cache "^2.0.3"
|
||||||
|
|
||||||
espree@^7.1.0:
|
espree@^7.2.0:
|
||||||
version "7.1.0"
|
version "7.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/espree/-/espree-7.1.0.tgz#a9c7f18a752056735bf1ba14cb1b70adc3a5ce1c"
|
resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.0.tgz#dc30437cf67947cf576121ebd780f15eeac72348"
|
||||||
integrity sha512-dcorZSyfmm4WTuTnE5Y7MEN1DyoPYy1ZR783QW1FJoenn7RailyWFsq/UL6ZAAA7uXurN9FIpYyUs3OfiIW+Qw==
|
integrity sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==
|
||||||
dependencies:
|
dependencies:
|
||||||
acorn "^7.2.0"
|
acorn "^7.4.0"
|
||||||
acorn-jsx "^5.2.0"
|
acorn-jsx "^5.2.0"
|
||||||
eslint-visitor-keys "^1.2.0"
|
eslint-visitor-keys "^1.3.0"
|
||||||
|
|
||||||
esprima@1.2.2:
|
esprima@1.2.2:
|
||||||
version "1.2.2"
|
version "1.2.2"
|
||||||
@ -5138,6 +5234,18 @@ fast-deep-equal@^3.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
||||||
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
|
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
|
||||||
|
|
||||||
|
fast-glob@^3.1.1:
|
||||||
|
version "3.2.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3"
|
||||||
|
integrity sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==
|
||||||
|
dependencies:
|
||||||
|
"@nodelib/fs.stat" "^2.0.2"
|
||||||
|
"@nodelib/fs.walk" "^1.2.3"
|
||||||
|
glob-parent "^5.1.0"
|
||||||
|
merge2 "^1.3.0"
|
||||||
|
micromatch "^4.0.2"
|
||||||
|
picomatch "^2.2.1"
|
||||||
|
|
||||||
fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0:
|
fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
|
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
|
||||||
@ -5153,6 +5261,13 @@ fast-safe-stringify@^2.0.4:
|
|||||||
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743"
|
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743"
|
||||||
integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==
|
integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==
|
||||||
|
|
||||||
|
fastq@^1.6.0:
|
||||||
|
version "1.8.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.8.0.tgz#550e1f9f59bbc65fe185cb6a9b4d95357107f481"
|
||||||
|
integrity sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q==
|
||||||
|
dependencies:
|
||||||
|
reusify "^1.0.4"
|
||||||
|
|
||||||
fb-watchman@^2.0.0:
|
fb-watchman@^2.0.0:
|
||||||
version "2.0.1"
|
version "2.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85"
|
resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85"
|
||||||
@ -5617,14 +5732,14 @@ glob-parent@^3.1.0:
|
|||||||
is-glob "^3.1.0"
|
is-glob "^3.1.0"
|
||||||
path-dirname "^1.0.0"
|
path-dirname "^1.0.0"
|
||||||
|
|
||||||
glob-parent@^5.0.0, glob-parent@~5.1.0:
|
glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0:
|
||||||
version "5.1.1"
|
version "5.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229"
|
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229"
|
||||||
integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==
|
integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
is-glob "^4.0.1"
|
is-glob "^4.0.1"
|
||||||
|
|
||||||
glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.1:
|
glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@~7.1.1:
|
||||||
version "7.1.6"
|
version "7.1.6"
|
||||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
|
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
|
||||||
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
|
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
|
||||||
@ -5721,6 +5836,18 @@ globalthis@^1.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
define-properties "^1.1.3"
|
define-properties "^1.1.3"
|
||||||
|
|
||||||
|
globby@^11.0.1:
|
||||||
|
version "11.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.1.tgz#9a2bf107a068f3ffeabc49ad702c79ede8cfd357"
|
||||||
|
integrity sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==
|
||||||
|
dependencies:
|
||||||
|
array-union "^2.1.0"
|
||||||
|
dir-glob "^3.0.1"
|
||||||
|
fast-glob "^3.1.1"
|
||||||
|
ignore "^5.1.4"
|
||||||
|
merge2 "^1.3.0"
|
||||||
|
slash "^3.0.0"
|
||||||
|
|
||||||
globule@^1.0.0:
|
globule@^1.0.0:
|
||||||
version "1.3.2"
|
version "1.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/globule/-/globule-1.3.2.tgz#d8bdd9e9e4eef8f96e245999a5dee7eb5d8529c4"
|
resolved "https://registry.yarnpkg.com/globule/-/globule-1.3.2.tgz#d8bdd9e9e4eef8f96e245999a5dee7eb5d8529c4"
|
||||||
@ -6114,6 +6241,11 @@ ignore@^4.0.6:
|
|||||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
|
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
|
||||||
integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
|
integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
|
||||||
|
|
||||||
|
ignore@^5.1.4:
|
||||||
|
version "5.1.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57"
|
||||||
|
integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==
|
||||||
|
|
||||||
immer@^7.0.5:
|
immer@^7.0.5:
|
||||||
version "7.0.5"
|
version "7.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/immer/-/immer-7.0.5.tgz#8af347db5b60b40af8ae7baf1784ea4d35b5208e"
|
resolved "https://registry.yarnpkg.com/immer/-/immer-7.0.5.tgz#8af347db5b60b40af8ae7baf1784ea4d35b5208e"
|
||||||
@ -7036,6 +7168,18 @@ jest-snapshot@^26.0.1:
|
|||||||
pretty-format "^26.0.1"
|
pretty-format "^26.0.1"
|
||||||
semver "^7.3.2"
|
semver "^7.3.2"
|
||||||
|
|
||||||
|
jest-util@26.x:
|
||||||
|
version "26.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.3.0.tgz#a8974b191df30e2bf523ebbfdbaeb8efca535b3e"
|
||||||
|
integrity sha512-4zpn6bwV0+AMFN0IYhH/wnzIQzRaYVrz1A8sYnRnj4UXDXbOVtWmlaZkO9mipFqZ13okIfN87aDoJWB7VH6hcw==
|
||||||
|
dependencies:
|
||||||
|
"@jest/types" "^26.3.0"
|
||||||
|
"@types/node" "*"
|
||||||
|
chalk "^4.0.0"
|
||||||
|
graceful-fs "^4.2.4"
|
||||||
|
is-ci "^2.0.0"
|
||||||
|
micromatch "^4.0.2"
|
||||||
|
|
||||||
jest-util@^26.0.1:
|
jest-util@^26.0.1:
|
||||||
version "26.0.1"
|
version "26.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.0.1.tgz#72c4c51177b695fdd795ca072a6f94e3d7cef00a"
|
resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.0.1.tgz#72c4c51177b695fdd795ca072a6f94e3d7cef00a"
|
||||||
@ -7531,6 +7675,11 @@ lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.1
|
|||||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
|
||||||
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
|
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
|
||||||
|
|
||||||
|
lodash@^4.17.19:
|
||||||
|
version "4.17.20"
|
||||||
|
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
|
||||||
|
integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==
|
||||||
|
|
||||||
log-symbols@^2.2.0:
|
log-symbols@^2.2.0:
|
||||||
version "2.2.0"
|
version "2.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a"
|
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a"
|
||||||
@ -7738,7 +7887,7 @@ memfs@^3.1.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
fs-monkey "1.0.1"
|
fs-monkey "1.0.1"
|
||||||
|
|
||||||
"memoize-one@>=3.1.1 <6", memoize-one@^5.0.0:
|
"memoize-one@>=3.1.1 <6", memoize-one@^5.0.0, memoize-one@^5.1.1:
|
||||||
version "5.1.1"
|
version "5.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0"
|
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0"
|
||||||
integrity sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==
|
integrity sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==
|
||||||
@ -7780,12 +7929,17 @@ merge-stream@^2.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
|
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
|
||||||
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
|
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
|
||||||
|
|
||||||
|
merge2@^1.3.0:
|
||||||
|
version "1.4.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
|
||||||
|
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
|
||||||
|
|
||||||
messageformat-parser@^4.1.3:
|
messageformat-parser@^4.1.3:
|
||||||
version "4.1.3"
|
version "4.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/messageformat-parser/-/messageformat-parser-4.1.3.tgz#b824787f57fcda7d50769f5b63e8d4fda68f5b9e"
|
resolved "https://registry.yarnpkg.com/messageformat-parser/-/messageformat-parser-4.1.3.tgz#b824787f57fcda7d50769f5b63e8d4fda68f5b9e"
|
||||||
integrity sha512-2fU3XDCanRqeOCkn7R5zW5VQHWf+T3hH65SzuqRvjatBK7r4uyFa5mEX+k6F9Bd04LVM5G4/BHBTUJsOdW7uyg==
|
integrity sha512-2fU3XDCanRqeOCkn7R5zW5VQHWf+T3hH65SzuqRvjatBK7r4uyFa5mEX+k6F9Bd04LVM5G4/BHBTUJsOdW7uyg==
|
||||||
|
|
||||||
micromatch@4.0.2, micromatch@4.x, micromatch@^4.0.0, micromatch@^4.0.2:
|
micromatch@4.0.2, micromatch@^4.0.0, micromatch@^4.0.2:
|
||||||
version "4.0.2"
|
version "4.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259"
|
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259"
|
||||||
integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==
|
integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==
|
||||||
@ -9361,6 +9515,11 @@ querystring@0.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
|
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
|
||||||
integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=
|
integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=
|
||||||
|
|
||||||
|
raf-schd@^4.0.2:
|
||||||
|
version "4.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.2.tgz#bd44c708188f2e84c810bf55fcea9231bcaed8a0"
|
||||||
|
integrity sha512-VhlMZmGy6A6hrkJWHLNTGl5gtgMUm+xfGza6wbwnE914yeQ5Ybm18vgM734RZhMgfw4tacUrWseGZlpUrrakEQ==
|
||||||
|
|
||||||
ramda@^0.27.0:
|
ramda@^0.27.0:
|
||||||
version "0.27.0"
|
version "0.27.0"
|
||||||
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.27.0.tgz#915dc29865c0800bf3f69b8fd6c279898b59de43"
|
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.27.0.tgz#915dc29865c0800bf3f69b8fd6c279898b59de43"
|
||||||
@ -9399,6 +9558,19 @@ rc@^1.2.8:
|
|||||||
minimist "^1.2.0"
|
minimist "^1.2.0"
|
||||||
strip-json-comments "~2.0.1"
|
strip-json-comments "~2.0.1"
|
||||||
|
|
||||||
|
react-beautiful-dnd@^13.0.0:
|
||||||
|
version "13.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-beautiful-dnd/-/react-beautiful-dnd-13.0.0.tgz#f70cc8ff82b84bc718f8af157c9f95757a6c3b40"
|
||||||
|
integrity sha512-87It8sN0ineoC3nBW0SbQuTFXM6bUqM62uJGY4BtTf0yzPl8/3+bHMWkgIe0Z6m8e+gJgjWxefGRVfpE3VcdEg==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.8.4"
|
||||||
|
css-box-model "^1.2.0"
|
||||||
|
memoize-one "^5.1.1"
|
||||||
|
raf-schd "^4.0.2"
|
||||||
|
react-redux "^7.1.1"
|
||||||
|
redux "^4.0.4"
|
||||||
|
use-memo-one "^1.1.1"
|
||||||
|
|
||||||
react-dom@^16.13.1:
|
react-dom@^16.13.1:
|
||||||
version "16.13.1"
|
version "16.13.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.13.1.tgz#c1bd37331a0486c078ee54c4740720993b2e0e7f"
|
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.13.1.tgz#c1bd37331a0486c078ee54c4740720993b2e0e7f"
|
||||||
@ -9416,11 +9588,22 @@ react-input-autosize@^2.2.2:
|
|||||||
dependencies:
|
dependencies:
|
||||||
prop-types "^15.5.8"
|
prop-types "^15.5.8"
|
||||||
|
|
||||||
react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1:
|
react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.0, react-is@^16.8.1, react-is@^16.9.0:
|
||||||
version "16.13.1"
|
version "16.13.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||||
|
|
||||||
|
react-redux@^7.1.1:
|
||||||
|
version "7.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.1.tgz#8dedf784901014db2feca1ab633864dee68ad985"
|
||||||
|
integrity sha512-T+VfD/bvgGTUA74iW9d2i5THrDQWbweXP0AVNI8tNd1Rk5ch1rnMiJkDD67ejw7YBKM4+REvcvqRuWJb7BLuEg==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.5.5"
|
||||||
|
hoist-non-react-statics "^3.3.0"
|
||||||
|
loose-envify "^1.4.0"
|
||||||
|
prop-types "^15.7.2"
|
||||||
|
react-is "^16.9.0"
|
||||||
|
|
||||||
react-router-dom@^5.2.0:
|
react-router-dom@^5.2.0:
|
||||||
version "5.2.0"
|
version "5.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.2.0.tgz#9e65a4d0c45e13289e66c7b17c7e175d0ea15662"
|
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-5.2.0.tgz#9e65a4d0c45e13289e66c7b17c7e175d0ea15662"
|
||||||
@ -9610,6 +9793,14 @@ redent@^1.0.0:
|
|||||||
indent-string "^2.1.0"
|
indent-string "^2.1.0"
|
||||||
strip-indent "^1.0.1"
|
strip-indent "^1.0.1"
|
||||||
|
|
||||||
|
redux@^4.0.4:
|
||||||
|
version "4.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f"
|
||||||
|
integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==
|
||||||
|
dependencies:
|
||||||
|
loose-envify "^1.4.0"
|
||||||
|
symbol-observable "^1.2.0"
|
||||||
|
|
||||||
regenerate-unicode-properties@^8.2.0:
|
regenerate-unicode-properties@^8.2.0:
|
||||||
version "8.2.0"
|
version "8.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec"
|
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec"
|
||||||
@ -9873,6 +10064,11 @@ retry@^0.12.0:
|
|||||||
resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
|
resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b"
|
||||||
integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=
|
integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=
|
||||||
|
|
||||||
|
reusify@^1.0.4:
|
||||||
|
version "1.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
|
||||||
|
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
|
||||||
|
|
||||||
rfc4648@^1.3.0:
|
rfc4648@^1.3.0:
|
||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/rfc4648/-/rfc4648-1.3.0.tgz#2a69c76f05bc0e388feab933672de9b492af95f1"
|
resolved "https://registry.yarnpkg.com/rfc4648/-/rfc4648-1.3.0.tgz#2a69c76f05bc0e388feab933672de9b492af95f1"
|
||||||
@ -9934,6 +10130,11 @@ run-async@^2.2.0, run-async@^2.4.0:
|
|||||||
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"
|
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"
|
||||||
integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==
|
integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==
|
||||||
|
|
||||||
|
run-parallel@^1.1.9:
|
||||||
|
version "1.1.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679"
|
||||||
|
integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==
|
||||||
|
|
||||||
run-queue@^1.0.0, run-queue@^1.0.3:
|
run-queue@^1.0.0, run-queue@^1.0.3:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47"
|
resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47"
|
||||||
@ -10771,6 +10972,11 @@ supports-hyperlinks@^2.0.0:
|
|||||||
has-flag "^4.0.0"
|
has-flag "^4.0.0"
|
||||||
supports-color "^7.0.0"
|
supports-color "^7.0.0"
|
||||||
|
|
||||||
|
symbol-observable@^1.2.0:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
|
||||||
|
integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
|
||||||
|
|
||||||
symbol-tree@^3.2.4:
|
symbol-tree@^3.2.4:
|
||||||
version "3.2.4"
|
version "3.2.4"
|
||||||
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
|
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
|
||||||
@ -10970,7 +11176,7 @@ timers-browserify@^2.0.4:
|
|||||||
dependencies:
|
dependencies:
|
||||||
setimmediate "^1.0.4"
|
setimmediate "^1.0.4"
|
||||||
|
|
||||||
tiny-invariant@^1.0.2:
|
tiny-invariant@^1.0.2, tiny-invariant@^1.0.6:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875"
|
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.1.0.tgz#634c5f8efdc27714b7f386c35e6760991d230875"
|
||||||
integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==
|
integrity sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==
|
||||||
@ -11120,17 +11326,18 @@ truncate-utf8-bytes@^1.0.0:
|
|||||||
utf8-byte-length "^1.0.1"
|
utf8-byte-length "^1.0.1"
|
||||||
|
|
||||||
ts-jest@^26.1.0:
|
ts-jest@^26.1.0:
|
||||||
version "26.1.0"
|
version "26.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.1.0.tgz#e9070fc97b3ea5557a48b67c631c74eb35e15417"
|
resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.3.0.tgz#6b2845045347dce394f069bb59358253bc1338a9"
|
||||||
integrity sha512-JbhQdyDMYN5nfKXaAwCIyaWLGwevcT2/dbqRPsQeh6NZPUuXjZQZEfeLb75tz0ubCIgEELNm6xAzTe5NXs5Y4Q==
|
integrity sha512-Jq2uKfx6bPd9+JDpZNMBJMdMQUC3sJ08acISj8NXlVgR2d5OqslEHOR2KHMgwymu8h50+lKIm0m0xj/ioYdW2Q==
|
||||||
dependencies:
|
dependencies:
|
||||||
|
"@types/jest" "26.x"
|
||||||
bs-logger "0.x"
|
bs-logger "0.x"
|
||||||
buffer-from "1.x"
|
buffer-from "1.x"
|
||||||
fast-json-stable-stringify "2.x"
|
fast-json-stable-stringify "2.x"
|
||||||
|
jest-util "26.x"
|
||||||
json5 "2.x"
|
json5 "2.x"
|
||||||
lodash.memoize "4.x"
|
lodash.memoize "4.x"
|
||||||
make-error "1.x"
|
make-error "1.x"
|
||||||
micromatch "4.x"
|
|
||||||
mkdirp "1.x"
|
mkdirp "1.x"
|
||||||
semver "7.x"
|
semver "7.x"
|
||||||
yargs-parser "18.x"
|
yargs-parser "18.x"
|
||||||
@ -11437,6 +11644,11 @@ url@^0.11.0, url@~0.11.0:
|
|||||||
punycode "1.3.2"
|
punycode "1.3.2"
|
||||||
querystring "0.2.0"
|
querystring "0.2.0"
|
||||||
|
|
||||||
|
use-memo-one@^1.1.1:
|
||||||
|
version "1.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.1.tgz#39e6f08fe27e422a7d7b234b5f9056af313bd22c"
|
||||||
|
integrity sha512-oFfsyun+bP7RX8X2AskHNTxu+R3QdE/RC5IefMbqptmACAA/gfol1KDD5KRzPsGMa62sWxGZw+Ui43u6x4ddoQ==
|
||||||
|
|
||||||
use@^3.1.0:
|
use@^3.1.0:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
|
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user