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
|
# 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.
|
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.
|
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 accomplished by returning a `Promise<T>` which needs to be `await`-ed.
|
||||||
|
|
||||||
This is a unidirectional form of communication.
|
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
|
## 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:
|
To register either a handler or a listener, you should do something like the following:
|
||||||
|
|
||||||
|
File1:
|
||||||
```typescript
|
```typescript
|
||||||
import { LensMainExtension, Interface, Types } from "@k8slens/extensions";
|
import { LensMainExtension, Interface, Types } from "@k8slens/extensions";
|
||||||
|
|
||||||
export class ExampleExtensionMain extends LensMainExtension {
|
export class ExampleExtensionMain extends LensMainExtension {
|
||||||
onActivate() {
|
onActivate() {
|
||||||
this.disposers.push(
|
|
||||||
this.listenIpc({
|
this.listenIpc({
|
||||||
channel: "initialize",
|
channel: "initialize",
|
||||||
listener: this.initializeListener,
|
listener: this.initializeListener,
|
||||||
verifier: this.initializeVerifier,
|
verifier: this.initializeVerifier,
|
||||||
})
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
initializeListener = (event: Types.IpcMainEvent, uid: string) => {
|
initializeListener = (event: Types.IpcMainEvent, id: string) => {
|
||||||
console.log(`starting to initialize: ${uid}`);
|
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";
|
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.
|
If you want to register a "handler" you would call `this.handleIpc(...)` instead.
|
||||||
|
The cleanup of these handlers is handled by Lens itself.
|
||||||
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.
|
|
||||||
|
|
||||||
### Note about verification
|
### 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.
|
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.
|
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
|
## Using IPC
|
||||||
|
|
||||||
Calling IPC is very simple.
|
Calling IPC is very simple.
|
||||||
|
|||||||
@ -42,27 +42,24 @@ function getSubFrames(): ClusterFrameInfo[] {
|
|||||||
return toJS(Array.from(clusterFrameMap.values()), { recurseEverything: true });
|
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();
|
const views = (webContents || remote?.webContents)?.getAllWebContents();
|
||||||
|
|
||||||
if (!views) return;
|
if (!views) return;
|
||||||
|
|
||||||
if (ipcRenderer) {
|
ipcRenderer?.send(channel, ...args);
|
||||||
ipcRenderer.send(channel, ...args);
|
ipcMain?.emit(channel, ...args);
|
||||||
} else if (ipcMain) {
|
|
||||||
ipcMain.emit(channel, ...args);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const subFramesP = ipcRenderer
|
||||||
|
? requestMain(subFramesChannel)
|
||||||
|
: Promise.resolve(getSubFrames());
|
||||||
|
|
||||||
|
subFramesP
|
||||||
|
.then(subFrames => {
|
||||||
for (const view of views) {
|
for (const view of views) {
|
||||||
const type = view.getType();
|
|
||||||
|
|
||||||
logger.silly(`[IPC]: broadcasting "${channel}" to ${type}=${view.id}`, { args });
|
|
||||||
view.send(channel, ...args);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const subFrames: ClusterFrameInfo[] = ipcRenderer
|
logger.silly(`[IPC]: broadcasting "${channel}" to ${view.getType()}=${view.id}`, { args });
|
||||||
? await requestMain(subFramesChannel)
|
view.send(channel, ...args);
|
||||||
: getSubFrames();
|
|
||||||
|
|
||||||
for (const frameInfo of subFrames) {
|
for (const frameInfo of subFrames) {
|
||||||
view.sendToFrame([frameInfo.processId, frameInfo.frameId], channel, ...args);
|
view.sendToFrame([frameInfo.processId, frameInfo.frameId], channel, ...args);
|
||||||
@ -71,6 +68,7 @@ export async function broadcastMessage(channel: string, ...args: any[]) {
|
|||||||
logger.error("[IPC]: failed to send IPC message", { error: String(error) });
|
logger.error("[IPC]: failed to send IPC message", { error: String(error) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function subscribeToBroadcast(channel: string, listener: (...args: any[]) => any) {
|
export function subscribeToBroadcast(channel: string, listener: (...args: any[]) => any) {
|
||||||
|
|||||||
@ -52,7 +52,7 @@ export class LensExtension {
|
|||||||
protocolHandlers: ProtocolHandlerRegistration[] = [];
|
protocolHandlers: ProtocolHandlerRegistration[] = [];
|
||||||
|
|
||||||
@observable private isEnabled = false;
|
@observable private isEnabled = false;
|
||||||
disposers = disposer();
|
protected disposers = disposer();
|
||||||
|
|
||||||
constructor({ id, manifest, manifestPath, isBundled }: InstalledExtension) {
|
constructor({ id, manifest, manifestPath, isBundled }: InstalledExtension) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
|||||||
@ -25,7 +25,6 @@ import { getExtensionPageUrl } from "./registries/page-registry";
|
|||||||
import { CatalogEntity, catalogEntityRegistry } from "../common/catalog";
|
import { CatalogEntity, catalogEntityRegistry } from "../common/catalog";
|
||||||
import { IObservableArray } from "mobx";
|
import { IObservableArray } from "mobx";
|
||||||
import { IpcHandlerRegistration, MenuRegistration } from "./registries";
|
import { IpcHandlerRegistration, MenuRegistration } from "./registries";
|
||||||
import type { Disposer } from "../common/utils";
|
|
||||||
import { handleCorrect, ListenerEvent, ListVerifier, onCorrect, Rest } from "../common/ipc";
|
import { handleCorrect, ListenerEvent, ListVerifier, onCorrect, Rest } from "../common/ipc";
|
||||||
import { ipcMain, IpcMain } from "electron";
|
import { ipcMain, IpcMain } from "electron";
|
||||||
|
|
||||||
@ -53,12 +52,14 @@ export class LensMainExtension extends LensExtension {
|
|||||||
|
|
||||||
handleIpc<
|
handleIpc<
|
||||||
Handler extends (event: Electron.IpcMainInvokeEvent, ...args: any[]) => any,
|
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
|
// This is to have a uniform length prefix so that two extensions cannot talk to each other's IPCs accidentally
|
||||||
return handleCorrect({
|
this.disposers.push(
|
||||||
|
handleCorrect({
|
||||||
channel: `extensions@${this[IpcPrefix]}:${channel}`,
|
channel: `extensions@${this[IpcPrefix]}:${channel}`,
|
||||||
...reg,
|
...reg,
|
||||||
});
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
listenIpc<
|
listenIpc<
|
||||||
@ -70,11 +71,13 @@ export class LensMainExtension extends LensExtension {
|
|||||||
channel: string,
|
channel: string,
|
||||||
listener: Listener,
|
listener: Listener,
|
||||||
verifier: ListVerifier<Rest<Parameters<Listener>>>,
|
verifier: ListVerifier<Rest<Parameters<Listener>>>,
|
||||||
}): Disposer {
|
}): void {
|
||||||
return onCorrect({
|
this.disposers.push(
|
||||||
|
onCorrect({
|
||||||
source: ipcMain,
|
source: ipcMain,
|
||||||
channel: `extensions@${this[IpcPrefix]}:${channel}`,
|
channel: `extensions@${this[IpcPrefix]}:${channel}`,
|
||||||
...reg,
|
...reg,
|
||||||
});
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,7 +30,6 @@ import { CommandRegistration } from "./registries/command-registry";
|
|||||||
import { EntitySettingRegistration } from "./registries/entity-setting-registry";
|
import { EntitySettingRegistration } from "./registries/entity-setting-registry";
|
||||||
import { ipcRenderer, IpcRenderer } from "electron";
|
import { ipcRenderer, IpcRenderer } from "electron";
|
||||||
import { ListenerEvent, ListVerifier, onCorrect, Rest } from "../common/ipc";
|
import { ListenerEvent, ListVerifier, onCorrect, Rest } from "../common/ipc";
|
||||||
import { Disposer } from "../common/utils";
|
|
||||||
|
|
||||||
export class LensRendererExtension extends LensExtension {
|
export class LensRendererExtension extends LensExtension {
|
||||||
globalPages: PageRegistration[] = [];
|
globalPages: PageRegistration[] = [];
|
||||||
@ -73,12 +72,14 @@ export class LensRendererExtension extends LensExtension {
|
|||||||
channel: string,
|
channel: string,
|
||||||
listener: Listener,
|
listener: Listener,
|
||||||
verifier: ListVerifier<Rest<Parameters<Listener>>>,
|
verifier: ListVerifier<Rest<Parameters<Listener>>>,
|
||||||
}): Disposer {
|
}): void {
|
||||||
return onCorrect({
|
this.disposers.push(
|
||||||
|
onCorrect({
|
||||||
source: ipcRenderer,
|
source: ipcRenderer,
|
||||||
channel: `extensions@${this[IpcPrefix]}:${channel}`,
|
channel: `extensions@${this[IpcPrefix]}:${channel}`,
|
||||||
...reg,
|
...reg,
|
||||||
});
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
invokeIpc(channel: string, ...args: any[]): Promise<any> {
|
invokeIpc(channel: string, ...args: any[]): Promise<any> {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user