diff --git a/.github/workflows/check-docs.yml b/.github/workflows/check-docs.yml index 9c942690b0..7418a86269 100644 --- a/.github/workflows/check-docs.yml +++ b/.github/workflows/check-docs.yml @@ -23,8 +23,8 @@ jobs: - name: Generate Extensions API Reference using typedocs run: | - yarn install - yarn typedocs-extensions-api + yarn install + yarn typedocs-extensions-api - name: Verify that the markdown is valid run: | diff --git a/.github/workflows/electronegativity.yml b/.github/workflows/electronegativity.yml index 6e634082c4..0cc4340f39 100644 --- a/.github/workflows/electronegativity.yml +++ b/.github/workflows/electronegativity.yml @@ -19,7 +19,7 @@ jobs: - uses: doyensec/electronegativity-action@v1.1 with: input: src/ - electron-version: "15.5.7" + electron-version: "19.0.4" severity: medium - name: Upload sarif diff --git a/.github/workflows/mkdocs-manual.yml b/.github/workflows/mkdocs-manual.yml index cda83b86c3..56bfd0c831 100644 --- a/.github/workflows/mkdocs-manual.yml +++ b/.github/workflows/mkdocs-manual.yml @@ -16,7 +16,7 @@ jobs: - name: Set up Python 3.7 uses: actions/setup-python@v2 with: - python-version: '3.x' + python-version: "3.x" - name: Install dependencies run: | @@ -28,7 +28,7 @@ jobs: uses: actions/checkout@v2 with: fetch-depth: 0 - ref: '${{ github.event.inputs.version }}' + ref: "${{ github.event.inputs.version }}" - name: Using Node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 @@ -43,8 +43,8 @@ jobs: - name: Checkout master branch from lens uses: actions/checkout@v2 with: - path: 'master' - ref: 'master' + path: "master" + ref: "master" - name: Bring in latest mkdocs.yml from master run: | diff --git a/.github/workflows/publish-release-npm.yml b/.github/workflows/publish-release-npm.yml index 79a06c000f..becca1f790 100644 --- a/.github/workflows/publish-release-npm.yml +++ b/.github/workflows/publish-release-npm.yml @@ -23,11 +23,11 @@ jobs: - name: Generate NPM package run: | - make build-npm + make build-npm - name: publish new release if: contains(github.ref, 'refs/tags/v') run: | - make publish-npm + make publish-npm env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.yarnrc b/.yarnrc index 22e66ac2fe..811b4fd7c3 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,3 +1,3 @@ -disturl "https://atom.io/download/electron" -target "15.5.0" +disturl "https://electronjs.org/headers" +target "19.0.4" runtime "electron" diff --git a/build/download_binaries.ts b/build/download_binaries.ts index 5b4f960c5e..ff26a8fb2b 100644 --- a/build/download_binaries.ts +++ b/build/download_binaries.ts @@ -13,7 +13,6 @@ import { promisify } from "util"; import { pipeline as _pipeline, Transform, Writable } from "stream"; import type { SingleBar } from "cli-progress"; import { MultiBar } from "cli-progress"; -import AbortController from "abort-controller"; import { extract } from "tar-stream"; import gunzip from "gunzip-maybe"; import { getBinaryName, normalizedPlatform } from "../src/common/vars"; diff --git a/docs/extensions/get-started/anatomy.md b/docs/extensions/get-started/anatomy.md index 8cfcd57076..4794261d8f 100644 --- a/docs/extensions/get-started/anatomy.md +++ b/docs/extensions/get-started/anatomy.md @@ -55,7 +55,7 @@ Some of the most-important fields include: "license": "MIT", "homepage": "https://github.com/lensapp/lens-extension-samples", "engines": { - "node": "^14.18.12", + "node": "^16.14.2", "lens": "5.4" }, "main": "dist/main.js", @@ -72,7 +72,7 @@ Some of the most-important fields include: "ts-loader": "^8.0.4", "typescript": "^4.5.5", "@types/react": "^17.0.44", - "@types/node": "^14.18.12", + "@types/node": "^16.14.2", "webpack": "^4.44.2", "webpack-cli": "^3.3.11" } diff --git a/package.json b/package.json index 3f0e438dea..781d97153a 100644 --- a/package.json +++ b/package.json @@ -222,7 +222,6 @@ "@sentry/integrations": "^6.19.3", "@side/jest-runtime": "^1.0.1", "@types/circular-dependency-plugin": "5.0.5", - "abort-controller": "^3.0.0", "auto-bind": "^4.0.0", "await-lock": "^2.2.2", "byline": "^5.0.0", @@ -369,7 +368,7 @@ "css-loader": "^6.7.1", "deepdash": "^5.3.9", "dompurify": "^2.3.10", - "electron": "^15.5.7", + "electron": "^19.0.4", "electron-builder": "^23.3.3", "electron-notarize": "^0.3.0", "esbuild": "^0.14.53", diff --git a/src/behaviours/tray/clicking-tray-menu-item-originating-from-extension.test.ts b/src/behaviours/tray/clicking-tray-menu-item-originating-from-extension.test.ts index 006760f902..31556a4b7d 100644 --- a/src/behaviours/tray/clicking-tray-menu-item-originating-from-extension.test.ts +++ b/src/behaviours/tray/clicking-tray-menu-item-originating-from-extension.test.ts @@ -8,6 +8,7 @@ import type { ApplicationBuilder } from "../../renderer/components/test-utils/ge import { getApplicationBuilder } from "../../renderer/components/test-utils/get-application-builder"; import loggerInjectable from "../../common/logger.injectable"; import type { Logger } from "../../common/logger"; +import getRandomIdInjectable from "../../common/utils/get-random-id.injectable"; describe("clicking tray menu item originating from extension", () => { let applicationBuilder: ApplicationBuilder; @@ -20,6 +21,7 @@ describe("clicking tray menu item originating from extension", () => { logErrorMock = jest.fn(); mainDi.override(loggerInjectable, () => ({ error: logErrorMock }) as unknown as Logger); + mainDi.override(getRandomIdInjectable, () => () => "some-random-id"); }); await applicationBuilder.render(); @@ -42,7 +44,7 @@ describe("clicking tray menu item originating from extension", () => { it("when item is clicked, triggers the click handler", () => { applicationBuilder.tray.click( - "some-label-tray-menu-item-for-extension-some-extension-id", + "some-random-id-tray-menu-item-for-extension-some-extension-id", ); expect(clickMock).toHaveBeenCalled(); @@ -55,13 +57,13 @@ describe("clicking tray menu item originating from extension", () => { }); applicationBuilder.tray.click( - "some-label-tray-menu-item-for-extension-some-extension-id", + "some-random-id-tray-menu-item-for-extension-some-extension-id", ); }); it("logs the error", () => { expect(logErrorMock).toHaveBeenCalledWith( - '[TRAY]: Clicking of tray item "some-label" from extension "some-extension-id" failed.', + '[TRAY]: Clicking of tray item "some-random-id" from extension "some-extension-id" failed.', expect.any(Error), ); }); @@ -72,13 +74,13 @@ describe("clicking tray menu item originating from extension", () => { clickMock.mockImplementation(() => Promise.reject("some-rejection")); applicationBuilder.tray.click( - "some-label-tray-menu-item-for-extension-some-extension-id", + "some-random-id-tray-menu-item-for-extension-some-extension-id", ); }); it("logs the error", () => { expect(logErrorMock).toHaveBeenCalledWith( - '[TRAY]: Clicking of tray item "some-label" from extension "some-extension-id" failed.', + '[TRAY]: Clicking of tray item "some-random-id" from extension "some-extension-id" failed.', "some-rejection", ); }); @@ -92,7 +94,7 @@ describe("clicking tray menu item originating from extension", () => { it("does not have the tray menu item from extension", () => { expect( applicationBuilder.tray.get( - "some-label-tray-menu-item-for-extension-some-extension-id", + "some-random-id-tray-menu-item-for-extension-some-extension-id", ), ).toBeNull(); }); @@ -103,7 +105,7 @@ describe("clicking tray menu item originating from extension", () => { expect( applicationBuilder.tray.get( - "some-label-tray-menu-item-for-extension-some-extension-id", + "some-random-id-tray-menu-item-for-extension-some-extension-id", ), ).not.toBeNull(); }); diff --git a/src/behaviours/tray/extension-adding-tray-items.test.tsx b/src/behaviours/tray/extension-adding-tray-items.test.tsx index 35e09e0f76..5d9004ba5e 100644 --- a/src/behaviours/tray/extension-adding-tray-items.test.tsx +++ b/src/behaviours/tray/extension-adding-tray-items.test.tsx @@ -13,6 +13,7 @@ describe("preferences: extension adding tray items", () => { let builder: ApplicationBuilder; let someObservableForVisibility: IObservableValue; let someObservableForEnabled: IObservableValue; + let someObservableLabel: IObservableValue; beforeEach(async () => { builder = getApplicationBuilder(); @@ -25,6 +26,7 @@ describe("preferences: extension adding tray items", () => { someObservableForVisibility = observable.box(false); someObservableForEnabled = observable.box(false); + someObservableLabel = observable.box("Some label"); const testExtension = getExtensionFake({ id: "some-extension-id", @@ -33,38 +35,51 @@ describe("preferences: extension adding tray items", () => { mainOptions: { trayMenus: [ { + id: "some-controlled-visibility", label: "some-controlled-visibility", click: () => {}, visible: computed(() => someObservableForVisibility.get()), }, { + id: "some-uncontrolled-visibility", label: "some-uncontrolled-visibility", click: () => {}, }, { + id: "some-controlled-enabled", label: "some-controlled-enabled", click: () => {}, enabled: computed(() => someObservableForEnabled.get()), }, { + id: "some-uncontrolled-enabled", label: "some-uncontrolled-enabled", click: () => {}, }, { + id: "some-statically-enabled", label: "some-statically-enabled", click: () => {}, enabled: true, }, { + id: "some-statically-disabled", label: "some-statically-disabled", click: () => {}, enabled: false, }, + + { + id: "some-item-with-controlled-label", + label: computed(() => someObservableLabel.get()), + click: () => {}, + enabled: true, + }, ], }, }); @@ -72,6 +87,37 @@ describe("preferences: extension adding tray items", () => { builder.extensions.enable(testExtension); }); + describe("given controlled label", () => { + it("has the label", () => { + const item = builder.tray.get( + "some-item-with-controlled-label-tray-menu-item-for-extension-some-extension", + ); + + expect(item?.label).toBe("Some label"); + }); + + it("when label changes, updates the label", () => { + runInAction(() => { + someObservableLabel.set("Some new label"); + }); + + const item = builder.tray.get( + "some-item-with-controlled-label-tray-menu-item-for-extension-some-extension", + ); + + expect(item?.label).toBe("Some new label"); + + }); + }); + + it("given item is statically disabled, item is disabled", () => { + const item = builder.tray.get( + "some-statically-disabled-tray-menu-item-for-extension-some-extension", + ); + + expect(item?.enabled).toBe(false); + }); + it("shows item which doesn't control the visibility", () => { expect( builder.tray.get( diff --git a/src/common/k8s-api/__tests__/kube-api.test.ts b/src/common/k8s-api/__tests__/kube-api.test.ts index 36f2aea748..0b0efd86da 100644 --- a/src/common/k8s-api/__tests__/kube-api.test.ts +++ b/src/common/k8s-api/__tests__/kube-api.test.ts @@ -6,7 +6,6 @@ import { forRemoteCluster, KubeApi } from "../kube-api"; import { KubeJsonApi } from "../kube-json-api"; import { KubeObject } from "../kube-object"; -import AbortController from "abort-controller"; import { delay } from "../../utils/delay"; import { PassThrough } from "stream"; import { ApiManager } from "../api-manager"; diff --git a/src/common/k8s-api/kube-api.ts b/src/common/k8s-api/kube-api.ts index 5427b22159..31fad33d72 100644 --- a/src/common/k8s-api/kube-api.ts +++ b/src/common/k8s-api/kube-api.ts @@ -19,7 +19,6 @@ import { KubeJsonApi } from "./kube-json-api"; import type { Disposer } from "../utils"; import { isDefined, noop, WrappedAbortController } from "../utils"; import type { RequestInit } from "node-fetch"; -import type AbortController from "abort-controller"; import type { AgentOptions } from "https"; import { Agent } from "https"; import type { Patch } from "rfc6902"; @@ -29,6 +28,9 @@ import logger from "../logger"; import { Environments, getEnvironmentSpecificLegacyGlobalDiForExtensionApi } from "../../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; import autoRegistrationEmitterInjectable from "./api-manager/auto-registration-emitter.injectable"; +// TODO: upgrade node-fetch once we are starting to use ES modules +type LegacyAbortSignal = NonNullable; + /** * The options used for creating a `KubeApi` */ @@ -717,7 +719,7 @@ export class KubeApi< const requestParams = timeout ? { query: { timeoutSeconds: timeout }} : {}; const watchUrl = this.getWatchUrl(namespace); const responsePromise = this.request.getResponse(watchUrl, requestParams, { - signal: abortController.signal, + signal: abortController.signal as LegacyAbortSignal, timeout: 600_000, }); diff --git a/src/common/k8s-api/kube-object.store.ts b/src/common/k8s-api/kube-object.store.ts index 0af08e560c..852e615332 100644 --- a/src/common/k8s-api/kube-object.store.ts +++ b/src/common/k8s-api/kube-object.store.ts @@ -15,13 +15,15 @@ import { ItemStore } from "../item.store"; import type { KubeApiQueryParams, KubeApi, KubeApiWatchCallback } from "./kube-api"; import { parseKubeApi } from "./kube-api-parse"; import type { RequestInit } from "node-fetch"; -import AbortController from "abort-controller"; import type { Patch } from "rfc6902"; import logger from "../logger"; import assert from "assert"; import type { PartialDeep } from "type-fest"; import { entries } from "../utils/objects"; +// TODO: upgrade node-fetch once we are starting to use ES modules +type LegacyAbortSignal = NonNullable; + export type OnLoadFailure = (error: unknown) => void; export interface KubeObjectStoreLoadingParams { @@ -477,7 +479,8 @@ export abstract class KubeObjectStore< callback, }); - const { signal } = abortController; + // TODO: upgrade node-fetch once we are starting to use ES modules + const signal = abortController.signal as LegacyAbortSignal; const callback: KubeApiWatchCallback = (data, error) => { if (!this.isLoaded || error?.type === "aborted") return; diff --git a/src/common/test-utils/get-global-override.ts b/src/common/test-utils/get-global-override.ts index 3a556df793..ac3c86a33e 100644 --- a/src/common/test-utils/get-global-override.ts +++ b/src/common/test-utils/get-global-override.ts @@ -4,6 +4,11 @@ */ import type { Injectable } from "@ogre-tools/injectable"; +export interface GlobalOverride { + injectable: Injectable; + overridingInstantiate: any; +} + export const getGlobalOverride = >( injectable: T, overridingInstantiate: T["instantiate"], diff --git a/src/common/utils/abort-controller.ts b/src/common/utils/abort-controller.ts index e115fd5868..5382281cf9 100644 --- a/src/common/utils/abort-controller.ts +++ b/src/common/utils/abort-controller.ts @@ -2,10 +2,13 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ -import AbortController from "abort-controller"; +/** + * This is like an `AbortController` but will also abort if the parent aborts, + * but won't make the parent abort if this aborts (single direction) + */ export class WrappedAbortController extends AbortController { - constructor(parent?: AbortController) { + constructor(parent?: AbortController | undefined) { super(); parent?.signal.addEventListener("abort", () => { diff --git a/src/common/utils/delay.ts b/src/common/utils/delay.ts index 2c4a5c455e..d86395026b 100644 --- a/src/common/utils/delay.ts +++ b/src/common/utils/delay.ts @@ -3,8 +3,6 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { AbortController } from "abort-controller"; - /** * Return a promise that will be resolved after at least `timeout` ms have * passed. If `failFast` is provided then the promise is also resolved if it has diff --git a/src/common/utils/reject-promise.ts b/src/common/utils/reject-promise.ts index 8212bacd3f..0211586783 100644 --- a/src/common/utils/reject-promise.ts +++ b/src/common/utils/reject-promise.ts @@ -3,8 +3,6 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import type { AbortSignal } from "abort-controller"; - /** * Creates a new promise that will be rejected when the signal rejects. * diff --git a/src/jest.setup.ts b/src/jest.setup.ts index 4b3f787cfc..c2f151ff76 100644 --- a/src/jest.setup.ts +++ b/src/jest.setup.ts @@ -8,6 +8,8 @@ import configurePackages from "./common/configure-packages"; import { configure } from "mobx"; import { setImmediate } from "timers"; import { TextEncoder, TextDecoder as TextDecoderNode } from "util"; +import glob from "glob"; +import path from "path"; // setup default configuration for external npm-packages configurePackages(); @@ -45,3 +47,13 @@ global.ResizeObserver = class { jest.mock("./renderer/components/monaco-editor/monaco-editor"); jest.mock("./renderer/components/tooltip/withTooltip"); + +const getInjectables = (environment: "renderer" | "main", filePathGlob: string) => + glob.sync(`./{common,extensions,${environment}}/**/${filePathGlob}`, { + cwd: __dirname, + }).map(x => path.resolve(__dirname, x)); + +(global as any).rendererInjectablePaths = getInjectables("renderer", "*.injectable.{ts,tsx}"); +(global as any).rendererGlobalOverridePaths = getInjectables("renderer", "*.global-override-for-injectable.{ts,tsx}"); +(global as any).mainInjectablePaths = getInjectables("main", "*.injectable.{ts,tsx}"); +(global as any).mainGlobalOverridePaths = getInjectables("main", "*.global-override-for-injectable.{ts,tsx}"); diff --git a/src/main/catalog-sources/kubeconfig-sync/manager.ts b/src/main/catalog-sources/kubeconfig-sync/manager.ts index 892fa97eb9..1d281bc936 100644 --- a/src/main/catalog-sources/kubeconfig-sync/manager.ts +++ b/src/main/catalog-sources/kubeconfig-sync/manager.ts @@ -11,9 +11,8 @@ import { watch } from "chokidar"; import type { Stats } from "fs"; import fs from "fs"; import path from "path"; -import type stream from "stream"; import type { Disposer } from "../../../common/utils"; -import { bytesToUnits, getOrInsertWith, iter, noop } from "../../../common/utils"; +import { disposer, bytesToUnits, getOrInsertWith, iter, noop } from "../../../common/utils"; import logger from "../../logger"; import type { KubeConfig } from "@kubernetes/client-node"; import { loadConfigFromString, splitConfig } from "../../../common/kube-helpers"; @@ -265,47 +264,35 @@ const diffChangedConfigFor = (dependencies: ComputeDiffDependencies) => ({ fileP return noop; } - // TODO: replace with an AbortController with fs.readFile when we upgrade to Node 16 (after it comes out) - const fileReader = fs.createReadStream(filePath, { - mode: fs.constants.O_RDONLY, + const controller = new AbortController(); + const fileContentsP = fs.promises.readFile(filePath, { + signal: controller.signal, }); - const readStream: stream.Readable = fileReader; - const decoder = new TextDecoder("utf-8", { fatal: true }); - let fileString = ""; - let closed = false; + const cleanup = disposer( + () => controller.abort(), + ); - const cleanup = () => { - closed = true; - fileReader.close(); // This may not close the stream. - // Artificially marking end-of-stream, as if the underlying resource had - // indicated end-of-file by itself, allows the stream to close. - // This does not cancel pending read operations, and if there is such an - // operation, the process may still not be able to exit successfully - // until it finishes. - fileReader.push(null); - fileReader.read(0); - readStream.removeAllListeners(); - }; + fileContentsP + .then((fileData) => { + const decoder = new TextDecoder("utf-8", { fatal: true }); - readStream - .on("data", (chunk: Buffer) => { try { - fileString += decoder.decode(chunk, { stream: true }); + const fileString = decoder.decode(fileData); + + computeDiff(dependencies)(fileString, source, filePath); } catch (error) { logger.warn(`${logPrefix} skipping ${filePath}: ${error}`); source.clear(); cleanup(); } }) - .on("close", () => cleanup()) - .on("error", error => { - cleanup(); - logger.warn(`${logPrefix} failed to read file: ${error}`, { filePath }); - }) - .on("end", () => { - if (!closed) { - computeDiff(dependencies)(fileString, source, filePath); + .catch(error => { + if (controller.signal.aborted) { + return; } + + logger.warn(`${logPrefix} failed to read file: ${error}`, { filePath }); + cleanup(); }); return cleanup; diff --git a/src/main/cluster-detectors/distribution-detector.ts b/src/main/cluster-detectors/distribution-detector.ts index c912fcf5b5..dcdacfb253 100644 --- a/src/main/cluster-detectors/distribution-detector.ts +++ b/src/main/cluster-detectors/distribution-detector.ts @@ -96,8 +96,6 @@ export class DistributionDetector extends BaseClusterDetector { } public async getKubernetesVersion() { - if (this.cluster.version) return this.cluster.version; - const response = await this.k8sRequest("/version"); return response.gitVersion; diff --git a/src/main/getDiForUnitTesting.ts b/src/main/getDiForUnitTesting.ts index 9c5a3ab741..2e9e5ad26d 100644 --- a/src/main/getDiForUnitTesting.ts +++ b/src/main/getDiForUnitTesting.ts @@ -3,8 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import glob from "glob"; -import { kebabCase, memoize, noop, chunk } from "lodash/fp"; +import { kebabCase, noop, chunk } from "lodash/fp"; import type { DiContainer, Injectable } from "@ogre-tools/injectable"; import { createContainer } from "@ogre-tools/injectable"; import { Environments, setLegacyGlobalDiForExtensionApi } from "../extensions/as-legacy-globals-for-extension-api/legacy-global-di-for-extension-api"; @@ -101,6 +100,7 @@ import { registerMobX } from "@ogre-tools/injectable-extension-for-mobx"; import electronInjectable from "./utils/resolve-system-proxy/electron.injectable"; import type { HotbarStore } from "../common/hotbars/store"; import focusApplicationInjectable from "./electron-app/features/focus-application.injectable"; +import type { GlobalOverride } from "../common/test-utils/get-global-override"; export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {}) { const { @@ -113,9 +113,9 @@ export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {}) setLegacyGlobalDiForExtensionApi(di, Environments.main); - const filePaths = getInjectableFilePaths(); - - const injectables = filePaths.map(filePath => require(filePath).default); + const injectables: Injectable[] = (global as any).mainInjectablePaths.map( + (filePath: string) => require(filePath).default, + ); chunk(100)(injectables).forEach(chunkInjectables => { di.register(...chunkInjectables); @@ -124,10 +124,8 @@ export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {}) di.preventSideEffects(); if (doGeneralOverrides) { - const globalOverrideFilePaths = getGlobalOverridePaths(); - - const globalOverrides = globalOverrideFilePaths.map( - (filePath) => require(filePath).default, + const globalOverrides: GlobalOverride[] = (global as any).mainGlobalOverridePaths.map( + (filePath: string) => require(filePath).default, ); globalOverrides.forEach(globalOverride => { @@ -215,20 +213,6 @@ export function getDiForUnitTesting(opts: { doGeneralOverrides?: boolean } = {}) return di; } -const getInjectableFilePaths = memoize(() => [ - ...glob.sync("./**/*.injectable.{ts,tsx}", { cwd: __dirname }), - ...glob.sync("../extensions/**/*.injectable.{ts,tsx}", { cwd: __dirname }), - ...glob.sync("../common/**/*.injectable.{ts,tsx}", { cwd: __dirname }), -]); - -const getGlobalOverridePaths = memoize(() => - glob.sync( - "../{common,extensions,main}/**/*.global-override-for-injectable.{ts,tsx}", - - { cwd: __dirname }, - ), -); - // TODO: Reorganize code in Runnables to get rid of requirement for override const overrideRunnablesHavingSideEffects = (di: DiContainer) => { [ diff --git a/src/main/start-main-application/lens-window/application-window/create-electron-window.injectable.ts b/src/main/start-main-application/lens-window/application-window/create-electron-window.injectable.ts index 7521a64696..2eef4b8196 100644 --- a/src/main/start-main-application/lens-window/application-window/create-electron-window.injectable.ts +++ b/src/main/start-main-application/lens-window/application-window/create-electron-window.injectable.ts @@ -77,9 +77,7 @@ const createElectronWindowInjectable = getInjectable({ webPreferences: { nodeIntegration: true, nodeIntegrationInSubFrames: true, - webviewTag: true, contextIsolation: false, - nativeWindowOpen: false, }, }); @@ -89,20 +87,16 @@ const createElectronWindowInjectable = getInjectable({ .on("focus", () => { configuration.onFocus?.(); }) - .on("blur", () => { configuration.onBlur?.(); }) - .on("closed", () => { configuration.onClose(); applicationWindowState.unmanage(); }) - .webContents.on("dom-ready", () => { configuration.onDomReady?.(); }) - .on("did-fail-load", (_event, code, desc) => { logger.error( `[CREATE-ELECTRON-WINDOW]: Failed to load window "${configuration.id}"`, @@ -112,52 +106,11 @@ const createElectronWindowInjectable = getInjectable({ }, ); }) - .on("did-finish-load", () => { logger.info( `[CREATE-ELECTRON-WINDOW]: Window "${configuration.id}" loaded`, ); }) - - .on("will-attach-webview", (event, webPreferences, params) => { - logger.debug( - `[CREATE-ELECTRON-WINDOW]: Attaching webview to window "${configuration.id}"`, - ); - // Following is security recommendations because we allow webview tag (webviewTag: true) - // suggested by https://www.electronjs.org/docs/tutorial/security#11-verify-webview-options-before-creation - // and https://www.electronjs.org/docs/tutorial/security#10-do-not-use-allowpopups - - if (webPreferences.preload) { - logger.warn( - "[CREATE-ELECTRON-WINDOW]: Strip away preload scripts of webview", - ); - delete webPreferences.preload; - } - - // @ts-expect-error some electron version uses webPreferences.preloadURL/webPreferences.preload - if (webPreferences.preloadURL) { - logger.warn( - "[CREATE-ELECTRON-WINDOW]: Strip away preload scripts of webview", - ); - delete webPreferences.preload; - } - - if (params.allowpopups) { - logger.warn( - "[CREATE-ELECTRON-WINDOW]: We do not allow allowpopups props, stop webview from renderer", - ); - - // event.preventDefault() will destroy the guest page. - event.preventDefault(); - - return; - } - - // Always disable Node.js integration for all webviews - webPreferences.nodeIntegration = false; - webPreferences.nativeWindowOpen = false; - }) - .setWindowOpenHandler((details) => { openLinkInBrowser(details.url).catch((error) => { logger.error("[CREATE-ELECTRON-WINDOW]: failed to open browser", { diff --git a/src/main/tray/tray-menu-item/tray-menu-item-registrator.injectable.ts b/src/main/tray/tray-menu-item/tray-menu-item-registrator.injectable.ts index 136f869b8f..f1ebe7885d 100644 --- a/src/main/tray/tray-menu-item/tray-menu-item-registrator.injectable.ts +++ b/src/main/tray/tray-menu-item/tray-menu-item-registrator.injectable.ts @@ -3,7 +3,6 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ import { pipeline } from "@ogre-tools/fp"; -import { kebabCase } from "lodash/fp"; import type { Injectable } from "@ogre-tools/injectable"; import { getInjectable } from "@ogre-tools/injectable"; import { computed } from "mobx"; @@ -16,7 +15,7 @@ import { withErrorSuppression } from "../../../common/utils/with-error-suppressi import type { WithErrorLoggingFor } from "../../../common/utils/with-error-logging/with-error-logging.injectable"; import withErrorLoggingInjectable from "../../../common/utils/with-error-logging/with-error-logging.injectable"; import getRandomIdInjectable from "../../../common/utils/get-random-id.injectable"; -import { isBoolean } from "../../../common/utils"; +import { isBoolean, isString } from "../../../common/utils"; const trayMenuItemRegistratorInjectable = getInjectable({ id: "tray-menu-item-registrator", @@ -38,7 +37,7 @@ export default trayMenuItemRegistratorInjectable; const toItemInjectablesFor = (extension: LensMainExtension, withErrorLoggingFor: WithErrorLoggingFor, getRandomId: () => string) => { const _toItemInjectables = (parentId: string | null) => (registration: TrayMenuRegistration): Injectable[] => { - const trayItemId = registration.id || kebabCase(registration.label || getRandomId()); + const trayItemId = registration.id || getRandomId(); const id = `${trayItemId}-tray-menu-item-for-extension-${extension.sanitizedExtensionId}`; const parentInjectable = getInjectable({ @@ -51,7 +50,18 @@ const toItemInjectablesFor = (extension: LensMainExtension, withErrorLoggingFor: separator: registration.type === "separator", - label: computed(() => registration.label || ""), + label: computed(() => { + if (!registration.label) { + return ""; + } + + if (isString(registration.label)) { + return registration.label; + } + + return registration.label.get(); + }), + tooltip: registration.toolTip, click: () => { diff --git a/src/main/tray/tray-menu-registration.ts b/src/main/tray/tray-menu-registration.ts index c192dd89fb..8b316b7b92 100644 --- a/src/main/tray/tray-menu-registration.ts +++ b/src/main/tray/tray-menu-registration.ts @@ -6,7 +6,7 @@ import type { IComputedValue } from "mobx"; export interface TrayMenuRegistration { - label?: string; + label?: string | IComputedValue; click?: (menuItem: TrayMenuRegistration) => void; id?: string; type?: "normal" | "separator" | "submenu"; diff --git a/src/renderer/components/tabs/tabs.scss b/src/renderer/components/tabs/tabs.scss index 2bee04e024..2eaf698676 100644 --- a/src/renderer/components/tabs/tabs.scss +++ b/src/renderer/components/tabs/tabs.scss @@ -54,11 +54,10 @@ position: absolute; right: 0; bottom: 0; - width: 0; - height: $unit * 0.5; - transition: width 150ms; + height: 3px; + transition: opacity 150ms; background: currentColor; - color: var(--halfGray) + opacity: 0; } &:focus { @@ -76,6 +75,7 @@ left: 0; right: auto; color: var(--line-color-active); + opacity: 1; } } } diff --git a/src/renderer/getDiForUnitTesting.tsx b/src/renderer/getDiForUnitTesting.tsx index 4b8a69c090..5789f513a4 100644 --- a/src/renderer/getDiForUnitTesting.tsx +++ b/src/renderer/getDiForUnitTesting.tsx @@ -3,8 +3,7 @@ * Licensed under MIT License. See LICENSE in root directory for more information. */ -import glob from "glob"; -import { memoize, noop, chunk } from "lodash/fp"; +import { noop, chunk } from "lodash/fp"; import type { DiContainer, Injectable } from "@ogre-tools/injectable"; import { createContainer, @@ -73,6 +72,7 @@ import forceUpdateModalRootFrameComponentInjectable from "./application-update/f import legacyOnChannelListenInjectable from "./ipc/legacy-channel-listen.injectable"; import getEntitySettingCommandsInjectable from "./components/command-palette/registered-commands/get-entity-setting-commands.injectable"; import storageSaveDelayInjectable from "./utils/create-storage/storage-save-delay.injectable"; +import type { GlobalOverride } from "../common/test-utils/get-global-override"; export const getDiForUnitTesting = (opts: { doGeneralOverrides?: boolean } = {}) => { const { @@ -85,9 +85,9 @@ export const getDiForUnitTesting = (opts: { doGeneralOverrides?: boolean } = {}) setLegacyGlobalDiForExtensionApi(di, Environments.renderer); - const filePaths = getInjectableFilePaths(); - - const injectables = filePaths.map(filePath => require(filePath).default); + const injectables: Injectable[] = (global as any).rendererInjectablePaths.map( + (filePath: string) => require(filePath).default, + ); chunk(100)(injectables).forEach(chunkInjectables => { di.register(...chunkInjectables); @@ -96,10 +96,8 @@ export const getDiForUnitTesting = (opts: { doGeneralOverrides?: boolean } = {}) di.preventSideEffects(); if (doGeneralOverrides) { - const globalOverrideFilePaths = getGlobalOverridePaths(); - - const globalOverrides = globalOverrideFilePaths.map( - (filePath) => require(filePath).default, + const globalOverrides: GlobalOverride[] = (global as any).rendererGlobalOverridePaths.map( + (filePath: string) => require(filePath).default, ); globalOverrides.forEach(globalOverride => { @@ -232,20 +230,6 @@ export const getDiForUnitTesting = (opts: { doGeneralOverrides?: boolean } = {}) return di; }; -const getInjectableFilePaths = memoize(() => [ - ...glob.sync("./**/*.injectable.{ts,tsx}", { cwd: __dirname }), - ...glob.sync("../common/**/*.injectable.{ts,tsx}", { cwd: __dirname }), - ...glob.sync("../extensions/**/*.injectable.{ts,tsx}", { cwd: __dirname }), -]); - -const getGlobalOverridePaths = memoize(() => - glob.sync( - "../{common,extensions,renderer}/**/*.global-override-for-injectable.{ts,tsx}", - - { cwd: __dirname }, - ), -); - const overrideFunctionalInjectables = (di: DiContainer, injectables: Injectable[]) => { injectables.forEach(injectable => { di.override(injectable, () => () => { diff --git a/src/renderer/kube-watch-api/kube-watch-api.ts b/src/renderer/kube-watch-api/kube-watch-api.ts index c28ea2e794..e1de15defe 100644 --- a/src/renderer/kube-watch-api/kube-watch-api.ts +++ b/src/renderer/kube-watch-api/kube-watch-api.ts @@ -5,11 +5,14 @@ import { comparer, reaction } from "mobx"; import type { Disposer } from "../../common/utils"; import { disposer, getOrInsert, noop, WrappedAbortController } from "../../common/utils"; -import AbortController from "abort-controller"; import { once } from "lodash"; import type { ClusterFrameContext } from "../cluster-frame-context/cluster-frame-context"; import logger from "../../common/logger"; import type { KubeObjectStoreLoadAllParams, KubeObjectStoreSubscribeParams } from "../../common/k8s-api/kube-object.store"; +import type { RequestInit } from "node-fetch"; + +// TODO: upgrade node-fetch once we are starting to use ES modules +type LegacyAbortSignal = NonNullable; // Kubernetes watch-api client // API: https://developer.mozilla.org/en-US/docs/Web/API/Streams_API/Using_readable_streams @@ -103,7 +106,7 @@ export class KubeWatchApi { const loadThenSubscribe = async (namespaces: string[] | undefined) => { try { - await store.loadAll({ namespaces, reqInit: { signal: childController.signal }, onLoadFailure }); + await store.loadAll({ namespaces, reqInit: { signal: childController.signal as LegacyAbortSignal }, onLoadFailure }); unsubscribe.push(store.subscribe({ onLoadFailure, abortController: childController })); } catch (error) { if (!(error instanceof DOMException)) { diff --git a/yarn.lock b/yarn.lock index 996efa8a03..22b146443b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -481,7 +481,7 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== -"@electron/get@^1.13.0": +"@electron/get@^1.14.1": version "1.14.1" resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.14.1.tgz#16ba75f02dffb74c23965e72d617adc721d27f40" integrity sha512-BrZYyL/6m0ZXz/lDxy/nlVhQz+WF+iPS6qXolEU8atw7h6v1aYkjwJZ63m+bJMBTxDE66X+r2tPS4a/8C82sZw== @@ -2106,19 +2106,19 @@ form-data "^3.0.0" "@types/node@*": - version "17.0.31" - resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.31.tgz#a5bb84ecfa27eec5e1c802c6bbf8139bdb163a5d" - integrity sha512-AR0x5HbXGqkEx9CadRH3EBYx/VkiUgZIhP4wvPn/+5KIsgpNoyFaRlVe0Zlx9gRtg8fA06a9tskE2MSN7TcG4Q== + version "17.0.24" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.24.tgz#20ba1bf69c1b4ab405c7a01e950c4f446b05029f" + integrity sha512-aveCYRQbgTH9Pssp1voEP7HiuWlD2jW2BO56w+bVrJn04i61yh6mRfoKO6hEYQD9vF+W8Chkwc6j1M36uPkx4g== "@types/node@^10.12.0": version "10.17.60" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b" integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== -"@types/node@^14.6.2": - version "14.18.18" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.18.tgz#5c9503030df484ccffcbb935ea9a9e1d6fad1a20" - integrity sha512-B9EoJFjhqcQ9OmQrNorItO+OwEOORNn3S31WuiHvZY/dm9ajkB7AKD/8toessEtHHNL+58jofbq7hMMY9v4yig== +"@types/node@^16.11.26": + version "16.11.34" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.34.tgz#520224e4be4448c279ecad09639ab460cc441a50" + integrity sha512-UrWGDyLAlQ2Z8bNOGWTsqbP9ZcBeTYBVuTRNxXTztBy5KhWUFI3BaeDWoCP/CzV/EVGgO1NTYzv9ZytBI9GAEw== "@types/node@^16.11.47": version "16.11.47" @@ -2838,14 +2838,15 @@ abbrev@1, abbrev@~1.1.1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== -abort-controller@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" - integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== +accepts@~1.3.4, accepts@~1.3.5: + version "1.3.7" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" + integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== dependencies: - event-target-shim "^5.0.0" + mime-types "~2.1.24" + negotiator "0.6.2" -accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: +accepts@~1.3.8: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== @@ -4483,11 +4484,16 @@ copy-concurrently@^1.0.0: rimraf "^2.5.4" run-queue "^1.0.0" -core-js-pure@^3.20.2, core-js-pure@^3.8.1: +core-js-pure@^3.20.2: version "3.22.5" resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.22.5.tgz#bdee0ed2f9b78f2862cda4338a07b13a49b6c9a9" integrity sha512-8xo9R00iYD7TcV7OrC98GwxiUEAabVWO3dix+uyWjnYrx9fyASLlIX+f/3p5dW5qByaP2bcZ8X/T47s55et/tA== +core-js-pure@^3.8.1: + version "3.21.0" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.21.0.tgz#819adc8dfb808205ce25b51d50591becd615db7e" + integrity sha512-VaJUunCZLnxuDbo1rNOzwbet9E1K9joiXS5+DQMPtgxd24wfsZbJZMMfQLGYMlCUvSxLfsRUUhoOR2x28mFfeg== + core-util-is@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -5325,13 +5331,13 @@ electron-window-state@^5.0.3: jsonfile "^4.0.0" mkdirp "^0.5.1" -electron@^15.5.7: - version "15.5.7" - resolved "https://registry.yarnpkg.com/electron/-/electron-15.5.7.tgz#aadb0081c504f2c2d8f81ea5fd23e38881afe86a" - integrity sha512-n4mVlxoMc4eYx07wWFWGficL+iOMz5xZEf5dBtE/wwLm0fQpYVyW4AlknMFG9F8Css0MM0JSwNMOyRg5e1vDtg== +electron@^19.0.4: + version "19.0.4" + resolved "https://registry.yarnpkg.com/electron/-/electron-19.0.4.tgz#a88d5e542868c4abd7704228ec62c553605605a0" + integrity sha512-roRYr1VNAWIhjD9n8qZdmhROtrzsFpuZEXrjWAw+GqPbZlrUInmvFCviRDC2Lt+VBsTNRpTfPpfzXSlLL4reEw== dependencies: - "@electron/get" "^1.13.0" - "@types/node" "^14.6.2" + "@electron/get" "^1.14.1" + "@types/node" "^16.11.26" extract-zip "^1.0.3" emittery@^0.10.2: @@ -5910,11 +5916,6 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= -event-target-shim@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" - integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== - eventemitter3@^4.0.0: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" @@ -9732,6 +9733,11 @@ needle@^2.5.2: iconv-lite "^0.4.4" sax "^1.2.4" +negotiator@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + negotiator@0.6.3, negotiator@^0.6.2: version "0.6.3" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"