import { EventEmitter } from "events" ; import { IAuth , IEncodedJsonPatch } from "../internal" ; export type MessageListener = ( type : string , data : any ) => void ; export type EventListener = ( ... args : any ) => void ; export interface IClientData { roomId : string ; state ? : any ; } export const ErrorCode = { NormalClose : 1000 , JoinError : 4000 , Unathorized : 4001 , ReservationExpired : 4003 , RoomNotFound : 4004 , ConnectionTimeout : 4005 , ReconnectError : 4002 , } ; export const ClientEvent = { error : 0 , connected : 1 , reconnected : 2 , joined : 3 , message : 4 , request : 5 , response : 6 , snapshot : 11 , patch : 12 , schema : 20 , encodedSnapshot : - 11 , encodedPatch : - 12 , } ; export type ClientStatus = "connecting" | "connected" | "disconnected" | "reconnected" ; export type EventArgs = [ number , ... any [ ] ] ; export abstract class Client < T = any > { public auth : IAuth = { } as IAuth ; public roomId : string ; public _status : ClientStatus ; public state : T ; public messages : any [ ] = [ ] ; get id ( ) : string { return this . auth . id ; } get data ( ) : any { return this . auth . data ; } get status ( ) : ClientStatus { return this . _status ; } set status ( status ) { this . _status = status ; if ( status === "connected" || status === "reconnected" ) { this . messages . forEach ( ( args ) => this . emit ( ... args ) ) ; this . messages = [ ] ; } } constructor ( data ) { this . roomId = data . roomId ; this . _status = "connecting" ; this . state = data . state || { } as T ; } private safeEmit ( event , ... args ) { if ( this . status === "connecting" ) { this . messages . push ( [ event , ... args ] ) ; } else { this . emit ( event , ... args ) ; } } public send ( type , data ) { this . safeEmit ( ClientEvent . message , type , data ) ; } public onMessage ( listener ) { this . on ( ClientEvent . message , listener ) ; } public patch ( patch ) { if ( patch . encoded ) { this . safeEmit ( ClientEvent . encodedPatch , patch . encoded ) ; } else { const { encoded , ... rest } = patch ; this . safeEmit ( ClientEvent . patch , rest ) ; } } public snapshot ( snapshot ) { this . safeEmit ( ClientEvent . snapshot , snapshot ) ; } public error ( code ? , message ? ) { this . emit ( ClientEvent . error , code , message ) ; this . terminate ( code , message ) ; } public connected ( ) { this . emit ( ClientEvent . connected ) ; } public async request ( type , data ) { return new Promise ( ( resolve , reject ) => { this . safeEmit ( ClientEvent . request , type , data , ( err , res ) => { if ( err ) { console . debug ( "REJECT: request error" ) ; reject ( err ) ; } else { resolve ( res ) ; } } ) ; } ) ; } public abstract on ( event , listner ) ; public abstract emit ( event , ... args ) ; public abstract terminate ( code ? , reason ? ) ; } export abstract class Transport < T extends Client = any > extends EventEmitter { public onConnect ( client , token ) { this . emit ( "connect" , client , token ) ; } public onDisconnect ( client , code ) { this . emit ( "disconnect" , client , code ) ; } abstract get port ( ) : number | undefined ; abstract get proxyPort ( ) : number | undefined ; public abstract terminate ( ) ; }