UNPKG

lightweight-socket

Version:

lightweight socket

298 lines (292 loc) 8.21 kB
// src/server.ts import * as http from "http"; import { WebSocketServer } from "ws"; // src/event.ts var Event = class { constructor(name, payload) { this.name = name; this.payload = payload; } toString() { return JSON.stringify({ name: this.name, payload: this.payload }); } }; var Emitter = class { constructor() { this.listeners = {}; } on(event, listener) { if (!this.listeners[event]) { this.listeners[event] = []; } this.listeners[event].push(listener); } once(event, listener) { const onceListener = (...args) => { this.off(event, onceListener); listener.apply(this, args); }; this.on(event, onceListener); } off(event, listener) { if (!this.listeners[event]) return; this.listeners[event] = this.listeners[event].filter((l) => l !== listener); } }; // src/client-server.ts import { WebSocket } from "ws"; var AbstractClient = class extends Emitter { emit(name, ...args) { if (name === "message") return; const event = new Event(name, args); const message = event.toString(); this.socket.send(message); } close() { this.socket.close(); } receive(data) { if (Buffer.isBuffer(data)) { data = data.toString(); } const parsedEvent = JSON.parse(data); if (this.listeners[parsedEvent.name]) { this.listeners[parsedEvent.name].forEach((listener) => listener(...parsedEvent.payload)); } } init() { this.socket.on("message", this.receive.bind(this)); } }; var Client = class extends AbstractClient { constructor(url) { super(); this.url = url; this.socket = new WebSocket(`${url}`); this.init(); this.socket.on("connect", (id) => this.id = id); } }; var ServerSideClient = class extends AbstractClient { constructor(url, socket, server) { super(); this.server = server; this.rooms = /* @__PURE__ */ new Map(); this.socket = socket; this.url = url; this.id = Math.random().toString(36).substr(2, 12); this.init(); this.rooms.set(this.id, this); } join(room) { this.emit("join-room", room); } leave(room) { this.emit("leave-room", room); } to(room) { const adapter = new Adapter(this.server); return adapter.of(this.url).to(room); } in(room) { return this.to(room); } except(room) { const adapter = new Adapter(this.server); return adapter.of(this.url).except(room); } emit(name, ...args) { if (name === "join-room" || name === "leave-room") { if (this.listeners[name]) { this.listeners[name].forEach((listener) => listener(...args)); } return; } const adapter = new Adapter(this.server); return adapter.of(this.url).to(this.id).emit(name, ...args); } }; // src/adapter.ts var Adapter = class { constructor(server) { this.server = server; this.rooms = /* @__PURE__ */ new Set(); this.exceptions = /* @__PURE__ */ new Set(); this.namespace = null; } to(room) { this.rooms.add(room); return this; } except(clientId) { this.exceptions.add(clientId); return this; } in(room) { return this.to(room); } of(namespace) { this.namespace = namespace; return this; } emit(name, ...args) { var _a; const event = new Event(name, args); const message = event.toString(); const namespace = this.server.namespaces.get((_a = this.namespace) != null ? _a : "/"); if (!namespace) return; this.rooms.forEach((room) => { if (!namespace.rooms.has(room)) { return; } const clients = namespace.rooms.get(room) instanceof ServerSideClient ? [namespace.rooms.get(room)] : namespace.rooms.get(room).clients; clients.forEach((client) => { if (this.exceptions.has(client.id)) return; client.socket.send(message); }); }); this.rooms.clear(); this.exceptions.clear(); this.namespace = null; } }; // src/namespace.ts var Namespace = class extends Emitter { constructor(namespace, server, ws) { super(); this.namespace = namespace; this.rooms = /* @__PURE__ */ new Map(); this.server = server; this.ws = ws; } except(room) { const adapter = new Adapter(this.server); return adapter.of(this.namespace).except(room); } emit(name, ...args) { if (name === "create-room" || name === "join-room" || name === "leave-room" || name === "delete-room" || name === "connection" || name === "close") { if (this.listeners[name]) { this.listeners[name].forEach((listener) => listener(...args)); } return; } const adapter = new Adapter(this.server); return adapter.of(this.namespace).emit(name, ...args); } adapter() { return new Adapter(this.server); } to(room) { const adapter = new Adapter(this.server); return adapter.of(this.namespace).to(room); } in(room) { return this.to(room); } }; // src/room.ts var Room = class { constructor(name) { this.clients = /* @__PURE__ */ new Map(); this.id = name; } add(client) { const clientId = client.id; this.clients.set(clientId, client); client.on("close", () => this.clients.delete(clientId)); } remove(client) { const clientId = client.id; this.clients.delete(clientId); } }; // src/server.ts var Server = class { constructor(options) { this.clients = /* @__PURE__ */ new Map(); this.namespaces = /* @__PURE__ */ new Map(); if (typeof options === "number") { options = { port: options }; } this.port = options.port; this.server = http.createServer((req, res) => res.writeHead(200, { "Content-Type": "text/plain" })).listen(this.port); this.server.on("upgrade", this.upgrade.bind(this)); const namespace = this.getNamespace("/"); this.namespaces.set("/", namespace); } upgrade(req, socket, head) { const [namespacePath, query] = req.url.split("?"); const namespace = this.getNamespace(namespacePath); const wss = namespace.ws; wss.handleUpgrade(req, socket, head, (socket2) => this.handler(namespace, socket2, namespacePath)); } handler(namespace, socket, namespacePath) { const client = new ServerSideClient(namespacePath, socket, this); this.clients.set(client.id, client); namespace.rooms.set(client.id, client); client.on("close", () => this.clients.delete(client.id)); client.on("join-room", (name) => { var room = null; if (!namespace.rooms.has(name)) { room = new Room(name); namespace.rooms.set(name, room); namespace.emit("create-room", room); } room = namespace.rooms.get(name); room.add(client); client.rooms.set(name, room); namespace.emit("join-room", room, client.id); }); client.on("leave-room", (name) => { if (!namespace.rooms.has(name)) { return; } const room = namespace.rooms.get(name); room.remove(client); namespace.emit("leave-room", room, client.id); if (room.clients.size === 0) { namespace.emit("delete-room", room); namespace.rooms.delete(name); } }); client.emit("connect", client.id); namespace.emit("connection", client); } getNamespace(namespacePath) { if (this.namespaces.has(namespacePath)) return this.namespaces.get(namespacePath); const wss = new WebSocketServer({ noServer: true }); wss.setMaxListeners(0); const namespace = new Namespace(namespacePath, this, wss); this.namespaces.set(namespacePath, namespace); return namespace; } emit(name, ...args) { this.namespaces.forEach((namespace) => { var adapter = namespace.adapter(); namespace.rooms.forEach((room) => adapter = adapter.to(room.id)); adapter.emit(name, ...args); }); } close() { this.clients.forEach((client) => client.close()); this.server.close(); } on(event, listener) { this.namespaces.forEach((namespace) => namespace.on(event, listener)); } of(namespace) { return this.getNamespace(namespace); } }; Server.isInitialized = false; export { Client, Server, ServerSideClient }; //# sourceMappingURL=index.mjs.map