mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Add better type safety around Channels (#6133)
* Add better type safety around Channels - Reallow non-JSON serializable complex types - Remove unnecessary JSON serialization step to IPC - Remove unnecesary injectable-ization of MessageChannel and RequestChannel declarations - Add channel listener injectable getter function to remove general need for type casting and improve type safety Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add check to verify data in IPC related tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix implementation in light of new safety check in tests Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix listener ID clash potential Signed-off-by: Sebastian Malton <sebastian@malton.name> * Switch to not forcing split of request channel handlers being seperate from the listener decl Signed-off-by: Sebastian Malton <sebastian@malton.name> * Move message channels to changed signature too Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix not registering listener Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix test imports Signed-off-by: Sebastian Malton <sebastian@malton.name> * Fix type errors Signed-off-by: Sebastian Malton <sebastian@malton.name> * Refactor listening of different channel kinds Signed-off-by: Sebastian Malton <sebastian@malton.name> * Add test covering new checking code for request channel handlers Signed-off-by: Sebastian Malton <sebastian@malton.name> Signed-off-by: Sebastian Malton <sebastian@malton.name>
This commit is contained in:
1 parent
d96918c966
commit
5a5eda935b
115 files changed
+949
-1568
No files matched your search
@@ -3,18 +3,27 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import type { DiContainer } from "@ogre-tools/injectable";
|
||||
import type { SendToViewArgs } from "../../main/start-main-application/lens-window/application-window/create-lens-window.injectable";
|
||||
import { overrideMessagingFromMainToWindow } from "./override-messaging-from-main-to-window";
|
||||
import { overrideMessagingFromWindowToMain } from "./override-messaging-from-window-to-main";
|
||||
import { overrideRequestingFromWindowToMain } from "./override-requesting-from-window-to-main";
|
||||
|
||||
export const overrideChannels = (mainDi: DiContainer) => {
|
||||
const overrideMessagingFromMainToWindowForWindow = overrideMessagingFromMainToWindow(mainDi);
|
||||
export interface OverrideChannels {
|
||||
overrideForWindow: (windowDi: DiContainer, windowId: string) => void;
|
||||
sendToWindow: (windowId: string, args: SendToViewArgs) => void;
|
||||
}
|
||||
|
||||
export const overrideChannels = (mainDi: DiContainer): OverrideChannels => {
|
||||
const { overrideEnlistForWindow, sendToWindow } = overrideMessagingFromMainToWindow();
|
||||
const overrideMessagingFromWindowToForWindow = overrideMessagingFromWindowToMain(mainDi);
|
||||
const overrideRequestingFromWindowToMainForWindow = overrideRequestingFromWindowToMain(mainDi);
|
||||
|
||||
return (windowDi: DiContainer, windowId: string) => {
|
||||
overrideMessagingFromMainToWindowForWindow(windowDi, windowId);
|
||||
overrideMessagingFromWindowToForWindow(windowDi);
|
||||
overrideRequestingFromWindowToMainForWindow(windowDi);
|
||||
return {
|
||||
overrideForWindow: (windowDi, windowId) => {
|
||||
overrideEnlistForWindow(windowDi, windowId);
|
||||
overrideMessagingFromWindowToForWindow(windowDi);
|
||||
overrideRequestingFromWindowToMainForWindow(windowDi);
|
||||
},
|
||||
sendToWindow,
|
||||
};
|
||||
};
|
||||
@@ -3,18 +3,22 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import type { MessageChannelListener } from "../../common/utils/channel/message-channel-listener-injection-token";
|
||||
import sendToChannelInElectronBrowserWindowInjectable from "../../main/start-main-application/lens-window/application-window/send-to-channel-in-electron-browser-window.injectable";
|
||||
import type { SendToViewArgs } from "../../main/start-main-application/lens-window/application-window/create-lens-window.injectable";
|
||||
import enlistMessageChannelListenerInjectableInRenderer from "../../renderer/utils/channel/channel-listeners/enlist-message-channel-listener.injectable";
|
||||
import type { DiContainer } from "@ogre-tools/injectable";
|
||||
import { tentativeParseJson } from "../../common/utils/tentative-parse-json";
|
||||
import { getOrInsert } from "../../common/utils";
|
||||
import { getOrInsert, getOrInsertSet } from "../../common/utils";
|
||||
import type { SendToViewArgs } from "../../main/start-main-application/lens-window/application-window/create-lens-window.injectable";
|
||||
import { deserialize, serialize } from "v8";
|
||||
|
||||
type ListenerSet = Set<MessageChannelListener<any>>;
|
||||
type WindowListenerMap = Map<string, ListenerSet>;
|
||||
type ListenerFakeMap = Map<string, WindowListenerMap>;
|
||||
|
||||
export const overrideMessagingFromMainToWindow = (mainDi: DiContainer) => {
|
||||
export interface OverriddenWindowMessaging {
|
||||
sendToWindow(windowId: string, args: SendToViewArgs): void;
|
||||
overrideEnlistForWindow(windowDi: DiContainer, windowId: string): void;
|
||||
}
|
||||
|
||||
export const overrideMessagingFromMainToWindow = (): OverriddenWindowMessaging => {
|
||||
const messageChannelListenerFakesForRenderer: ListenerFakeMap = new Map();
|
||||
|
||||
const getWindowListeners = (channelId: string, windowId: string) => {
|
||||
@@ -24,68 +28,52 @@ export const overrideMessagingFromMainToWindow = (mainDi: DiContainer) => {
|
||||
new Map(),
|
||||
);
|
||||
|
||||
return getOrInsert<string, ListenerSet>(
|
||||
channelListeners,
|
||||
windowId,
|
||||
new Set(),
|
||||
);
|
||||
return getOrInsertSet(channelListeners, windowId);
|
||||
};
|
||||
|
||||
mainDi.override(
|
||||
sendToChannelInElectronBrowserWindowInjectable,
|
||||
return {
|
||||
overrideEnlistForWindow: (windowDi, windowId) => {
|
||||
windowDi.override(
|
||||
enlistMessageChannelListenerInjectableInRenderer,
|
||||
|
||||
() =>
|
||||
(
|
||||
windowId: string,
|
||||
browserWindow,
|
||||
{ channel: channelId, frameInfo, data = [] }: SendToViewArgs,
|
||||
) => {
|
||||
const windowListeners = getWindowListeners(channelId, windowId);
|
||||
|
||||
if (frameInfo) {
|
||||
throw new Error(
|
||||
`Tried to send message to frame "${frameInfo.frameId}" in process "${frameInfo.processId}" using channel "${channelId}" which isn't supported yet.`,
|
||||
() => (listener) => {
|
||||
const windowListeners = getWindowListeners(
|
||||
listener.channel.id,
|
||||
windowId,
|
||||
);
|
||||
}
|
||||
|
||||
if (data.length > 1) {
|
||||
throw new Error(
|
||||
`Tried to send message to channel "${channelId}" with more than one argument which is not supported in MessageChannelListener yet.`,
|
||||
);
|
||||
}
|
||||
windowListeners.add(listener);
|
||||
|
||||
if (windowListeners.size === 0) {
|
||||
throw new Error(
|
||||
`Tried to send message to channel "${channelId}" but there where no listeners. Current channels with listeners: "${[
|
||||
...messageChannelListenerFakesForRenderer.keys(),
|
||||
].join('", "')}"`,
|
||||
);
|
||||
}
|
||||
return () => {
|
||||
windowListeners.delete(listener);
|
||||
};
|
||||
},
|
||||
);
|
||||
},
|
||||
sendToWindow: (windowId, { channel, data, frameInfo }) => {
|
||||
try {
|
||||
data = deserialize(serialize(data));
|
||||
} catch (error) {
|
||||
throw new Error(`Tried to send a message to channel "${channel}" that is not compatible with StructuredClone: ${error}`);
|
||||
}
|
||||
|
||||
const message = tentativeParseJson(data[0]);
|
||||
const windowListeners = getWindowListeners(channel, windowId);
|
||||
|
||||
windowListeners.forEach((listener) =>
|
||||
listener.handler(message),
|
||||
if (frameInfo) {
|
||||
throw new Error(
|
||||
`Tried to send message to frame "${frameInfo.frameId}" in process "${frameInfo.processId}" using channel "${channel}" which isn't supported yet.`,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
return (windowDi: DiContainer, windowId: string) => {
|
||||
windowDi.override(
|
||||
enlistMessageChannelListenerInjectableInRenderer,
|
||||
|
||||
() => (listener) => {
|
||||
const windowListeners = getWindowListeners(
|
||||
listener.channel.id,
|
||||
windowId,
|
||||
if (windowListeners.size === 0) {
|
||||
throw new Error(
|
||||
`Tried to send message to channel "${channel}" but there where no listeners. Current channels with listeners: "${[
|
||||
...messageChannelListenerFakesForRenderer.keys(),
|
||||
].join('", "')}"`,
|
||||
);
|
||||
}
|
||||
|
||||
windowListeners.add(listener);
|
||||
|
||||
return () => {
|
||||
windowListeners.delete(listener);
|
||||
};
|
||||
},
|
||||
);
|
||||
windowListeners.forEach((listener) => listener.handler(data));
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -3,54 +3,36 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import type { DiContainer } from "@ogre-tools/injectable";
|
||||
import assert from "assert";
|
||||
import type { MessageChannel } from "../../common/utils/channel/message-channel-injection-token";
|
||||
import type { MessageChannelListener } from "../../common/utils/channel/message-channel-listener-injection-token";
|
||||
import type { MessageChannel, MessageChannelListener } from "../../common/utils/channel/message-channel-listener-injection-token";
|
||||
import enlistMessageChannelListenerInjectableInMain from "../../main/utils/channel/channel-listeners/enlist-message-channel-listener.injectable";
|
||||
import { getOrInsertSet } from "../../renderer/utils";
|
||||
import sendToMainInjectable from "../../renderer/utils/channel/send-to-main.injectable";
|
||||
|
||||
export const overrideMessagingFromWindowToMain = (mainDi: DiContainer) => {
|
||||
const messageChannelListenerFakesForMain = new Map<
|
||||
string,
|
||||
Set<MessageChannelListener<MessageChannel<any>>>
|
||||
Set<MessageChannelListener<MessageChannel<unknown>>>
|
||||
>();
|
||||
|
||||
mainDi.override(
|
||||
enlistMessageChannelListenerInjectableInMain,
|
||||
|
||||
() => (listener) => {
|
||||
const channelId = listener.channel.id;
|
||||
const listeners = getOrInsertSet(messageChannelListenerFakesForMain, listener.channel.id);
|
||||
|
||||
if (!messageChannelListenerFakesForMain.has(channelId)) {
|
||||
messageChannelListenerFakesForMain.set(channelId, new Set());
|
||||
}
|
||||
|
||||
const listeners = messageChannelListenerFakesForMain.get(
|
||||
channelId,
|
||||
);
|
||||
|
||||
assert(listeners);
|
||||
|
||||
// TODO: Figure out typing
|
||||
listeners.add(
|
||||
listener as unknown as MessageChannelListener<MessageChannel<any>>,
|
||||
);
|
||||
listeners.add(listener);
|
||||
|
||||
return () => {
|
||||
// TODO: Figure out typing
|
||||
listeners.delete(
|
||||
listener as unknown as MessageChannelListener<MessageChannel<any>>,
|
||||
);
|
||||
listeners.delete(listener);
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
return (windowDi: DiContainer) => {
|
||||
windowDi.override(sendToMainInjectable, () => (channelId, message) => {
|
||||
const listeners =
|
||||
messageChannelListenerFakesForMain.get(channelId) || new Set();
|
||||
const listeners = messageChannelListenerFakesForMain.get(channelId);
|
||||
|
||||
if (listeners.size === 0) {
|
||||
if (!listeners || listeners.size === 0) {
|
||||
throw new Error(
|
||||
`Tried to send message to channel "${channelId}" but there where no listeners. Current channels with listeners: "${[
|
||||
...messageChannelListenerFakesForMain.keys(),
|
||||
|
||||
@@ -3,35 +3,29 @@
|
||||
* Licensed under MIT License. See LICENSE in root directory for more information.
|
||||
*/
|
||||
import type { DiContainer } from "@ogre-tools/injectable";
|
||||
import type { RequestChannel } from "../../common/utils/channel/request-channel-injection-token";
|
||||
import type { RequestChannelListener } from "../../common/utils/channel/request-channel-listener-injection-token";
|
||||
import type { RequestChannel } from "../../common/utils/channel/request-channel-listener-injection-token";
|
||||
import type { RequestFromChannel } from "../../common/utils/channel/request-from-channel-injection-token";
|
||||
import enlistRequestChannelListenerInjectableInMain from "../../main/utils/channel/channel-listeners/enlist-request-channel-listener.injectable";
|
||||
import type { RequestChannelListener } from "../../main/utils/channel/channel-listeners/listener-tokens";
|
||||
import requestFromChannelInjectable from "../../renderer/utils/channel/request-from-channel.injectable";
|
||||
|
||||
export const overrideRequestingFromWindowToMain = (mainDi: DiContainer) => {
|
||||
const requestChannelListenerFakesForMain = new Map<
|
||||
string,
|
||||
RequestChannelListener<RequestChannel<any, any>>
|
||||
>();
|
||||
string,
|
||||
RequestChannelListener<RequestChannel<unknown, unknown>>
|
||||
>();
|
||||
|
||||
mainDi.override(
|
||||
enlistRequestChannelListenerInjectableInMain,
|
||||
|
||||
() => (listener) => {
|
||||
if (requestChannelListenerFakesForMain.get(listener.channel.id)) {
|
||||
if (requestChannelListenerFakesForMain.has(listener.channel.id)) {
|
||||
throw new Error(
|
||||
`Tried to enlist listener for channel "${listener.channel.id}", but it was already enlisted`,
|
||||
);
|
||||
}
|
||||
|
||||
requestChannelListenerFakesForMain.set(
|
||||
listener.channel.id,
|
||||
|
||||
// TODO: Figure out typing
|
||||
listener as unknown as RequestChannelListener<
|
||||
RequestChannel<any, any>
|
||||
>,
|
||||
);
|
||||
requestChannelListenerFakesForMain.set(listener.channel.id, listener);
|
||||
|
||||
return () => {
|
||||
requestChannelListenerFakesForMain.delete(listener.channel.id);
|
||||
@@ -43,7 +37,7 @@ export const overrideRequestingFromWindowToMain = (mainDi: DiContainer) => {
|
||||
windowDi.override(
|
||||
requestFromChannelInjectable,
|
||||
|
||||
() => async (channel, ...[request]) => {
|
||||
() => (async (channel, request) => {
|
||||
const requestListener = requestChannelListenerFakesForMain.get(channel.id);
|
||||
|
||||
if (!requestListener) {
|
||||
@@ -53,7 +47,7 @@ export const overrideRequestingFromWindowToMain = (mainDi: DiContainer) => {
|
||||
}
|
||||
|
||||
return requestListener.handler(request);
|
||||
},
|
||||
}) as RequestFromChannel,
|
||||
);
|
||||
};
|
||||
};
|
||||
Reference in new issue
Block a user