diff --git a/src/common/ipc/__tests__/type-enforced-ipc.test.ts b/src/common/ipc/__tests__/type-enforced-ipc.test.ts deleted file mode 100644 index bd456257cb..0000000000 --- a/src/common/ipc/__tests__/type-enforced-ipc.test.ts +++ /dev/null @@ -1,131 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import { EventEmitter } from "events"; -import { onCorrect, onceCorrect } from "../type-enforced-ipc"; - -describe("type enforced ipc tests", () => { - describe("onCorrect tests", () => { - it("should call the handler if the args are valid", () => { - let called = false; - const source = new EventEmitter(); - const listener = () => called = true; - const verifier = (args: unknown[]): args is [] => true; - const channel = "foobar"; - - onCorrect({ source, listener, verifier, channel }); - - source.emit(channel); - expect(called).toBe(true); - }); - - it("should not call the handler if the args are not valid", () => { - let called = false; - const source = new EventEmitter(); - const listener = () => called = true; - const verifier = (args: unknown[]): args is [] => false; - const channel = "foobar"; - - onCorrect({ source, listener, verifier, channel }); - - source.emit(channel); - expect(called).toBe(false); - }); - - it("should call the handler twice if the args are valid on two emits", () => { - let called = 0; - const source = new EventEmitter(); - const listener = () => called += 1; - const verifier = (args: unknown[]): args is [] => true; - const channel = "foobar"; - - onCorrect({ source, listener, verifier, channel }); - - source.emit(channel); - source.emit(channel); - expect(called).toBe(2); - }); - - it("should call the handler twice if the args are [valid, invalid, valid]", () => { - let called = 0; - const source = new EventEmitter(); - const listener = () => called += 1; - const results = [true, false, true]; - const verifier = (args: unknown[]): args is [] => results.pop() ?? false; - const channel = "foobar"; - - onCorrect({ source, listener, verifier, channel }); - - source.emit(channel); - source.emit(channel); - source.emit(channel); - expect(called).toBe(2); - }); - }); - - describe("onceCorrect tests", () => { - it("should call the handler if the args are valid", () => { - let called = false; - const source = new EventEmitter(); - const listener = () => called = true; - const verifier = (args: unknown[]): args is [] => true; - const channel = "foobar"; - - onceCorrect({ source, listener, verifier, channel }); - - source.emit(channel); - expect(called).toBe(true); - }); - - it("should not call the handler if the args are not valid", () => { - let called = false; - const source = new EventEmitter(); - const listener = () => called = true; - const verifier = (args: unknown[]): args is [] => false; - const channel = "foobar"; - - onceCorrect({ source, listener, verifier, channel }); - - source.emit(channel); - expect(called).toBe(false); - }); - - it("should call the handler only once even if args are valid multiple times", () => { - let called = 0; - const source = new EventEmitter(); - const listener = () => called += 1; - const verifier = (args: unknown[]): args is [] => true; - const channel = "foobar"; - - onceCorrect({ source, listener, verifier, channel }); - - source.emit(channel); - source.emit(channel); - expect(called).toBe(1); - }); - - it("should call the handler on only the first valid set of args", () => { - let called = ""; - let verifierCalled = 0; - const source = new EventEmitter(); - const listener = (info: any, arg: string) => called = arg; - const verifier = (args: unknown[]): args is [string] => (++verifierCalled) % 3 === 0; - const channel = "foobar"; - - onceCorrect({ source, listener, verifier, channel }); - - source.emit(channel, {}, "a"); - source.emit(channel, {}, "b"); - source.emit(channel, {}, "c"); - source.emit(channel, {}, "d"); - source.emit(channel, {}, "e"); - source.emit(channel, {}, "f"); - source.emit(channel, {}, "g"); - source.emit(channel, {}, "h"); - source.emit(channel, {}, "i"); - expect(called).toBe("c"); - }); - }); -}); diff --git a/src/common/ipc/type-enforced-ipc.ts b/src/common/ipc/type-enforced-ipc.ts deleted file mode 100644 index d37dce2f85..0000000000 --- a/src/common/ipc/type-enforced-ipc.ts +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Copyright (c) OpenLens Authors. All rights reserved. - * Licensed under MIT License. See LICENSE in root directory for more information. - */ - -import type { EventEmitter } from "events"; -import { ipcMain } from "electron"; -import logger from "../../main/logger"; -import type { Disposer } from "../utils"; -import { ipcMainHandle } from "./ipc"; - -export type ListenerEvent = Parameters[1]>[0]; -export type ListVerifier = (args: unknown[]) => args is T; -export type Rest = T extends [any, ...infer R] ? R : []; - -/** - * Adds a listener to `source` that waits for the first IPC message with the correct - * argument data is sent. - * @param channel The channel to be listened on - * @param listener The function for the channel to be called if the args of the correct type - * @param verifier The function to be called to verify that the args are the correct type - */ -export function onceCorrect< - IPC extends EventEmitter, - Listener extends (event: ListenerEvent, ...args: any[]) => any, ->({ - source, - channel, - listener, - verifier, -}: { - source: IPC; - channel: string; - listener: Listener; - verifier: ListVerifier>>; -}): void { - function wrappedListener(event: ListenerEvent, ...args: unknown[]): void { - if (verifier(args)) { - source.removeListener(channel, wrappedListener); // remove immediately - - (async () => (listener(event, ...args)))() // might return a promise, or throw, or reject - .catch((error: any) => logger.error("[IPC]: channel once handler threw error", { channel, error })); - } else { - logger.error("[IPC]: channel was emitted with invalid data", { channel, args }); - } - } - - source.on(channel, wrappedListener); -} - -/** - * Adds a listener to `source` that checks to verify the arguments before calling the handler. - * @param channel The channel to be listened on - * @param listener The function for the channel to be called if the args of the correct type - * @param verifier The function to be called to verify that the args are the correct type - */ -export function onCorrect< - IPC extends EventEmitter, - Listener extends (event: ListenerEvent, ...args: any[]) => any, ->({ - source, - channel, - listener, - verifier, -}: { - source: IPC; - channel: string; - listener: Listener; - verifier: ListVerifier>>; -}): Disposer { - function wrappedListener(event: ListenerEvent, ...args: unknown[]) { - if (verifier(args)) { - (async () => (listener(event, ...args)))() // might return a promise, or throw, or reject - .catch(error => logger.error("[IPC]: channel on handler threw error", { channel, error })); - } else { - logger.error("[IPC]: channel was emitted with invalid data", { channel, args }); - } - } - - source.on(channel, wrappedListener); - - return () => source.off(channel, wrappedListener); -} - -export function handleCorrect< - Handler extends (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any, ->({ - channel, - handler, - verifier, -}: { - channel: string; - handler: Handler; - verifier: ListVerifier>>; -}): Disposer { - function wrappedHandler(event: Electron.IpcMainInvokeEvent, ...args: unknown[]): ReturnType { - if (verifier(args)) { - return handler(event, ...args); - } - - throw new TypeError(`Invalid args for invoke on channel: ${channel}`); - } - - ipcMainHandle(channel, wrappedHandler); - - return () => ipcMain.removeHandler(channel); -} diff --git a/src/renderer/ipc/register-listeners.tsx b/src/renderer/ipc/register-listeners.tsx index 4d2ed9f5c8..c7b447c334 100644 --- a/src/renderer/ipc/register-listeners.tsx +++ b/src/renderer/ipc/register-listeners.tsx @@ -5,10 +5,9 @@ import type { IpcRendererEvent } from "electron"; import { ipcRenderer } from "electron"; -import { onCorrect } from "../../common/ipc"; import { Notifications } from "../components/notifications"; import { defaultHotbarCells } from "../../common/hotbars/types"; -import { type ListNamespaceForbiddenArgs, clusterListNamespaceForbiddenChannel, isListNamespaceForbiddenArgs } from "../../common/ipc/cluster"; +import { type ListNamespaceForbiddenArgs, clusterListNamespaceForbiddenChannel } from "../../common/ipc/cluster"; import { hotbarTooManyItemsChannel } from "../../common/ipc/hotbar"; function HotbarTooManyItemsHandler(): void { @@ -23,15 +22,6 @@ interface Dependencies { } export const registerIpcListeners = ({ listNamespacesForbiddenHandler }: Dependencies) => () => { - onCorrect({ - source: ipcRenderer, - channel: clusterListNamespaceForbiddenChannel, - listener: listNamespacesForbiddenHandler, - verifier: isListNamespaceForbiddenArgs, - }); - onCorrect({ - source: ipcRenderer, - channel: hotbarTooManyItemsChannel, - listener: HotbarTooManyItemsHandler, - verifier: (args: unknown[]): args is [] => args.length === 0, - });}; + ipcRenderer.on(clusterListNamespaceForbiddenChannel, listNamespacesForbiddenHandler); + ipcRenderer.on(hotbarTooManyItemsChannel, HotbarTooManyItemsHandler); +}; diff --git a/src/renderer/protocol-handler/lens-protocol-router-renderer/lens-protocol-router-renderer.tsx b/src/renderer/protocol-handler/lens-protocol-router-renderer/lens-protocol-router-renderer.tsx index 8015678361..0875037134 100644 --- a/src/renderer/protocol-handler/lens-protocol-router-renderer/lens-protocol-router-renderer.tsx +++ b/src/renderer/protocol-handler/lens-protocol-router-renderer/lens-protocol-router-renderer.tsx @@ -7,30 +7,10 @@ import React from "react"; import { ipcRenderer } from "electron"; import * as proto from "../../../common/protocol-handler"; import Url from "url-parse"; -import { onCorrect } from "../../../common/ipc"; import type { LensProtocolRouterDependencies } from "../../../common/protocol-handler"; import { foldAttemptResults, ProtocolHandlerInvalid, RouteAttempt } from "../../../common/protocol-handler"; import { Notifications } from "../../components/notifications"; -function verifyIpcArgs(args: unknown[]): args is [string, RouteAttempt] { - if (args.length !== 2) { - return false; - } - - if (typeof args[0] !== "string") { - return false; - } - - switch (args[1]) { - case RouteAttempt.MATCHED: - case RouteAttempt.MISSING: - case RouteAttempt.MISSING_EXTENSION: - return true; - default: - return false; - } -} - interface Dependencies extends LensProtocolRouterDependencies {} export class LensProtocolRouterRenderer extends proto.LensProtocolRouter { @@ -42,75 +22,58 @@ export class LensProtocolRouterRenderer extends proto.LensProtocolRouter { * This function is needed to be called early on in the renderers lifetime. */ public init(): void { - onCorrect({ - channel: proto.ProtocolHandlerInternal, - source: ipcRenderer, - verifier: verifyIpcArgs, - listener: (event, rawUrl, mainAttemptResult) => { - const rendererAttempt = this._routeToInternal(new Url(rawUrl, true)); + ipcRenderer.on(proto.ProtocolHandlerInternal, (event, rawUrl: string, mainAttemptResult: RouteAttempt) => { + const rendererAttempt = this._routeToInternal(new Url(rawUrl, true)); - if (foldAttemptResults(mainAttemptResult, rendererAttempt) === RouteAttempt.MISSING) { + if (foldAttemptResults(mainAttemptResult, rendererAttempt) === RouteAttempt.MISSING) { + Notifications.shortInfo(( +

+ {"Unknown action "} + {rawUrl} + {". Are you on the latest version?"} +

+ )); + } + }); + ipcRenderer.on(proto.ProtocolHandlerExtension, async (event, rawUrl: string, mainAttemptResult: RouteAttempt) => { + const rendererAttempt = await this._routeToExtension(new Url(rawUrl, true)); + + switch (foldAttemptResults(mainAttemptResult, rendererAttempt)) { + case RouteAttempt.MISSING: Notifications.shortInfo((

{"Unknown action "} {rawUrl} - {". Are you on the latest version?"} + {". Are you on the latest version of the extension?"}

)); - } - }, - }); - onCorrect({ - channel: proto.ProtocolHandlerExtension, - source: ipcRenderer, - verifier: verifyIpcArgs, - listener: async (event, rawUrl, mainAttemptResult) => { - const rendererAttempt = await this._routeToExtension(new Url(rawUrl, true)); - - switch (foldAttemptResults(mainAttemptResult, rendererAttempt)) { - case RouteAttempt.MISSING: - Notifications.shortInfo(( -

- {"Unknown action "} - {rawUrl} - {". Are you on the latest version of the extension?"} -

- )); - break; - case RouteAttempt.MISSING_EXTENSION: - Notifications.shortInfo(( -

- {"Missing extension for action "} - {rawUrl} - {". Not able to find extension in our known list. Try installing it manually."} -

- )); - break; - } - }, - }); - onCorrect({ - channel: ProtocolHandlerInvalid, - source: ipcRenderer, - listener: (event, error, rawUrl) => { - Notifications.error(( - <> + break; + case RouteAttempt.MISSING_EXTENSION: + Notifications.shortInfo((

- {"Failed to route "} + {"Missing extension for action "} {rawUrl} - . + {". Not able to find extension in our known list. Try installing it manually."}

-

- Error: - {" "} - {error} -

- - )); - }, - verifier: (args): args is [string, string] => { - return args.length === 2 && typeof args[0] === "string"; - }, + )); + break; + } + }); + ipcRenderer.on(ProtocolHandlerInvalid, (event, error: string, rawUrl: string) => { + Notifications.error(( + <> +

+ {"Failed to route "} + {rawUrl} + . +

+

+ Error: + {" "} + {error} +

+ + )); }); } }