import { Socket } from "net" ; import ipc from 'node-ipc' ; const isDev = process . env . NODE_ENV === 'development' ; let sendQueue = [ ] ; let clientConnected = false ; export interface ServerMessage < T extends HandlerMap < T > > { id : string ; name : keyof T ; args : Parameters < T [ keyof T ] > ; } export type ClientMessageTypes = 'reply' | 'error' | 'push' ; export interface Reply { type : 'reply' ; id : string ; result : unknown | null ; } export interface Error { type : 'error' ; id : string ; } export interface Push { type : 'push' ; name : string ; args : unknown [ ] ; } export type ClientMessage = Reply | Error | Push ; export type Handler < Result = unknown > = ( ... args : unknown [ ] ) => Promise < Result > | Result ; export type HandlerEntry < HandlerSet > = { name : keyof HandlerSet ; handler : Handler ; } ; export type HandlerMap < T > = { [ key in keyof T ] : Handler ; } ; async function handle < T extends HandlerMap < T > > ( { id , args } , handler , socket ) { try { const result = args . length > 0 ? await handler ( ... args ) : await handler ( ) ; const reply = { type : 'reply' , id , result } ; ipc . server . emit ( socket , 'message' , JSON . stringify ( reply ) ) ; } catch ( err ) { const error = { type : 'error' , id } ; ipc . server . emit ( socket , 'message' , JSON . stringify ( error ) ) ; throw err ; } } function noHandle < T extends HandlerMap < T > > ( { id , name } , socket ) { const reply = { type : 'reply' , id , result : null } ; ipc . server . emit ( socket , 'message' , JSON . stringify ( reply ) ) ; console . warn ( 'Unknown method: ' + name ) ; } function serve < T extends HandlerMap < T > > ( handlers ) { ipc . server . on ( 'message' , ( data , socket ) => { const msg = JSON . parse ( data ) ; const handler = handlers [ msg . name ] ; console . log ( Date . now ( ) , 'server received:' , msg ) ; if ( ! handler ) return noHandle ( msg , socket ) ; handle ( msg , handler , socket ) ; } ) ; } export function send ( name , ... args ) { const msg = { type : 'push' , name , args } ; if ( ! clientConnected ) { sendQueue . push ( msg ) ; return ; } ipc . server . broadcast ( 'message' , JSON . stringify ( msg ) ) ; isDev && console . log ( Date . now ( ) , 'server sending:' , msg ) ; } async function flushSendQueue ( ) { sendQueue . forEach ( item => { send ( item . name , ... item . args ) ; } ) ; sendQueue = [ ] ; } async function handleConnection ( ) { if ( clientConnected ) { return ; } clientConnected = true ; send ( 'connected' ) ; flushSendQueue ( ) ; } async function handleDisconnect ( ) { clientConnected = false ; } export function init < T > ( socketName , handlers ) { ipc . config . id = socketName ; ipc . config . silent = true ; ipc . serve ( ( ) => serve ( { ... handlers , connected : handleConnection , disconnected : handleDisconnect } ) ) ; ipc . server . start ( ) ; ipc . server . broadcast ( 'message' , JSON . stringify ( { type : 'push' , name : 'connected' , args : [ ] } ) ) ; isDev && console . log ( Date . now ( ) , 'server starting' , 'socket:' , socketName ) ; }