mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
tracker / user-store refactoring, part 1
Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
parent
c9fb015bcf
commit
4270ec4240
@ -170,9 +170,9 @@
|
|||||||
"@types/node": "^12.12.45",
|
"@types/node": "^12.12.45",
|
||||||
"@types/proper-lockfile": "^4.1.1",
|
"@types/proper-lockfile": "^4.1.1",
|
||||||
"@types/tar": "^4.0.3",
|
"@types/tar": "^4.0.3",
|
||||||
|
"conf": "^7.0.1",
|
||||||
"crypto-js": "^4.0.0",
|
"crypto-js": "^4.0.0",
|
||||||
"electron-promise-ipc": "^2.1.0",
|
"electron-promise-ipc": "^2.1.0",
|
||||||
"electron-store": "^5.2.0",
|
|
||||||
"electron-updater": "^4.3.1",
|
"electron-updater": "^4.3.1",
|
||||||
"electron-window-state": "^5.0.3",
|
"electron-window-state": "^5.0.3",
|
||||||
"filenamify": "^4.1.0",
|
"filenamify": "^4.1.0",
|
||||||
@ -193,6 +193,7 @@
|
|||||||
"request": "^2.88.2",
|
"request": "^2.88.2",
|
||||||
"request-promise-native": "^1.0.8",
|
"request-promise-native": "^1.0.8",
|
||||||
"semver": "^7.3.2",
|
"semver": "^7.3.2",
|
||||||
|
"serializr": "^2.0.3",
|
||||||
"shell-env": "^3.0.0",
|
"shell-env": "^3.0.0",
|
||||||
"tar": "^6.0.2",
|
"tar": "^6.0.2",
|
||||||
"tcp-port-used": "^1.0.1",
|
"tcp-port-used": "^1.0.1",
|
||||||
|
|||||||
@ -1,32 +1,32 @@
|
|||||||
import ElectronStore from "electron-store"
|
import Config from "conf"
|
||||||
import { Singleton } from "./utils/singleton";
|
import Singleton from "./utils/singleton";
|
||||||
import migrations from "../migrations/cluster-store"
|
import migrations from "../migrations/cluster-store"
|
||||||
import { Cluster, ClusterBaseInfo } from "../main/cluster";
|
import { Cluster, ClusterBaseInfo } from "../main/cluster";
|
||||||
|
|
||||||
export class ClusterStore extends Singleton {
|
export class ClusterStore extends Singleton {
|
||||||
private store = new ElectronStore({
|
private storeConfig = new Config({
|
||||||
name: "lens-cluster-store",
|
configName: "lens-cluster-store",
|
||||||
accessPropertiesByDotNotation: false, // To make dots safe in cluster context names
|
accessPropertiesByDotNotation: false, // To make dots safe in cluster context names
|
||||||
migrations: migrations,
|
migrations: migrations,
|
||||||
})
|
})
|
||||||
|
|
||||||
public getAllClusterObjects(): Cluster[] {
|
public getAllClusterObjects(): Cluster[] {
|
||||||
return this.store.get("clusters", []).map((clusterInfo: ClusterBaseInfo) => {
|
return this.storeConfig.get("clusters", []).map((clusterInfo: ClusterBaseInfo) => {
|
||||||
return new Cluster(clusterInfo)
|
return new Cluster(clusterInfo)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public getAllClusters(): ClusterBaseInfo[] {
|
public getAllClusters(): ClusterBaseInfo[] {
|
||||||
return this.store.get("clusters", [])
|
return this.storeConfig.get("clusters", [])
|
||||||
}
|
}
|
||||||
|
|
||||||
public removeCluster(id: string): void {
|
public removeCluster(id: string): void {
|
||||||
this.store.delete(id);
|
this.storeConfig.delete(id);
|
||||||
const clusterBaseInfos = this.getAllClusters()
|
const clusterBaseInfos = this.getAllClusters()
|
||||||
const index = clusterBaseInfos.findIndex((cbi) => cbi.id === id)
|
const index = clusterBaseInfos.findIndex((cbi) => cbi.id === id)
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
clusterBaseInfos.splice(index, 1)
|
clusterBaseInfos.splice(index, 1)
|
||||||
this.store.set("clusters", clusterBaseInfos)
|
this.storeConfig.set("clusters", clusterBaseInfos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ export class ClusterStore extends Singleton {
|
|||||||
} else {
|
} else {
|
||||||
clusters[index] = storable
|
clusters[index] = storable
|
||||||
}
|
}
|
||||||
this.store.set("clusters", clusters)
|
this.storeConfig.set("clusters", clusters)
|
||||||
}
|
}
|
||||||
|
|
||||||
public storeClusters(clusters: ClusterBaseInfo[]) {
|
public storeClusters(clusters: ClusterBaseInfo[]) {
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
import request from "request"
|
import request from "request"
|
||||||
import { userStore } from "./user-store"
|
import { userStore } from "./user-store"
|
||||||
|
|
||||||
export function globalRequestOpts(requestOpts: request.Options ) {
|
export function globalRequestOpts(requestOpts: request.Options) {
|
||||||
const userPrefs = userStore.getPreferences()
|
const { httpsProxy, allowUntrustedCAs } = userStore.preferences
|
||||||
if (userPrefs.httpsProxy) {
|
if (httpsProxy) {
|
||||||
requestOpts.proxy = userPrefs.httpsProxy
|
requestOpts.proxy = httpsProxy
|
||||||
}
|
}
|
||||||
requestOpts.rejectUnauthorized = !userPrefs.allowUntrustedCAs;
|
requestOpts.rejectUnauthorized = !allowUntrustedCAs;
|
||||||
|
|
||||||
return requestOpts
|
return requestOpts
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
|
import { app, App, remote } from "electron"
|
||||||
import ua from "universal-analytics"
|
import ua from "universal-analytics"
|
||||||
import { machineIdSync } from "node-machine-id"
|
import { machineIdSync } from "node-machine-id"
|
||||||
|
import Singleton from "./utils/singleton";
|
||||||
import { userStore } from "./user-store"
|
import { userStore } from "./user-store"
|
||||||
|
|
||||||
const GA_ID = "UA-159377374-1"
|
export class Tracker extends Singleton {
|
||||||
|
static readonly GA_ID = "UA-159377374-1"
|
||||||
|
|
||||||
export class Tracker {
|
|
||||||
protected visitor: ua.Visitor
|
protected visitor: ua.Visitor
|
||||||
protected machineId: string = null;
|
protected machineId: string = null;
|
||||||
protected ip: string = null;
|
protected ip: string = null;
|
||||||
@ -12,31 +14,35 @@ export class Tracker {
|
|||||||
protected locale: string;
|
protected locale: string;
|
||||||
protected electronUA: string;
|
protected electronUA: string;
|
||||||
|
|
||||||
constructor(app: Electron.App) {
|
private constructor(app: App) {
|
||||||
|
super();
|
||||||
try {
|
try {
|
||||||
this.visitor = ua(GA_ID, machineIdSync(), {strictCidFormat: false})
|
this.visitor = ua(Tracker.GA_ID, machineIdSync(), { strictCidFormat: false })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.visitor = ua(GA_ID)
|
this.visitor = ua(Tracker.GA_ID)
|
||||||
}
|
}
|
||||||
this.visitor.set("dl", "https://lensapptelemetry.lakendlabs.com")
|
this.visitor.set("dl", "https://lensapptelemetry.lakendlabs.com")
|
||||||
}
|
}
|
||||||
|
|
||||||
public async event(eventCategory: string, eventAction: string) {
|
protected async isTelemetryAllowed(): Promise<boolean> {
|
||||||
return new Promise(async (resolve, reject) => {
|
return userStore.preferences.allowTelemetry;
|
||||||
if (!this.telemetryAllowed()) {
|
}
|
||||||
resolve()
|
|
||||||
return
|
async event(eventCategory: string, eventAction: string, otherParams = {}) {
|
||||||
|
try {
|
||||||
|
const allowed = await this.isTelemetryAllowed();
|
||||||
|
if (!allowed) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
this.visitor.event({
|
this.visitor.event({
|
||||||
ec: eventCategory,
|
ec: eventCategory,
|
||||||
ea: eventAction
|
ea: eventAction,
|
||||||
|
...otherParams,
|
||||||
}).send()
|
}).send()
|
||||||
resolve()
|
} catch (err) {
|
||||||
})
|
console.error(`Failed to track "${eventCategory}:${eventAction}"`, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected telemetryAllowed() {
|
|
||||||
const userPrefs = userStore.getPreferences()
|
|
||||||
return !!userPrefs.allowTelemetry
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const tracker: Tracker = Tracker.getInstance(app || remote.app);
|
||||||
|
|||||||
@ -1,59 +1,105 @@
|
|||||||
import ElectronStore from "electron-store"
|
import { app, remote } from "electron"
|
||||||
|
import { computed, observable, reaction, toJS } from "mobx";
|
||||||
|
import Config from "conf"
|
||||||
|
import semver from "semver"
|
||||||
import migrations from "../migrations/user-store"
|
import migrations from "../migrations/user-store"
|
||||||
import { Singleton } from "./utils/singleton";
|
import Singleton from "./utils/singleton";
|
||||||
|
import { getAppVersion } from "./utils/app-version";
|
||||||
|
import { tracker } from "./tracker";
|
||||||
|
|
||||||
|
export interface UserStoreModel {
|
||||||
|
lastSeenAppVersion: string;
|
||||||
|
seenContexts: string[];
|
||||||
|
preferences: UserPreferences;
|
||||||
|
}
|
||||||
|
|
||||||
export interface UserPreferences {
|
export interface UserPreferences {
|
||||||
httpsProxy?: string;
|
httpsProxy?: string;
|
||||||
colorTheme?: string;
|
colorTheme?: string | "dark";
|
||||||
allowUntrustedCAs?: boolean;
|
allowUntrustedCAs?: boolean;
|
||||||
allowTelemetry?: boolean;
|
allowTelemetry?: boolean;
|
||||||
downloadMirror?: string;
|
downloadMirror?: string | "default";
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UserStore extends Singleton {
|
export class UserStore extends Singleton {
|
||||||
protected store = new ElectronStore({
|
private storeConfig: Config<UserStoreModel>;
|
||||||
name: "lens-user-store",
|
|
||||||
migrations: migrations,
|
|
||||||
});
|
|
||||||
|
|
||||||
public lastSeenAppVersion() {
|
@observable isReady = false;
|
||||||
return this.store.get('lastSeenAppVersion', "0.0.0")
|
@observable lastSeenAppVersion = "0.0.0"
|
||||||
|
@observable seenContexts = observable.set();
|
||||||
|
|
||||||
|
@observable preferences: UserPreferences = {
|
||||||
|
allowTelemetry: true,
|
||||||
|
colorTheme: "dark",
|
||||||
|
downloadMirror: "default",
|
||||||
|
};
|
||||||
|
|
||||||
|
@computed get hasNewAppVersion() {
|
||||||
|
return semver.gt(getAppVersion(), this.lastSeenAppVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
public setLastSeenAppVersion(version: string) {
|
private constructor() {
|
||||||
this.store.set('lastSeenAppVersion', version)
|
super();
|
||||||
|
this.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
public getSeenContexts(): Array<string> {
|
async init() {
|
||||||
return this.store.get("seenContexts", [])
|
/*await*/ this.load();
|
||||||
|
this.bindEvents();
|
||||||
|
this.isReady = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public storeSeenContext(newContexts: string[]) {
|
saveLastSeenAppVersion() {
|
||||||
const seenContexts = this.getSeenContexts().concat(newContexts)
|
this.lastSeenAppVersion = getAppVersion();
|
||||||
// store unique contexts by casting array to set first
|
|
||||||
const newContextSet = new Set(seenContexts)
|
|
||||||
const allContexts = [...newContextSet]
|
|
||||||
this.store.set("seenContexts", allContexts)
|
|
||||||
return allContexts
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public setPreferences(preferences: UserPreferences) {
|
// todo: use "conf" as pseudo-async for more future-proof usages
|
||||||
this.store.set('preferences', preferences)
|
protected async load() {
|
||||||
|
this.storeConfig = new Config<UserStoreModel>({
|
||||||
|
configName: "lens-user-store",
|
||||||
|
migrations: migrations,
|
||||||
|
cwd: (app || remote.app).getPath("userData"),
|
||||||
|
});
|
||||||
|
this.fromStore(this.storeConfig.store);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPreferences(): UserPreferences {
|
protected bindEvents() {
|
||||||
const prefs = this.store.get("preferences", {})
|
// refresh from file-system updates
|
||||||
if (!prefs.colorTheme) {
|
this.storeConfig.onDidAnyChange((data, oldValue) => {
|
||||||
prefs.colorTheme = "dark"
|
this.fromStore(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// refresh config file from runtime
|
||||||
|
reaction(() => this.toJSON(), model => {
|
||||||
|
this.storeConfig.store = model;
|
||||||
|
});
|
||||||
|
|
||||||
|
// track telemetry availability
|
||||||
|
reaction(() => this.preferences.allowTelemetry, allowed => {
|
||||||
|
tracker.event("telemetry", allowed ? "enabled" : "disabled");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: use "serializr" ?
|
||||||
|
protected fromStore(data: Partial<UserStoreModel> = {}) {
|
||||||
|
const { lastSeenAppVersion, seenContexts, preferences } = data
|
||||||
|
if (lastSeenAppVersion) {
|
||||||
|
this.lastSeenAppVersion = lastSeenAppVersion;
|
||||||
}
|
}
|
||||||
if (!prefs.downloadMirror) {
|
if (seenContexts) {
|
||||||
prefs.downloadMirror = "default"
|
this.seenContexts = observable.set(seenContexts)
|
||||||
}
|
}
|
||||||
if (prefs.allowTelemetry === undefined) {
|
if (preferences) {
|
||||||
prefs.allowTelemetry = true
|
Object.assign(this.preferences, preferences);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return prefs
|
protected toJSON(): UserStoreModel {
|
||||||
|
return toJS({
|
||||||
|
lastSeenAppVersion: this.lastSeenAppVersion,
|
||||||
|
seenContexts: Array.from(this.seenContexts),
|
||||||
|
preferences: this.preferences,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,8 @@
|
|||||||
* const usersStore: UsersStore = UsersStore.getInstance();
|
* const usersStore: UsersStore = UsersStore.getInstance();
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export class Singleton {
|
// todo: maybe convert to @decorator
|
||||||
|
class Singleton {
|
||||||
private static instances = new WeakMap<object, Singleton>();
|
private static instances = new WeakMap<object, Singleton>();
|
||||||
|
|
||||||
// todo: figure out how to infer child class + arguments types
|
// todo: figure out how to infer child class + arguments types
|
||||||
@ -21,3 +22,6 @@ export class Singleton {
|
|||||||
Singleton.instances.delete(this);
|
Singleton.instances.delete(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export { Singleton }
|
||||||
|
export default Singleton;
|
||||||
@ -1,30 +1,26 @@
|
|||||||
import ElectronStore from "electron-store"
|
import Config from "conf"
|
||||||
import { Singleton } from "./utils/singleton";
|
import Singleton from "./utils/singleton";
|
||||||
import { clusterStore } from "./cluster-store"
|
import { clusterStore } from "./cluster-store"
|
||||||
|
import { getAppVersion } from "./utils/app-version";
|
||||||
|
|
||||||
export type WorkspaceId = string;
|
export type WorkspaceId = string;
|
||||||
|
|
||||||
export interface WorkspaceData {
|
export interface WorkspaceStoreModel {
|
||||||
|
workspaces: Workspace[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Workspace {
|
||||||
id: WorkspaceId;
|
id: WorkspaceId;
|
||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Workspace implements WorkspaceData {
|
|
||||||
public id: string
|
|
||||||
public name: string
|
|
||||||
public description?: string
|
|
||||||
|
|
||||||
public constructor(data: WorkspaceData) {
|
|
||||||
Object.assign(this, data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class WorkspaceStore extends Singleton {
|
export class WorkspaceStore extends Singleton {
|
||||||
static defaultId = "default"
|
static readonly defaultId = "default"
|
||||||
|
|
||||||
private store = new ElectronStore({
|
private storeConfig = new Config<WorkspaceStoreModel>({
|
||||||
name: "lens-workspace-store"
|
configName: "lens-workspace-store",
|
||||||
|
projectVersion: getAppVersion(),
|
||||||
});
|
});
|
||||||
|
|
||||||
private constructor() {
|
private constructor() {
|
||||||
@ -46,11 +42,10 @@ export class WorkspaceStore extends Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getAllWorkspaces(): Workspace[] {
|
public getAllWorkspaces(): Workspace[] {
|
||||||
const workspacesData: WorkspaceData[] = this.store.get("workspaces", [])
|
return this.storeConfig.get("workspaces", [])
|
||||||
return workspacesData.map((wsd) => new Workspace(wsd))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public saveWorkspace(workspace: WorkspaceData) {
|
public saveWorkspace(workspace: Workspace) {
|
||||||
const workspaces = this.getAllWorkspaces()
|
const workspaces = this.getAllWorkspaces()
|
||||||
const index = workspaces.findIndex((w) => w.id === workspace.id)
|
const index = workspaces.findIndex((w) => w.id === workspace.id)
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
@ -58,7 +53,7 @@ export class WorkspaceStore extends Singleton {
|
|||||||
} else {
|
} else {
|
||||||
workspaces.push(workspace)
|
workspaces.push(workspace)
|
||||||
}
|
}
|
||||||
this.store.set("workspaces", workspaces)
|
this.storeConfig.set("workspaces", workspaces)
|
||||||
}
|
}
|
||||||
|
|
||||||
public removeWorkspace(workspace: Workspace) {
|
public removeWorkspace(workspace: Workspace) {
|
||||||
@ -70,7 +65,7 @@ export class WorkspaceStore extends Singleton {
|
|||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
clusterStore.removeClustersByWorkspace(workspace.id)
|
clusterStore.removeClustersByWorkspace(workspace.id)
|
||||||
workspaces.splice(index, 1)
|
workspaces.splice(index, 1)
|
||||||
this.store.set("workspaces", workspaces)
|
this.storeConfig.set("workspaces", workspaces)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,7 +18,6 @@ import initMenu from "./menu"
|
|||||||
import * as proxy from "./proxy"
|
import * as proxy from "./proxy"
|
||||||
import { WindowManager } from "./window-manager";
|
import { WindowManager } from "./window-manager";
|
||||||
import { clusterStore } from "../common/cluster-store"
|
import { clusterStore } from "../common/cluster-store"
|
||||||
import { tracker } from "./tracker"
|
|
||||||
import { ClusterManager } from "./cluster-manager";
|
import { ClusterManager } from "./cluster-manager";
|
||||||
import AppUpdater from "./app-updater"
|
import AppUpdater from "./app-updater"
|
||||||
import { shellSync } from "./shell-sync"
|
import { shellSync } from "./shell-sync"
|
||||||
@ -26,6 +25,7 @@ import { getFreePort } from "./port"
|
|||||||
import { mangleProxyEnv } from "./proxy-env"
|
import { mangleProxyEnv } from "./proxy-env"
|
||||||
import { findMainWebContents } from "./webcontents"
|
import { findMainWebContents } from "./webcontents"
|
||||||
import { registerStaticProtocol } from "../common/register-static";
|
import { registerStaticProtocol } from "../common/register-static";
|
||||||
|
import { tracker } from "../common/tracker";
|
||||||
|
|
||||||
mangleProxyEnv()
|
mangleProxyEnv()
|
||||||
if (app.commandLine.getSwitchValue("proxy-server") !== "") {
|
if (app.commandLine.getSwitchValue("proxy-server") !== "") {
|
||||||
|
|||||||
@ -285,7 +285,7 @@ export class Kubectl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected getDownloadMirror() {
|
protected getDownloadMirror() {
|
||||||
const mirror = packageMirrors.get(userStore.getPreferences().downloadMirror)
|
const mirror = packageMirrors.get(userStore.preferences.downloadMirror)
|
||||||
if (mirror) {
|
if (mirror) {
|
||||||
return mirror
|
return mirror
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,19 +1,7 @@
|
|||||||
import packageInfo from "../../package.json"
|
import packageInfo from "../../package.json"
|
||||||
import { bundledKubectl, Kubectl } from "../../src/main/kubectl";
|
import { bundledKubectl, Kubectl } from "../../src/main/kubectl";
|
||||||
import { UserStore } from "../common/user-store";
|
|
||||||
|
|
||||||
jest.mock("../common/user-store", () => {
|
jest.mock("../common/user-store");
|
||||||
const userStoreMock: Partial<UserStore> = {
|
|
||||||
getPreferences() {
|
|
||||||
return {
|
|
||||||
downloadMirror: "default"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
userStore: userStoreMock,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("kubectlVersion", () => {
|
describe("kubectlVersion", () => {
|
||||||
it("returns bundled version if exactly same version used", async () => {
|
it("returns bundled version if exactly same version used", async () => {
|
||||||
|
|||||||
@ -3,10 +3,10 @@ import * as pty from "node-pty"
|
|||||||
import { ShellSession } from "./shell-session";
|
import { ShellSession } from "./shell-session";
|
||||||
import { v4 as uuid } from "uuid"
|
import { v4 as uuid } from "uuid"
|
||||||
import * as k8s from "@kubernetes/client-node"
|
import * as k8s from "@kubernetes/client-node"
|
||||||
|
import { KubeConfig } from "@kubernetes/client-node"
|
||||||
|
import { Cluster } from "./cluster"
|
||||||
import logger from "./logger";
|
import logger from "./logger";
|
||||||
import { KubeConfig, V1Pod } from "@kubernetes/client-node";
|
import { tracker } from "../common/tracker";
|
||||||
import { tracker } from "./tracker"
|
|
||||||
import { Cluster, ClusterPreferences } from "./cluster"
|
|
||||||
|
|
||||||
export class NodeShellSession extends ShellSession {
|
export class NodeShellSession extends ShellSession {
|
||||||
protected nodeName: string;
|
protected nodeName: string;
|
||||||
@ -107,7 +107,7 @@ export class NodeShellSession extends ShellSession {
|
|||||||
const watch = new k8s.Watch(kc);
|
const watch = new k8s.Watch(kc);
|
||||||
|
|
||||||
const req = await watch.watch(`/api/v1/namespaces/kube-system/pods`, {},
|
const req = await watch.watch(`/api/v1/namespaces/kube-system/pods`, {},
|
||||||
// callback is called for each received object.
|
// callback is called for each received object.
|
||||||
(_type, obj) => {
|
(_type, obj) => {
|
||||||
if (obj.metadata.name == podId && obj.status.phase === "Running") {
|
if (obj.metadata.name == podId && obj.status.phase === "Running") {
|
||||||
resolve(true)
|
resolve(true)
|
||||||
@ -119,9 +119,13 @@ export class NodeShellSession extends ShellSession {
|
|||||||
reject(false)
|
reject(false)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
setTimeout(() => { req.abort(); reject(false); }, 120 * 1000);
|
setTimeout(() => {
|
||||||
|
req.abort();
|
||||||
|
reject(false);
|
||||||
|
}, 120 * 1000);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
protected deleteNodeShellPod() {
|
protected deleteNodeShellPod() {
|
||||||
const kc = this.getKubeConfig();
|
const kc = this.getKubeConfig();
|
||||||
const k8sApi = kc.makeApiClient(k8s.CoreV1Api);
|
const k8sApi = kc.makeApiClient(k8s.CoreV1Api);
|
||||||
@ -130,12 +134,11 @@ export class NodeShellSession extends ShellSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function open(socket: WebSocket, pathToKubeconfig: string, cluster: Cluster, nodeName?: string): Promise<ShellSession> {
|
export async function open(socket: WebSocket, pathToKubeconfig: string, cluster: Cluster, nodeName?: string): Promise<ShellSession> {
|
||||||
return new Promise(async(resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
let shell = null
|
let shell = null
|
||||||
if (nodeName) {
|
if (nodeName) {
|
||||||
shell = new NodeShellSession(socket, pathToKubeconfig, cluster, nodeName)
|
shell = new NodeShellSession(socket, pathToKubeconfig, cluster, nodeName)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
shell = new ShellSession(socket, pathToKubeconfig, cluster)
|
shell = new ShellSession(socket, pathToKubeconfig, cluster)
|
||||||
}
|
}
|
||||||
shell.open()
|
shell.open()
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import path from "path";
|
|||||||
import * as tempy from "tempy";
|
import * as tempy from "tempy";
|
||||||
import logger from "./logger"
|
import logger from "./logger"
|
||||||
import { Cluster } from "./cluster";
|
import { Cluster } from "./cluster";
|
||||||
import { tracker } from "./tracker";
|
import { tracker } from "../common/tracker";
|
||||||
|
|
||||||
type KubeObject = {
|
type KubeObject = {
|
||||||
status: {};
|
status: {};
|
||||||
|
|||||||
@ -94,7 +94,7 @@ class ConfigRoute extends LensApi {
|
|||||||
const data: IConfigRoutePayload = {
|
const data: IConfigRoutePayload = {
|
||||||
clusterName: cluster.contextName,
|
clusterName: cluster.contextName,
|
||||||
lensVersion: app.getVersion(),
|
lensVersion: app.getVersion(),
|
||||||
lensTheme: `kontena-${userStore.getPreferences().colorTheme}`,
|
lensTheme: `kontena-${userStore.preferences.colorTheme}`,
|
||||||
kubeVersion: cluster.version,
|
kubeVersion: cluster.version,
|
||||||
chartsEnabled: true,
|
chartsEnabled: true,
|
||||||
isClusterAdmin: cluster.isAdmin,
|
isClusterAdmin: cluster.isAdmin,
|
||||||
|
|||||||
@ -5,10 +5,10 @@ import path from "path"
|
|||||||
import shellEnv from "shell-env"
|
import shellEnv from "shell-env"
|
||||||
import { app } from "electron"
|
import { app } from "electron"
|
||||||
import { Kubectl } from "./kubectl"
|
import { Kubectl } from "./kubectl"
|
||||||
import { tracker } from "./tracker"
|
|
||||||
import { Cluster, ClusterPreferences } from "./cluster"
|
import { Cluster, ClusterPreferences } from "./cluster"
|
||||||
import { helmCli } from "./helm-cli"
|
import { helmCli } from "./helm-cli"
|
||||||
import { isWindows } from "../common/vars";
|
import { isWindows } from "../common/vars";
|
||||||
|
import { tracker } from "../common/tracker";
|
||||||
|
|
||||||
export class ShellSession extends EventEmitter {
|
export class ShellSession extends EventEmitter {
|
||||||
static shellEnvs: Map<string, any> = new Map()
|
static shellEnvs: Map<string, any> = new Map()
|
||||||
|
|||||||
@ -1,4 +0,0 @@
|
|||||||
import { Tracker } from "../common/tracker"
|
|
||||||
import { app, remote } from "electron"
|
|
||||||
|
|
||||||
export const tracker = new Tracker(app || remote.app);
|
|
||||||
@ -1,8 +1,8 @@
|
|||||||
import { BrowserWindow, shell } from "electron"
|
import { BrowserWindow, shell } from "electron"
|
||||||
import { PromiseIpc } from "electron-promise-ipc"
|
import { PromiseIpc } from "electron-promise-ipc"
|
||||||
import windowStateKeeper from "electron-window-state"
|
import windowStateKeeper from "electron-window-state"
|
||||||
import { tracker } from "./tracker";
|
|
||||||
import { getStaticUrl } from "../common/register-static";
|
import { getStaticUrl } from "../common/register-static";
|
||||||
|
import { tracker } from "../common/tracker";
|
||||||
|
|
||||||
export class WindowManager {
|
export class WindowManager {
|
||||||
public mainWindow: BrowserWindow = null;
|
public mainWindow: BrowserWindow = null;
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
import ElectronStore from "electron-store";
|
import Config from "conf";
|
||||||
import { isTestEnv } from "../common/vars";
|
import { isTestEnv } from "../common/vars";
|
||||||
|
|
||||||
export interface MigrationOpts {
|
export interface MigrationOpts {
|
||||||
version: string;
|
version: string;
|
||||||
run(store: ElectronStore, log: (...args: any[]) => void): void;
|
run(storeConfig: Config<any>, log: (...args: any[]) => void): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function infoLog(...args: any[]) {
|
function infoLog(...args: any[]) {
|
||||||
@ -12,12 +12,12 @@ function infoLog(...args: any[]) {
|
|||||||
console.log(...args);
|
console.log(...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function migration({ version, run }: MigrationOpts) {
|
export function migration<S = any>({ version, run }: MigrationOpts) {
|
||||||
return {
|
return {
|
||||||
[version]: (store: ElectronStore) => {
|
[version]: (storeConfig: Config<S>) => {
|
||||||
const storeName = path.dirname(store.path);
|
const storeName = path.dirname(storeConfig.path);
|
||||||
infoLog(`STORE MIGRATION (${storeName}): ${version}`, );
|
infoLog(`STORE MIGRATION (${storeName}): ${version}`,);
|
||||||
run(store, infoLog);
|
run(storeConfig, infoLog);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,8 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import CubeSpinner from "@/_vue/components/CubeSpinner";
|
import CubeSpinner from "@/_vue/components/CubeSpinner";
|
||||||
|
import { tracker } from "../../../common/tracker"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "ClusterPage",
|
name: "ClusterPage",
|
||||||
components: {
|
components: {
|
||||||
@ -112,7 +114,7 @@ export default {
|
|||||||
this.lens.webview = webview;
|
this.lens.webview = webview;
|
||||||
}
|
}
|
||||||
this.$store.dispatch("attachWebview", this.lens);
|
this.$store.dispatch("attachWebview", this.lens);
|
||||||
this.$tracker.event("cluster", "open");
|
tracker.event("cluster", "open");
|
||||||
},
|
},
|
||||||
hideLens: function() {
|
hideLens: function() {
|
||||||
this.$store.dispatch("hideWebviews");
|
this.$store.dispatch("hideWebviews");
|
||||||
@ -131,7 +133,7 @@ export default {
|
|||||||
"accessible": function(newStatus, oldStatus) {
|
"accessible": function(newStatus, oldStatus) {
|
||||||
console.log("accessible watch, vals:", newStatus, oldStatus);
|
console.log("accessible watch, vals:", newStatus, oldStatus);
|
||||||
if(newStatus === false) { // accessble == false
|
if(newStatus === false) { // accessble == false
|
||||||
this.$tracker.event("cluster", "open-failed");
|
tracker.event("cluster", "open-failed");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,17 +2,14 @@ import "../../common/system-ca"
|
|||||||
import "./assets/css/app.scss"
|
import "./assets/css/app.scss"
|
||||||
import "prismjs";
|
import "prismjs";
|
||||||
import "prismjs/components/prism-yaml"
|
import "prismjs/components/prism-yaml"
|
||||||
import { remote } from "electron"
|
import { PromiseIpc } from 'electron-promise-ipc'
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import VueElectron from 'vue-electron'
|
import VueElectron from 'vue-electron'
|
||||||
import BootstrapVue from 'bootstrap-vue'
|
import BootstrapVue from 'bootstrap-vue'
|
||||||
import { PromiseIpc } from 'electron-promise-ipc'
|
|
||||||
import { Tracker } from "../../common/tracker"
|
|
||||||
import App from './App'
|
import App from './App'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
import store from './store'
|
import store from './store'
|
||||||
|
|
||||||
const tracker = new Tracker(remote.app);
|
|
||||||
const promiseIpc = new PromiseIpc({maxTimeoutMs: 6000});
|
const promiseIpc = new PromiseIpc({maxTimeoutMs: 6000});
|
||||||
|
|
||||||
promiseIpc.on('navigate', async (view) => {
|
promiseIpc.on('navigate', async (view) => {
|
||||||
@ -26,7 +23,6 @@ Vue.use(BootstrapVue)
|
|||||||
Vue.mixin({
|
Vue.mixin({
|
||||||
created: function () {
|
created: function () {
|
||||||
this.$promiseIpc = promiseIpc;
|
this.$promiseIpc = promiseIpc;
|
||||||
this.$tracker = tracker;
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -7,18 +7,13 @@ import KubeContexts from './modules/kube-contexts'
|
|||||||
import Clusters from './modules/clusters'
|
import Clusters from './modules/clusters'
|
||||||
import HelmRepos from './modules/helm-repos'
|
import HelmRepos from './modules/helm-repos'
|
||||||
import Workspaces from './modules/workspaces'
|
import Workspaces from './modules/workspaces'
|
||||||
|
import { tracker } from "../../../common/tracker"
|
||||||
// promise ipc
|
|
||||||
import { PromiseIpc } from 'electron-promise-ipc'
|
import { PromiseIpc } from 'electron-promise-ipc'
|
||||||
const promiseIpc = new PromiseIpc( { maxTimeoutMs: 120000 } );
|
|
||||||
|
|
||||||
// tracker
|
|
||||||
import { Tracker } from "../../../common/tracker"
|
|
||||||
import { remote } from "electron"
|
|
||||||
const tracker = new Tracker(remote.app);
|
|
||||||
|
|
||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
|
|
||||||
|
const promiseIpc = new PromiseIpc( { maxTimeoutMs: 120000 } );
|
||||||
|
|
||||||
export default new Vuex.Store({
|
export default new Vuex.Store({
|
||||||
modules: {
|
modules: {
|
||||||
Clusters,
|
Clusters,
|
||||||
@ -31,29 +26,24 @@ export default new Vuex.Store({
|
|||||||
hud: {
|
hud: {
|
||||||
isMenuVisible: true,
|
isMenuVisible: true,
|
||||||
},
|
},
|
||||||
seenContexts: userStore.getSeenContexts(),
|
seenContexts: userStore.seenContexts,
|
||||||
lastSeenAppVersion: userStore.lastSeenAppVersion(),
|
lastSeenAppVersion: userStore.lastSeenAppVersion,
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
storeSeenContexts(state, context) {
|
storeSeenContexts(state, context) {
|
||||||
const seenContexts = userStore.storeSeenContext(context);
|
userStore.seenContexts.add(context);
|
||||||
state.seenContexts = seenContexts
|
state.seenContexts = Array.from(userStore.seenContexts);
|
||||||
},
|
},
|
||||||
updateLastSeenAppVersion(state, appVersion) {
|
updateLastSeenAppVersion(state, appVersion) {
|
||||||
state.lastSeenAppVersion = appVersion;
|
state.lastSeenAppVersion = appVersion;
|
||||||
userStore.setLastSeenAppVersion(appVersion)
|
userStore.lastSeenAppVersion = appVersion
|
||||||
},
|
},
|
||||||
loadPreferences(state) {
|
loadPreferences(state) {
|
||||||
this.commit("savePreferences", userStore.getPreferences());
|
this.commit("savePreferences", userStore.preferences);
|
||||||
},
|
},
|
||||||
savePreferences(state, prefs) {
|
savePreferences(state, prefs) {
|
||||||
if (prefs.allowTelemetry) {
|
|
||||||
tracker.event("telemetry", "enabled")
|
|
||||||
} else {
|
|
||||||
tracker.event("telemetry", "disabled")
|
|
||||||
}
|
|
||||||
state.preferences = prefs;
|
state.preferences = prefs;
|
||||||
userStore.setPreferences(prefs);
|
userStore.preferences = prefs;
|
||||||
this.dispatch("destroyWebviews")
|
this.dispatch("destroyWebviews")
|
||||||
promiseIpc.send("preferencesSaved")
|
promiseIpc.send("preferencesSaved")
|
||||||
},
|
},
|
||||||
@ -67,9 +57,7 @@ export default new Vuex.Store({
|
|||||||
actions: {
|
actions: {
|
||||||
async init({commit, getters}) {
|
async init({commit, getters}) {
|
||||||
commit("loadPreferences");
|
commit("loadPreferences");
|
||||||
|
|
||||||
await this.dispatch('refreshClusters', getters.currentWorkspace);
|
await this.dispatch('refreshClusters', getters.currentWorkspace);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
async addSeenContexts({commit}, data){
|
async addSeenContexts({commit}, data){
|
||||||
|
|||||||
@ -1,14 +1,12 @@
|
|||||||
import Vue from "vue"
|
import Vue from "vue"
|
||||||
import { ClusterInfo } from "../../../../main/cluster"
|
import { ClusterInfo } from "../../../../main/cluster"
|
||||||
import { MutationTree, ActionTree, GetterTree } from "vuex"
|
import { ActionTree, GetterTree, MutationTree } from "vuex"
|
||||||
import { PromiseIpc } from 'electron-promise-ipc'
|
import { PromiseIpc } from 'electron-promise-ipc'
|
||||||
import { Tracker } from "../../../../common/tracker"
|
|
||||||
import { remote } from "electron"
|
|
||||||
import { clusterStore } from "../../../../common/cluster-store"
|
import { clusterStore } from "../../../../common/cluster-store"
|
||||||
import { Workspace } from "../../../../common/workspace-store"
|
import { Workspace } from "../../../../common/workspace-store"
|
||||||
|
import { tracker } from "../../../../common/tracker";
|
||||||
|
|
||||||
const promiseIpc = new PromiseIpc( { maxTimeoutMs: 120000 } );
|
const promiseIpc = new PromiseIpc({ maxTimeoutMs: 120000 });
|
||||||
const tracker = new Tracker(remote.app);
|
|
||||||
|
|
||||||
export interface LensWebview {
|
export interface LensWebview {
|
||||||
id: string;
|
id: string;
|
||||||
@ -26,12 +24,12 @@ const state: ClusterState = {
|
|||||||
clusters: []
|
clusters: []
|
||||||
}
|
}
|
||||||
|
|
||||||
const actions: ActionTree<ClusterState, any> = {
|
const actions: ActionTree<ClusterState, any> = {
|
||||||
async refreshClusters({commit}, currentWorkspace: Workspace) {
|
async refreshClusters({ commit }, currentWorkspace: Workspace) {
|
||||||
const clusters: ClusterInfo[] = await promiseIpc.send('getClusters', currentWorkspace.id).catch((error: Error) => {
|
const clusters: ClusterInfo[] = await promiseIpc.send('getClusters', currentWorkspace.id).catch((error: Error) => {
|
||||||
return false;
|
return false;
|
||||||
})
|
})
|
||||||
if(!clusters) return false;
|
if (!clusters) return false;
|
||||||
commit('updateClusters', clusters);
|
commit('updateClusters', clusters);
|
||||||
clusters.forEach((cluster: ClusterInfo) => {
|
clusters.forEach((cluster: ClusterInfo) => {
|
||||||
const lens: LensWebview = {
|
const lens: LensWebview = {
|
||||||
@ -43,29 +41,29 @@ const actions: ActionTree<ClusterState, any> = {
|
|||||||
})
|
})
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
async getCluster({commit, getters}, id: string) {
|
async getCluster({ commit, getters }, id: string) {
|
||||||
const cluster: ClusterInfo = getters.clusters.find((c: ClusterInfo) => c.id === id)
|
const cluster: ClusterInfo = getters.clusters.find((c: ClusterInfo) => c.id === id)
|
||||||
if(!cluster) return null;
|
if (!cluster) return null;
|
||||||
|
|
||||||
const remoteCluster = await promiseIpc.send("getCluster", cluster.id)
|
const remoteCluster = await promiseIpc.send("getCluster", cluster.id)
|
||||||
if(!remoteCluster) return null;
|
if (!remoteCluster) return null;
|
||||||
|
|
||||||
Object.assign(cluster, remoteCluster)
|
Object.assign(cluster, remoteCluster)
|
||||||
commit('updateCluster', cluster);
|
commit('updateCluster', cluster);
|
||||||
|
|
||||||
return cluster;
|
return cluster;
|
||||||
},
|
},
|
||||||
async refineCluster({commit}, id: string) {
|
async refineCluster({ commit }, id: string) {
|
||||||
console.log("VUEX: ACTION: REFINE CLUSTER", id);
|
console.log("VUEX: ACTION: REFINE CLUSTER", id);
|
||||||
|
|
||||||
const remoteCluster = await promiseIpc.send("getCluster", id)
|
const remoteCluster = await promiseIpc.send("getCluster", id)
|
||||||
if(!remoteCluster) return null;
|
if (!remoteCluster) return null;
|
||||||
|
|
||||||
commit('updateCluster', remoteCluster);
|
commit('updateCluster', remoteCluster);
|
||||||
|
|
||||||
return remoteCluster;
|
return remoteCluster;
|
||||||
},
|
},
|
||||||
async stopCluster({dispatch, getters}, id: string) {
|
async stopCluster({ dispatch, getters }, id: string) {
|
||||||
const cluster: ClusterInfo = getters.clusters.find((c: ClusterInfo) => c.id === id)
|
const cluster: ClusterInfo = getters.clusters.find((c: ClusterInfo) => c.id === id)
|
||||||
if (!cluster) return;
|
if (!cluster) return;
|
||||||
|
|
||||||
@ -76,7 +74,7 @@ const actions: ActionTree<ClusterState, any> = {
|
|||||||
tracker.event("cluster", "stop")
|
tracker.event("cluster", "stop")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async removeCluster({getters, dispatch}, id: string) {
|
async removeCluster({ getters, dispatch }, id: string) {
|
||||||
const cluster: ClusterInfo = getters.clusters.find((c: ClusterInfo) => c.id === id)
|
const cluster: ClusterInfo = getters.clusters.find((c: ClusterInfo) => c.id === id)
|
||||||
if (!cluster) {
|
if (!cluster) {
|
||||||
return
|
return
|
||||||
@ -92,16 +90,16 @@ const actions: ActionTree<ClusterState, any> = {
|
|||||||
await dispatch("refreshClusters", getters.currentWorkspace)
|
await dispatch("refreshClusters", getters.currentWorkspace)
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
async addCluster({commit, getters, dispatch}, data) {
|
async addCluster({ commit, getters, dispatch }, data) {
|
||||||
const res = await promiseIpc.send("addCluster", data)
|
const res = await promiseIpc.send("addCluster", data)
|
||||||
if(!res) return null;
|
if (!res) return null;
|
||||||
|
|
||||||
tracker.event("cluster", "add");
|
tracker.event("cluster", "add");
|
||||||
commit('updateClusters', res.allClusters);
|
commit('updateClusters', res.allClusters);
|
||||||
await dispatch("refreshClusters", getters.currentWorkspace);
|
await dispatch("refreshClusters", getters.currentWorkspace);
|
||||||
return res.addedCluster;
|
return res.addedCluster;
|
||||||
},
|
},
|
||||||
async clearClusters({commit, getters, dispatch}){
|
async clearClusters({ commit, getters, dispatch }) {
|
||||||
// todo: clean from main process as well?
|
// todo: clean from main process as well?
|
||||||
getters.lenses.forEach((lens: LensWebview) => {
|
getters.lenses.forEach((lens: LensWebview) => {
|
||||||
if (lens.webview) {
|
if (lens.webview) {
|
||||||
@ -113,14 +111,14 @@ const actions: ActionTree<ClusterState, any> = {
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
async uploadClusterIcon({commit}, data) {
|
async uploadClusterIcon({ commit }, data) {
|
||||||
const res = await promiseIpc.send("saveClusterIcon", data)
|
const res = await promiseIpc.send("saveClusterIcon", data)
|
||||||
tracker.event("cluster", "upload-icon")
|
tracker.event("cluster", "upload-icon")
|
||||||
if (res.cluster) commit("updateCluster", res.cluster)
|
if (res.cluster) commit("updateCluster", res.cluster)
|
||||||
return res
|
return res
|
||||||
},
|
},
|
||||||
|
|
||||||
async resetClusterIcon({commit}, data) {
|
async resetClusterIcon({ commit }, data) {
|
||||||
const res = await promiseIpc.send("resetClusterIcon", data.clusterId)
|
const res = await promiseIpc.send("resetClusterIcon", data.clusterId)
|
||||||
tracker.event("cluster", "reset-icon")
|
tracker.event("cluster", "reset-icon")
|
||||||
if (res.cluster) commit("updateCluster", res.cluster)
|
if (res.cluster) commit("updateCluster", res.cluster)
|
||||||
@ -128,7 +126,7 @@ const actions: ActionTree<ClusterState, any> = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// For data structure see: cluster-manager.ts / FeatureInstallRequest
|
// For data structure see: cluster-manager.ts / FeatureInstallRequest
|
||||||
async installClusterFeature({commit}, data) {
|
async installClusterFeature({ commit }, data) {
|
||||||
// Custom no timeout IPC as install can take very variable time
|
// Custom no timeout IPC as install can take very variable time
|
||||||
const ipc = new PromiseIpc();
|
const ipc = new PromiseIpc();
|
||||||
const response = await ipc.send('installFeature', data)
|
const response = await ipc.send('installFeature', data)
|
||||||
@ -140,20 +138,19 @@ const actions: ActionTree<ClusterState, any> = {
|
|||||||
return response
|
return response
|
||||||
},
|
},
|
||||||
// For data structure see: cluster-manager.ts / FeatureInstallRequest
|
// For data structure see: cluster-manager.ts / FeatureInstallRequest
|
||||||
async upgradeClusterFeature({commit}, data) {
|
async upgradeClusterFeature({ commit }, data) {
|
||||||
// Custom no timeout IPC as install can take very variable time
|
// Custom no timeout IPC as install can take very variable time
|
||||||
const ipc = new PromiseIpc();
|
const ipc = new PromiseIpc();
|
||||||
const response = await ipc.send('upgradeFeature', data)
|
const response = await ipc.send('upgradeFeature', data)
|
||||||
console.log("upgrade result:", response);
|
console.log("upgrade result:", response);
|
||||||
const cluster = await ipc.send('refreshCluster', data.clusterId)
|
const cluster = await ipc.send('refreshCluster', data.clusterId)
|
||||||
|
|
||||||
|
|
||||||
tracker.event("cluster", "upgrade-feature")
|
tracker.event("cluster", "upgrade-feature")
|
||||||
commit("updateCluster", cluster)
|
commit("updateCluster", cluster)
|
||||||
return response
|
return response
|
||||||
},
|
},
|
||||||
// For data structure see: cluster-manager.ts / FeatureInstallRequest
|
// For data structure see: cluster-manager.ts / FeatureInstallRequest
|
||||||
async uninstallClusterFeature({commit}, data) {
|
async uninstallClusterFeature({ commit }, data) {
|
||||||
// Custom no timeout IPC as uninstall can take very variable time
|
// Custom no timeout IPC as uninstall can take very variable time
|
||||||
const ipc = new PromiseIpc();
|
const ipc = new PromiseIpc();
|
||||||
const response = await ipc.send('uninstallFeature', data)
|
const response = await ipc.send('uninstallFeature', data)
|
||||||
@ -165,7 +162,7 @@ const actions: ActionTree<ClusterState, any> = {
|
|||||||
return response
|
return response
|
||||||
},
|
},
|
||||||
|
|
||||||
attachWebview({commit}, lens: LensWebview) {
|
attachWebview({ commit }, lens: LensWebview) {
|
||||||
const container: any = document.getElementById("lens-container");
|
const container: any = document.getElementById("lens-container");
|
||||||
if (!container || !lens.webview) {
|
if (!container || !lens.webview) {
|
||||||
return
|
return
|
||||||
@ -189,9 +186,11 @@ const actions: ActionTree<ClusterState, any> = {
|
|||||||
})
|
})
|
||||||
promiseIpc.send("enableClusterSettingsMenuItem", lens.id)
|
promiseIpc.send("enableClusterSettingsMenuItem", lens.id)
|
||||||
},
|
},
|
||||||
detachWebview({commit}, lens: LensWebview) {
|
detachWebview({ commit }, lens: LensWebview) {
|
||||||
const container: any = document.getElementById("lens-container");
|
const container: any = document.getElementById("lens-container");
|
||||||
if (!container) { return }
|
if (!container) {
|
||||||
|
return
|
||||||
|
}
|
||||||
container.childNodes.forEach((child: any) => {
|
container.childNodes.forEach((child: any) => {
|
||||||
if (child === lens.webview) {
|
if (child === lens.webview) {
|
||||||
container.removeChild(lens.webview)
|
container.removeChild(lens.webview)
|
||||||
@ -202,21 +201,23 @@ const actions: ActionTree<ClusterState, any> = {
|
|||||||
})
|
})
|
||||||
promiseIpc.send("disableClusterSettingsMenuItem")
|
promiseIpc.send("disableClusterSettingsMenuItem")
|
||||||
},
|
},
|
||||||
hideWebviews({commit}) {
|
hideWebviews({ commit }) {
|
||||||
const container: any = document.getElementById("lens-container");
|
const container: any = document.getElementById("lens-container");
|
||||||
if (!container) { return }
|
if (!container) {
|
||||||
|
return
|
||||||
|
}
|
||||||
container.style = "display: none;"
|
container.style = "display: none;"
|
||||||
container.childNodes.forEach((child: any) => {
|
container.childNodes.forEach((child: any) => {
|
||||||
child.style = "display: none;"
|
child.style = "display: none;"
|
||||||
})
|
})
|
||||||
promiseIpc.send("disableClusterSettingsMenuItem")
|
promiseIpc.send("disableClusterSettingsMenuItem")
|
||||||
},
|
},
|
||||||
destroyWebviews({commit}) {
|
destroyWebviews({ commit }) {
|
||||||
state.lenses.forEach((lens) => {
|
state.lenses.forEach((lens) => {
|
||||||
this.dispatch("detachWebview", lens)
|
this.dispatch("detachWebview", lens)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
storeCluster({commit}, cluster: ClusterInfo) {
|
storeCluster({ commit }, cluster: ClusterInfo) {
|
||||||
clusterStore.saveCluster(cluster);
|
clusterStore.saveCluster(cluster);
|
||||||
commit("updateCluster", cluster)
|
commit("updateCluster", cluster)
|
||||||
promiseIpc.send("clusterStored", cluster.id)
|
promiseIpc.send("clusterStored", cluster.id)
|
||||||
@ -250,7 +251,7 @@ const mutations: MutationTree<ClusterState> = {
|
|||||||
},
|
},
|
||||||
updateCluster(state, cluster) {
|
updateCluster(state, cluster) {
|
||||||
state.clusters.forEach((c, index) => {
|
state.clusters.forEach((c, index) => {
|
||||||
if(c.id === cluster.id) {
|
if (c.id === cluster.id) {
|
||||||
Vue.set(state.clusters, index, cluster)
|
Vue.set(state.clusters, index, cluster)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { MutationTree, ActionTree, GetterTree } from "vuex"
|
import { MutationTree, ActionTree, GetterTree } from "vuex"
|
||||||
import { workspaceStore, Workspace, WorkspaceData } from "../../../../common/workspace-store"
|
import { workspaceStore, Workspace } from "../../../../common/workspace-store"
|
||||||
|
|
||||||
export interface WorkspaceState {
|
export interface WorkspaceState {
|
||||||
workspaces: Array<Workspace>;
|
workspaces: Array<Workspace>;
|
||||||
@ -26,11 +26,11 @@ const mutations: MutationTree<WorkspaceState> = {
|
|||||||
setCurrentWorkspace(state, workspace: Workspace) {
|
setCurrentWorkspace(state, workspace: Workspace) {
|
||||||
state.currentWorkspace = workspace
|
state.currentWorkspace = workspace
|
||||||
},
|
},
|
||||||
addWorkspace(state, workspace: WorkspaceData) {
|
addWorkspace(state, workspace: Workspace) {
|
||||||
workspaceStore.saveWorkspace(workspace)
|
workspaceStore.saveWorkspace(workspace)
|
||||||
state.workspaces = workspaceStore.getAllWorkspaces()
|
state.workspaces = workspaceStore.getAllWorkspaces()
|
||||||
},
|
},
|
||||||
updateWorkspace(state, workspace: WorkspaceData) {
|
updateWorkspace(state, workspace: Workspace) {
|
||||||
workspaceStore.saveWorkspace(workspace)
|
workspaceStore.saveWorkspace(workspace)
|
||||||
state.workspaces = workspaceStore.getAllWorkspaces()
|
state.workspaces = workspaceStore.getAllWorkspaces()
|
||||||
},
|
},
|
||||||
|
|||||||
67
yarn.lock
67
yarn.lock
@ -3877,21 +3877,21 @@ concurrently@^5.2.0:
|
|||||||
tree-kill "^1.2.2"
|
tree-kill "^1.2.2"
|
||||||
yargs "^13.3.0"
|
yargs "^13.3.0"
|
||||||
|
|
||||||
conf@^6.2.1:
|
conf@^7.0.1:
|
||||||
version "6.2.4"
|
version "7.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/conf/-/conf-6.2.4.tgz#49d23c4e21ef2ac2860f7b5ed25b7b7e484f769f"
|
resolved "https://registry.yarnpkg.com/conf/-/conf-7.0.1.tgz#a3ddd860db0357219b9ccdf92fd618e161fe6848"
|
||||||
integrity sha512-GjgyPRLo1qK1LR9RWAdUagqo+DP18f5HWCFk4va7GS+wpxQTOzfuKTwKOvGW2c01/YXNicAyyoyuSddmdkBzZQ==
|
integrity sha512-UFA9EPFKK+tedCjz4JKQSxAN0/0r0rjURdq9N805JXWOhFzmr7fTLmG1cnsHCvyYYd65wRjf0KB8zg+WPelkvA==
|
||||||
dependencies:
|
dependencies:
|
||||||
ajv "^6.10.2"
|
ajv "^6.12.2"
|
||||||
debounce-fn "^3.0.1"
|
debounce-fn "^4.0.0"
|
||||||
dot-prop "^5.0.0"
|
dot-prop "^5.2.0"
|
||||||
env-paths "^2.2.0"
|
env-paths "^2.2.0"
|
||||||
json-schema-typed "^7.0.1"
|
json-schema-typed "^7.0.3"
|
||||||
make-dir "^3.0.0"
|
make-dir "^3.1.0"
|
||||||
onetime "^5.1.0"
|
onetime "^5.1.0"
|
||||||
pkg-up "^3.0.1"
|
pkg-up "^3.1.0"
|
||||||
semver "^6.2.0"
|
semver "^7.3.2"
|
||||||
write-file-atomic "^3.0.0"
|
write-file-atomic "^3.0.3"
|
||||||
|
|
||||||
config-chain@^1.1.11:
|
config-chain@^1.1.11:
|
||||||
version "1.1.12"
|
version "1.1.12"
|
||||||
@ -4233,12 +4233,12 @@ de-indent@^1.0.2:
|
|||||||
resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
|
resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
|
||||||
integrity sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=
|
integrity sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=
|
||||||
|
|
||||||
debounce-fn@^3.0.1:
|
debounce-fn@^4.0.0:
|
||||||
version "3.0.1"
|
version "4.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/debounce-fn/-/debounce-fn-3.0.1.tgz#034afe8b904d985d1ec1aa589cd15f388741d680"
|
resolved "https://registry.yarnpkg.com/debounce-fn/-/debounce-fn-4.0.0.tgz#ed76d206d8a50e60de0dd66d494d82835ffe61c7"
|
||||||
integrity sha512-aBoJh5AhpqlRoHZjHmOzZlRx+wz2xVwGL9rjs+Kj0EWUrL4/h4K7OD176thl2Tdoqui/AaA4xhHrNArGLAaI3Q==
|
integrity sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
mimic-fn "^2.1.0"
|
mimic-fn "^3.0.0"
|
||||||
|
|
||||||
debug@4.1.0:
|
debug@4.1.0:
|
||||||
version "4.1.0"
|
version "4.1.0"
|
||||||
@ -4547,7 +4547,7 @@ dot-case@^3.0.3:
|
|||||||
no-case "^3.0.3"
|
no-case "^3.0.3"
|
||||||
tslib "^1.10.0"
|
tslib "^1.10.0"
|
||||||
|
|
||||||
dot-prop@^5.0.0, dot-prop@^5.2.0:
|
dot-prop@^5.2.0:
|
||||||
version "5.2.0"
|
version "5.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb"
|
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb"
|
||||||
integrity sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==
|
integrity sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==
|
||||||
@ -4674,14 +4674,6 @@ electron-publish@22.7.0:
|
|||||||
lazy-val "^1.0.4"
|
lazy-val "^1.0.4"
|
||||||
mime "^2.4.5"
|
mime "^2.4.5"
|
||||||
|
|
||||||
electron-store@^5.2.0:
|
|
||||||
version "5.2.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/electron-store/-/electron-store-5.2.0.tgz#a15718fc1fa21acfd07af55f9b94f9fa6a536665"
|
|
||||||
integrity sha512-iU3WDqEDAYNYR9XV7p0tJajq/zs9z7Nrn0sAoR5nDyn8h/9dr9kusKbTxD8NtVEBD1TB1pkGMqcbIt/y6knDwQ==
|
|
||||||
dependencies:
|
|
||||||
conf "^6.2.1"
|
|
||||||
type-fest "^0.7.1"
|
|
||||||
|
|
||||||
electron-to-chromium@^1.3.413:
|
electron-to-chromium@^1.3.413:
|
||||||
version "1.3.464"
|
version "1.3.464"
|
||||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.464.tgz#fe13feaa08f6f865d3c89d5d72e54c194f463aa5"
|
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.464.tgz#fe13feaa08f6f865d3c89d5d72e54c194f463aa5"
|
||||||
@ -7260,7 +7252,7 @@ json-schema-traverse@^0.4.1:
|
|||||||
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
|
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
|
||||||
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
|
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
|
||||||
|
|
||||||
json-schema-typed@^7.0.1:
|
json-schema-typed@^7.0.3:
|
||||||
version "7.0.3"
|
version "7.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/json-schema-typed/-/json-schema-typed-7.0.3.tgz#23ff481b8b4eebcd2ca123b4fa0409e66469a2d9"
|
resolved "https://registry.yarnpkg.com/json-schema-typed/-/json-schema-typed-7.0.3.tgz#23ff481b8b4eebcd2ca123b4fa0409e66469a2d9"
|
||||||
integrity sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==
|
integrity sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==
|
||||||
@ -7703,7 +7695,7 @@ make-dir@^2.0.0:
|
|||||||
pify "^4.0.1"
|
pify "^4.0.1"
|
||||||
semver "^5.6.0"
|
semver "^5.6.0"
|
||||||
|
|
||||||
make-dir@^3.0.0, make-dir@^3.0.2:
|
make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
|
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
|
||||||
integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==
|
integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==
|
||||||
@ -7914,6 +7906,11 @@ mimic-fn@^2.0.0, mimic-fn@^2.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
|
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
|
||||||
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
|
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
|
||||||
|
|
||||||
|
mimic-fn@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-3.0.0.tgz#76044cfa8818bbf6999c5c9acadf2d3649b14b4b"
|
||||||
|
integrity sha512-PiVO95TKvhiwgSwg1IdLYlCTdul38yZxZMIcnDSFIBUm4BNZha2qpQ4GpJ++15bHoKDtrW2D69lMfFwdFYtNZQ==
|
||||||
|
|
||||||
mimic-response@^1.0.0, mimic-response@^1.0.1:
|
mimic-response@^1.0.0, mimic-response@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
|
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
|
||||||
@ -9071,7 +9068,7 @@ pkg-up@^2.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
find-up "^2.1.0"
|
find-up "^2.1.0"
|
||||||
|
|
||||||
pkg-up@^3.0.1:
|
pkg-up@^3.1.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5"
|
resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5"
|
||||||
integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==
|
integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==
|
||||||
@ -10197,6 +10194,11 @@ serialize-javascript@^3.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
randombytes "^2.1.0"
|
randombytes "^2.1.0"
|
||||||
|
|
||||||
|
serializr@^2.0.3:
|
||||||
|
version "2.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/serializr/-/serializr-2.0.3.tgz#f163b2b39938e1313beaf8457133713df2a936bb"
|
||||||
|
integrity sha512-YHDTg+QE//5vESifXc7vvncxIAiUiX04FAy1RmSgACkwhvJ9DivljDEjqs5WJzg5KLokI3x3jh30KEfXq5nDJA==
|
||||||
|
|
||||||
set-blocking@^2.0.0, set-blocking@~2.0.0:
|
set-blocking@^2.0.0, set-blocking@~2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
|
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
|
||||||
@ -11292,11 +11294,6 @@ type-fest@^0.6.0:
|
|||||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b"
|
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b"
|
||||||
integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==
|
integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==
|
||||||
|
|
||||||
type-fest@^0.7.1:
|
|
||||||
version "0.7.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48"
|
|
||||||
integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==
|
|
||||||
|
|
||||||
type-fest@^0.8.0, type-fest@^0.8.1:
|
type-fest@^0.8.0, type-fest@^0.8.1:
|
||||||
version "0.8.1"
|
version "0.8.1"
|
||||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
|
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
|
||||||
@ -11984,7 +11981,7 @@ wrappy@1:
|
|||||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||||
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
|
||||||
|
|
||||||
write-file-atomic@^3.0.0:
|
write-file-atomic@^3.0.0, write-file-atomic@^3.0.3:
|
||||||
version "3.0.3"
|
version "3.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8"
|
resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8"
|
||||||
integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==
|
integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user