UNPKG

lightweight-socket

Version:

lightweight socket

336 lines (328 loc) 9.89 kB
var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var src_exports = {}; __export(src_exports, { Client: () => Client, Server: () => Server, ServerSideClient: () => ServerSideClient }); module.exports = __toCommonJS(src_exports); // src/server.ts var http = __toESM(require("http")); var import_ws2 = require("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 var import_ws = require("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 import_ws.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 import_ws2.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; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { Client, Server, ServerSideClient }); //# sourceMappingURL=index.js.map