From ccac859b0ba585bde42f89735b77c01c37c70bd2 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Fri, 21 May 2021 10:34:06 -0400 Subject: [PATCH] Change where extension IPC is exported - Now under Ipc.IpcMain and Ipc.IpcRenderer - Allow listener's to be cleaned up early by extensions Signed-off-by: Sebastian Malton --- src/extensions/core-api/index.ts | 2 + src/extensions/core-api/ipc.ts | 23 +++++++++++ src/extensions/core-api/stores.ts | 2 - .../{main-ipc-store.ts => ipc/ipc-main.ts} | 40 +++++++++++++------ .../{ipc-store.ts => ipc/ipc-registrar.ts} | 13 ++++-- .../ipc-renderer.ts} | 31 ++++++++++---- 6 files changed, 86 insertions(+), 25 deletions(-) create mode 100644 src/extensions/core-api/ipc.ts rename src/extensions/{main-ipc-store.ts => ipc/ipc-main.ts} (58%) rename src/extensions/{ipc-store.ts => ipc/ipc-registrar.ts} (79%) rename src/extensions/{renderer-ipc-store.ts => ipc/ipc-renderer.ts} (59%) diff --git a/src/extensions/core-api/index.ts b/src/extensions/core-api/index.ts index 56e4076ddb..249655394d 100644 --- a/src/extensions/core-api/index.ts +++ b/src/extensions/core-api/index.ts @@ -31,6 +31,7 @@ import * as Util from "./utils"; import * as Interface from "../interfaces"; import * as Catalog from "./catalog"; import * as Types from "./types"; +import * as Ipc from "./ipc"; export { App, @@ -40,4 +41,5 @@ export { Store, Types, Util, + Ipc, }; diff --git a/src/extensions/core-api/ipc.ts b/src/extensions/core-api/ipc.ts new file mode 100644 index 0000000000..3f967e3bb0 --- /dev/null +++ b/src/extensions/core-api/ipc.ts @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2021 OpenLens Authors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +export { IpcMain } from "../ipc/ipc-main"; +export { IpcRegistrar } from "../ipc/ipc-registrar"; diff --git a/src/extensions/core-api/stores.ts b/src/extensions/core-api/stores.ts index a9dfe9bf1e..17eac539d6 100644 --- a/src/extensions/core-api/stores.ts +++ b/src/extensions/core-api/stores.ts @@ -20,5 +20,3 @@ */ export { ExtensionStore } from "../extension-store"; -export { MainIpcStore } from "../main-ipc-store"; -export { RendererIpcStore } from "../renderer-ipc-store"; diff --git a/src/extensions/main-ipc-store.ts b/src/extensions/ipc/ipc-main.ts similarity index 58% rename from src/extensions/main-ipc-store.ts rename to src/extensions/ipc/ipc-main.ts index 664fc0d965..fcc97fc42f 100644 --- a/src/extensions/main-ipc-store.ts +++ b/src/extensions/ipc/ipc-main.ts @@ -19,27 +19,43 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ import { ipcMain } from "electron"; -import { IpcPrefix, IpcStore } from "./ipc-store"; -import { Disposers } from "./lens-extension"; -import type { LensMainExtension } from "./lens-main-extension"; +import { IpcPrefix, IpcRegistrar } from "./ipc-registrar"; +import { Disposers } from "../lens-extension"; +import type { LensMainExtension } from "../lens-main-extension"; +import type { Disposer } from "../../common/utils"; +import { once } from "lodash"; -export abstract class MainIpcStore extends IpcStore { +export abstract class IpcMain extends IpcRegistrar { constructor(extension: LensMainExtension) { super(extension); - extension[Disposers].push(() => MainIpcStore.resetInstance()); + extension[Disposers].push(() => IpcMain.resetInstance()); } + /** + * Listen for broadcasts within your extension + * @param channel The channel to listen for broadcasts on + * @param listener The function that will be called with the arguments of the broadcast + * @returns An optional disopser, Lens will cleanup when the extension is disabled or uninstalled even if this is not called + */ + listenIpc(channel: string, listener: (event: Electron.IpcRendererEvent, ...args: any[]) => any): Disposer { + const prefixedChannel = `extensions@${this[IpcPrefix]}:${channel}`; + const cleanup = once(() => ipcMain.removeListener(prefixedChannel, listener)); + + ipcMain.addListener(prefixedChannel, listener); + this.extension[Disposers].push(cleanup); + + return cleanup; + } + + /** + * Declare a RPC over `channel`. Lens will cleanup when the extension is disabled or uninstalled + * @param channel The name of the RPC + * @param handler The remote procedure that is called + */ handleIpc(channel: string, handler: (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any): void { const prefixedChannel = `extensions@${this[IpcPrefix]}:${channel}`; ipcMain.handle(prefixedChannel, handler); this.extension[Disposers].push(() => ipcMain.removeHandler(prefixedChannel)); } - - listenIpc(channel: string, listener: (event: Electron.IpcMainEvent, ...args: any[]) => any): void { - const prefixedChannel = `extensions@${this[IpcPrefix]}:${channel}`; - - ipcMain.addListener(prefixedChannel, listener); - this.extension[Disposers].push(() => ipcMain.removeListener(prefixedChannel, listener)); - } } diff --git a/src/extensions/ipc-store.ts b/src/extensions/ipc/ipc-registrar.ts similarity index 79% rename from src/extensions/ipc-store.ts rename to src/extensions/ipc/ipc-registrar.ts index 275522338c..f80030ddca 100644 --- a/src/extensions/ipc-store.ts +++ b/src/extensions/ipc/ipc-registrar.ts @@ -18,14 +18,14 @@ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { Singleton } from "../common/utils"; -import type { LensExtension } from "./lens-extension"; +import { Singleton } from "../../common/utils"; +import type { LensExtension } from "../lens-extension"; import { createHash } from "crypto"; -import { broadcastMessage } from "../common/ipc"; +import { broadcastMessage } from "../../common/ipc"; export const IpcPrefix = Symbol(); -export abstract class IpcStore extends Singleton { +export abstract class IpcRegistrar extends Singleton { readonly [IpcPrefix]: string; constructor(protected extension: LensExtension) { @@ -33,6 +33,11 @@ export abstract class IpcStore extends Singleton { this[IpcPrefix] = createHash("sha256").update(extension.id).digest("hex"); } + /** + * + * @param channel The channel to broadcast to your whole extension, both `main` and `renderer` + * @param args The arguments passed to all listeners + */ broadcastIpc(channel: string, ...args: any[]): void { broadcastMessage(`extensions@${this[IpcPrefix]}:${channel}`, ...args); } diff --git a/src/extensions/renderer-ipc-store.ts b/src/extensions/ipc/ipc-renderer.ts similarity index 59% rename from src/extensions/renderer-ipc-store.ts rename to src/extensions/ipc/ipc-renderer.ts index 75824fece7..2a60d95388 100644 --- a/src/extensions/renderer-ipc-store.ts +++ b/src/extensions/ipc/ipc-renderer.ts @@ -19,23 +19,40 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ import { ipcRenderer } from "electron"; -import { IpcPrefix, IpcStore } from "./ipc-store"; -import { Disposers } from "./lens-extension"; -import type { LensRendererExtension } from "./lens-renderer-extension"; +import { IpcPrefix, IpcRegistrar } from "./ipc-registrar"; +import { Disposers } from "../lens-extension"; +import type { LensRendererExtension } from "../lens-renderer-extension"; +import type { Disposer } from "../../common/utils"; +import { once } from "lodash"; -export abstract class RendererIpcStore extends IpcStore { +export abstract class IpcRenderer extends IpcRegistrar { constructor(extension: LensRendererExtension) { super(extension); - extension[Disposers].push(() => RendererIpcStore.resetInstance()); + extension[Disposers].push(() => IpcRenderer.resetInstance()); } - listenIpc(channel: string, listener: (event: Electron.IpcRendererEvent, ...args: any[]) => any): void { + /** + * Listen for broadcasts within your extension + * @param channel The channel to listen for broadcasts on + * @param listener The function that will be called with the arguments of the broadcast + * @returns An optional disopser, Lens will cleanup even if this is not called + */ + listenIpc(channel: string, listener: (event: Electron.IpcRendererEvent, ...args: any[]) => any): Disposer { const prefixedChannel = `extensions@${this[IpcPrefix]}:${channel}`; + const cleanup = once(() => ipcRenderer.removeListener(prefixedChannel, listener)); ipcRenderer.addListener(prefixedChannel, listener); - this.extension[Disposers].push(() => ipcRenderer.removeListener(prefixedChannel, listener)); + this.extension[Disposers].push(cleanup); + + return cleanup; } + /** + * Request main to execute its function over the `channel` channel. + * @param channel The channel to invoke a RPC on + * @param args The arguments to pass to the RPC + * @returns A promise of the resulting value + */ invokeIpc(channel: string, ...args: any[]): Promise { const prefixedChannel = `extensions@${this[IpcPrefix]}:${channel}`;