mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Switch to pushing the disposer in the methods
Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
parent
5e1af1be2b
commit
7a93d8f5c4
@ -1,6 +1,6 @@
|
||||
# Inter Process Communication
|
||||
|
||||
A Lens Extension can utilise IPC to send information between its `LensRendererExtension` and its `LensMainExtension`.
|
||||
A Lens Extension can utilize IPC to send information between its `LensRendererExtension` and its `LensMainExtension`.
|
||||
This is useful when wanting to communicate directly within your extension.
|
||||
For example, if a user logs into a service that your extension is a facade for and `main` needs to know some information so that you can start syncing items to the `Catalog`, this would be a good way to send that information along.
|
||||
|
||||
@ -29,7 +29,7 @@ With this sort of IPC the caller waits for the result from the other side.
|
||||
This is accomplished by returning a `Promise<T>` which needs to be `await`-ed.
|
||||
|
||||
This is a unidirectional form of communication.
|
||||
Only `LensRendererExtension` can initiate this kind of request, and only `LensMainExtension` and respond this this kind of request.
|
||||
Only `LensRendererExtension` can initiate this kind of request, and only `LensMainExtension` can and respond this this kind of request.
|
||||
|
||||
## Registering IPC Handlers and Listeners
|
||||
|
||||
@ -40,38 +40,46 @@ The general terminology is as follows:
|
||||
|
||||
To register either a handler or a listener, you should do something like the following:
|
||||
|
||||
File1:
|
||||
```typescript
|
||||
import { LensMainExtension, Interface, Types } from "@k8slens/extensions";
|
||||
|
||||
export class ExampleExtensionMain extends LensMainExtension {
|
||||
onActivate() {
|
||||
this.disposers.push(
|
||||
this.listenIpc({
|
||||
channel: "initialize",
|
||||
listener: this.initializeListener,
|
||||
verifier: this.initializeVerifier,
|
||||
})
|
||||
);
|
||||
this.listenIpc({
|
||||
channel: "initialize",
|
||||
listener: this.initializeListener,
|
||||
verifier: this.initializeVerifier,
|
||||
});
|
||||
}
|
||||
|
||||
initializeListener = (event: Types.IpcMainEvent, uid: string) => {
|
||||
console.log(`starting to initialize: ${uid}`);
|
||||
initializeListener = (event: Types.IpcMainEvent, id: string) => {
|
||||
console.log(`starting to initialize: ${id}`);
|
||||
};
|
||||
|
||||
initializeVerifier = (args: unknown[]): args is [uid: string] => {
|
||||
initializeVerifier = (args: unknown[]): args is [id: string] => {
|
||||
return args.length === 1 && typeof args[0] === "string";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
File2:
|
||||
```typescript
|
||||
import { LensMainExtension, Interface, Types } from "@k8slens/extensions";
|
||||
|
||||
export class ExampleExtensionRenderer extends LensRendererExtension {
|
||||
onActivate() {
|
||||
setTimeout(() => this.sendIpc("initialize", "an-id"), 5000);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
As this example shows: the channel names *must* be the same.
|
||||
It should also be noted that "listeners" and "handlers" are specific to either `LensRendererExtension` and `LensMainExtension`.
|
||||
There is no behind the scenes transfer of these functions.
|
||||
|
||||
If you want to register a "handler" you would call `this.handleIpc(...)` instead.
|
||||
|
||||
The `LensExtension.prototype.disopsers` is a list of `() => void`'s (or `Utils.Disposer`'s).
|
||||
Those functions are sort of like destructors in that they should clean up some state.
|
||||
Some functions return them to indicate that it will clean up the state which was just registered.
|
||||
|
||||
As an extension developer you do not need to run them yourself.
|
||||
Lens will run them when your extension gets deactivated or uninstalled.
|
||||
The cleanup of these handlers is handled by Lens itself.
|
||||
|
||||
### Note about verification
|
||||
|
||||
@ -84,6 +92,11 @@ Your handler or listener will not be called if it fails this validation.
|
||||
Instead an error or log message will occur.
|
||||
This should help with debugging because you are notified immediately that there is a mismatch between what you are expecting and what was sent.
|
||||
|
||||
### Allowed Values
|
||||
|
||||
This IPC mechanism utilizes the [Structured Clone Algorithm](developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm) for serialization.
|
||||
This means that more types than what are JSON serializable can be used, but not all the information will be passed through.
|
||||
|
||||
## Using IPC
|
||||
|
||||
Calling IPC is very simple.
|
||||
|
||||
@ -42,35 +42,33 @@ function getSubFrames(): ClusterFrameInfo[] {
|
||||
return toJS(Array.from(clusterFrameMap.values()), { recurseEverything: true });
|
||||
}
|
||||
|
||||
export async function broadcastMessage(channel: string, ...args: any[]) {
|
||||
export function broadcastMessage(channel: string, ...args: any[]) {
|
||||
const views = (webContents || remote?.webContents)?.getAllWebContents();
|
||||
|
||||
if (!views) return;
|
||||
|
||||
if (ipcRenderer) {
|
||||
ipcRenderer.send(channel, ...args);
|
||||
} else if (ipcMain) {
|
||||
ipcMain.emit(channel, ...args);
|
||||
}
|
||||
ipcRenderer?.send(channel, ...args);
|
||||
ipcMain?.emit(channel, ...args);
|
||||
|
||||
for (const view of views) {
|
||||
const type = view.getType();
|
||||
const subFramesP = ipcRenderer
|
||||
? requestMain(subFramesChannel)
|
||||
: Promise.resolve(getSubFrames());
|
||||
|
||||
logger.silly(`[IPC]: broadcasting "${channel}" to ${type}=${view.id}`, { args });
|
||||
view.send(channel, ...args);
|
||||
subFramesP
|
||||
.then(subFrames => {
|
||||
for (const view of views) {
|
||||
try {
|
||||
logger.silly(`[IPC]: broadcasting "${channel}" to ${view.getType()}=${view.id}`, { args });
|
||||
view.send(channel, ...args);
|
||||
|
||||
try {
|
||||
const subFrames: ClusterFrameInfo[] = ipcRenderer
|
||||
? await requestMain(subFramesChannel)
|
||||
: getSubFrames();
|
||||
|
||||
for (const frameInfo of subFrames) {
|
||||
view.sendToFrame([frameInfo.processId, frameInfo.frameId], channel, ...args);
|
||||
for (const frameInfo of subFrames) {
|
||||
view.sendToFrame([frameInfo.processId, frameInfo.frameId], channel, ...args);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error("[IPC]: failed to send IPC message", { error: String(error) });
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error("[IPC]: failed to send IPC message", { error: String(error) });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function subscribeToBroadcast(channel: string, listener: (...args: any[]) => any) {
|
||||
|
||||
@ -52,7 +52,7 @@ export class LensExtension {
|
||||
protocolHandlers: ProtocolHandlerRegistration[] = [];
|
||||
|
||||
@observable private isEnabled = false;
|
||||
disposers = disposer();
|
||||
protected disposers = disposer();
|
||||
|
||||
constructor({ id, manifest, manifestPath, isBundled }: InstalledExtension) {
|
||||
this.id = id;
|
||||
|
||||
@ -25,7 +25,6 @@ import { getExtensionPageUrl } from "./registries/page-registry";
|
||||
import { CatalogEntity, catalogEntityRegistry } from "../common/catalog";
|
||||
import { IObservableArray } from "mobx";
|
||||
import { IpcHandlerRegistration, MenuRegistration } from "./registries";
|
||||
import type { Disposer } from "../common/utils";
|
||||
import { handleCorrect, ListenerEvent, ListVerifier, onCorrect, Rest } from "../common/ipc";
|
||||
import { ipcMain, IpcMain } from "electron";
|
||||
|
||||
@ -53,12 +52,14 @@ export class LensMainExtension extends LensExtension {
|
||||
|
||||
handleIpc<
|
||||
Handler extends (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any,
|
||||
>({ channel, ...reg }: IpcHandlerRegistration<Handler>): Disposer {
|
||||
>({ channel, ...reg }: IpcHandlerRegistration<Handler>): void {
|
||||
// This is to have a uniform length prefix so that two extensions cannot talk to each other's IPCs accidentally
|
||||
return handleCorrect({
|
||||
channel: `extensions@${this[IpcPrefix]}:${channel}`,
|
||||
...reg,
|
||||
});
|
||||
this.disposers.push(
|
||||
handleCorrect({
|
||||
channel: `extensions@${this[IpcPrefix]}:${channel}`,
|
||||
...reg,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
listenIpc<
|
||||
@ -70,11 +71,13 @@ export class LensMainExtension extends LensExtension {
|
||||
channel: string,
|
||||
listener: Listener,
|
||||
verifier: ListVerifier<Rest<Parameters<Listener>>>,
|
||||
}): Disposer {
|
||||
return onCorrect({
|
||||
source: ipcMain,
|
||||
channel: `extensions@${this[IpcPrefix]}:${channel}`,
|
||||
...reg,
|
||||
});
|
||||
}): void {
|
||||
this.disposers.push(
|
||||
onCorrect({
|
||||
source: ipcMain,
|
||||
channel: `extensions@${this[IpcPrefix]}:${channel}`,
|
||||
...reg,
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,7 +30,6 @@ import { CommandRegistration } from "./registries/command-registry";
|
||||
import { EntitySettingRegistration } from "./registries/entity-setting-registry";
|
||||
import { ipcRenderer, IpcRenderer } from "electron";
|
||||
import { ListenerEvent, ListVerifier, onCorrect, Rest } from "../common/ipc";
|
||||
import { Disposer } from "../common/utils";
|
||||
|
||||
export class LensRendererExtension extends LensExtension {
|
||||
globalPages: PageRegistration[] = [];
|
||||
@ -73,12 +72,14 @@ export class LensRendererExtension extends LensExtension {
|
||||
channel: string,
|
||||
listener: Listener,
|
||||
verifier: ListVerifier<Rest<Parameters<Listener>>>,
|
||||
}): Disposer {
|
||||
return onCorrect({
|
||||
source: ipcRenderer,
|
||||
channel: `extensions@${this[IpcPrefix]}:${channel}`,
|
||||
...reg,
|
||||
});
|
||||
}): void {
|
||||
this.disposers.push(
|
||||
onCorrect({
|
||||
source: ipcRenderer,
|
||||
channel: `extensions@${this[IpcPrefix]}:${channel}`,
|
||||
...reg,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
invokeIpc(channel: string, ...args: any[]): Promise<any> {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user