import { BrowserWindow , IpcMain , IpcRenderer } from 'electron' ; import { Observable , Subject } from 'rxjs' ; import { makePropDecorator } from './decorators' ; type IpcActionHandler < T , R > = ( data ? : T ) => Promise < R > ; export interface IpcAction < D > { name : string ; data ? : D ; } export interface IpcActionResponse < R = any > { result ? : R ; error ? : any ; } function makeResponseChannelName ( namespace , actionName ) { return `${ namespace }-${ actionName }-response` ; } export interface IpcMessage < D > { name : string ; data ? : D ; } example IpcActionHandler export const IpcActionHandler = makePropDecorator ( 'IpcActionHandler' , ( actionName ) => ( { actionName } ) ) ; example export class IpcActionServer { readonly _ipc : IpcMain ; private readonly actionHandlers = new Map < string , IpcActionHandler < any , any > > ( ) ; private actionErrorHandler : ( error : any ) => any ; private readonly actionListener : any ; constructor ( public readonly namespace ) { this . _ipc = require ( 'electron' ) . ipcMain ; this . actionListener = ( event , action ) => this . handleIpcEvent ( event , action ) ; this . _ipc . on ( this . namespace , this . actionListener ) ; } destroy ( ) { this . actionHandlers . clear ( ) ; this . _ipc . removeListener ( this . namespace , this . actionListener ) ; } setActionHandler < D , R > ( actionName , handler ) { this . actionHandlers . set ( actionName , handler ) ; return this ; } setActionErrorHandler ( handler ) { this . actionErrorHandler = handler ; return this ; } sendMessage < D > ( window , message ) { window . webContents . send ( this . namespace , message ) ; } private async handleIpcEvent ( event , action ) { if ( ! this . actionHandlers . has ( action . name ) ) { return ; } const handler = this . actionHandlers . get ( action . name ) ; let result = null ; let error = null ; try { result = await handler ( action . data ) ; } catch ( err ) { error = this . actionErrorHandler ? this . actionErrorHandler ( err ) : err ; } event . sender . send ( makeResponseChannelName ( this . namespace , action . name ) , { result , error } as IpcActionResponse ) ; } } example export class IpcActionClient { readonly _ipc : IpcRenderer ; private readonly messageListener : any ; private readonly messageStream = new Subject < IpcMessage < any > > ( ) ; constructor ( public readonly namespace ) { this . _ipc = require ( 'electron' ) . ipcRenderer ; this . messageListener = ( event , message ) => this . handleIpcEvent ( message ) ; this . _ipc . on ( namespace , this . messageListener ) ; } destroy ( ) { this . _ipc . removeListener ( this . namespace , this . messageListener ) ; } /** Send action to server which handles ipc main. */ performAction < D , R > ( actionName , data ? ) { return new Promise < R > ( ( resolve , reject ) => { const channelName = makeResponseChannelName ( this . namespace , actionName ) ; const action = { name : actionName , data } ; this . _ipc . once ( channelName , ( event , response ) => { if ( response . error ) { reject ( response . error ) ; } else { resolve ( response . result ) ; } } ) ; this . _ipc . send ( this . namespace , action ) ; } ) ; } onMessage < D > ( ) { return this . messageStream . asObservable ( ) ; } private handleIpcEvent ( message ) { this . messageStream . next ( message ) ; } }