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

epic: getting rid of vue -- part 2

Signed-off-by: Roman <ixrock@gmail.com>
This commit is contained in:
Roman 2020-07-08 09:09:11 +03:00
parent 63de5fce3c
commit 1f26f0b3a7
22 changed files with 469 additions and 297 deletions

View File

@ -12,8 +12,7 @@
},
"scripts": {
"dev": "concurrently -k \"yarn dev-run -C\" \"yarn dev:main\" \"yarn dev:renderer\"",
"dev-run": "nodemon --watch out/main.* --exec \"electron --inspect .\" $@",
"dev-test": "yarn test --watch",
"dev-run": "env DEBUG=true nodemon --watch out/main.* --exec \"electron --inspect .\" $@",
"dev:main": "env DEBUG=true yarn compile:main --watch $@",
"dev:renderer": "env DEBUG=true yarn compile:renderer --watch $@",
"compile": "concurrently \"yarn i18n:compile\" \"yarn compile:main -p\" \"yarn compile:renderer -p\"",
@ -34,7 +33,8 @@
"download-bins": "concurrently yarn:download:*",
"download:kubectl": "yarn run ts-node build/download_kubectl.ts",
"download:helm": "yarn run ts-node build/download_helm.ts",
"lint": "eslint $@ --ext js,ts,tsx --max-warnings=0 src/"
"lint": "eslint $@ --ext js,ts,tsx --max-warnings=0 src/",
"rebuild": "electron-rebuild -f -w node-pty"
},
"config": {
"bundledKubectlVersion": "1.17.4",
@ -260,9 +260,10 @@
"css-element-queries": "^1.2.3",
"css-loader": "^3.5.3",
"dompurify": "^2.0.11",
"electron": "^6.1.12",
"electron": "^7.3.2",
"electron-builder": "^22.7.0",
"electron-notarize": "^0.3.0",
"electron-rebuild": "^1.11.0",
"eslint": "^7.3.1",
"file-loader": "^6.0.0",
"flex.box": "^3.4.4",

View File

@ -21,7 +21,7 @@ export class BaseStore<T = any> extends Singleton {
public whenLoaded = when(() => this.isLoaded);
@observable isLoaded = false;
@observable protected data = {} as T;
@observable protected data: T;
protected constructor(protected params: BaseStoreParams) {
super();
@ -69,9 +69,9 @@ export class BaseStore<T = any> extends Singleton {
},
...confOptions,
});
const data = this.storeConfig.store;
console.info(`[STORE]: [LOADED] ${this.storeConfig.path}`, data);
this.fromStore(data);
const jsonModel = this.storeConfig.store;
console.info(`[STORE]: [LOADED] ${this.storeConfig.path}`, jsonModel);
this.fromStore(jsonModel);
this.isLoaded = true;
}
@ -111,8 +111,8 @@ export class BaseStore<T = any> extends Singleton {
}
@action
protected fromStore(data: Partial<T> = {}) {
Object.assign(this.data, data);
protected fromStore(data: T) {
this.data = data;
}
@action
@ -120,6 +120,7 @@ export class BaseStore<T = any> extends Singleton {
this.data = produce(this.data, updater);
}
// todo: use "serializr" ?
toJSON(): T {
return toJS(this.data, {
recurseEverything: true,

View File

@ -81,12 +81,11 @@ export class ClusterStore extends BaseStore<ClusterStoreModel> {
@action
protected fromStore({ clusters = [] }: Partial<ClusterStoreModel> = {}) {
// fixme: handle clusters update + delete
clusters.forEach(model => {
if (!this.clusters.has(model.id)) {
this.clusters.set(model.id, new Cluster(model));
}
})
const clustersMap = new Map<ClusterId, Cluster>();
clusters.forEach(clusterModel => {
clustersMap.set(clusterModel.id, new Cluster(clusterModel));
});
this.clusters.replace(clustersMap);
}
toJSON(): ClusterStoreModel {

View File

@ -9,12 +9,16 @@ export interface IpcOptions {
timeout?: number;
}
export interface IpcMessageHandler {
(...args: any[]): any;
}
export async function invokeMessage(channel: string, ...args: any[]) {
logger.debug(`[IPC]: invoke channel "${channel}"`, { args });
return ipcRenderer.invoke(channel, ...args);
}
export function onMessage(channel: string, handler: (...args: any[]) => any, options: IpcOptions = {}) {
export function onMessage(channel: string, handler: IpcMessageHandler, options: IpcOptions = {}) {
const { timeout = 0 } = options;
ipcMain.handle(channel, async (event, ...args: any[]) => {
logger.debug(`[IPC]: handle "${channel}"`, { event, args });
@ -37,8 +41,8 @@ export function onMessage(channel: string, handler: (...args: any[]) => any, opt
})
}
export function onMessages(messages: Record<string, Function>, options?: IpcOptions) {
export function onMessages(messages: Record<string, IpcMessageHandler>, options?: IpcOptions) {
Object.entries(messages).forEach(([channel, handler]) => {
this.onMessage(channel, handler, options);
onMessage(channel, handler, options);
})
}

View File

@ -0,0 +1,17 @@
// Register custom protocols
import path from "path";
import { protocol } from "electron"
import logger from "../main/logger";
export function registerFileProtocol(name: string, basePath: string) {
protocol.registerFileProtocol(name, (request, callback) => {
const filePath = request.url.replace(name + "://", "");
const absPath = path.resolve(basePath, filePath);
callback(absPath);
}, (error) => {
if (error) {
logger.error(`Failed to register protocol "${name}"`, { basePath, error });
}
})
}

View File

@ -1,25 +0,0 @@
// Setup static folder for common assets
import path from "path";
import { protocol } from "electron"
import logger from "../main/logger";
import { staticDir, staticProto, outDir } from "./vars";
export function registerStaticProtocol(rootFolder = staticDir) {
const scheme = staticProto.replace("://", "");
protocol.registerFileProtocol(scheme, (request, callback) => {
const relativePath = request.url.replace(staticProto, "");
const absPath = path.resolve(rootFolder, relativePath);
callback(absPath);
}, (error) => {
logger.debug(`Failed to register protocol "${scheme}"`, error);
})
}
export function getStaticUrl(filePath: string) {
return staticProto + filePath;
}
export function getStaticPath(filePath: string) {
return path.resolve(staticDir, filePath);
}

View File

@ -7,8 +7,8 @@ import * as path from "path"
// Writes kubeconfigs to "embedded" store, i.e. .../Lens/kubeconfigs/
export function writeEmbeddedKubeConfig(clusterId: string, kubeConfig: string): string {
// This can be called from main & renderer
const a = (app || remote.app)
const kubeConfigBase = path.join(a.getPath("userData"), "kubeconfigs")
const userData = (app || remote.app).getPath("userData");
const kubeConfigBase = path.join(userData, "kubeconfigs")
ensureDirSync(kubeConfigBase)
const kubeConfigFile = path.join(kubeConfigBase, clusterId)

View File

@ -1,9 +1,7 @@
// App's common configuration for any process (main, renderer, build pipeline, etc.)
import packageInfo from "../../package.json"
import path from "path";
export const appName = "lens"
// Flags
export const isMac = process.platform === "darwin"
export const isWindows = process.platform === "win32"
export const isDebugging = process.env.DEBUG === "true";
@ -12,6 +10,10 @@ export const isDevelopment = isDebugging || !isProduction;
export const buildVersion = process.env.BUILD_VERSION;
export const isTestEnv = !!process.env.JEST_WORKER_ID;
export const appName = `${packageInfo.productName}${isDevelopment ? "Dev" : ""}`
export const appProto = "lens" // app's "userData" folder (e.g. "lens://icons/logo.svg")
export const staticProto = "static" // static content folder (e.g. "static://RELEASE_NOTES.md")
// Paths
export const contextDir = process.cwd();
export const staticDir = path.join(contextDir, "static");
@ -22,8 +24,6 @@ export const htmlTemplate = path.resolve(rendererDir, "template.html");
export const sassCommonVars = path.resolve(rendererDir, "components/vars.scss");
// Apis
export const staticProto = "static://"
export const apiPrefix = {
BASE: '/api',
KUBE_BASE: '/api-kube', // kubernetes cluster api

View File

@ -1,11 +1,11 @@
import { action, computed, toJS } from "mobx";
import { action, observable } from "mobx";
import { BaseStore } from "./base-store";
import { clusterStore } from "./cluster-store"
export type WorkspaceId = string;
export interface WorkspaceStoreModel {
currentWorkspace?: WorkspaceId; // last visited/activated
currentWorkspace?: WorkspaceId;
workspaces: Workspace[]
}
@ -18,17 +18,14 @@ export interface Workspace {
export class WorkspaceStore extends BaseStore<WorkspaceStoreModel> {
static readonly defaultId: WorkspaceId = "default"
protected data: WorkspaceStoreModel = {
currentWorkspace: WorkspaceStore.defaultId,
workspaces: [{
@observable currentWorkspace = WorkspaceStore.defaultId;
@observable workspaces = observable.map<WorkspaceId, Workspace>({
[WorkspaceStore.defaultId]: {
id: WorkspaceStore.defaultId,
name: "default"
}]
}
@computed get workspaces() {
return toJS(this.data.workspaces);
}
}
});
private constructor() {
super({
@ -36,40 +33,59 @@ export class WorkspaceStore extends BaseStore<WorkspaceStoreModel> {
});
}
public getById(id: WorkspaceId): Workspace {
return this.workspaces.find(workspace => workspace.id === id);
}
public getIndexById(id: WorkspaceId): number {
return this.workspaces.findIndex(workspace => workspace.id === id);
getById(id: WorkspaceId): Workspace {
return this.workspaces.get(id);
}
@action
setCurrent(id: WorkspaceId) {
this.data.currentWorkspace = id;
if (!this.getById(id)) return;
this.currentWorkspace = id;
}
@action
public saveWorkspace(newWorkspace: Workspace) {
const workspace = this.getById(newWorkspace.id);
if (workspace) {
Object.assign(workspace, newWorkspace);
public saveWorkspace(workspace: Workspace) {
const id = workspace.id;
const existingWorkspace = this.getById(id);
if (existingWorkspace) {
Object.assign(existingWorkspace, workspace);
} else {
this.data.workspaces.push(newWorkspace);
this.workspaces.set(id, workspace);
}
}
@action
public removeWorkspace(workspaceOrId: Workspace | WorkspaceId) {
const workspace = this.getById(typeof workspaceOrId == "string" ? workspaceOrId : workspaceOrId.id);
public removeWorkspace(id: WorkspaceId) {
const workspace = this.getById(id);
if (!workspace) return;
if (workspace.id === WorkspaceStore.defaultId) {
if (id === WorkspaceStore.defaultId) {
throw new Error("Cannot remove default workspace");
}
const index = this.getIndexById(workspace.id);
if (index > -1) {
this.data.workspaces.splice(index, 1)
clusterStore.removeByWorkspaceId(workspace.id)
if (id === this.currentWorkspace) {
this.currentWorkspace = WorkspaceStore.defaultId;
}
this.workspaces.delete(id);
clusterStore.removeByWorkspaceId(id)
}
@action
protected fromStore({ currentWorkspace, workspaces = [] }: WorkspaceStoreModel) {
if (currentWorkspace) {
this.currentWorkspace = currentWorkspace
}
if (workspaces.length) {
this.workspaces.clear();
workspaces.forEach(workspace => {
this.workspaces.set(workspace.id, workspace)
})
}
}
toJSON(): WorkspaceStoreModel {
const { currentWorkspace, workspaces } = this;
return {
currentWorkspace: currentWorkspace,
workspaces: Array.from(workspaces.values()),
}
}
}

View File

@ -1,5 +1,5 @@
import { autorun } from "mobx";
import { apiPrefix } from "../common/vars";
import { apiPrefix, appProto } from "../common/vars";
import { app } from "electron"
import path from "path"
import http from "http"
@ -26,8 +26,8 @@ export class ClusterManager {
}
constructor(protected port: number) {
ClusterManager.ipcListen(this);
autorun(() => {
// fixme: detect and stop removed clusters from config file ?
clusterStore.clusters.forEach((cluster: Cluster) => {
if (!cluster.initialized) {
cluster.init(this.port);
@ -35,6 +35,8 @@ export class ClusterManager {
}
})
});
ClusterManager.ipcListen(this);
}
stop() {
@ -48,6 +50,7 @@ export class ClusterManager {
}
protected async addCluster(clusterModel: ClusterModel): Promise<Cluster> {
tracker.event("cluster", "add");
try {
await validateConfig(clusterModel.kubeConfigPath);
return clusterStore.addCluster({
@ -60,7 +63,13 @@ export class ClusterManager {
}
}
protected stopCluster(clusterId: ClusterId) {
tracker.event("cluster", "stop");
this.getCluster(clusterId)?.stopServer();
}
protected removeAllByWorkspace(workspaceId: string) {
tracker.event("cluster", "remove-workspace");
const clusters = clusterStore.getByWorkspaceId(workspaceId);
clusters.forEach(cluster => {
this.removeCluster(cluster.id);
@ -68,6 +77,7 @@ export class ClusterManager {
}
protected removeCluster(clusterId: string): Cluster {
tracker.event("cluster", "remove");
const cluster = this.getCluster(clusterId);
if (cluster) {
cluster.stopServer()
@ -97,64 +107,70 @@ export class ClusterManager {
return cluster;
}
protected async uploadClusterIcon(cluster: Cluster, fileName: string, src: string): Promise<string> {
await ensureDir(ClusterManager.clusterIconDir)
fileName = filenamify(cluster.contextName + "-" + fileName)
const dest = path.join(ClusterManager.clusterIconDir, fileName)
await copyFile(src, dest)
return "store:///icons/" + fileName
protected async uploadClusterIcon({ clusterId, name: fileName, path: src }: ClusterIconUpload): Promise<string> {
const cluster = this.getCluster(clusterId);
if (cluster) {
tracker.event("cluster", "upload-icon");
await ensureDir(ClusterManager.clusterIconDir)
fileName = filenamify(cluster.contextName + "-" + fileName)
const dest = path.join(ClusterManager.clusterIconDir, fileName)
await copyFile(src, dest)
cluster.preferences.icon = `${appProto}:///icons/${fileName}`
return cluster.preferences.icon;
}
}
// todo: remove current icon file ?
protected resetClusterIcon(clusterId: ClusterId) {
const cluster = this.getCluster(clusterId);
if (cluster) {
tracker.event("cluster", "reset-icon")
cluster.preferences.icon = null;
}
}
// todo: check feature failures
protected async installFeature({ clusterId, name, config }: FeatureInstallRequest) {
tracker.event("cluster", "install-feature")
return this.getCluster(clusterId)?.installFeature(name, config)
}
protected async upgradeFeature({ clusterId, name, config }: FeatureInstallRequest) {
tracker.event("cluster", "upgrade-feature")
return this.getCluster(clusterId)?.upgradeFeature(name, config)
}
protected async uninstallFeature({ clusterId, name }: FeatureInstallRequest) {
tracker.event("cluster", "uninstall-feature")
return this.getCluster(clusterId)?.uninstallFeature(name);
}
protected async refreshCluster(clusterId: ClusterId) {
await this.getCluster(clusterId)?.refreshCluster();
}
protected async getEventsCount(clusterId: ClusterId): Promise<number> {
return await this.getCluster(clusterId)?.getEventCount() || 0;
}
static ipcListen(clusterManager: ClusterManager) {
onMessages({
[ClusterIpcMessage.CLUSTER_ADD]: async (model: ClusterModel): Promise<boolean> => {
tracker.event("cluster", "add");
await clusterManager.addCluster(model);
return true;
},
[ClusterIpcMessage.CLUSTER_STOP]: (clusterId: ClusterId) => {
tracker.event("cluster", "stop");
clusterManager.getCluster(clusterId)?.stopServer();
},
[ClusterIpcMessage.CLUSTER_REMOVE]: (clusterId: ClusterId) => {
tracker.event("cluster", "remove");
clusterManager.removeCluster(clusterId);
},
[ClusterIpcMessage.CLUSTER_REMOVE_WORKSPACE]: (workspaceId: ClusterId) => {
clusterManager.removeAllByWorkspace(workspaceId);
},
[ClusterIpcMessage.CLUSTER_REFRESH]: (clusterId: ClusterId) => {
clusterManager.getCluster(clusterId)?.refreshCluster();
},
[ClusterIpcMessage.CLUSTER_EVENTS]: async (clusterId: ClusterId): Promise<number> => {
return await clusterManager.getCluster(clusterId)?.getEventCount() || 0;
},
// todo: check feature failures
[ClusterIpcMessage.FEATURE_INSTALL]: ({ clusterId, name, config }: FeatureInstallRequest) => {
tracker.event("cluster", "install-feature")
return clusterManager.getCluster(clusterId)?.installFeature(name, config)
},
[ClusterIpcMessage.FEATURE_UPGRADE]: ({ clusterId, name, config }: FeatureInstallRequest) => {
tracker.event("cluster", "upgrade-feature")
return clusterManager.getCluster(clusterId)?.upgradeFeature(name, config)
},
[ClusterIpcMessage.FEATURE_REMOVE]: ({ clusterId, name }: FeatureInstallRequest) => {
tracker.event("cluster", "uninstall-feature")
return clusterManager.getCluster(clusterId)?.uninstallFeature(name);
},
[ClusterIpcMessage.ICON_SAVE]: async ({ clusterId, name, path }: ClusterIconUpload) => {
tracker.event("cluster", "upload-icon");
const cluster = clusterManager.getCluster(clusterId);
if (!cluster) return false;
cluster.preferences.icon = await clusterManager.uploadClusterIcon(cluster, name, path);
},
[ClusterIpcMessage.ICON_RESET]: async (clusterId: ClusterId) => {
tracker.event("cluster", "reset-icon")
const cluster = clusterManager.getCluster(clusterId);
if (!cluster) return false;
cluster.preferences.icon = null; // todo: remove current file-icon ?
},
}, {
const handlers = {
[ClusterIpcMessage.CLUSTER_ADD]: clusterManager.addCluster,
[ClusterIpcMessage.CLUSTER_STOP]: clusterManager.stopCluster,
[ClusterIpcMessage.CLUSTER_REMOVE]: clusterManager.removeCluster,
[ClusterIpcMessage.CLUSTER_REMOVE_WORKSPACE]: clusterManager.removeAllByWorkspace,
[ClusterIpcMessage.CLUSTER_REFRESH]: clusterManager.refreshCluster,
[ClusterIpcMessage.CLUSTER_EVENTS]: clusterManager.getEventsCount,
[ClusterIpcMessage.FEATURE_INSTALL]: clusterManager.installFeature,
[ClusterIpcMessage.FEATURE_UPGRADE]: clusterManager.upgradeFeature,
[ClusterIpcMessage.FEATURE_REMOVE]: clusterManager.uninstallFeature,
[ClusterIpcMessage.ICON_SAVE]: clusterManager.uploadClusterIcon,
[ClusterIpcMessage.ICON_RESET]: clusterManager.removeCluster,
};
Object.entries(handlers).forEach(([key, handler]) => {
handlers[key as keyof typeof handlers] = handler.bind(clusterManager);
})
onMessages(handlers, {
timeout: 2000,
})
}

View File

@ -1,13 +1,13 @@
import { observable } from "mobx";
import type { ClusterId, ClusterModel, ClusterPreferences } from "../common/cluster-store"
import type { FeatureStatusMap } from "./feature"
import { observable, toJS } from "mobx";
import { apiPrefix } from "../common/vars";
import { ContextHandler } from "./context-handler"
import { FeatureStatusMap } from "./feature"
import { AuthorizationV1Api, CoreV1Api, KubeConfig, V1ResourceAttributes } from "@kubernetes/client-node"
import { Kubectl } from "./kubectl";
import { KubeconfigManager } from "./kubeconfig-manager"
import { getNodeWarningConditions, loadConfig, podHasIssues } from "./k8s"
import { getFeatures, installFeature, uninstallFeature, upgradeFeature } from "./feature-manager";
import type { ClusterId, ClusterModel, ClusterPreferences } from "../common/cluster-store"
import request from "request-promise-native"
import logger from "./logger"
@ -17,30 +17,43 @@ enum ClusterStatus {
Offline = 0
}
export interface ClusterState extends ClusterModel {
url: string;
apiUrl: string;
online?: boolean;
accessible?: boolean;
failureReason?: string;
nodes?: number;
eventCount?: number;
version?: string;
distribution?: string;
isAdmin?: boolean;
features?: FeatureStatusMap;
}
export class Cluster implements ClusterModel {
public contextHandler: ContextHandler;
public kubeCtl: Kubectl
protected kubeconfigManager: KubeconfigManager;
@observable initialized = false;
@observable id: ClusterId;
@observable workspace: string;
@observable kubeConfigPath: string;
@observable contextName: string;
@observable url: string;
@observable port: number;
@observable apiUrl: string;
@observable online: boolean;
@observable accessible: boolean;
@observable failureReason: string;
@observable nodes: number;
@observable version: string;
@observable distribution: string;
@observable isAdmin: boolean;
@observable eventCount: number;
@observable preferences: ClusterPreferences = {};
public contextHandler: ContextHandler;
public url: string;
public port: number;
public apiUrl: string;
public online: boolean;
public accessible: boolean;
public failureReason: string;
public nodes: number;
public version: string;
public distribution: string;
public isAdmin: boolean;
public eventCount: number;
public kubeCtl: Kubectl
public features: FeatureStatusMap = {};
protected kubeconfigManager: KubeconfigManager;
@observable features: FeatureStatusMap = {};
constructor(model: ClusterModel) {
Object.assign(this, model)
@ -50,7 +63,7 @@ export class Cluster implements ClusterModel {
const { contextName } = this
try {
const kubeConfig = loadConfig(this.kubeConfigPath)
kubeConfig.setCurrentContext(contextName); // fixme: is it needed at all?
kubeConfig.setCurrentContext(contextName); // fixme: is it required, when if so?
this.port = port;
this.apiUrl = kubeConfig.getCurrentCluster().server
this.contextHandler = new ContextHandler(kubeConfig, this)
@ -238,12 +251,34 @@ export class Cluster implements ClusterModel {
}
toJSON(): ClusterModel {
return {
return toJS({
id: this.id,
contextName: this.contextName,
kubeConfigPath: this.kubeConfigPath,
workspace: this.workspace,
preferences: this.preferences,
}
}, {
recurseEverything: true
})
}
getState(): ClusterState {
const storeModel = this.toJSON();
return toJS({
...storeModel,
url: this.url,
apiUrl: this.apiUrl,
online: this.online,
accessible: this.accessible,
failureReason: this.failureReason,
nodes: this.nodes,
version: this.version,
distribution: this.distribution,
isAdmin: this.isAdmin,
features: this.features,
eventCount: this.eventCount,
}, {
recurseEverything: true
})
}
}

View File

@ -3,30 +3,22 @@ import path from "path"
import hb from "handlebars"
import { ResourceApplier } from "./resource-applier"
import { KubeConfig, CoreV1Api, Watch } from "@kubernetes/client-node"
import logger from "./logger";
import { Cluster } from "./cluster";
import logger from "./logger";
export type FeatureInstallRequest = {
export type FeatureStatusMap = Record<string, FeatureStatus>
export interface FeatureInstallRequest {
clusterId: string;
name: string;
config?: any;
}
export type FeatureInstallResponse = {
success: boolean;
message: string;
}
export type FeatureStatus = {
export interface FeatureStatus {
currentVersion: string;
installed: boolean;
latestVersion: string;
canUpgrade: boolean;
// TODO We need bunch of other stuff too: upgradeable, latestVersion, ...
};
export type FeatureStatusMap = {
[name: string]: FeatureStatus;
}
export abstract class Feature {
@ -35,9 +27,7 @@ export abstract class Feature {
latestVersion: string;
constructor(config: any) {
if(config) {
this.config = config;
}
if(config) this.config = config;
}
// TODO Return types for these?

View File

@ -2,12 +2,10 @@
import "../common/system-ca"
import "../common/prometheus-providers"
import { app, dialog, protocol } from "electron"
import { appName, isDevelopment, isMac } from "../common/vars";
import { PromiseIpc } from "electron-promise-ipc"
import { app, dialog } from "electron"
import { appName, appProto, isDevelopment, isMac, staticDir, staticProto } from "../common/vars";
import path from "path"
import { format as formatUrl } from "url"
import logger from "./logger"
import initMenu from "./menu"
import * as proxy from "./proxy"
import { WindowManager } from "./window-manager";
@ -16,25 +14,13 @@ import AppUpdater from "./app-updater"
import { shellSync } from "./shell-sync"
import { getFreePort } from "./port"
import { mangleProxyEnv } from "./proxy-env"
import { findMainWebContents } from "./webcontents"
import { registerStaticProtocol } from "../common/register-static";
import { registerFileProtocol } from "../common/register-protocol";
import { clusterStore } from "../common/cluster-store"
import { userStore } from "../common/user-store";
import { workspaceStore } from "../common/workspace-store";
import { tracker } from "../common/tracker";
import logger from "./logger"
if (isDevelopment) {
const appName = "LensDev";
const appData = app.getPath("appData");
app.setName(appName);
app.setPath("userData", path.join(appData, appName));
}
mangleProxyEnv()
if (app.commandLine.getSwitchValue("proxy-server") !== "") {
process.env.HTTPS_PROXY = app.commandLine.getSwitchValue("proxy-server")
}
const promiseIpc = new PromiseIpc({ timeout: 2000 })
let windowManager: WindowManager = null;
let clusterManager: ClusterManager = null;
let proxyServer: proxy.LensProxy = null;
@ -45,24 +31,30 @@ const vmURL = formatUrl({
slashes: true,
})
async function main() {
shellSync(app.getLocale())
mangleProxyEnv()
if (app.commandLine.getSwitchValue("proxy-server") !== "") {
process.env.HTTPS_PROXY = app.commandLine.getSwitchValue("proxy-server")
}
async function main() {
shellSync(app.getLocale());
// todo: check other usages .getPath("userData") and enable "lazy-evaluation"
const workingDir = path.join(app.getPath("appData"), appName);
app.setName(appName);
app.setPath("userData", workingDir);
logger.info(`Start app from "${workingDir}"`)
tracker.event("app", "start");
const updater = new AppUpdater()
updater.start();
tracker.event("app", "start");
initMenu();
registerFileProtocol(appProto, app.getPath("userData"));
registerFileProtocol(staticProto, staticDir);
registerStaticProtocol();
protocol.registerFileProtocol('store', (request, callback) => {
const url = request.url.substr(8)
callback(path.normalize(`${app.getPath("userData")}/${url}`))
}, (error) => {
if (error) console.error('Failed to register protocol')
})
let port: number = null
// find free port
let port: number
try {
port = await getFreePort()
} catch (error) {
@ -71,10 +63,11 @@ async function main() {
app.quit();
}
// preload required stores
// preload configuration from stores
await Promise.all([
userStore.load(),
clusterStore.load(),
workspaceStore.load(),
]);
// create cluster manager
@ -89,39 +82,9 @@ async function main() {
app.quit();
}
// boot windowmanager
// manage lens windows
windowManager = new WindowManager();
windowManager.showMain(vmURL)
initMenu({
logoutHook: async () => {
// IPC send needs webContents as we're sending it to renderer
promiseIpc.send('logout', findMainWebContents(), {}).then((data: any) => {
logger.debug("logout IPC sent");
})
},
showPreferencesHook: async () => {
// IPC send needs webContents as we're sending it to renderer
promiseIpc.send('navigate', findMainWebContents(), { name: 'preferences-page' }).then((data: any) => {
logger.debug("navigate: preferences IPC sent");
})
},
addClusterHook: async () => {
promiseIpc.send('navigate', findMainWebContents(), { name: "add-cluster-page" }).then((data: any) => {
logger.debug("navigate: add-cluster-page IPC sent");
})
},
showWhatsNewHook: async () => {
promiseIpc.send('navigate', findMainWebContents(), { name: "whats-new-page" }).then((data: any) => {
logger.debug("navigate: whats-new-page IPC sent");
})
},
clusterSettingsHook: async () => {
promiseIpc.send('navigate', findMainWebContents(), { name: "cluster-settings-page" }).then((data: any) => {
logger.debug("navigate: cluster-settings-page IPC sent");
})
},
}, promiseIpc)
}
app.on("ready", main)

View File

@ -1,5 +1,6 @@
import { PromiseIpc } from "electron-promise-ipc";
import { app, BrowserWindow, dialog, Menu, MenuItem, MenuItemConstructorOptions, shell, webContents } from "electron"
import { isDevelopment, isMac, issuesTrackerUrl, isWindows, slackUrl } from "../common/vars";
import { isMac, issuesTrackerUrl, isWindows, slackUrl } from "../common/vars";
// todo: refactor + split menu sections to separated files, e.g. menus/file.menu.ts
@ -38,10 +39,8 @@ function showAbout(_menuitem: MenuItem, browserWindow: BrowserWindow) {
/**
* Constructs the menu based on the example at: https://electronjs.org/docs/api/menu#main-process
* Menu items are constructed piece-by-piece to have slightly better control on individual sub-menus
*
* @param ipc the main promiceIpc handle. Needed to be able to hook IPC sending into logout click handler.
*/
export default function initMenu(opts: MenuOptions, promiseIpc: any) {
export default function initMenu(opts: Partial<MenuOptions> = {}) {
const mt: MenuItemConstructorOptions[] = [];
const macAppMenu: MenuItemConstructorOptions = {
label: app.getName(),
@ -199,6 +198,8 @@ export default function initMenu(opts: MenuOptions, promiseIpc: any) {
const menu = Menu.buildFromTemplate(mt);
Menu.setApplicationMenu(menu);
const promiseIpc = new PromiseIpc({ timeout: 2000 })
promiseIpc.on("enableClusterSettingsMenuItem", (clusterId: string) => {
setClusterSettingsEnabled(true)
});

View File

@ -1,17 +1,13 @@
import { BrowserWindow, shell } from "electron"
import { PromiseIpc } from "electron-promise-ipc"
import windowStateKeeper from "electron-window-state"
import { getStaticUrl } from "../common/register-static";
import { tracker } from "../common/tracker";
export class WindowManager {
public mainWindow: BrowserWindow = null;
public splashWindow: BrowserWindow = null;
protected promiseIpc: any
protected windowState: windowStateKeeper.State;
constructor({ showSplash = true } = {}) {
this.promiseIpc = new PromiseIpc({ timeout: 2000 })
// Manage main window size&position with persistence
this.windowState = windowStateKeeper({
defaultHeight: 900,
@ -31,7 +27,7 @@ export class WindowManager {
}
})
if (showSplash) {
this.splashWindow.loadURL(getStaticUrl("splash.html"))
this.splashWindow.loadURL("static://splash.html")
this.splashWindow.show()
}

View File

@ -29,14 +29,14 @@
<script>
import marked from 'marked'
import {readFileSync} from 'fs'
import { getStaticPath } from "../../../common/register-static"
import { userStore } from "../../../common/user-store"
export default {
name: 'WhatsNewPage',
data() {
let releaseNotes = getStaticPath("RELEASE_NOTES.md");
let content = marked(readFileSync(releaseNotes, 'utf8'));
// todo: check if "fs" can work with custom protocols
let fileContents = readFileSync("static://RELEASE_NOTES.md", 'utf8');
let content = marked(fileContents);
return {
error: "",
content: content,

View File

@ -2,14 +2,14 @@
@import "fonts";
*, *:before, *:after {
@include set-draggable(false);
box-sizing: border-box;
padding: 0;
margin: 0;
border: 0;
outline: none;
-webkit-font-smoothing: antialiased;
-webkit-user-drag: none;
-webkit-app-region: no-drag;
}
::selection {
@ -167,14 +167,6 @@ a {
}
}
.drag {
-webkit-app-region: drag;
}
.nodrag {
-webkit-app-region: no-drag;
}
// app's common loading indicator, displaying on the route transitions
#loading {
position: absolute;

View File

@ -89,3 +89,13 @@
text-decoration: underline;
cursor: pointer;
}
@mixin set-draggable($isDraggable: true) {
@if ($isDraggable) {
-webkit-user-drag: auto;
-webkit-app-region: drag;
} @else {
-webkit-user-drag: none;
-webkit-app-region: no-drag;
}
}

View File

@ -1,14 +0,0 @@
import "../../common/system-ca"
import { userStore } from "../common/user-store";
import { workspaceStore } from "../common/workspace-store";
import { clusterStore } from "../common/cluster-store";
// import { App } from "./components/app";
await Promise.all([
userStore.whenLoaded,
workspaceStore.whenLoaded,
clusterStore.whenLoaded,
]);
// App.init();
document.getElementById("app").innerHTML = "<p>Hello from renderer!</p>"

19
src/renderer/index.tsx Normal file
View File

@ -0,0 +1,19 @@
import "../common/system-ca"
import { userStore } from "../common/user-store";
import { workspaceStore } from "../common/workspace-store";
import { clusterStore } from "../common/cluster-store";
// import { App } from "./components/app";
async function render() {
await Promise.all([
userStore.whenLoaded,
workspaceStore.whenLoaded,
clusterStore.whenLoaded,
]);
// App.init();
document.getElementById("app").innerHTML = "<p>Hello from renderer!</p>"
}
// run
render();

View File

@ -10,7 +10,7 @@ import CircularDependencyPlugin from "circular-dependency-plugin"
export default function (): webpack.Configuration {
return {
context: __dirname,
target: "web",
target: "electron-renderer",
devtool: "source-map", // todo: optimize in dev-mode with webpack.SourceMapDevToolPlugin
mode: isProduction ? "production" : "development",
cache: isDevelopment,
@ -47,6 +47,10 @@ export default function (): webpack.Configuration {
module: {
rules: [
{
test: /\.node$/,
use: "node-loader"
},
{
test: /\.tsx?$/,
exclude: /node_modules/,

187
yarn.lock
View File

@ -1914,11 +1914,6 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.24.tgz#c57511e3a19c4b5e9692bb2995c40a3a52167944"
integrity sha512-5SCfvCxV74kzR3uWgTYiGxrd69TbT1I6+cMx1A5kEly/IVveJBimtAMlXiEyVFn5DvUFewQWxOOiJhlxeQwxgA==
"@types/node@^10.12.18":
version "10.17.26"
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.26.tgz#a8a119960bff16b823be4c617da028570779bcfd"
integrity sha512-myMwkO2Cr82kirHY8uknNRHEVtn0wV3DTQfkrjx17jmkstDRZ24gNUdl8AHXVyVclTYI/bNjgTPTAWvWLqXqkw==
"@types/node@^12.0.12":
version "12.12.44"
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.44.tgz#0d400a1453adcb359b133acceae4dd8bb0e0a159"
@ -3396,7 +3391,7 @@ caseless@~0.12.0:
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
chalk@2.4.2, chalk@^2.0.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2:
chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@ -3590,7 +3585,7 @@ cli-cursor@^3.1.0:
dependencies:
restore-cursor "^3.1.0"
cli-spinners@^2.2.0:
cli-spinners@^2.0.0, cli-spinners@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.3.0.tgz#0632239a4b5aa4c958610142c34bb7a651fc8df5"
integrity sha512-Xs2Hf2nzrvJMFKimOR7YR0QwZ8fc0u98kdtwN1eNAZzNQgH3vK2pXzff6GJtKh7S5hoJ87ECiAiZFS2fb5Ii2w==
@ -3732,7 +3727,7 @@ colors@1.0.3:
resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b"
integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=
colors@^1.2.1:
colors@^1.2.1, colors@^1.3.3:
version "1.4.0"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==
@ -4170,7 +4165,7 @@ debug@4.1.0:
dependencies:
ms "^2.1.1"
debug@^2.1.3, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9:
debug@^2.1.3, debug@^2.2.0, debug@^2.3.3, debug@^2.5.1, debug@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
@ -4309,6 +4304,11 @@ detect-file@^1.0.0:
resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7"
integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=
detect-libc@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=
detect-newline@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
@ -4537,7 +4537,7 @@ electron-chromedriver@^6.0.0:
electron-download "^4.1.1"
extract-zip "^1.6.7"
electron-download@^4.1.0, electron-download@^4.1.1:
electron-download@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/electron-download/-/electron-download-4.1.1.tgz#02e69556705cc456e520f9e035556ed5a015ebe8"
integrity sha512-FjEWG9Jb/ppK/2zToP+U5dds114fM1ZOJqMAR4aXXL5CvyPE9fiqBK/9YcwC9poIFQTEJk/EM/zyRwziziRZrg==
@ -4584,6 +4584,21 @@ electron-publish@22.7.0:
lazy-val "^1.0.4"
mime "^2.4.5"
electron-rebuild@^1.11.0:
version "1.11.0"
resolved "https://registry.yarnpkg.com/electron-rebuild/-/electron-rebuild-1.11.0.tgz#e384773a9ad30fe0a6a5bbb326b779d51f668b6a"
integrity sha512-cn6AqZBQBVtaEyj5jZW1/LOezZZ22PA1HvhEP7asvYPJ8PDF4i4UFt9be4i9T7xJKiSiomXvY5Fd+dSq3FXZxA==
dependencies:
colors "^1.3.3"
debug "^4.1.1"
detect-libc "^1.0.3"
fs-extra "^8.1.0"
node-abi "^2.11.0"
node-gyp "^6.0.1"
ora "^3.4.0"
spawn-rx "^3.0.0"
yargs "^14.2.0"
electron-to-chromium@^1.3.413:
version "1.3.464"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.464.tgz#fe13feaa08f6f865d3c89d5d72e54c194f463aa5"
@ -4619,13 +4634,13 @@ electron@*:
"@types/node" "^12.0.12"
extract-zip "^1.0.3"
electron@^6.1.12:
version "6.1.12"
resolved "https://registry.yarnpkg.com/electron/-/electron-6.1.12.tgz#a7aee6dfa75b57f32b3645ef8e14dcef6d5f31a9"
integrity sha512-RUPM8xJfTcm53V9EKMBhvpLu1+CQkmuvWDmVCypR5XbUG1OOrOLiKl0CqUZ9+tEDuOmC+DmzmJP2MZXScBU5IA==
electron@^7.3.2:
version "7.3.2"
resolved "https://registry.yarnpkg.com/electron/-/electron-7.3.2.tgz#184b69fe9089693e179b3b34effa975dfc8e505d"
integrity sha512-5uSWVfCJogiPiU0G+RKi4ECnNs0gPNjAwYVE9KR7RXaOJYcpNIC5RFejaaUnuRoBssJ5B1n/5WU6wDUxvPajWQ==
dependencies:
"@types/node" "^10.12.18"
electron-download "^4.1.0"
"@electron/get" "^1.0.1"
"@types/node" "^12.0.12"
extract-zip "^1.0.3"
elliptic@^6.0.0, elliptic@^6.5.2:
@ -5412,6 +5427,13 @@ fs-extra@^9.0.0, fs-extra@^9.0.1:
jsonfile "^6.0.1"
universalify "^1.0.0"
fs-minipass@^1.2.5:
version "1.2.7"
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7"
integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==
dependencies:
minipass "^2.6.0"
fs-minipass@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb"
@ -5710,7 +5732,7 @@ got@^9.6.0:
to-readable-stream "^1.0.0"
url-parse-lax "^3.0.0"
graceful-fs@^4.1.0, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4:
graceful-fs@^4.1.0, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4:
version "4.2.4"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
@ -7458,6 +7480,11 @@ locate-path@^5.0.0:
dependencies:
p-locate "^4.1.0"
lodash.assign@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7"
integrity sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=
lodash.isequal@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
@ -7478,6 +7505,13 @@ 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"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
log-symbols@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a"
integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==
dependencies:
chalk "^2.0.1"
log-symbols@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4"
@ -7855,6 +7889,14 @@ minipass-pipeline@^1.2.2:
dependencies:
minipass "^3.0.0"
minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0:
version "2.9.0"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6"
integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==
dependencies:
safe-buffer "^5.1.2"
yallist "^3.0.0"
minipass@^3.0.0, minipass@^3.1.1:
version "3.1.3"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd"
@ -7862,6 +7904,13 @@ minipass@^3.0.0, minipass@^3.1.1:
dependencies:
yallist "^4.0.0"
minizlib@^1.2.1:
version "1.3.3"
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d"
integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==
dependencies:
minipass "^2.9.0"
minizlib@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.0.tgz#fd52c645301ef09a63a2c209697c294c6ce02cf3"
@ -8019,6 +8068,13 @@ no-case@^3.0.3:
lower-case "^2.0.1"
tslib "^1.10.0"
node-abi@^2.11.0:
version "2.18.0"
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-2.18.0.tgz#1f5486cfd7d38bd4f5392fa44a4ad4d9a0dffbf4"
integrity sha512-yi05ZoiuNNEbyT/xXfSySZE+yVnQW6fxPZuFbLyS1s6b5Kw3HzV2PHOM4XR+nsjzkHxByK+2Wg+yCQbe35l8dw==
dependencies:
semver "^5.4.1"
node-forge@^0.7.5:
version "0.7.6"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.6.tgz#fdf3b418aee1f94f0ef642cd63486c77ca9724ac"
@ -8047,6 +8103,23 @@ node-gyp@^3.8.0:
tar "^2.0.0"
which "1"
node-gyp@^6.0.1:
version "6.1.0"
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-6.1.0.tgz#64e31c61a4695ad304c1d5b82cf6b7c79cc79f3f"
integrity sha512-h4A2zDlOujeeaaTx06r4Vy+8MZ1679lU+wbCKDS4ZtvY2A37DESo37oejIw0mtmR3+rvNwts5B6Kpt1KrNYdNw==
dependencies:
env-paths "^2.2.0"
glob "^7.1.4"
graceful-fs "^4.2.2"
mkdirp "^0.5.1"
nopt "^4.0.1"
npmlog "^4.1.2"
request "^2.88.0"
rimraf "^2.6.3"
semver "^5.7.1"
tar "^4.4.12"
which "^1.3.1"
node-int64@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
@ -8182,6 +8255,14 @@ nodemon@^2.0.4:
dependencies:
abbrev "1"
nopt@^4.0.1:
version "4.0.3"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48"
integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==
dependencies:
abbrev "1"
osenv "^0.1.4"
nopt@~1.0.10:
version "1.0.10"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee"
@ -8262,7 +8343,7 @@ npm-run-path@^4.0.0:
dependencies:
path-key "^3.0.0"
"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0:
"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
@ -8485,6 +8566,18 @@ optionator@^0.9.1:
type-check "^0.4.0"
word-wrap "^1.2.3"
ora@^3.4.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318"
integrity sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==
dependencies:
chalk "^2.4.2"
cli-cursor "^2.1.0"
cli-spinners "^2.0.0"
log-symbols "^2.2.0"
strip-ansi "^5.2.0"
wcwidth "^1.0.1"
ora@^4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/ora/-/ora-4.0.4.tgz#e8da697cc5b6a47266655bf68e0fb588d29a545d"
@ -8523,7 +8616,7 @@ os-tmpdir@^1.0.0, os-tmpdir@~1.0.2:
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
osenv@0:
osenv@0, osenv@^0.1.4:
version "0.1.5"
resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410"
integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==
@ -9859,6 +9952,13 @@ rx-lite@*, rx-lite@^4.0.8:
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444"
integrity sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=
rxjs@^6.3.1:
version "6.6.0"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.0.tgz#af2901eedf02e3a83ffa7f886240ff9018bbec84"
integrity sha512-3HMA8z/Oz61DUHe+SdOiQyzIf4tOx5oQHmMir7IZEu6TMqCLHT4LRcmNaUS0NwOz8VLvmmBduMsoaUvMaIiqzg==
dependencies:
tslib "^1.9.0"
rxjs@^6.5.2, rxjs@^6.5.3:
version "6.5.5"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec"
@ -10265,6 +10365,15 @@ spawn-command@^0.0.2-1:
resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0"
integrity sha1-YvXpRmmBwbeW3Fkpk34RycaSG9A=
spawn-rx@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/spawn-rx/-/spawn-rx-3.0.0.tgz#1d33511e13ec26337da51d78630e08beb57a6767"
integrity sha512-dw4Ryg/KMNfkKa5ezAR5aZe9wNwPdKlnHEXtHOjVnyEDSPQyOpIPPRtcIiu7127SmtHhaCjw21yC43HliW0iIg==
dependencies:
debug "^2.5.1"
lodash.assign "^4.2.0"
rxjs "^6.3.1"
spdx-correct@^3.0.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9"
@ -10716,6 +10825,19 @@ tar@^2.0.0:
fstream "^1.0.12"
inherits "2"
tar@^4.4.12:
version "4.4.13"
resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525"
integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==
dependencies:
chownr "^1.1.1"
fs-minipass "^1.2.5"
minipass "^2.8.6"
minizlib "^1.2.1"
mkdirp "^0.5.0"
safe-buffer "^5.1.2"
yallist "^3.0.3"
tar@^6.0.2:
version "6.0.2"
resolved "https://registry.yarnpkg.com/tar/-/tar-6.0.2.tgz#5df17813468a6264ff14f766886c622b84ae2f39"
@ -11791,7 +11913,7 @@ yallist@^2.1.2:
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=
yallist@^3.0.2:
yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3:
version "3.1.1"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
@ -11822,6 +11944,14 @@ yargs-parser@^13.1.0, yargs-parser@^13.1.2:
camelcase "^5.0.0"
decamelize "^1.2.0"
yargs-parser@^15.0.1:
version "15.0.1"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-15.0.1.tgz#54786af40b820dcb2fb8025b11b4d659d76323b3"
integrity sha512-0OAMV2mAZQrs3FkNpDQcBk1x5HXb8X4twADss4S0Iuk+2dGnLOE/fRHrsYm542GduMveyA77OF4wrNJuanRCWw==
dependencies:
camelcase "^5.0.0"
decamelize "^1.2.0"
yargs@13.2.4:
version "13.2.4"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.2.4.tgz#0b562b794016eb9651b98bd37acf364aa5d6dc83"
@ -11855,6 +11985,23 @@ yargs@^13.3.0, yargs@^13.3.2:
y18n "^4.0.0"
yargs-parser "^13.1.2"
yargs@^14.2.0:
version "14.2.3"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-14.2.3.tgz#1a1c3edced1afb2a2fea33604bc6d1d8d688a414"
integrity sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==
dependencies:
cliui "^5.0.0"
decamelize "^1.2.0"
find-up "^3.0.0"
get-caller-file "^2.0.1"
require-directory "^2.1.1"
require-main-filename "^2.0.0"
set-blocking "^2.0.0"
string-width "^3.0.0"
which-module "^2.0.0"
y18n "^4.0.0"
yargs-parser "^15.0.1"
yargs@^15.3.1:
version "15.3.1"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.1.tgz#9505b472763963e54afe60148ad27a330818e98b"