Restructure to monorepo (#6907)

* wip: restructure to monorepo

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* refactor create-release-pr to a package

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* build fixes

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* github workflow fixes

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* fix typo

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* add webpack-env types to core

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* fix github workflows

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* refactor/fix integration tests

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* lint fix

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* yarn run dev

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* eslint settings for vscode

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* move templates to right package

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* open-lens build fixes

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* integration test fix

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* fix nx task dependencies

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* use bash shell for unit tests in test workflow

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* fix test:unit for windows

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* fix win-ca webpack error in open-lens

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* fix win-ca webpack error in open-lens

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* fix build:app on windows

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* remove ELECTRON_BUILDER_EXTRA_ARGS

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* sync src/ from master

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

* remove Makefile from core

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>

Signed-off-by: Jari Kolehmainen <jari.kolehmainen@gmail.com>
This commit is contained in:
Jari Kolehmainen authored and GitHub committed 2023-01-24 10:46:26 -08:00
1 parent 0066ca4ea2
commit 2657df2293
3321 files changed
+28699 -11447

No files matched your search

@@ -0,0 +1,10 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectionToken } from "@ogre-tools/injectable";
import type { LensWindow } from "./create-lens-window.injectable";
export const applicationWindowInjectionToken = getInjectionToken<LensWindow>({
id: "application-window-injection-token",
});
@@ -0,0 +1,28 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable, lifecycleEnum } from "@ogre-tools/injectable";
import windowStateKeeper from "electron-window-state";
interface WindowStateConfiguration {
id: string;
defaultHeight: number;
defaultWidth: number;
}
const applicationWindowStateInjectable = getInjectable({
id: "application-window-state",
instantiate: (di, { id, defaultHeight, defaultWidth }) => windowStateKeeper({
defaultHeight,
defaultWidth,
file: `window-state-for-${id}.json`,
}),
lifecycle: lifecycleEnum.keyedSingleton({
getInstanceKey: (di, { id }: WindowStateConfiguration) => id,
}),
});
export default applicationWindowStateInjectable;
@@ -0,0 +1,73 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import createLensWindowInjectable from "./create-lens-window.injectable";
import lensProxyPortInjectable from "../../../lens-proxy/lens-proxy-port.injectable";
import isMacInjectable from "../../../../common/vars/is-mac.injectable";
import waitUntilBundledExtensionsAreLoadedInjectable from "./wait-until-bundled-extensions-are-loaded.injectable";
import { applicationWindowInjectionToken } from "./application-window-injection-token";
import emitAppEventInjectable from "../../../../common/app-event-bus/emit-event.injectable";
import { runInAction } from "mobx";
import appNameInjectable from "../../../../common/vars/app-name.injectable";
const createApplicationWindowInjectable = getInjectable({
id: "create-application-window",
instantiate: (parentDi) => (id: string) => {
const windowInjectable = getInjectable({
id: `application-window-for-${id}`,
instantiate: (di) => {
const createLensWindow = di.inject(createLensWindowInjectable);
const isMac = di.inject(isMacInjectable);
const applicationName = di.inject(appNameInjectable);
const waitUntilBundledExtensionsAreLoaded = di.inject(waitUntilBundledExtensionsAreLoadedInjectable);
const lensProxyPort = di.inject(lensProxyPortInjectable);
const emitAppEvent = di.inject(emitAppEventInjectable);
return createLensWindow({
id,
title: applicationName,
defaultHeight: 900,
defaultWidth: 1440,
getContentSource: () => ({
url: `https://lens.app:${lensProxyPort.get()}`,
}),
resizable: true,
windowFrameUtilitiesAreShown: isMac,
titleBarStyle: isMac ? "hiddenInset" : "hidden",
centered: false,
onFocus: () => {
emitAppEvent({ name: "app", action: "focus" });
},
onBlur: () => {
emitAppEvent({ name: "app", action: "blur" });
},
onDomReady: () => {
emitAppEvent({ name: "app", action: "dom-ready" });
},
onClose: () => {
runInAction(() => {
parentDi.deregister(windowInjectable);
});
},
beforeOpen: waitUntilBundledExtensionsAreLoaded,
});
},
injectionToken: applicationWindowInjectionToken,
});
runInAction(() => {
parentDi.register(windowInjectable);
});
return parentDi.inject(windowInjectable);
},
});
export default createApplicationWindowInjectable;
@@ -0,0 +1,15 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getGlobalOverride } from "../../../../common/test-utils/get-global-override";
import createElectronWindowInjectable from "./create-electron-window.injectable";
export default getGlobalOverride(createElectronWindowInjectable, () => () => ({
loadFile: async () => {},
loadUrl: async () => {},
show: () => {},
close: () => {},
send: () => {},
reload: () => {},
}));
@@ -0,0 +1,204 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { timingSafeEqual, X509Certificate } from "crypto";
import loggerInjectable from "../../../../common/logger.injectable";
import applicationWindowStateInjectable from "./application-window-state.injectable";
import { BrowserWindow } from "electron";
import type { ElectronWindow } from "./create-lens-window.injectable";
import type { RequireExactlyOne } from "type-fest";
import openLinkInBrowserInjectable from "../../../../common/utils/open-link-in-browser.injectable";
import getAbsolutePathInjectable from "../../../../common/path/get-absolute-path.injectable";
import lensResourcesDirInjectable from "../../../../common/vars/lens-resources-dir.injectable";
import isLinuxInjectable from "../../../../common/vars/is-linux.injectable";
import applicationInformationToken from "../../../../common/vars/application-information-token";
import pathExistsSyncInjectable from "../../../../common/fs/path-exists-sync.injectable";
import lensProxyCertificateInjectable from "../../../../common/certificate/lens-proxy-certificate.injectable";
export type ElectronWindowTitleBarStyle = "hiddenInset" | "hidden" | "default" | "customButtonsOnHover";
export interface FileSource {
file: string;
}
export interface UrlSource {
url: string;
}
export type ContentSource = RequireExactlyOne<FileSource & UrlSource>;
export interface ElectronWindowConfiguration {
id: string;
title: string;
defaultHeight: number;
defaultWidth: number;
getContentSource: () => ContentSource;
resizable: boolean;
windowFrameUtilitiesAreShown: boolean;
centered: boolean;
titleBarStyle?: ElectronWindowTitleBarStyle;
beforeOpen?: () => Promise<void>;
onClose: () => void;
onFocus?: () => void;
onBlur?: () => void;
onDomReady?: () => void;
}
export type CreateElectronWindow = (config: ElectronWindowConfiguration) => ElectronWindow;
const createElectronWindowInjectable = getInjectable({
id: "create-electron-window",
instantiate: (di): CreateElectronWindow => {
const logger = di.inject(loggerInjectable);
const openLinkInBrowser = di.inject(openLinkInBrowserInjectable);
const getAbsolutePath = di.inject(getAbsolutePathInjectable);
const lensResourcesDir = di.inject(lensResourcesDirInjectable);
const isLinux = di.inject(isLinuxInjectable);
const applicationInformation = di.inject(applicationInformationToken);
const pathExistsSync = di.inject(pathExistsSyncInjectable);
const lensProxyCertificate = di.inject(lensProxyCertificateInjectable).get();
const lensProxyX509Cert = new X509Certificate(lensProxyCertificate.cert);
return (configuration) => {
const applicationWindowState = di.inject(
applicationWindowStateInjectable,
{
id: configuration.id,
defaultHeight: configuration.defaultHeight,
defaultWidth: configuration.defaultWidth,
},
);
const { width, height, x, y } = applicationWindowState;
const browserWindow = new BrowserWindow({
x,
y,
width,
height,
title: configuration.title,
resizable: configuration.resizable,
center: configuration.centered,
frame: configuration.windowFrameUtilitiesAreShown,
show: false,
minWidth: 700, // accommodate 800 x 600 display minimum
minHeight: 500, // accommodate 800 x 600 display minimum
titleBarStyle: configuration.titleBarStyle,
backgroundColor: "#1e2124",
webPreferences: {
nodeIntegration: true,
nodeIntegrationInSubFrames: true,
contextIsolation: false,
},
});
if (isLinux) {
const iconFileName = [
getAbsolutePath(lensResourcesDir, `../${applicationInformation.name}.png`),
`/usr/share/icons/hicolor/512x512/apps/${applicationInformation.name}.png`,
].find(pathExistsSync);
if (iconFileName != null) {
try {
browserWindow.setIcon(iconFileName);
} catch (err) {
logger.warn(`Error while setting window icon ${err}`);
}
} else {
logger.warn(`No suitable icon found for task bar.`);
}
}
applicationWindowState.manage(browserWindow);
browserWindow
.on("focus", () => {
configuration.onFocus?.();
})
.on("blur", () => {
configuration.onBlur?.();
})
.on("closed", () => {
configuration.onClose();
applicationWindowState.unmanage();
})
.webContents.on("dom-ready", () => {
configuration.onDomReady?.();
})
.on("certificate-error", (event, url, error, certificate, shouldBeTrusted) => {
const cert = new X509Certificate(certificate.data);
const shouldTrustCert = cert.raw.length === lensProxyX509Cert.raw.length
&& timingSafeEqual(cert.raw, lensProxyX509Cert.raw);
shouldBeTrusted(shouldTrustCert);
})
.on("did-fail-load", (_event, code, desc) => {
logger.error(
`[CREATE-ELECTRON-WINDOW]: Failed to load window "${configuration.id}"`,
{
code,
desc,
},
);
})
.on("did-finish-load", () => {
logger.info(
`[CREATE-ELECTRON-WINDOW]: Window "${configuration.id}" loaded`,
);
})
.setWindowOpenHandler((details) => {
openLinkInBrowser(details.url).catch((error) => {
logger.error("[CREATE-ELECTRON-WINDOW]: failed to open browser", {
error,
});
});
return { action: "deny" };
});
return {
loadFile: async (filePath) => {
logger.info(
`[CREATE-ELECTRON-WINDOW]: Loading content for window "${configuration.id}" from file: ${filePath}...`,
);
await browserWindow.loadFile(filePath);
},
loadUrl: async (url) => {
logger.info(
`[CREATE-ELECTRON-WINDOW]: Loading content for window "${configuration.id}" from url: ${url}...`,
);
await browserWindow.loadURL(url);
},
show: () => browserWindow.show(),
close: () => browserWindow.close(),
send: ({ channel, data, frameInfo }) => {
if (frameInfo) {
browserWindow.webContents.sendToFrame(
[frameInfo.processId, frameInfo.frameId],
channel,
data,
);
} else {
browserWindow.webContents.send(channel, data);
}
},
reload: () => {
const wc = browserWindow.webContents;
wc.reload();
wc.clearHistory();
},
};
};
},
causesSideEffects: true,
});
export default createElectronWindowInjectable;
@@ -0,0 +1,19 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import createApplicationWindowInjectable from "./create-application-window.injectable";
// Note: Motivation is to create the window with same ID to use stored dimensions
const createFirstApplicationWindowInjectable = getInjectable({
id: "create-first-application-window",
instantiate: (di) => {
const createApplicationWindow = di.inject(createApplicationWindowInjectable);
return () => createApplicationWindow("first-application-window");
},
});
export default createFirstApplicationWindowInjectable;
@@ -0,0 +1,152 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import type { ContentSource, ElectronWindowTitleBarStyle } from "./create-electron-window.injectable";
import createElectronWindowForInjectable from "./create-electron-window.injectable";
import type { ClusterFrameInfo } from "../../../../common/cluster-frames";
export interface ElectronWindow {
show: () => void;
close: () => void;
send: (args: SendToViewArgs) => void;
loadFile: (filePath: string) => Promise<void>;
loadUrl: (url: string) => Promise<void>;
reload: () => void;
}
export interface SendToViewArgs {
channel: string;
frameInfo?: ClusterFrameInfo;
data?: unknown;
}
export interface LensWindow {
id: string;
start: () => Promise<void>;
close: () => void;
show: () => void;
send: (args: SendToViewArgs) => void;
isVisible: boolean;
isStarting: boolean;
reload: () => void;
}
export interface LensWindowConfiguration {
id: string;
title: string;
defaultHeight: number;
defaultWidth: number;
getContentSource: () => ContentSource;
resizable: boolean;
windowFrameUtilitiesAreShown: boolean;
centered: boolean;
titleBarStyle?: ElectronWindowTitleBarStyle;
/**
* This function is called before the ContentSource is used and then awaited after
* the open call resolves
*/
beforeOpen?: () => Promise<void>;
onFocus?: () => void;
onBlur?: () => void;
onDomReady?: () => void;
onClose?: () => void;
}
const createLensWindowInjectable = getInjectable({
id: "create-lens-window",
instantiate: (di) => {
const createElectronWindow = di.inject(createElectronWindowForInjectable);
return (configuration: LensWindowConfiguration): LensWindow => {
let browserWindow: ElectronWindow | undefined;
let windowIsShown = false;
let windowIsStarting = false;
const showWindow = () => {
if (!browserWindow) {
throw new Error("Cannot show browserWindow, does not exist");
}
browserWindow.show();
windowIsShown = true;
};
return {
id: configuration.id,
get isVisible() {
return windowIsShown;
},
get isStarting() {
return windowIsStarting;
},
start: async () => {
if (!browserWindow) {
windowIsStarting = true;
browserWindow = createElectronWindow({
...configuration,
onClose: () => {
browserWindow = undefined;
windowIsShown = false;
},
});
const {
file: filePathForContent,
url: urlForContent,
} = configuration.getContentSource();
const beforeOpen = configuration.beforeOpen?.();
if (filePathForContent) {
await browserWindow.loadFile(filePathForContent);
} else if (urlForContent) {
await browserWindow.loadUrl(urlForContent);
}
await beforeOpen;
}
showWindow();
windowIsStarting = false;
},
show: showWindow,
close: () => {
browserWindow?.close();
browserWindow = undefined;
windowIsShown = false;
configuration.onClose?.();
},
send: (args: SendToViewArgs) => {
if (!browserWindow) {
throw new Error(`Tried to send message to window "${configuration.id}" but the window was closed`);
}
return browserWindow.send(args);
},
reload: () => {
if (!browserWindow) {
throw new Error(`Tried to reload window "${configuration.id}" but the window was closed`);
}
return browserWindow.reload();
},
};
};
},
});
export default createLensWindowInjectable;
@@ -0,0 +1,16 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { first } from "lodash/fp";
import { applicationWindowInjectionToken } from "./application-window-injection-token";
const getCurrentApplicationWindowInjectable = getInjectable({
id: "get-current-application-window",
instantiate: (di) => () =>
first(di.injectMany(applicationWindowInjectionToken)),
});
export default getCurrentApplicationWindowInjectable;
@@ -0,0 +1,29 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import ipcMainInjectionToken from "../../../../common/ipc/ipc-main-injection-token";
import { bundledExtensionsLoaded } from "../../../../common/ipc/extension-handling";
import { delay } from "../../../../common/utils";
const waitUntilBundledExtensionsAreLoadedInjectable = getInjectable({
id: "wait-until-bundled-extensions-are-loaded",
instantiate: (di) => {
const ipcMain = di.inject(ipcMainInjectionToken);
return async () => {
const viewHasLoaded = new Promise<void>((resolve) => {
ipcMain.once(bundledExtensionsLoaded, () => resolve());
});
await viewHasLoaded;
await delay(50); // wait just a bit longer to let the first round of rendering happen
};
},
causesSideEffects: true,
});
export default waitUntilBundledExtensionsAreLoadedInjectable;
@@ -0,0 +1,15 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { observable } from "mobx";
import type { ClusterId } from "../../../../common/cluster-types";
const currentClusterFrameClusterIdStateInjectable = getInjectable({
id: "current-cluster-frame-cluster-id-state",
instantiate: () => observable.box<ClusterId>(),
});
export default currentClusterFrameClusterIdStateInjectable;
@@ -0,0 +1,29 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { computed } from "mobx";
import currentClusterFrameClusterIdStateInjectable from "./current-cluster-frame-cluster-id-state.injectable";
import clusterFramesInjectable from "../../../../common/cluster-frames.injectable";
const currentClusterFrameInjectable = getInjectable({
id: "current-cluster-frame",
instantiate: (di) => {
const currentClusterFrameState = di.inject(currentClusterFrameClusterIdStateInjectable);
const clusterFrames = di.inject(clusterFramesInjectable);
return computed(() => {
const clusterId = currentClusterFrameState.get();
if (!clusterId) {
return undefined;
}
return clusterFrames.get(clusterId);
});
},
});
export default currentClusterFrameInjectable;
@@ -0,0 +1,19 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { currentClusterMessageChannel } from "../../../../common/cluster/current-cluster-channel";
import { getMessageChannelListenerInjectable } from "../../../../common/utils/channel/message-channel-listener-injection-token";
import currentClusterFrameClusterIdStateInjectable from "./current-cluster-frame-cluster-id-state.injectable";
const currentVisibileClusterListenerInjectable = getMessageChannelListenerInjectable({
id: "current-visibile-cluster",
channel: currentClusterMessageChannel,
handler: (di) => {
const currentClusterFrameState = di.inject(currentClusterFrameClusterIdStateInjectable);
return clusterId => currentClusterFrameState.set(clusterId);
},
});
export default currentVisibileClusterListenerInjectable;
@@ -0,0 +1,24 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { pipeline } from "@ogre-tools/fp";
import { getInjectable } from "@ogre-tools/injectable";
import { filter } from "lodash/fp";
import { applicationWindowInjectionToken } from "./application-window/application-window-injection-token";
const getVisibleWindowsInjectable = getInjectable({
id: "get-visible-windows",
instantiate: (di) => {
const getAllLensWindows = () => di.injectMany(applicationWindowInjectionToken);
return () =>
pipeline(
getAllLensWindows(),
filter((lensWindow) => !!lensWindow.isVisible),
);
},
});
export default getVisibleWindowsInjectable;
@@ -0,0 +1,20 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { applicationWindowInjectionToken } from "../application-window/application-window-injection-token";
const closeAllWindowsInjectable = getInjectable({
id: "close-all-windows",
instantiate: (di) => () => {
const lensWindows = di.injectMany(applicationWindowInjectionToken);
lensWindows.forEach((lensWindow) => {
lensWindow.close();
});
},
});
export default closeAllWindowsInjectable;
@@ -0,0 +1,55 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { iter } from "../../../common/utils";
import clusterFramesInjectable from "../../../common/cluster-frames.injectable";
import showApplicationWindowInjectable from "./show-application-window.injectable";
import getCurrentApplicationWindowInjectable from "./application-window/get-current-application-window.injectable";
import assert from "assert";
import type { NavigateForExtensionArgs } from "../../../features/extensions/navigate/common/channel";
import { navigateForExtensionChannel } from "../../../features/extensions/navigate/common/channel";
export type NavigateForExtension = (
extId: string,
pageId?: string,
params?: Record<string, any>,
frameId?: number
) => Promise<void>;
const navigateForExtensionInjectable = getInjectable({
id: "navigate-for-extension",
instantiate: (di): NavigateForExtension => {
const getApplicationWindow = di.inject(getCurrentApplicationWindowInjectable);
const clusterFrames = di.inject(clusterFramesInjectable);
const showApplicationWindow = di.inject(showApplicationWindowInjectable);
return async (
extId: string,
pageId?: string,
params?: Record<string, any>,
frameId?: number,
) => {
await showApplicationWindow();
const applicationWindow = getApplicationWindow();
assert(applicationWindow);
const frameInfo = iter.find(
clusterFrames.values(),
(frameInfo) => frameInfo.frameId === frameId,
);
applicationWindow.send({
channel: navigateForExtensionChannel.id,
frameInfo,
data: { extId, pageId, params } as NavigateForExtensionArgs,
});
};
},
});
export default navigateForExtensionInjectable;
@@ -0,0 +1,46 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { iter } from "../../../common/utils";
import clusterFramesInjectable from "../../../common/cluster-frames.injectable";
import { IpcRendererNavigationEvents } from "../../../common/ipc/navigation-events";
import showApplicationWindowInjectable from "./show-application-window.injectable";
import getCurrentApplicationWindowInjectable from "./application-window/get-current-application-window.injectable";
import assert from "assert";
const navigateInjectable = getInjectable({
id: "navigate",
instantiate: (di) => {
const getApplicationWindow = di.inject(getCurrentApplicationWindowInjectable);
const showApplicationWindow = di.inject(showApplicationWindowInjectable);
const clusterFrames = di.inject(clusterFramesInjectable);
return async (url: string, frameId?: number) => {
await showApplicationWindow();
const applicationWindow = getApplicationWindow();
assert(applicationWindow);
const frameInfo = iter.find(
clusterFrames.values(),
(frameInfo) => frameInfo.frameId === frameId,
);
const channel = frameInfo
? IpcRendererNavigationEvents.NAVIGATE_IN_CLUSTER
: IpcRendererNavigationEvents.NAVIGATE_IN_APP;
applicationWindow.send({
channel,
frameInfo,
data: url,
});
};
},
});
export default navigateInjectable;
@@ -0,0 +1,38 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import currentClusterFrameInjectable from "./current-cluster-frame/current-cluster-frame.injectable";
import getCurrentApplicationWindowInjectable from "./application-window/get-current-application-window.injectable";
import { reloadPageChannel } from "../../../features/navigation/reload-page/common/channel";
const reloadCurrentApplicationWindowInjectable = getInjectable({
id: "reload-current-application-window",
instantiate: (di) => {
const getCurrentApplicationWindow = di.inject(getCurrentApplicationWindowInjectable);
const currentClusterIframe = di.inject(currentClusterFrameInjectable);
return () => {
const lensWindow = getCurrentApplicationWindow();
if (!lensWindow) {
return;
}
const frameInfo = currentClusterIframe.get();
if (frameInfo) {
lensWindow.send({
channel: reloadPageChannel.id,
frameInfo,
});
} else {
lensWindow.reload();
}
};
},
});
export default reloadCurrentApplicationWindowInjectable;
@@ -0,0 +1,52 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import splashWindowInjectable from "./splash-window/splash-window.injectable";
import { identity, some } from "lodash/fp";
import focusApplicationInjectable from "../../electron-app/features/focus-application.injectable";
import getCurrentApplicationWindowInjectable from "./application-window/get-current-application-window.injectable";
import createFirstApplicationWindowInjectable from "./application-window/create-first-application-window.injectable";
const someIsTruthy = some(identity);
const showApplicationWindowInjectable = getInjectable({
id: "show-application-window",
instantiate: (di) => {
const getApplicationWindow = di.inject(getCurrentApplicationWindowInjectable);
const createFirstApplicationWindow = di.inject(createFirstApplicationWindowInjectable);
const splashWindow = di.inject(splashWindowInjectable);
const focusApplication = di.inject(focusApplicationInjectable);
return async () => {
focusApplication();
const applicationWindow = getApplicationWindow() ?? createFirstApplicationWindow();
if (applicationWindow.isStarting) {
applicationWindow.show();
splashWindow.close();
return;
}
const windowIsAlreadyBeingShown = someIsTruthy([
applicationWindow.isVisible,
splashWindow.isStarting,
]);
if (windowIsAlreadyBeingShown) {
return;
}
await splashWindow.start();
await applicationWindow.start();
splashWindow.close();
};
},
});
export default showApplicationWindowInjectable;
@@ -0,0 +1,34 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import createLensWindowInjectable from "../application-window/create-lens-window.injectable";
import staticFilesDirectoryInjectable from "../../../../common/vars/static-files-directory.injectable";
import joinPathsInjectable from "../../../../common/path/join-paths.injectable";
const splashWindowInjectable = getInjectable({
id: "splash-window",
instantiate: (di) => {
const createLensWindow = di.inject(createLensWindowInjectable);
const staticFilesDirectory = di.inject(staticFilesDirectoryInjectable);
const joinPaths = di.inject(joinPathsInjectable);
const splashWindowFile = joinPaths(staticFilesDirectory, "splash.html");
return createLensWindow({
id: "splash",
title: "Loading",
getContentSource: () => ({
file: splashWindowFile,
}),
defaultWidth: 500,
defaultHeight: 300,
resizable: false,
windowFrameUtilitiesAreShown: false,
centered: true,
});
},
});
export default splashWindowInjectable;
@@ -0,0 +1,10 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectionToken } from "@ogre-tools/injectable";
import type { Runnable } from "../../../common/runnable/run-many-for";
export const afterApplicationIsLoadedInjectionToken = getInjectionToken<Runnable>({
id: "after-application-is-loaded",
});
@@ -0,0 +1,10 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectionToken } from "@ogre-tools/injectable";
import type { Runnable } from "../../../common/runnable/run-many-for";
export const afterRootFrameIsReadyInjectionToken = getInjectionToken<Runnable>({
id: "after-root-frame-is-ready",
});
@@ -0,0 +1,10 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectionToken } from "@ogre-tools/injectable";
import type { Runnable } from "../../../common/runnable/run-many-for";
export const afterWindowIsOpenedInjectionToken = getInjectionToken<Runnable>({
id: "after-window-is-opened",
});
@@ -0,0 +1,10 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectionToken } from "@ogre-tools/injectable";
import type { Runnable } from "../../../common/runnable/run-many-for";
export const beforeApplicationIsLoadingInjectionToken = getInjectionToken<Runnable>({
id: "before-application-is-loading",
});
@@ -0,0 +1,10 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectionToken } from "@ogre-tools/injectable";
import type { RunnableSync } from "../../../common/runnable/run-many-sync-for";
export const beforeElectronIsReadyInjectionToken = getInjectionToken<RunnableSync>({
id: "before-electron-is-ready",
});
@@ -0,0 +1,10 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectionToken } from "@ogre-tools/injectable";
import type { RunnableSync } from "../../../common/runnable/run-many-sync-for";
export const beforeQuitOfBackEndInjectionToken = getInjectionToken<RunnableSync>({
id: "before-quit-of-back-end",
});
@@ -0,0 +1,10 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectionToken } from "@ogre-tools/injectable";
import type { RunnableSync } from "../../../common/runnable/run-many-sync-for";
export const beforeQuitOfFrontEndInjectionToken = getInjectionToken<RunnableSync>({
id: "before-quit-of-front-end",
});
@@ -0,0 +1,10 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectionToken } from "@ogre-tools/injectable";
import type { Runnable } from "../../../common/runnable/run-many-for";
export const onLoadOfApplicationInjectionToken = getInjectionToken<Runnable>({
id: "on-load-of-application",
});
@@ -0,0 +1,28 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectionToken } from "@ogre-tools/injectable";
import type { Runnable } from "../../../common/runnable/run-many-for";
import type { RunnableSync } from "../../../common/runnable/run-many-sync-for";
/**
* These tokens are here so that the importing of their respective dependencies
* can be delayed until all of them are ready
*/
/**
* This runnable token should only have the app paths init so that it can be run by itself
*/
export const appPathsRunnablePhaseInjectionToken = getInjectionToken<RunnableSync>({
id: "app-paths-runnable-phase",
});
export const showLoadingRunnablePhaseInjectionToken = getInjectionToken<Runnable>({
id: "show-loading-runnable-phase",
});
export const showInitialWindowRunnablePhaseInjectionToken = getInjectionToken<Runnable>({
id: "show-initial-window-runnable-phase",
});
@@ -0,0 +1,20 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { beforeQuitOfBackEndInjectionToken } from "../runnable-tokens/before-quit-of-back-end-injection-token";
import { ShellSession } from "../../shell-session/shell-session";
const cleanUpShellSessionsInjectable = getInjectable({
id: "clean-up-shell-sessions",
instantiate: () => ({
id: "clean-up-shell-sessions",
run: () => void ShellSession.cleanup(),
}),
injectionToken: beforeQuitOfBackEndInjectionToken,
});
export default cleanUpShellSessionsInjectable;
@@ -0,0 +1,28 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import emitAppEventInjectable from "../../../common/app-event-bus/emit-event.injectable";
import { beforeQuitOfFrontEndInjectionToken } from "../runnable-tokens/before-quit-of-front-end-injection-token";
const emitCloseToEventBusInjectable = getInjectable({
id: "emit-close-to-event-bus",
instantiate: (di) => {
const emitAppEvent = di.inject(emitAppEventInjectable);
return {
id: "emit-close-to-event-bus",
run: () => {
emitAppEvent({ name: "app", action: "close" });
return undefined;
},
};
},
injectionToken: beforeQuitOfFrontEndInjectionToken,
});
export default emitCloseToEventBusInjectable;
@@ -0,0 +1,26 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import emitAppEventInjectable from "../../../common/app-event-bus/emit-event.injectable";
import { afterApplicationIsLoadedInjectionToken } from "../runnable-tokens/after-application-is-loaded-injection-token";
const emitServiceStartToEventBusInjectable = getInjectable({
id: "emit-service-start-to-event-bus",
instantiate: (di) => {
const emitAppEvent = di.inject(emitAppEventInjectable);
return {
id: "emit-service-start-to-event-bus",
run: () => {
emitAppEvent({ name: "service", action: "start" });
},
};
},
injectionToken: afterApplicationIsLoadedInjectionToken,
});
export default emitServiceStartToEventBusInjectable;
@@ -0,0 +1,30 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { afterRootFrameIsReadyInjectionToken } from "../../runnable-tokens/after-root-frame-is-ready-injection-token";
import lensProtocolRouterMainInjectable from "../../../protocol-handler/lens-protocol-router-main/lens-protocol-router-main.injectable";
import { runInAction } from "mobx";
const flagRendererAsLoadedInjectable = getInjectable({
id: "flag-renderer-as-loaded",
instantiate: (di) => {
const lensProtocolRouterMain = di.inject(lensProtocolRouterMainInjectable);
return {
id: "flag-renderer-as-loaded",
run: () => {
runInAction(() => {
// Todo: remove this kludge which enables out-of-place temporal dependency.
lensProtocolRouterMain.rendererLoaded = true;
});
},
};
},
injectionToken: afterRootFrameIsReadyInjectionToken,
});
export default flagRendererAsLoadedInjectable;
@@ -0,0 +1,32 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { runInAction } from "mobx";
import lensProtocolRouterMainInjectable from "../../../protocol-handler/lens-protocol-router-main/lens-protocol-router-main.injectable";
import { beforeQuitOfFrontEndInjectionToken } from "../../runnable-tokens/before-quit-of-front-end-injection-token";
const flagRendererAsNotLoadedInjectable = getInjectable({
id: "stop-deep-linking",
instantiate: (di) => {
const lensProtocolRouterMain = di.inject(lensProtocolRouterMainInjectable);
return {
id: "stop-deep-linking",
run: () => {
runInAction(() => {
// Todo: remove this kludge which enables out-of-place temporal dependency.
lensProtocolRouterMain.rendererLoaded = false;
});
return undefined;
},
};
},
injectionToken: beforeQuitOfFrontEndInjectionToken,
});
export default flagRendererAsNotLoadedInjectable;
@@ -0,0 +1,68 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import type { InstalledExtension } from "../../../extensions/extension-discovery/extension-discovery";
import type { LensExtensionId } from "../../../extensions/lens-extension";
import loggerInjectable from "../../../common/logger.injectable";
import extensionDiscoveryInjectable from "../../../extensions/extension-discovery/extension-discovery.injectable";
import extensionLoaderInjectable from "../../../extensions/extension-loader/extension-loader.injectable";
import showErrorPopupInjectable from "../../electron-app/features/show-error-popup.injectable";
import { onLoadOfApplicationInjectionToken } from "../runnable-tokens/on-load-of-application-injection-token";
const initializeExtensionsInjectable = getInjectable({
id: "initialize-extensions",
instantiate: (di) => {
const logger = di.inject(loggerInjectable);
const extensionDiscovery = di.inject(extensionDiscoveryInjectable);
const extensionLoader = di.inject(extensionLoaderInjectable);
const showErrorPopup = di.inject(showErrorPopupInjectable);
return {
id: "initialize-extensions",
run: async () => {
logger.info("🧩 Initializing extensions");
await extensionDiscovery.init();
await extensionLoader.init();
try {
const extensions = await extensionDiscovery.load();
// Start watching after bundled extensions are loaded
extensionDiscovery.watchExtensions();
// Subscribe to extensions that are copied or deleted to/from the extensions folder
extensionDiscovery.events
.on("add", (extension: InstalledExtension) => {
extensionLoader.addExtension(extension);
})
.on("remove", (lensExtensionId: LensExtensionId) => {
extensionLoader.removeExtension(lensExtensionId);
});
extensionLoader.initExtensions(extensions);
} catch (error: any) {
showErrorPopup(
"Lens Error",
`Could not load extensions${
error?.message ? `: ${error.message}` : ""
}`,
);
console.error(error);
console.trace();
}
},
};
},
causesSideEffects: true,
injectionToken: onLoadOfApplicationInjectionToken,
});
export default initializeExtensionsInjectable;
@@ -0,0 +1,26 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import kubeconfigSyncManagerInjectable from "../../../catalog-sources/kubeconfig-sync/manager.injectable";
import catalogEntityRegistryInjectable from "../../../catalog/entity-registry.injectable";
import { afterApplicationIsLoadedInjectionToken } from "../../runnable-tokens/after-application-is-loaded-injection-token";
const addKubeconfigSyncAsEntitySourceInjectable = getInjectable({
id: "add-kubeconfig-sync-as-entity-source",
instantiate: (di) => {
const kubeConfigSyncManager = di.inject(kubeconfigSyncManagerInjectable);
const entityRegistry = di.inject(catalogEntityRegistryInjectable);
return {
id: "add-kubeconfig-sync-as-entity-source",
run: () => {
entityRegistry.addComputedSource("kubeconfig-sync", kubeConfigSyncManager.source);
},
};
},
injectionToken: afterApplicationIsLoadedInjectionToken,
});
export default addKubeconfigSyncAsEntitySourceInjectable;
@@ -0,0 +1,34 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { afterApplicationIsLoadedInjectionToken } from "../../runnable-tokens/after-application-is-loaded-injection-token";
import directoryForKubeConfigsInjectable from "../../../../common/app-paths/directory-for-kube-configs/directory-for-kube-configs.injectable";
import ensureDirInjectable from "../../../../common/fs/ensure-dir.injectable";
import kubeconfigSyncManagerInjectable from "../../../catalog-sources/kubeconfig-sync/manager.injectable";
import addKubeconfigSyncAsEntitySourceInjectable from "./add-source.injectable";
const startKubeConfigSyncInjectable = getInjectable({
id: "start-kubeconfig-sync",
instantiate: (di) => {
const directoryForKubeConfigs = di.inject(directoryForKubeConfigsInjectable);
const kubeConfigSyncManager = di.inject(kubeconfigSyncManagerInjectable);
const ensureDir = di.inject(ensureDirInjectable);
return {
id: "start-kubeconfig-sync",
run: async () => {
await ensureDir(directoryForKubeConfigs);
kubeConfigSyncManager.startSync();
},
runAfter: di.inject(addKubeconfigSyncAsEntitySourceInjectable),
};
},
injectionToken: afterApplicationIsLoadedInjectionToken,
});
export default startKubeConfigSyncInjectable;
@@ -0,0 +1,24 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { beforeQuitOfBackEndInjectionToken } from "../../runnable-tokens/before-quit-of-back-end-injection-token";
import kubeconfigSyncManagerInjectable from "../../../catalog-sources/kubeconfig-sync/manager.injectable";
const stopKubeConfigSyncInjectable = getInjectable({
id: "stop-kube-config-sync",
instantiate: (di) => {
const kubeConfigSyncManager = di.inject(kubeconfigSyncManagerInjectable);
return {
id: "stop-kube-config-sync",
run: () => void kubeConfigSyncManager.stopSync(),
};
},
injectionToken: beforeQuitOfBackEndInjectionToken,
});
export default stopKubeConfigSyncInjectable;
@@ -0,0 +1,20 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getMessageChannelListenerInjectable } from "../../../../common/utils/channel/message-channel-listener-injection-token";
import { rootFrameHasRenderedChannel } from "../../../../common/root-frame/root-frame-rendered-channel";
import { runManyFor } from "../../../../common/runnable/run-many-for";
import { afterRootFrameIsReadyInjectionToken } from "../../runnable-tokens/after-root-frame-is-ready-injection-token";
const rootFrameRenderedChannelListenerInjectable = getMessageChannelListenerInjectable({
id: "action",
channel: rootFrameHasRenderedChannel,
handler: (di) => {
const runMany = runManyFor(di);
return runMany(afterRootFrameIsReadyInjectionToken);
},
});
export default rootFrameRenderedChannelListenerInjectable;
@@ -0,0 +1,9 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getGlobalOverride } from "../../../../common/test-utils/get-global-override";
import initializeSentryOnMainInjectable from "./initialize-on-main.injectable";
export default getGlobalOverride(initializeSentryOnMainInjectable, () => () => {});
@@ -0,0 +1,14 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { init } from "@sentry/electron/main";
const initializeSentryOnMainInjectable = getInjectable({
id: "initialize-sentry-on-main",
instantiate: () => init,
causesSideEffects: true,
});
export default initializeSentryOnMainInjectable;
@@ -0,0 +1,24 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { beforeElectronIsReadyInjectionToken } from "../../runnable-tokens/before-electron-is-ready-injection-token";
import initializeSentryReportingWithInjectable from "../../../../common/error-reporting/initialize-sentry-reporting.injectable";
import initializeSentryOnMainInjectable from "./initialize-on-main.injectable";
const setupSentryInjectable = getInjectable({
id: "setup-sentry",
instantiate: (di) => {
const initializeSentryReportingWith = di.inject(initializeSentryReportingWithInjectable);
const initializeSentryOnMain = di.inject(initializeSentryOnMainInjectable);
return {
id: "setup-sentry",
run: () => void initializeSentryReportingWith(initializeSentryOnMain),
};
},
injectionToken: beforeElectronIsReadyInjectionToken,
});
export default setupSentryInjectable;
@@ -0,0 +1,32 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import disableHardwareAccelerationInjectable from "../../electron-app/features/disable-hardware-acceleration.injectable";
import hardwareAccelerationShouldBeDisabledInjectable from "../../vars/hardware-acceleration-should-be-disabled.injectable";
import { beforeElectronIsReadyInjectionToken } from "../runnable-tokens/before-electron-is-ready-injection-token";
const setupHardwareAccelerationInjectable = getInjectable({
id: "setup-hardware-acceleration",
instantiate: (di) => {
const hardwareAccelerationShouldBeDisabled = di.inject(hardwareAccelerationShouldBeDisabledInjectable);
const disableHardwareAcceleration = di.inject(disableHardwareAccelerationInjectable);
return {
id: "setup-hardware-acceleration",
run: () => {
if (hardwareAccelerationShouldBeDisabled) {
disableHardwareAcceleration();
}
return undefined;
},
};
},
injectionToken: beforeElectronIsReadyInjectionToken,
});
export default setupHardwareAccelerationInjectable;
@@ -0,0 +1,32 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import electronAppInjectable from "../../electron-app/electron-app.injectable";
import { beforeElectronIsReadyInjectionToken } from "../runnable-tokens/before-electron-is-ready-injection-token";
const setupHostnamesInjectable = getInjectable({
id: "setup-hostnames",
instantiate: (di) => {
const app = di.inject(electronAppInjectable);
return {
id: "setup-hostnames",
run: () => {
app.commandLine.appendSwitch("host-rules", [
"MAP localhost 127.0.0.1",
"MAP lens.app 127.0.0.1",
"MAP *.lens.app 127.0.0.1",
].join());
return undefined;
},
};
},
injectionToken: beforeElectronIsReadyInjectionToken,
});
export default setupHostnamesInjectable;
@@ -0,0 +1,27 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import * as Immer from "immer";
import { beforeElectronIsReadyInjectionToken } from "../runnable-tokens/before-electron-is-ready-injection-token";
const setupImmerInjectable = getInjectable({
id: "setup-immer",
instantiate: () => ({
id: "setup-immer",
run: () => {
// Docs: https://immerjs.github.io/immer/
// Required in `utils/storage-helper.ts`
Immer.setAutoFreeze(false); // allow to merge mobx observables
Immer.enableMapSet(); // allow to merge maps and sets
return undefined;
},
}),
injectionToken: beforeElectronIsReadyInjectionToken,
});
export default setupImmerInjectable;
@@ -0,0 +1,53 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { generate } from "selfsigned";
import lensProxyCertificateInjectable from "../../../common/certificate/lens-proxy-certificate.injectable";
import { beforeElectronIsReadyInjectionToken } from "../runnable-tokens/before-electron-is-ready-injection-token";
const setupLensProxyCertificateInjectable = getInjectable({
id: "setup-lens-proxy-certificate",
instantiate: (di) => {
const lensProxyCertificate = di.inject(lensProxyCertificateInjectable);
return {
id: "setup-lens-proxy-certificate",
run: () => {
const cert = generate([
{ name: "commonName", value: "Lens Certificate Authority" },
{ name: "organizationName", value: "Lens" },
], {
keySize: 2048,
algorithm: "sha256",
days: 365,
extensions: [
{
name: "basicConstraints",
cA: true,
},
{
name: "subjectAltName",
altNames: [
{ type: 2, value: "*.lens.app" },
{ type: 2, value: "lens.app" },
{ type: 2, value: "localhost" },
{ type: 7, ip: "127.0.0.1" },
],
},
],
});
lensProxyCertificate.set(cert);
return undefined;
},
};
},
injectionToken: beforeElectronIsReadyInjectionToken,
});
export default setupLensProxyCertificateInjectable;
@@ -0,0 +1,90 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import exitAppInjectable from "../../electron-app/features/exit-app.injectable";
import lensProxyInjectable from "../../lens-proxy/lens-proxy.injectable";
import loggerInjectable from "../../../common/logger.injectable";
import lensProxyPortInjectable from "../../lens-proxy/lens-proxy-port.injectable";
import isWindowsInjectable from "../../../common/vars/is-windows.injectable";
import showErrorPopupInjectable from "../../electron-app/features/show-error-popup.injectable";
import { beforeApplicationIsLoadingInjectionToken } from "../runnable-tokens/before-application-is-loading-injection-token";
import buildVersionInjectable from "../../vars/build-version/build-version.injectable";
import initializeBuildVersionInjectable from "../../vars/build-version/init.injectable";
import lensProxyCertificateInjectable from "../../../common/certificate/lens-proxy-certificate.injectable";
import fetchInjectable from "../../../common/fetch/fetch.injectable";
import { Agent } from "https";
const setupLensProxyInjectable = getInjectable({
id: "setup-lens-proxy",
instantiate: (di) => {
const lensProxy = di.inject(lensProxyInjectable);
const exitApp = di.inject(exitAppInjectable);
const logger = di.inject(loggerInjectable);
const lensProxyPort = di.inject(lensProxyPortInjectable);
const isWindows = di.inject(isWindowsInjectable);
const showErrorPopup = di.inject(showErrorPopupInjectable);
const buildVersion = di.inject(buildVersionInjectable);
const lensProxyCertificate = di.inject(lensProxyCertificateInjectable);
const fetch = di.inject(fetchInjectable);
return {
id: "setup-lens-proxy",
run: async () => {
try {
logger.info("🔌 Starting LensProxy");
await lensProxy.listen(); // lensProxy.port available
} catch (error: any) {
showErrorPopup("Lens Error", `Could not start proxy: ${error?.message || "unknown error"}`);
return exitApp();
}
// test proxy connection
try {
logger.info("🔎 Testing LensProxy connection ...");
const versionResponse = await fetch(`https://127.0.0.1:${lensProxyPort.get()}/version`, {
agent: new Agent({
ca: lensProxyCertificate.get()?.cert,
}),
});
const { version: versionFromProxy } = await versionResponse.json() as { version: string };
if (buildVersion.get() !== versionFromProxy) {
logger.error("Proxy server responded with invalid response");
return exitApp();
}
logger.info("⚡ LensProxy connection OK");
} catch (error) {
logger.error(`🛑 LensProxy: failed connection test: ${error}`);
const hostsPath = isWindows
? "C:\\windows\\system32\\drivers\\etc\\hosts"
: "/etc/hosts";
const message = [
`Failed connection test: ${error}`,
"Check to make sure that no other versions of Lens are running",
`Check ${hostsPath} to make sure that it is clean and that the localhost loopback is at the top and set to 127.0.0.1`,
"If you have HTTP_PROXY or http_proxy set in your environment, make sure that the localhost and the ipv4 loopback address 127.0.0.1 are added to the NO_PROXY environment variable.",
];
showErrorPopup("Lens Proxy Error", message.join("\n\n"));
return exitApp();
}
},
runAfter: di.inject(initializeBuildVersionInjectable),
};
},
causesSideEffects: true,
injectionToken: beforeApplicationIsLoadingInjectionToken,
});
export default setupLensProxyInjectable;
@@ -0,0 +1,32 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import * as Mobx from "mobx";
import { getInjectable } from "@ogre-tools/injectable";
import { beforeElectronIsReadyInjectionToken } from "../runnable-tokens/before-electron-is-ready-injection-token";
const setupMobxInjectable = getInjectable({
id: "setup-mobx",
instantiate: () => ({
id: "setup-mobx",
run: () => {
// Docs: https://mobx.js.org/configuration.html
Mobx.configure({
enforceActions: "never",
// TODO: enable later (read more: https://mobx.js.org/migrating-from-4-or-5.html)
// computedRequiresReaction: true,
// reactionRequiresObservable: true,
// observableRequiresReaction: true,
});
return undefined;
},
}),
injectionToken: beforeElectronIsReadyInjectionToken,
});
export default setupMobxInjectable;
@@ -0,0 +1,46 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { beforeElectronIsReadyInjectionToken } from "../runnable-tokens/before-electron-is-ready-injection-token";
import getCommandLineSwitchInjectable from "../../electron-app/features/get-command-line-switch.injectable";
const setupProxyEnvInjectable = getInjectable({
id: "setup-proxy-env",
instantiate: (di) => {
const getCommandLineSwitch = di.inject(getCommandLineSwitchInjectable);
return {
id: "setup-proxy-env",
run: () => {
const switchValue = getCommandLineSwitch("proxy-server");
let httpsProxy =
process.env.HTTPS_PROXY || process.env.HTTP_PROXY || "";
delete process.env.HTTPS_PROXY;
delete process.env.HTTP_PROXY;
if (switchValue !== "") {
httpsProxy = switchValue;
}
if (httpsProxy !== "") {
process.env.APP_HTTPS_PROXY = httpsProxy;
}
if (getCommandLineSwitch("proxy-server") !== "") {
process.env.HTTPS_PROXY = getCommandLineSwitch("proxy-server");
}
return undefined;
},
};
},
injectionToken: beforeElectronIsReadyInjectionToken,
});
export default setupProxyEnvInjectable;
@@ -0,0 +1,28 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import syncGeneralCatalogEntitiesInjectable from "../../catalog-sources/sync-general-catalog-entities.injectable";
import { onLoadOfApplicationInjectionToken } from "../runnable-tokens/on-load-of-application-injection-token";
const setupSyncingOfGeneralCatalogEntitiesInjectable = getInjectable({
id: "setup-syncing-of-general-catalog-entities",
instantiate: (di) => {
const syncGeneralCatalogEntities = di.inject(
syncGeneralCatalogEntitiesInjectable,
);
return {
id: "setup-syncing-of-general-catalog-entities",
run: () => {
syncGeneralCatalogEntities();
},
};
},
injectionToken: onLoadOfApplicationInjectionToken,
});
export default setupSyncingOfGeneralCatalogEntitiesInjectable;
@@ -0,0 +1,26 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { onLoadOfApplicationInjectionToken } from "../runnable-tokens/on-load-of-application-injection-token";
import syncWeblinksInjectable from "../../catalog-sources/sync-weblinks.injectable";
const setupSyncingOfWeblinksInjectable = getInjectable({
id: "setup-syncing-of-weblinks",
instantiate: (di) => {
const syncWeblinks = di.inject(syncWeblinksInjectable);
return {
id: "setup-syncing-of-weblinks",
run: () => {
syncWeblinks();
},
};
},
injectionToken: onLoadOfApplicationInjectionToken,
});
export default setupSyncingOfWeblinksInjectable;
@@ -0,0 +1,18 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { beforeApplicationIsLoadingInjectionToken } from "../runnable-tokens/before-application-is-loading-injection-token";
import injectSystemCAsInjectable from "../../../features/certificate-authorities/common/inject-system-cas.injectable";
const setupSystemCaInjectable = getInjectable({
id: "setup-system-ca",
instantiate: (di) => ({
id: "setup-system-ca",
run: di.inject(injectSystemCAsInjectable),
}),
injectionToken: beforeApplicationIsLoadingInjectionToken,
});
export default setupSystemCaInjectable;
@@ -0,0 +1,51 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import shouldStartHiddenInjectable from "../../electron-app/features/should-start-hidden.injectable";
import openDeepLinkInjectable from "../../protocol-handler/lens-protocol-router-main/open-deep-link-for-url/open-deep-link.injectable";
import commandLineArgumentsInjectable from "../../utils/command-line-arguments.injectable";
import createFirstApplicationWindowInjectable from "../lens-window/application-window/create-first-application-window.injectable";
import splashWindowInjectable from "../lens-window/splash-window/splash-window.injectable";
import { showInitialWindowRunnablePhaseInjectionToken } from "../runnable-tokens/phases";
const getDeepLinkUrl = (commandLineArguments: string[]) => (
commandLineArguments
.map(arg => arg.toLowerCase())
.find(arg => arg.startsWith("lens://"))
);
const showInitialWindowInjectable = getInjectable({
id: "show-initial-window",
instantiate: (di) => {
const shouldStartHidden = di.inject(shouldStartHiddenInjectable);
const shouldStartWindow = !shouldStartHidden;
const createFirstApplicationWindow = di.inject(createFirstApplicationWindowInjectable);
const splashWindow = di.inject(splashWindowInjectable);
const openDeepLink = di.inject(openDeepLinkInjectable);
const commandLineArguments = di.inject(commandLineArgumentsInjectable);
return {
id: "show-initial-window",
run: async () => {
if (shouldStartWindow) {
const deepLinkUrl = getDeepLinkUrl(commandLineArguments);
if (deepLinkUrl) {
await openDeepLink(deepLinkUrl);
} else {
const applicationWindow = createFirstApplicationWindow();
await applicationWindow.start();
}
splashWindow.close();
}
},
};
},
injectionToken: showInitialWindowRunnablePhaseInjectionToken,
});
export default showInitialWindowInjectable;
@@ -0,0 +1,29 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import shouldStartHiddenInjectable from "../../electron-app/features/should-start-hidden.injectable";
import splashWindowInjectable from "../lens-window/splash-window/splash-window.injectable";
import { showLoadingRunnablePhaseInjectionToken } from "../runnable-tokens/phases";
const showLoadingInjectable = getInjectable({
id: "show-loading",
instantiate: (di) => {
const shouldStartHidden = di.inject(shouldStartHiddenInjectable);
const shouldShowLoadingWindow = !shouldStartHidden;
const splashWindow = di.inject(splashWindowInjectable);
return {
id: "show-loading",
run: async () => {
if (shouldShowLoadingWindow) {
await splashWindow.start();
}
},
};
},
injectionToken: showLoadingRunnablePhaseInjectionToken,
});
export default showLoadingInjectable;
@@ -0,0 +1,28 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import clusterManagerInjectable from "../../cluster/manager.injectable";
import { beforeQuitOfFrontEndInjectionToken } from "../runnable-tokens/before-quit-of-front-end-injection-token";
const stopClusterManagerInjectable = getInjectable({
id: "stop-cluster-manager",
instantiate: (di) => {
const clusterManager = di.inject(clusterManagerInjectable);
return {
id: "stop-cluster-manager",
run: () => {
clusterManager.stop();
return undefined;
},
};
},
injectionToken: beforeQuitOfFrontEndInjectionToken,
});
export default stopClusterManagerInjectable;
@@ -0,0 +1,50 @@
/**
* Copyright (c) OpenLens Authors. All rights reserved.
* Licensed under MIT License. See LICENSE in root directory for more information.
*/
import { getInjectable } from "@ogre-tools/injectable";
import { runManyFor } from "../../common/runnable/run-many-for";
import { runManySyncFor } from "../../common/runnable/run-many-sync-for";
import { beforeElectronIsReadyInjectionToken } from "./runnable-tokens/before-electron-is-ready-injection-token";
import { beforeApplicationIsLoadingInjectionToken } from "./runnable-tokens/before-application-is-loading-injection-token";
import { onLoadOfApplicationInjectionToken } from "./runnable-tokens/on-load-of-application-injection-token";
import { afterApplicationIsLoadedInjectionToken } from "./runnable-tokens/after-application-is-loaded-injection-token";
import waitForElectronToBeReadyInjectable from "../electron-app/features/wait-for-electron-to-be-ready.injectable";
import { appPathsRunnablePhaseInjectionToken, showInitialWindowRunnablePhaseInjectionToken, showLoadingRunnablePhaseInjectionToken } from "./runnable-tokens/phases";
const startMainApplicationInjectable = getInjectable({
id: "start-main-application",
instantiate: (di) => {
const runMany = runManyFor(di);
const runManySync = runManySyncFor(di);
const waitForElectronToBeReady = di.inject(waitForElectronToBeReadyInjectable);
const appPathsRunnablePhase = runManySync(appPathsRunnablePhaseInjectionToken);
const beforeElectronIsReady = runManySync(beforeElectronIsReadyInjectionToken);
const beforeApplicationIsLoading = runMany(beforeApplicationIsLoadingInjectionToken);
const showLoadingRunnablePhase = runMany(showLoadingRunnablePhaseInjectionToken);
const onLoadOfApplication = runMany(onLoadOfApplicationInjectionToken);
const showInitialWindowRunnablePhase = runMany(showInitialWindowRunnablePhaseInjectionToken);
const afterApplicationIsLoaded = runMany(afterApplicationIsLoadedInjectionToken);
return () => {
// Stuff happening before application is ready needs to be synchronous because of
// https://github.com/electron/electron/issues/21370
appPathsRunnablePhase();
beforeElectronIsReady();
return (async () => {
await waitForElectronToBeReady();
await beforeApplicationIsLoading();
await showLoadingRunnablePhase();
await onLoadOfApplication();
await showInitialWindowRunnablePhase();
await afterApplicationIsLoaded();
})();
};
},
});
export default startMainApplicationInjectable;