Created
January 14, 2026 07:14
-
-
Save WilliamStam/2f392fb1620ca00e827ea8ae70f22f88 to your computer and use it in GitHub Desktop.
Electron auto registering IPC services
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import {IpcHandler} from "../utilities/ipc-registry"; | |
| export class FishService { | |
| @IpcHandler() | |
| async cake(): Promise<string | null> { | |
| return "Cake is a lie"; | |
| } | |
| } | |
| // Export a singleton instance | |
| export const fishService = new FishService(); | |
| export default fishService; | |
| // add typescript aware stuff to window.fish.xx | |
| declare global { | |
| interface Window { | |
| fish: { | |
| cake: () => Promise<string | null>; | |
| }; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import {ipcMain, contextBridge, ipcRenderer} from 'electron'; | |
| interface HandlerMetadata { | |
| service: string; | |
| method: string; | |
| channel: string; | |
| } | |
| const handlersRegistry = new Map<any, HandlerMetadata[]>(); | |
| /** | |
| * Decorator to mark a method as an IPC handler | |
| */ | |
| export function IpcHandler(channel?: string) { | |
| return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { | |
| const serviceName = target.constructor.name.replace('Service', '').toLowerCase(); | |
| const channelName = channel || `${serviceName}:${propertyKey}`; | |
| if (!handlersRegistry.has(target.constructor)) { | |
| handlersRegistry.set(target.constructor, []); | |
| } | |
| handlersRegistry.get(target.constructor)!.push({ | |
| service: serviceName, | |
| method: propertyKey, | |
| channel: channelName, | |
| }); | |
| return descriptor; | |
| }; | |
| } | |
| /** | |
| * Register all IPC handlers for a service instance (MAIN PROCESS ONLY) | |
| */ | |
| export function registerServiceHandlers(serviceInstance: any) { | |
| const handlers = handlersRegistry.get(serviceInstance.constructor); | |
| console.log("main handlers", handlers); | |
| if (!handlers) { | |
| console.warn(`No handlers registered for ${serviceInstance.constructor.name}`); | |
| return; | |
| } | |
| handlers.forEach(({service, method, channel}) => { | |
| ipcMain.handle(channel, async (event, ...args) => { | |
| try { | |
| return await serviceInstance[method](...args); | |
| } catch (error) { | |
| console.error(`Error in handler ${channel}:`, error); | |
| throw error; | |
| } | |
| }); | |
| console.log(`Registered IPC handler: ${channel} -> ${serviceInstance.constructor.name}.${method}`); | |
| }); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import {registerServiceHandlers} from "./utilities/ipc-registry"; | |
| import fishService from "./datasets/fish"; | |
| // Normal main file stuff. im not including it to keep this easy to follow | |
| // This is all thats needed for the main service. just import the | |
| registerServiceHandlers(fishService) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // unfortunately we cant use the auto registering thing because preload is only able to laod a subset of imports. | |
| // the registry above can actualy build this SOOO easily but because of the security stuff we cant do so.. so sadly we need to duplicate | |
| import {contextBridge, ipcRenderer} from 'electron'; | |
| contextBridge.exposeInMainWorld('fish', { | |
| cake: () => ipcRenderer.invoke('fish:cake'), | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment