'use strict'; const createEventEmitter = require("create-event-emitter"); const defaultValue = require("default-value"); function removeFromArray(array, item) { let index = array.indexOf(item); if (index !== -1) { array.splice(index, 1); } } module.exports = function createClientStore(options = {}) { let clients = []; let subscribedClients = []; let store = createEventEmitter({ add: function addClient(client) { let clientObject = createEventEmitter({ socket: client, subscribed: false, disconnected: false, kill: function killClient() { store.kill(client); }, send: function sendMessage(message) { let data = JSON.stringify(message); this.socket.send(data, (error) => { if (error != null) { if (error.message === "not opened") { /* The socket has disappeared. We can safely ignore this. Is there a more reliable way to detect this error than by its message, though? */ } else { store.emit("error", error); } } }); } }); client.on("message", (data) => { let message; try { message = JSON.parse(data); } catch (err) { /* User sent invalid data; disconnect them. */ return this.kill(client); } if (message == null || typeof message !== "object") { /* User sent invalid data; disconnect them. */ return this.kill(client); } if (message.type === "subscribe") { if (clientObject.subscribed === false) { subscribedClients.push(clientObject); clientObject.subscribed = true; clientObject.emit("subscribed"); } } else { clientObject.emit("message", message); } }); client.on("close", (code, reason) => { client.disconnected = true; this.remove(clientObject); }); clients.push(clientObject); return clientObject; }, kill: function killClient(client) { client.close(); }, remove: function removeClient(client) { removeFromArray(clients, client); removeFromArray(subscribedClients, client); }, broadcast: function broadcastMessage(message, clients) { defaultValue(clients, subscribedClients).forEach((client) => { client.send(message); }); }, broadcastAll: function broadcastMessageToAll(message) { this.broadcast(message, clients); } }); let pingInterval = defaultValue(options.pingInterval, 5000); function pingClients() { store.broadcastAll({ type: "ping" }); setTimeout(pingClients, pingInterval); } pingClients(); return store; };