import * as socketio from 'socket.io' import * as http from 'http' const END_CHARACTER = '\0' const map: { [propName: string]: Socketio } = {} const defaultKey = Symbol('DefaultKey').toString() // tslint:disable-next-line:no-empty let log = (..._msgs) => { } // @SOCKETIO: pooler export function SOCKETIO(key?: string) { return function (target: any, propertyKey: string | symbol) { Object.defineProperty(target, propertyKey, { get() { return Socketio.pool(key) } }) } } // SocketIO(): Config export function Socketio(...values: { key?: string, server: any, opts?: any, path: Socketio.SocketIOBinder }[]) { for (const { key = defaultKey, server, opts, path } of values) { console.info(`» SocketIO ${key.toString()} is bound`) map[key] = new SocketIOImpl(server, opts) if (path) { for (const p in path) { map[key].bind(p, path[p]) } } } return { debug(isDebug = true) { if (isDebug) log = console.log } } } export interface Socketio { // on(path: string, eventName: string, fcListen: (socket: SocketIO.Socket, _this: Socketio) => void): Socketio bind(path: string, handler?: Socketio.SocketIOHandler): Socketio send(path: string, sessionId: string, data: any, event?: string): { end: Function, disconnect: Function } end(path: string, sessionId: string, event?: string): void } // SocketIO: Helper export namespace Socketio { export interface SocketIOHandler { register?: (sessionId: string) => void } export interface SocketIOBinder { [path: string]: SocketIOHandler } export function pool(key = defaultKey): Socketio { return map[key] } } class SocketIOImpl implements Socketio { private io: SocketIO.Server private ioers: { [path: string]: { [key: string]: SocketIO.Socket } } = {} constructor(server: http.Server, opts) { this.io = socketio(server, opts) } // public on(path, eventName, fcListen: (socket: SocketIO.Socket, _this: Socketio) => void) { // console.info(` √ Path [${path}] is on`) // let ioers = this.ioers // ioers[path] = {} // this.io.of(path).on(eventName, (socket: SocketIO.Socket) => { // fcListen(socket, this) // }) // return this // } public bind(path: string, _handler: Socketio.SocketIOHandler | Function = {}) { console.info(` √ Path [${path}] is bound`) this.ioers[path] = {} let handler: Function if (typeof _handler !== 'function') { handler = (socket: SocketIO.Socket) => { socket.emit('connected') socket.on('register', (sessionId) => { this.ioers[path][sessionId] = socket log('[SocketIO]', `REGISTER "${path}"`, sessionId.toString()) if ((_handler as Socketio.SocketIOHandler).register) (_handler as Socketio.SocketIOHandler).register(sessionId) }) socket.on('disconnect', () => { for (let i in this.ioers[path]) { if (this.ioers[path][i] === socket) { log('[SocketIO]', `DISCONNECTED "${path}"`, i.toString()) delete this.ioers[path][i] } } }) } } else { handler = _handler as Function } this.io.of(path).on('connection', handler) return this } public send(path: string, sessionId: string, data: any, event?: string) { try { let socket: SocketIO.Socket = this.ioers[path][sessionId] if (socket) { if (socket.connected) { log('[SocketIO]', 'SEND', event || sessionId.toString(), JSON.stringify(data)) socket.emit(event || sessionId.toString(), typeof data !== 'string' ? JSON.stringify(data) : data) } else { delete this.ioers[path][sessionId] } socket = null return { end: () => { log('[SocketIO]', 'SEND', event || sessionId.toString(), 'END_CHARACTER') let socket: SocketIO.Socket = this.ioers[path][sessionId] if (socket && socket.connected) socket.emit(event || sessionId.toString(), END_CHARACTER) socket = null }, disconnect: () => { let socket: SocketIO.Socket = this.ioers[path][sessionId] if (socket && socket.connected) socket.disconnect(true) socket = null } } } } catch (e) { console.warn('SocketIO not sent: ' + sessionId) } return { end: Function, disconnect: Function } } public end(path: string, sessionId: string, event?: string) { let socket: SocketIO.Socket try { socket = this.ioers[path][sessionId] if (socket && socket.connected) { log('[SocketIO]', 'SEND', event || sessionId.toString(), 'END_CHARACTER') socket.emit(event || sessionId.toString(), END_CHARACTER) } } catch (e) { console.warn('SocketIO could not close: ' + sessionId) } finally { socket = null } } }