mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Merge remote-tracking branch 'origin/master' into feature/tray
# Conflicts: # locales/en/messages.po # locales/fi/messages.po # locales/ru/messages.po
This commit is contained in:
commit
b0d192309a
9
src/common/utils/debouncePromise.ts
Executable file
9
src/common/utils/debouncePromise.ts
Executable file
@ -0,0 +1,9 @@
|
|||||||
|
// Debouncing promise evaluation
|
||||||
|
|
||||||
|
export function debouncePromise<T, F extends any[]>(func: (...args: F) => T | Promise<T>, timeout = 0): (...args: F) => Promise<T> {
|
||||||
|
let timer: NodeJS.Timeout;
|
||||||
|
return (...params: any[]) => new Promise((resolve, reject) => {
|
||||||
|
clearTimeout(timer);
|
||||||
|
timer = setTimeout(() => resolve(func.apply(this, params)), timeout);
|
||||||
|
});
|
||||||
|
}
|
||||||
@ -1,7 +1,14 @@
|
|||||||
// Common utils (main/renderer)
|
// Common utils (main OR renderer)
|
||||||
|
|
||||||
|
export * from "./app-version"
|
||||||
|
export * from "./autobind"
|
||||||
export * from "./base64"
|
export * from "./base64"
|
||||||
export * from "./camelCase"
|
export * from "./camelCase"
|
||||||
export * from "./splitArray"
|
export * from "./cloneJson"
|
||||||
export * from "./getRandId"
|
export * from "./debouncePromise"
|
||||||
|
export * from "./defineGlobal"
|
||||||
|
export * from "./getRandId"
|
||||||
|
export * from "./splitArray"
|
||||||
|
export * from "./saveToAppFiles"
|
||||||
|
export * from "./singleton"
|
||||||
export * from "./cloneJson"
|
export * from "./cloneJson"
|
||||||
|
|||||||
@ -32,7 +32,7 @@ import { KubeAuthProxy } from "../kube-auth-proxy"
|
|||||||
import { getFreePort } from "../port"
|
import { getFreePort } from "../port"
|
||||||
import { broadcastIpc } from "../../common/ipc"
|
import { broadcastIpc } from "../../common/ipc"
|
||||||
import { ChildProcess, spawn, SpawnOptions } from "child_process"
|
import { ChildProcess, spawn, SpawnOptions } from "child_process"
|
||||||
import { Kubectl } from "../kubectl"
|
import { bundledKubectlPath, Kubectl } from "../kubectl"
|
||||||
import { mock, MockProxy } from 'jest-mock-extended';
|
import { mock, MockProxy } from 'jest-mock-extended';
|
||||||
import { waitUntilUsed } from 'tcp-port-used';
|
import { waitUntilUsed } from 'tcp-port-used';
|
||||||
import { Readable } from "stream"
|
import { Readable } from "stream"
|
||||||
@ -81,7 +81,7 @@ describe("kube auth proxy tests", () => {
|
|||||||
return mockedCP.stdout
|
return mockedCP.stdout
|
||||||
})
|
})
|
||||||
mockSpawn.mockImplementationOnce((command: string, args: readonly string[], options: SpawnOptions): ChildProcess => {
|
mockSpawn.mockImplementationOnce((command: string, args: readonly string[], options: SpawnOptions): ChildProcess => {
|
||||||
expect(command).toBe(Kubectl.bundledKubectlPath)
|
expect(command).toBe(bundledKubectlPath())
|
||||||
return mockedCP
|
return mockedCP
|
||||||
})
|
})
|
||||||
mockWaitUntilUsed.mockReturnValueOnce(Promise.resolve())
|
mockWaitUntilUsed.mockReturnValueOnce(Promise.resolve())
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { ChildProcess, spawn } from "child_process"
|
|||||||
import { waitUntilUsed } from "tcp-port-used";
|
import { waitUntilUsed } from "tcp-port-used";
|
||||||
import { broadcastIpc } from "../common/ipc";
|
import { broadcastIpc } from "../common/ipc";
|
||||||
import type { Cluster } from "./cluster"
|
import type { Cluster } from "./cluster"
|
||||||
import { bundledKubectl, Kubectl } from "./kubectl"
|
import { Kubectl } from "./kubectl"
|
||||||
import logger from "./logger"
|
import logger from "./logger"
|
||||||
|
|
||||||
export interface KubeAuthProxyLog {
|
export interface KubeAuthProxyLog {
|
||||||
@ -23,7 +23,7 @@ export class KubeAuthProxy {
|
|||||||
this.env = env
|
this.env = env
|
||||||
this.port = port
|
this.port = port
|
||||||
this.cluster = cluster
|
this.cluster = cluster
|
||||||
this.kubectl = bundledKubectl
|
this.kubectl = Kubectl.bundled()
|
||||||
}
|
}
|
||||||
|
|
||||||
public async run(): Promise<void> {
|
public async run(): Promise<void> {
|
||||||
|
|||||||
@ -36,15 +36,21 @@ const packageMirrors: Map<string, string> = new Map([
|
|||||||
let bundledPath: string
|
let bundledPath: string
|
||||||
const initScriptVersionString = "# lens-initscript v3\n"
|
const initScriptVersionString = "# lens-initscript v3\n"
|
||||||
|
|
||||||
if (isDevelopment || isTestEnv) {
|
export function bundledKubectlPath(): string {
|
||||||
|
if (bundledPath) { return bundledPath }
|
||||||
|
|
||||||
|
if (isDevelopment || isTestEnv) {
|
||||||
const platformName = isWindows ? "windows" : process.platform
|
const platformName = isWindows ? "windows" : process.platform
|
||||||
bundledPath = path.join(process.cwd(), "binaries", "client", platformName, process.arch, "kubectl")
|
bundledPath = path.join(process.cwd(), "binaries", "client", platformName, process.arch, "kubectl")
|
||||||
} else {
|
} else {
|
||||||
bundledPath = path.join(process.resourcesPath, process.arch, "kubectl")
|
bundledPath = path.join(process.resourcesPath, process.arch, "kubectl")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isWindows) {
|
if (isWindows) {
|
||||||
bundledPath = `${bundledPath}.exe`
|
bundledPath = `${bundledPath}.exe`
|
||||||
|
}
|
||||||
|
|
||||||
|
return bundledPath
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Kubectl {
|
export class Kubectl {
|
||||||
@ -58,7 +64,6 @@ export class Kubectl {
|
|||||||
return path.join((app || remote.app).getPath("userData"), "binaries", "kubectl")
|
return path.join((app || remote.app).getPath("userData"), "binaries", "kubectl")
|
||||||
}
|
}
|
||||||
|
|
||||||
public static readonly bundledKubectlPath = bundledPath
|
|
||||||
public static readonly bundledKubectlVersion: string = bundledVersion
|
public static readonly bundledKubectlVersion: string = bundledVersion
|
||||||
public static invalidBundle = false
|
public static invalidBundle = false
|
||||||
private static bundledInstance: Kubectl;
|
private static bundledInstance: Kubectl;
|
||||||
@ -102,7 +107,7 @@ export class Kubectl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public getBundledPath() {
|
public getBundledPath() {
|
||||||
return Kubectl.bundledKubectlPath
|
return bundledKubectlPath()
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPathFromPreferences() {
|
public getPathFromPreferences() {
|
||||||
@ -125,19 +130,19 @@ export class Kubectl {
|
|||||||
// return binary name if bundled path is not functional
|
// return binary name if bundled path is not functional
|
||||||
if (!await this.checkBinary(this.getBundledPath(), false)) {
|
if (!await this.checkBinary(this.getBundledPath(), false)) {
|
||||||
Kubectl.invalidBundle = true
|
Kubectl.invalidBundle = true
|
||||||
return path.basename(bundledPath)
|
return path.basename(this.getBundledPath())
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!await this.ensureKubectl()) {
|
if (!await this.ensureKubectl()) {
|
||||||
logger.error("Failed to ensure kubectl, fallback to the bundled version")
|
logger.error("Failed to ensure kubectl, fallback to the bundled version")
|
||||||
return Kubectl.bundledKubectlPath
|
return this.getBundledPath()
|
||||||
}
|
}
|
||||||
return this.path
|
return this.path
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error("Failed to ensure kubectl, fallback to the bundled version")
|
logger.error("Failed to ensure kubectl, fallback to the bundled version")
|
||||||
logger.error(err)
|
logger.error(err)
|
||||||
return Kubectl.bundledKubectlPath
|
return this.getBundledPath()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,7 +188,7 @@ export class Kubectl {
|
|||||||
try {
|
try {
|
||||||
const exist = await pathExists(this.path)
|
const exist = await pathExists(this.path)
|
||||||
if (!exist) {
|
if (!exist) {
|
||||||
await fs.promises.copyFile(Kubectl.bundledKubectlPath, this.path)
|
await fs.promises.copyFile(this.getBundledPath(), this.path)
|
||||||
await fs.promises.chmod(this.path, 0o755)
|
await fs.promises.chmod(this.path, 0o755)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@ -332,6 +337,3 @@ export class Kubectl {
|
|||||||
return packageMirrors.get("default") // MacOS packages are only available from default
|
return packageMirrors.get("default") // MacOS packages are only available from default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const bundledKubectl = Kubectl.bundled()
|
|
||||||
export { bundledKubectl }
|
|
||||||
|
|||||||
@ -1,20 +1,20 @@
|
|||||||
import packageInfo from "../../package.json"
|
import packageInfo from "../../package.json"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { bundledKubectl, Kubectl } from "../../src/main/kubectl";
|
import { Kubectl } from "../../src/main/kubectl";
|
||||||
import { isWindows } from "../common/vars";
|
import { isWindows } from "../common/vars";
|
||||||
|
|
||||||
jest.mock("../common/user-store");
|
jest.mock("../common/user-store");
|
||||||
|
|
||||||
describe("kubectlVersion", () => {
|
describe("kubectlVersion", () => {
|
||||||
it("returns bundled version if exactly same version used", async () => {
|
it("returns bundled version if exactly same version used", async () => {
|
||||||
const kubectl = new Kubectl(bundledKubectl.kubectlVersion)
|
const kubectl = new Kubectl(Kubectl.bundled().kubectlVersion)
|
||||||
expect(kubectl.kubectlVersion).toBe(bundledKubectl.kubectlVersion)
|
expect(kubectl.kubectlVersion).toBe(Kubectl.bundled().kubectlVersion)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("returns bundled version if same major.minor version is used", async () => {
|
it("returns bundled version if same major.minor version is used", async () => {
|
||||||
const { bundledKubectlVersion } = packageInfo.config;
|
const { bundledKubectlVersion } = packageInfo.config;
|
||||||
const kubectl = new Kubectl(bundledKubectlVersion);
|
const kubectl = new Kubectl(bundledKubectlVersion);
|
||||||
expect(kubectl.kubectlVersion).toBe(bundledKubectl.kubectlVersion)
|
expect(kubectl.kubectlVersion).toBe(Kubectl.bundled().kubectlVersion)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { LensApiRequest } from "../router"
|
import { LensApiRequest } from "../router"
|
||||||
import { LensApi } from "../lens-api"
|
import { LensApi } from "../lens-api"
|
||||||
import { spawn, ChildProcessWithoutNullStreams } from "child_process"
|
import { spawn, ChildProcessWithoutNullStreams } from "child_process"
|
||||||
import { bundledKubectl } from "../kubectl"
|
import { Kubectl } from "../kubectl"
|
||||||
import { getFreePort } from "../port"
|
import { getFreePort } from "../port"
|
||||||
import { shell } from "electron"
|
import { shell } from "electron"
|
||||||
import * as tcpPortUsed from "tcp-port-used"
|
import * as tcpPortUsed from "tcp-port-used"
|
||||||
@ -37,7 +37,7 @@ class PortForward {
|
|||||||
|
|
||||||
public async start() {
|
public async start() {
|
||||||
this.localPort = await getFreePort()
|
this.localPort = await getFreePort()
|
||||||
const kubectlBin = await bundledKubectl.getPath()
|
const kubectlBin = await Kubectl.bundled().getPath()
|
||||||
const args = [
|
const args = [
|
||||||
"--kubeconfig", this.kubeConfig,
|
"--kubeconfig", this.kubeConfig,
|
||||||
"port-forward",
|
"port-forward",
|
||||||
|
|||||||
@ -38,7 +38,7 @@ export class ShellSession extends EventEmitter {
|
|||||||
|
|
||||||
public async open() {
|
public async open() {
|
||||||
this.kubectlBinDir = await this.kubectl.binDir()
|
this.kubectlBinDir = await this.kubectl.binDir()
|
||||||
const pathFromPreferences = userStore.preferences.kubectlBinariesPath || Kubectl.bundledKubectlPath
|
const pathFromPreferences = userStore.preferences.kubectlBinariesPath || this.kubectl.getBundledPath()
|
||||||
this.kubectlPathDir = userStore.preferences.downloadKubectlBinaries ? this.kubectlBinDir : path.dirname(pathFromPreferences)
|
this.kubectlPathDir = userStore.preferences.downloadKubectlBinaries ? this.kubectlBinDir : path.dirname(pathFromPreferences)
|
||||||
this.helmBinDir = helmCli.getBinaryDir()
|
this.helmBinDir = helmCli.getBinaryDir()
|
||||||
const env = await this.getCachedShellEnv()
|
const env = await this.getCachedShellEnv()
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import type { KubeObjectDetailsProps, KubeObjectListLayoutProps, KubeObjectMenuP
|
|||||||
import type React from "react";
|
import type React from "react";
|
||||||
|
|
||||||
import { observable } from "mobx";
|
import { observable } from "mobx";
|
||||||
import { autobind } from "../utils/autobind";
|
import { autobind } from "../utils";
|
||||||
import { KubeApi } from "./kube-api";
|
import { KubeApi } from "./kube-api";
|
||||||
|
|
||||||
export interface ApiComponents {
|
export interface ApiComponents {
|
||||||
|
|||||||
@ -42,12 +42,14 @@ export interface IPodMetrics<T = IMetrics> {
|
|||||||
networkTransmit: T;
|
networkTransmit: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reference: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#read-log-pod-v1-core
|
||||||
export interface IPodLogsQuery {
|
export interface IPodLogsQuery {
|
||||||
container?: string;
|
container?: string;
|
||||||
tailLines?: number;
|
tailLines?: number;
|
||||||
timestamps?: boolean;
|
timestamps?: boolean;
|
||||||
sinceTime?: string; // Date.toISOString()-format
|
sinceTime?: string; // Date.toISOString()-format
|
||||||
follow?: boolean;
|
follow?: boolean;
|
||||||
|
previous?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum PodStatus {
|
export enum PodStatus {
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { Input } from '../input';
|
|||||||
import { SubTitle } from '../layout/sub-title';
|
import { SubTitle } from '../layout/sub-title';
|
||||||
import { UserPreferences, userStore } from '../../../common/user-store';
|
import { UserPreferences, userStore } from '../../../common/user-store';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import { Kubectl } from '../../../main/kubectl';
|
import { bundledKubectlPath } from '../../../main/kubectl';
|
||||||
import { SelectOption, Select } from '../select';
|
import { SelectOption, Select } from '../select';
|
||||||
|
|
||||||
export const KubectlBinaries = observer(({ preferences }: { preferences: UserPreferences }) => {
|
export const KubectlBinaries = observer(({ preferences }: { preferences: UserPreferences }) => {
|
||||||
@ -58,7 +58,7 @@ export const KubectlBinaries = observer(({ preferences }: { preferences: UserPre
|
|||||||
<SubTitle title="Path to Kubectl binary" />
|
<SubTitle title="Path to Kubectl binary" />
|
||||||
<Input
|
<Input
|
||||||
theme="round-black"
|
theme="round-black"
|
||||||
placeholder={Kubectl.bundledKubectlPath}
|
placeholder={bundledKubectlPath()}
|
||||||
value={binariesPath}
|
value={binariesPath}
|
||||||
validators={isPath}
|
validators={isPath}
|
||||||
onChange={setBinariesPath}
|
onChange={setBinariesPath}
|
||||||
|
|||||||
@ -50,6 +50,7 @@ export class PodMenu extends React.Component<Props> {
|
|||||||
initContainers: pod.getInitContainers(),
|
initContainers: pod.getInitContainers(),
|
||||||
selectedContainer: container,
|
selectedContainer: container,
|
||||||
showTimestamps: false,
|
showTimestamps: false,
|
||||||
|
previous: false,
|
||||||
tailLines: 1000
|
tailLines: 1000
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,7 @@ export interface IPodLogsData {
|
|||||||
initContainers: IPodContainer[]
|
initContainers: IPodContainer[]
|
||||||
showTimestamps: boolean
|
showTimestamps: boolean
|
||||||
tailLines: number
|
tailLines: number
|
||||||
|
previous: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
type TabId = string;
|
type TabId = string;
|
||||||
@ -48,7 +49,7 @@ export class PodLogsStore extends DockTabStore<IPodLogsData> {
|
|||||||
}
|
}
|
||||||
const data = this.getData(tabId);
|
const data = this.getData(tabId);
|
||||||
const { oldLogs, newLogs } = this.logs.get(tabId);
|
const { oldLogs, newLogs } = this.logs.get(tabId);
|
||||||
const { selectedContainer, tailLines } = data;
|
const { selectedContainer, tailLines, previous } = data;
|
||||||
const pod = new Pod(data.pod);
|
const pod = new Pod(data.pod);
|
||||||
try {
|
try {
|
||||||
// if logs already loaded, check the latest timestamp for getting updates only from this point
|
// if logs already loaded, check the latest timestamp for getting updates only from this point
|
||||||
@ -64,14 +65,15 @@ export class PodLogsStore extends DockTabStore<IPodLogsData> {
|
|||||||
sinceTime: lastLogDate.toISOString(),
|
sinceTime: lastLogDate.toISOString(),
|
||||||
timestamps: true, // Always setting timestampt to separate old logs from new ones
|
timestamps: true, // Always setting timestampt to separate old logs from new ones
|
||||||
container: selectedContainer.name,
|
container: selectedContainer.name,
|
||||||
tailLines: tailLines,
|
tailLines,
|
||||||
|
previous
|
||||||
});
|
});
|
||||||
if (!oldLogs) {
|
if (!oldLogs) {
|
||||||
this.logs.set(tabId, { oldLogs: loadedLogs, newLogs });
|
this.logs.set(tabId, { oldLogs: loadedLogs, newLogs });
|
||||||
} else {
|
} else {
|
||||||
this.logs.set(tabId, { oldLogs, newLogs: loadedLogs });
|
this.logs.set(tabId, { oldLogs, newLogs: loadedLogs });
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch ({error}) {
|
||||||
this.logs.set(tabId, {
|
this.logs.set(tabId, {
|
||||||
oldLogs: [
|
oldLogs: [
|
||||||
_i18n._(t`Failed to load logs: ${error.message}`),
|
_i18n._(t`Failed to load logs: ${error.message}`),
|
||||||
|
|||||||
@ -94,6 +94,11 @@ export class PodLogs extends React.Component<Props> {
|
|||||||
this.save({ showTimestamps: !this.tabData.showTimestamps });
|
this.save({ showTimestamps: !this.tabData.showTimestamps });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
togglePrevious = () => {
|
||||||
|
this.save({ previous: !this.tabData.previous });
|
||||||
|
this.reload();
|
||||||
|
}
|
||||||
|
|
||||||
onScroll = (evt: React.UIEvent<HTMLDivElement>) => {
|
onScroll = (evt: React.UIEvent<HTMLDivElement>) => {
|
||||||
const logsArea = evt.currentTarget;
|
const logsArea = evt.currentTarget;
|
||||||
const { scrollHeight, clientHeight, scrollTop } = logsArea;
|
const { scrollHeight, clientHeight, scrollTop } = logsArea;
|
||||||
@ -148,7 +153,7 @@ export class PodLogs extends React.Component<Props> {
|
|||||||
|
|
||||||
renderControls() {
|
renderControls() {
|
||||||
if (!this.ready) return null;
|
if (!this.ready) return null;
|
||||||
const { selectedContainer, showTimestamps, tailLines } = this.tabData;
|
const { selectedContainer, showTimestamps, tailLines, previous } = this.tabData;
|
||||||
const timestamps = podLogsStore.getTimestamps(podLogsStore.logs.get(this.tabId).oldLogs);
|
const timestamps = podLogsStore.getTimestamps(podLogsStore.logs.get(this.tabId).oldLogs);
|
||||||
return (
|
return (
|
||||||
<div className="controls flex gaps align-center">
|
<div className="controls flex gaps align-center">
|
||||||
@ -181,6 +186,12 @@ export class PodLogs extends React.Component<Props> {
|
|||||||
className={cssNames("timestamps-icon", { active: showTimestamps })}
|
className={cssNames("timestamps-icon", { active: showTimestamps })}
|
||||||
tooltip={(showTimestamps ? _i18n._(t`Hide`) : _i18n._(t`Show`)) + " " + _i18n._(t`timestamps`)}
|
tooltip={(showTimestamps ? _i18n._(t`Hide`) : _i18n._(t`Show`)) + " " + _i18n._(t`timestamps`)}
|
||||||
/>
|
/>
|
||||||
|
<Icon
|
||||||
|
material="undo"
|
||||||
|
onClick={this.togglePrevious}
|
||||||
|
className={cssNames("undo-icon", { active: previous })}
|
||||||
|
tooltip={(previous ? _i18n._(t`Show current logs`) : _i18n._(t`Show previous terminated container logs`))}
|
||||||
|
/>
|
||||||
<Icon
|
<Icon
|
||||||
material="get_app"
|
material="get_app"
|
||||||
onClick={this.downloadLogs}
|
onClick={this.downloadLogs}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { FitAddon } from "xterm-addon-fit";
|
|||||||
import { dockStore, TabId } from "./dock.store";
|
import { dockStore, TabId } from "./dock.store";
|
||||||
import { TerminalApi } from "../../api/terminal-api";
|
import { TerminalApi } from "../../api/terminal-api";
|
||||||
import { themeStore } from "../../theme.store";
|
import { themeStore } from "../../theme.store";
|
||||||
import { autobind } from "../../utils/autobind";
|
import { autobind } from "../../utils";
|
||||||
|
|
||||||
export class Terminal {
|
export class Terminal {
|
||||||
static spawningPool: HTMLElement;
|
static spawningPool: HTMLElement;
|
||||||
|
|||||||
@ -39,7 +39,7 @@ export const isNumber: Validator = {
|
|||||||
export const isUrl: Validator = {
|
export const isUrl: Validator = {
|
||||||
condition: ({ type }) => type === "url",
|
condition: ({ type }) => type === "url",
|
||||||
message: () => _i18n._(t`Wrong url format`),
|
message: () => _i18n._(t`Wrong url format`),
|
||||||
validate: value => !!value.match(/^http(s)?:\/\/\w+(\.\w+)*(:[0-9]+)?\/?(\/[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]*)*$/),
|
validate: value => !!value.match(/^$|^http(s)?:\/\/\w+(\.\w+)*(:[0-9]+)?\/?(\/[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]*)*$/),
|
||||||
};
|
};
|
||||||
|
|
||||||
export const isPath: Validator = {
|
export const isPath: Validator = {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { computed, observable, reaction } from "mobx";
|
import { computed, observable, reaction } from "mobx";
|
||||||
import { autobind } from "./utils/autobind";
|
import { autobind } from "./utils";
|
||||||
import { userStore } from "../common/user-store";
|
import { userStore } from "../common/user-store";
|
||||||
import logger from "../main/logger";
|
import logger from "../main/logger";
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +0,0 @@
|
|||||||
// Debouncing promise evaluation
|
|
||||||
|
|
||||||
export const debouncePromise = function (promisedFunc: Function, timeout = 0) {
|
|
||||||
let timer: number;
|
|
||||||
return (...params: any[]) => new Promise((resolve, reject) => {
|
|
||||||
clearTimeout(timer);
|
|
||||||
timer = window.setTimeout(() => resolve(promisedFunc.apply(this, params)), timeout);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
@ -3,21 +3,18 @@
|
|||||||
export const noop: any = Function();
|
export const noop: any = Function();
|
||||||
export const isElectron = !!navigator.userAgent.match(/Electron/);
|
export const isElectron = !!navigator.userAgent.match(/Electron/);
|
||||||
|
|
||||||
export * from '../../common/utils/camelCase'
|
export * from "../../common/utils"
|
||||||
export * from '../../common/utils/base64'
|
|
||||||
|
|
||||||
export * from './autobind'
|
export * from "./cssVar"
|
||||||
export * from './cssVar'
|
export * from "./cssNames"
|
||||||
export * from './cssNames'
|
export * from "./eventEmitter"
|
||||||
export * from './eventEmitter'
|
export * from "./downloadFile"
|
||||||
export * from './downloadFile'
|
export * from "./prevDefault"
|
||||||
export * from './prevDefault'
|
export * from "./createStorage"
|
||||||
export * from './createStorage'
|
export * from "./interval"
|
||||||
export * from './interval'
|
export * from "./copyToClipboard"
|
||||||
export * from './debouncePromise'
|
export * from "./formatDuration"
|
||||||
export * from './copyToClipboard'
|
export * from "./isReactNode"
|
||||||
export * from './formatDuration'
|
export * from "./convertMemory"
|
||||||
export * from './isReactNode'
|
export * from "./convertCpu"
|
||||||
export * from './convertMemory'
|
export * from "./metricUnitsToNumber"
|
||||||
export * from './convertCpu'
|
|
||||||
export * from './metricUnitsToNumber'
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user