import * as log from 'loglevel' ; import { v4 as uuidv4 } from 'uuid' ; import logger from './util/logger' ; import Connection from './services/Connection' ; import BaseMessage from './messages/BaseMessage' ; import { deRegister , register , trigger , deRegisterAll , } from './services/Handler' ; import { ADD , REMOVE , SwEvent } from './util/constants' ; import { BroadcastParams , IVertoOptions } from './util/interfaces' ; import { isFunction , randomInt , isValidOptions } from './util/helpers' ; import { sessionStorage } from './util/storage' ; const KEEPALIVE_INTERVAL = 10 * 1000 ; export default abstract class BaseSession { public uuid : string = uuidv4 ( ) ; public sessionid : string = '' ; public subscriptions : { [ channel ] : any ; } = { } ; public nodeid : string ; public master_nodeid : string ; public signature : string = null ; public relayProtocol : string = null ; public contexts : string [ ] = [ ] ; public timeoutErrorCode = - 32000 ; public connection : Connection = null ; protected _jwtAuth : boolean = false ; protected _doKeepAlive : boolean = false ; protected _keepAliveTimeout : any ; protected _reconnectTimeout : any ; protected _autoReconnect : boolean = true ; protected _idle : boolean = false ; private _executeQueue : { resolve ? : Function ; msg : any ; } [ ] = [ ] ; private _pong : boolean ; constructor ( public options ) { if ( ! this . validateOptions ( ) ) { throw new Error ( 'Invalid init options' ) ; } this . _onSocketOpen = this . _onSocketOpen . bind ( this ) ; this . _onSocketCloseOrError = this . _onSocketCloseOrError . bind ( this ) ; this . _onSocketMessage = this . _onSocketMessage . bind ( this ) ; this . _handleLoginError = this . _handleLoginError . bind ( this ) ; this . _attachListeners ( ) ; this . connection = new Connection ( this ) ; } get __logger ( ) : log . Logger { return logger ; } example get connected ( ) { return this . connection && this . connection . connected ; } get reconnectDelay ( ) { return randomInt ( 2 , 6 ) * 1000 ; } return ignore execute ( msg ) { if ( this . _idle ) { return new Promise ( ( resolve ) => this . _executeQueue . push ( { resolve , msg } ) ) ; } if ( ! this . connected ) { return new Promise ( ( resolve ) => { this . _executeQueue . push ( { resolve , msg } ) ; this . connect ( ) ; } ) ; } return this . connection . send ( msg ) . catch ( ( error ) => { if ( error . code && error . code === this . timeoutErrorCode ) { this . _closeConnection ( ) ; } throw error ; } ) ; } return ignore executeRaw ( text ) { if ( this . _idle ) { this . _executeQueue . push ( { msg : text } ) ; return ; } this . connection . sendRawText ( text ) ; } return ignore validateOptions ( ) { return isValidOptions ( this . options ) ; } todo return ignore broadcast ( params ) { } return async disconnect ( ) { clearTimeout ( this . _reconnectTimeout ) ; this . subscriptions = { } ; this . _autoReconnect = false ; this . relayProtocol = null ; this . _closeConnection ( ) ; await sessionStorage . removeItem ( this . signature ) ; this . _executeQueue = [ ] ; this . _detachListeners ( ) ; } param eventName param callback return examples on ( eventName , callback ) { register ( eventName , callback , this . uuid ) ; return this ; } param eventName param callback return examples off ( eventName , callback ? ) { deRegister ( eventName , callback , this . uuid ) ; return this ; } abstract async return async connect ( ) { if ( ! this . connection ) { this . connection = new Connection ( this ) ; } this . _attachListeners ( ) ; if ( ! this . connection . isAlive ) { this . connection . connect ( ) ; } } return protected _handleLoginError ( error ) { trigger ( SwEvent . Error , error , this . uuid ) ; } return protected async _onSocketOpen ( ) { } return protected _onSocketCloseOrError ( event ) { logger . error ( `Socket ${ event . type } ${ event . message }` ) ; if ( this . relayProtocol ) { deRegisterAll ( this . relayProtocol ) ; } for ( const sub in this . subscriptions ) { deRegisterAll ( sub ) ; } this . subscriptions = { } ; this . contexts = [ ] ; if ( this . _autoReconnect ) { this . _reconnectTimeout = setTimeout ( ( ) => this . connect ( ) , this . reconnectDelay ) ; } } return protected _onSocketMessage ( response ) { } return protected _removeSubscription ( protocol , channel ? ) { if ( ! this . _existsSubscription ( protocol , channel ) ) { return ; } if ( channel ) { delete this . subscriptions [ protocol ] [ channel ] ; deRegister ( protocol , null , channel ) ; } else { delete this . subscriptions [ protocol ] ; deRegisterAll ( protocol ) ; } } return protected _addSubscription ( protocol , handler = null , channel ) { if ( this . _existsSubscription ( protocol , channel ) ) { return ; } if ( ! this . _existsSubscription ( protocol ) ) { this . subscriptions [ protocol ] = { } ; } this . subscriptions [ protocol ] [ channel ] = { } ; if ( isFunction ( handler ) ) { register ( protocol , handler , channel ) ; } } return ignore public _existsSubscription ( protocol , channel ? ) { if ( this . subscriptions . hasOwnProperty ( protocol ) ) { if ( ! channel || ( channel && this . subscriptions [ protocol ] . hasOwnProperty ( channel ) ) ) { return true ; } } return false ; } return private _attachListeners ( ) { this . _detachListeners ( ) ; this . on ( SwEvent . SocketOpen , this . _onSocketOpen ) ; this . on ( SwEvent . SocketClose , this . _onSocketCloseOrError ) ; this . on ( SwEvent . SocketError , this . _onSocketCloseOrError ) ; this . on ( SwEvent . SocketMessage , this . _onSocketMessage ) ; } return private _detachListeners ( ) { this . off ( SwEvent . SocketOpen , this . _onSocketOpen ) ; this . off ( SwEvent . SocketClose , this . _onSocketCloseOrError ) ; this . off ( SwEvent . SocketError , this . _onSocketCloseOrError ) ; this . off ( SwEvent . SocketMessage , this . _onSocketMessage ) ; } return private _emptyExecuteQueues ( ) { this . _executeQueue . forEach ( ( { resolve , msg } ) => { if ( typeof msg === 'string' ) { this . executeRaw ( msg ) ; } else { resolve ( this . execute ( msg ) ) ; } } ) ; } return private _closeConnection ( ) { this . _idle = true ; clearTimeout ( this . _keepAliveTimeout ) ; if ( this . connection ) { this . connection . close ( ) ; } } private _keepAlive ( ) { if ( this . _doKeepAlive !== true ) { return ; } if ( this . _pong === false ) { return this . _closeConnection ( ) ; } this . _pong = false ; this . _keepAliveTimeout = setTimeout ( ( ) => this . _keepAlive ( ) , KEEPALIVE_INTERVAL ) ; } static on ( eventName , callback ) { register ( eventName , callback ) ; } static off ( eventName ) { deRegister ( eventName ) ; } static uuid ( ) { return uuidv4 ( ) ; } public clearConnection ( ) { this . connection = null ; } public hasAutoReconnect ( ) { return this . _autoReconnect ; } }