| "use strict"; |
| var __importDefault = (this && this.__importDefault) || function (mod) { |
| return (mod && mod.__esModule) ? mod : { "default": mod }; |
| }; |
| Object.defineProperty(exports, "__esModule", { value: true }); |
| exports.Socket = exports.RESERVED_EVENTS = void 0; |
| const socket_io_parser_1 = require("socket.io-parser"); |
| const debug_1 = __importDefault(require("debug")); |
| const typed_events_1 = require("./typed-events"); |
| const base64id_1 = __importDefault(require("base64id")); |
| const broadcast_operator_1 = require("./broadcast-operator"); |
| const debug = (0, debug_1.default)("socket.io:socket"); |
| const RECOVERABLE_DISCONNECT_REASONS = new Set([ |
| "transport error", |
| "transport close", |
| "forced close", |
| "ping timeout", |
| "server shutting down", |
| "forced server close", |
| ]); |
| exports.RESERVED_EVENTS = new Set([ |
| "connect", |
| "connect_error", |
| "disconnect", |
| "disconnecting", |
| "newListener", |
| "removeListener", |
| ]); |
| function noop() { } |
| /** |
| * This is the main object for interacting with a client. |
| * |
| * A Socket belongs to a given {@link Namespace} and uses an underlying {@link Client} to communicate. |
| * |
| * Within each {@link Namespace}, you can also define arbitrary channels (called "rooms") that the {@link Socket} can |
| * join and leave. That provides a convenient way to broadcast to a group of socket instances. |
| * |
| * @example |
| * io.on("connection", (socket) => { |
| * console.log(`socket ${socket.id} connected`); |
| * |
| * // send an event to the client |
| * socket.emit("foo", "bar"); |
| * |
| * socket.on("foobar", () => { |
| * // an event was received from the client |
| * }); |
| * |
| * // join the room named "room1" |
| * socket.join("room1"); |
| * |
| * // broadcast to everyone in the room named "room1" |
| * io.to("room1").emit("hello"); |
| * |
| * // upon disconnection |
| * socket.on("disconnect", (reason) => { |
| * console.log(`socket ${socket.id} disconnected due to ${reason}`); |
| * }); |
| * }); |
| */ |
| class Socket extends typed_events_1.StrictEventEmitter { |
| /** |
| * Interface to a `Client` for a given `Namespace`. |
| * |
| * @param {Namespace} nsp |
| * @param {Client} client |
| * @param {Object} auth |
| * @package |
| */ |
| constructor(nsp, client, auth, previousSession) { |
| super(); |
| this.nsp = nsp; |
| this.client = client; |
| /** |
| * Whether the connection state was recovered after a temporary disconnection. In that case, any missed packets will |
| * be transmitted to the client, the data attribute and the rooms will be restored. |
| */ |
| this.recovered = false; |
| /** |
| * Additional information that can be attached to the Socket instance and which will be used in the |
| * {@link Server.fetchSockets()} method. |
| */ |
| this.data = {}; |
| /** |
| * Whether the socket is currently connected or not. |
| * |
| * @example |
| * io.use((socket, next) => { |
| * console.log(socket.connected); // false |
| * next(); |
| * }); |
| * |
| * io.on("connection", (socket) => { |
| * console.log(socket.connected); // true |
| * }); |
| */ |
| this.connected = false; |
| this.acks = new Map(); |
| this.fns = []; |
| this.flags = {}; |
| this.server = nsp.server; |
| this.adapter = this.nsp.adapter; |
| if (previousSession) { |
| this.id = previousSession.sid; |
| this.pid = previousSession.pid; |
| previousSession.rooms.forEach((room) => this.join(room)); |
| this.data = previousSession.data; |
| previousSession.missedPackets.forEach((packet) => { |
| this.packet({ |
| type: socket_io_parser_1.PacketType.EVENT, |
| data: packet, |
| }); |
| }); |
| this.recovered = true; |
| } |
| else { |
| if (client.conn.protocol === 3) { |
| // @ts-ignore |
| this.id = nsp.name !== "/" ? nsp.name + "#" + client.id : client.id; |
| } |
| else { |
| this.id = base64id_1.default.generateId(); // don't reuse the Engine.IO id because it's sensitive information |
| } |
| if (this.server._opts.connectionStateRecovery) { |
| this.pid = base64id_1.default.generateId(); |
| } |
| } |
| this.handshake = this.buildHandshake(auth); |
| } |
| /** |
| * Builds the `handshake` BC object |
| * |
| * @private |
| */ |
| buildHandshake(auth) { |
| return { |
| headers: this.request.headers, |
| time: new Date() + "", |
| address: this.conn.remoteAddress, |
| xdomain: !!this.request.headers.origin, |
| // @ts-ignore |
| secure: !!this.request.connection.encrypted, |
| issued: +new Date(), |
| url: this.request.url, |
| // @ts-ignore |
| query: this.request._query, |
| auth, |
| }; |
| } |
| /** |
| * Emits to this client. |
| * |
| * @example |
| * io.on("connection", (socket) => { |
| * socket.emit("hello", "world"); |
| * |
| * // all serializable datastructures are supported (no need to call JSON.stringify) |
| * socket.emit("hello", 1, "2", { 3: ["4"], 5: Buffer.from([6]) }); |
| * |
| * // with an acknowledgement from the client |
| * socket.emit("hello", "world", (val) => { |
| * // ... |
| * }); |
| * }); |
| * |
| * @return Always returns `true`. |
| */ |
| emit(ev, ...args) { |
| if (exports.RESERVED_EVENTS.has(ev)) { |
| throw new Error(`"${String(ev)}" is a reserved event name`); |
| } |
| const data = [ev, ...args]; |
| const packet = { |
| type: socket_io_parser_1.PacketType.EVENT, |
| data: data, |
| }; |
| // access last argument to see if it's an ACK callback |
| if (typeof data[data.length - 1] === "function") { |
| const id = this.nsp._ids++; |
| debug("emitting packet with ack id %d", id); |
| this.registerAckCallback(id, data.pop()); |
| packet.id = id; |
| } |
| const flags = Object.assign({}, this.flags); |
| this.flags = {}; |
| // @ts-ignore |
| if (this.nsp.server.opts.connectionStateRecovery) { |
| // this ensures the packet is stored and can be transmitted upon reconnection |
| this.adapter.broadcast(packet, { |
| rooms: new Set([this.id]), |
| except: new Set(), |
| flags, |
| }); |
| } |
| else { |
| this.notifyOutgoingListeners(packet); |
| this.packet(packet, flags); |
| } |
| return true; |
| } |
| /** |
| * Emits an event and waits for an acknowledgement |
| * |
| * @example |
| * io.on("connection", async (socket) => { |
| * // without timeout |
| * const response = await socket.emitWithAck("hello", "world"); |
| * |
| * // with a specific timeout |
| * try { |
| * const response = await socket.timeout(1000).emitWithAck("hello", "world"); |
| * } catch (err) { |
| * // the client did not acknowledge the event in the given delay |
| * } |
| * }); |
| * |
| * @return a Promise that will be fulfilled when the client acknowledges the event |
| */ |
| emitWithAck(ev, ...args) { |
| // the timeout flag is optional |
| const withErr = this.flags.timeout !== undefined; |
| return new Promise((resolve, reject) => { |
| args.push((arg1, arg2) => { |
| if (withErr) { |
| return arg1 ? reject(arg1) : resolve(arg2); |
| } |
| else { |
| return resolve(arg1); |
| } |
| }); |
| this.emit(ev, ...args); |
| }); |
| } |
| /** |
| * @private |
| */ |
| registerAckCallback(id, ack) { |
| const timeout = this.flags.timeout; |
| if (timeout === undefined) { |
| this.acks.set(id, ack); |
| return; |
| } |
| const timer = setTimeout(() => { |
| debug("event with ack id %d has timed out after %d ms", id, timeout); |
| this.acks.delete(id); |
| ack.call(this, new Error("operation has timed out")); |
| }, timeout); |
| this.acks.set(id, (...args) => { |
| clearTimeout(timer); |
| ack.apply(this, [null, ...args]); |
| }); |
| } |
| /** |
| * Targets a room when broadcasting. |
| * |
| * @example |
| * io.on("connection", (socket) => { |
| * // the “foo” event will be broadcast to all connected clients in the “room-101” room, except this socket |
| * socket.to("room-101").emit("foo", "bar"); |
| * |
| * // the code above is equivalent to: |
| * io.to("room-101").except(socket.id).emit("foo", "bar"); |
| * |
| * // with an array of rooms (a client will be notified at most once) |
| * socket.to(["room-101", "room-102"]).emit("foo", "bar"); |
| * |
| * // with multiple chained calls |
| * socket.to("room-101").to("room-102").emit("foo", "bar"); |
| * }); |
| * |
| * @param room - a room, or an array of rooms |
| * @return a new {@link BroadcastOperator} instance for chaining |
| */ |
| to(room) { |
| return this.newBroadcastOperator().to(room); |
| } |
| /** |
| * Targets a room when broadcasting. Similar to `to()`, but might feel clearer in some cases: |
| * |
| * @example |
| * io.on("connection", (socket) => { |
| * // disconnect all clients in the "room-101" room, except this socket |
| * socket.in("room-101").disconnectSockets(); |
| * }); |
| * |
| * @param room - a room, or an array of rooms |
| * @return a new {@link BroadcastOperator} instance for chaining |
| */ |
| in(room) { |
| return this.newBroadcastOperator().in(room); |
| } |
| /** |
| * Excludes a room when broadcasting. |
| * |
| * @example |
| * io.on("connection", (socket) => { |
| * // the "foo" event will be broadcast to all connected clients, except the ones that are in the "room-101" room |
| * // and this socket |
| * socket.except("room-101").emit("foo", "bar"); |
| * |
| * // with an array of rooms |
| * socket.except(["room-101", "room-102"]).emit("foo", "bar"); |
| * |
| * // with multiple chained calls |
| * socket.except("room-101").except("room-102").emit("foo", "bar"); |
| * }); |
| * |
| * @param room - a room, or an array of rooms |
| * @return a new {@link BroadcastOperator} instance for chaining |
| */ |
| except(room) { |
| return this.newBroadcastOperator().except(room); |
| } |
| /** |
| * Sends a `message` event. |
| * |
| * This method mimics the WebSocket.send() method. |
| * |
| * @see https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/send |
| * |
| * @example |
| * io.on("connection", (socket) => { |
| * socket.send("hello"); |
| * |
| * // this is equivalent to |
| * socket.emit("message", "hello"); |
| * }); |
| * |
| * @return self |
| */ |
| send(...args) { |
| this.emit("message", ...args); |
| return this; |
| } |
| /** |
| * Sends a `message` event. Alias of {@link send}. |
| * |
| * @return self |
| */ |
| write(...args) { |
| this.emit("message", ...args); |
| return this; |
| } |
| /** |
| * Writes a packet. |
| * |
| * @param {Object} packet - packet object |
| * @param {Object} opts - options |
| * @private |
| */ |
| packet(packet, opts = {}) { |
| packet.nsp = this.nsp.name; |
| opts.compress = false !== opts.compress; |
| this.client._packet(packet, opts); |
| } |
| /** |
| * Joins a room. |
| * |
| * @example |
| * io.on("connection", (socket) => { |
| * // join a single room |
| * socket.join("room1"); |
| * |
| * // join multiple rooms |
| * socket.join(["room1", "room2"]); |
| * }); |
| * |
| * @param {String|Array} rooms - room or array of rooms |
| * @return a Promise or nothing, depending on the adapter |
| */ |
| join(rooms) { |
| debug("join room %s", rooms); |
| return this.adapter.addAll(this.id, new Set(Array.isArray(rooms) ? rooms : [rooms])); |
| } |
| /** |
| * Leaves a room. |
| * |
| * @example |
| * io.on("connection", (socket) => { |
| * // leave a single room |
| * socket.leave("room1"); |
| * |
| * // leave multiple rooms |
| * socket.leave("room1").leave("room2"); |
| * }); |
| * |
| * @param {String} room |
| * @return a Promise or nothing, depending on the adapter |
| */ |
| leave(room) { |
| debug("leave room %s", room); |
| return this.adapter.del(this.id, room); |
| } |
| /** |
| * Leave all rooms. |
| * |
| * @private |
| */ |
| leaveAll() { |
| this.adapter.delAll(this.id); |
| } |
| /** |
| * Called by `Namespace` upon successful |
| * middleware execution (ie: authorization). |
| * Socket is added to namespace array before |
| * call to join, so adapters can access it. |
| * |
| * @private |
| */ |
| _onconnect() { |
| debug("socket connected - writing packet"); |
| this.connected = true; |
| this.join(this.id); |
| if (this.conn.protocol === 3) { |
| this.packet({ type: socket_io_parser_1.PacketType.CONNECT }); |
| } |
| else { |
| this.packet({ |
| type: socket_io_parser_1.PacketType.CONNECT, |
| data: { sid: this.id, pid: this.pid }, |
| }); |
| } |
| } |
| /** |
| * Called with each packet. Called by `Client`. |
| * |
| * @param {Object} packet |
| * @private |
| */ |
| _onpacket(packet) { |
| debug("got packet %j", packet); |
| switch (packet.type) { |
| case socket_io_parser_1.PacketType.EVENT: |
| this.onevent(packet); |
| break; |
| case socket_io_parser_1.PacketType.BINARY_EVENT: |
| this.onevent(packet); |
| break; |
| case socket_io_parser_1.PacketType.ACK: |
| this.onack(packet); |
| break; |
| case socket_io_parser_1.PacketType.BINARY_ACK: |
| this.onack(packet); |
| break; |
| case socket_io_parser_1.PacketType.DISCONNECT: |
| this.ondisconnect(); |
| break; |
| } |
| } |
| /** |
| * Called upon event packet. |
| * |
| * @param {Packet} packet - packet object |
| * @private |
| */ |
| onevent(packet) { |
| const args = packet.data || []; |
| debug("emitting event %j", args); |
| if (null != packet.id) { |
| debug("attaching ack callback to event"); |
| args.push(this.ack(packet.id)); |
| } |
| if (this._anyListeners && this._anyListeners.length) { |
| const listeners = this._anyListeners.slice(); |
| for (const listener of listeners) { |
| listener.apply(this, args); |
| } |
| } |
| this.dispatch(args); |
| } |
| /** |
| * Produces an ack callback to emit with an event. |
| * |
| * @param {Number} id - packet id |
| * @private |
| */ |
| ack(id) { |
| const self = this; |
| let sent = false; |
| return function () { |
| // prevent double callbacks |
| if (sent) |
| return; |
| const args = Array.prototype.slice.call(arguments); |
| debug("sending ack %j", args); |
| self.packet({ |
| id: id, |
| type: socket_io_parser_1.PacketType.ACK, |
| data: args, |
| }); |
| sent = true; |
| }; |
| } |
| /** |
| * Called upon ack packet. |
| * |
| * @private |
| */ |
| onack(packet) { |
| const ack = this.acks.get(packet.id); |
| if ("function" == typeof ack) { |
| debug("calling ack %s with %j", packet.id, packet.data); |
| ack.apply(this, packet.data); |
| this.acks.delete(packet.id); |
| } |
| else { |
| debug("bad ack %s", packet.id); |
| } |
| } |
| /** |
| * Called upon client disconnect packet. |
| * |
| * @private |
| */ |
| ondisconnect() { |
| debug("got disconnect packet"); |
| this._onclose("client namespace disconnect"); |
| } |
| /** |
| * Handles a client error. |
| * |
| * @private |
| */ |
| _onerror(err) { |
| if (this.listeners("error").length) { |
| this.emitReserved("error", err); |
| } |
| else { |
| console.error("Missing error handler on `socket`."); |
| console.error(err.stack); |
| } |
| } |
| /** |
| * Called upon closing. Called by `Client`. |
| * |
| * @param {String} reason |
| * @param description |
| * @throw {Error} optional error object |
| * |
| * @private |
| */ |
| _onclose(reason, description) { |
| if (!this.connected) |
| return this; |
| debug("closing socket - reason %s", reason); |
| this.emitReserved("disconnecting", reason, description); |
| if (this.server._opts.connectionStateRecovery && |
| RECOVERABLE_DISCONNECT_REASONS.has(reason)) { |
| debug("connection state recovery is enabled for sid %s", this.id); |
| this.adapter.persistSession({ |
| sid: this.id, |
| pid: this.pid, |
| rooms: [...this.rooms], |
| data: this.data, |
| }); |
| } |
| this._cleanup(); |
| this.nsp._remove(this); |
| this.client._remove(this); |
| this.connected = false; |
| this.emitReserved("disconnect", reason, description); |
| return; |
| } |
| /** |
| * Makes the socket leave all the rooms it was part of and prevents it from joining any other room |
| * |
| * @private |
| */ |
| _cleanup() { |
| this.leaveAll(); |
| this.join = noop; |
| } |
| /** |
| * Produces an `error` packet. |
| * |
| * @param {Object} err - error object |
| * |
| * @private |
| */ |
| _error(err) { |
| this.packet({ type: socket_io_parser_1.PacketType.CONNECT_ERROR, data: err }); |
| } |
| /** |
| * Disconnects this client. |
| * |
| * @example |
| * io.on("connection", (socket) => { |
| * // disconnect this socket (the connection might be kept alive for other namespaces) |
| * socket.disconnect(); |
| * |
| * // disconnect this socket and close the underlying connection |
| * socket.disconnect(true); |
| * }) |
| * |
| * @param {Boolean} close - if `true`, closes the underlying connection |
| * @return self |
| */ |
| disconnect(close = false) { |
| if (!this.connected) |
| return this; |
| if (close) { |
| this.client._disconnect(); |
| } |
| else { |
| this.packet({ type: socket_io_parser_1.PacketType.DISCONNECT }); |
| this._onclose("server namespace disconnect"); |
| } |
| return this; |
| } |
| /** |
| * Sets the compress flag. |
| * |
| * @example |
| * io.on("connection", (socket) => { |
| * socket.compress(false).emit("hello"); |
| * }); |
| * |
| * @param {Boolean} compress - if `true`, compresses the sending data |
| * @return {Socket} self |
| */ |
| compress(compress) { |
| this.flags.compress = compress; |
| return this; |
| } |
| /** |
| * Sets a modifier for a subsequent event emission that the event data may be lost if the client is not ready to |
| * receive messages (because of network slowness or other issues, or because they’re connected through long polling |
| * and is in the middle of a request-response cycle). |
| * |
| * @example |
| * io.on("connection", (socket) => { |
| * socket.volatile.emit("hello"); // the client may or may not receive it |
| * }); |
| * |
| * @return {Socket} self |
| */ |
| get volatile() { |
| this.flags.volatile = true; |
| return this; |
| } |
| /** |
| * Sets a modifier for a subsequent event emission that the event data will only be broadcast to every sockets but the |
| * sender. |
| * |
| * @example |
| * io.on("connection", (socket) => { |
| * // the “foo” event will be broadcast to all connected clients, except this socket |
| * socket.broadcast.emit("foo", "bar"); |
| * }); |
| * |
| * @return a new {@link BroadcastOperator} instance for chaining |
| */ |
| get broadcast() { |
| return this.newBroadcastOperator(); |
| } |
| /** |
| * Sets a modifier for a subsequent event emission that the event data will only be broadcast to the current node. |
| * |
| * @example |
| * io.on("connection", (socket) => { |
| * // the “foo” event will be broadcast to all connected clients on this node, except this socket |
| * socket.local.emit("foo", "bar"); |
| * }); |
| * |
| * @return a new {@link BroadcastOperator} instance for chaining |
| */ |
| get local() { |
| return this.newBroadcastOperator().local; |
| } |
| /** |
| * Sets a modifier for a subsequent event emission that the callback will be called with an error when the |
| * given number of milliseconds have elapsed without an acknowledgement from the client: |
| * |
| * @example |
| * io.on("connection", (socket) => { |
| * socket.timeout(5000).emit("my-event", (err) => { |
| * if (err) { |
| * // the client did not acknowledge the event in the given delay |
| * } |
| * }); |
| * }); |
| * |
| * @returns self |
| */ |
| timeout(timeout) { |
| this.flags.timeout = timeout; |
| return this; |
| } |
| /** |
| * Dispatch incoming event to socket listeners. |
| * |
| * @param {Array} event - event that will get emitted |
| * @private |
| */ |
| dispatch(event) { |
| debug("dispatching an event %j", event); |
| this.run(event, (err) => { |
| process.nextTick(() => { |
| if (err) { |
| return this._onerror(err); |
| } |
| if (this.connected) { |
| super.emitUntyped.apply(this, event); |
| } |
| else { |
| debug("ignore packet received after disconnection"); |
| } |
| }); |
| }); |
| } |
| /** |
| * Sets up socket middleware. |
| * |
| * @example |
| * io.on("connection", (socket) => { |
| * socket.use(([event, ...args], next) => { |
| * if (isUnauthorized(event)) { |
| * return next(new Error("unauthorized event")); |
| * } |
| * // do not forget to call next |
| * next(); |
| * }); |
| * |
| * socket.on("error", (err) => { |
| * if (err && err.message === "unauthorized event") { |
| * socket.disconnect(); |
| * } |
| * }); |
| * }); |
| * |
| * @param {Function} fn - middleware function (event, next) |
| * @return {Socket} self |
| */ |
| use(fn) { |
| this.fns.push(fn); |
| return this; |
| } |
| /** |
| * Executes the middleware for an incoming event. |
| * |
| * @param {Array} event - event that will get emitted |
| * @param {Function} fn - last fn call in the middleware |
| * @private |
| */ |
| run(event, fn) { |
| const fns = this.fns.slice(0); |
| if (!fns.length) |
| return fn(null); |
| function run(i) { |
| fns[i](event, function (err) { |
| // upon error, short-circuit |
| if (err) |
| return fn(err); |
| // if no middleware left, summon callback |
| if (!fns[i + 1]) |
| return fn(null); |
| // go on to next |
| run(i + 1); |
| }); |
| } |
| run(0); |
| } |
| /** |
| * Whether the socket is currently disconnected |
| */ |
| get disconnected() { |
| return !this.connected; |
| } |
| /** |
| * A reference to the request that originated the underlying Engine.IO Socket. |
| */ |
| get request() { |
| return this.client.request; |
| } |
| /** |
| * A reference to the underlying Client transport connection (Engine.IO Socket object). |
| * |
| * @example |
| * io.on("connection", (socket) => { |
| * console.log(socket.conn.transport.name); // prints "polling" or "websocket" |
| * |
| * socket.conn.once("upgrade", () => { |
| * console.log(socket.conn.transport.name); // prints "websocket" |
| * }); |
| * }); |
| */ |
| get conn() { |
| return this.client.conn; |
| } |
| /** |
| * Returns the rooms the socket is currently in. |
| * |
| * @example |
| * io.on("connection", (socket) => { |
| * console.log(socket.rooms); // Set { <socket.id> } |
| * |
| * socket.join("room1"); |
| * |
| * console.log(socket.rooms); // Set { <socket.id>, "room1" } |
| * }); |
| */ |
| get rooms() { |
| return this.adapter.socketRooms(this.id) || new Set(); |
| } |
| /** |
| * Adds a listener that will be fired when any event is received. The event name is passed as the first argument to |
| * the callback. |
| * |
| * @example |
| * io.on("connection", (socket) => { |
| * socket.onAny((event, ...args) => { |
| * console.log(`got event ${event}`); |
| * }); |
| * }); |
| * |
| * @param listener |
| */ |
| onAny(listener) { |
| this._anyListeners = this._anyListeners || []; |
| this._anyListeners.push(listener); |
| return this; |
| } |
| /** |
| * Adds a listener that will be fired when any event is received. The event name is passed as the first argument to |
| * the callback. The listener is added to the beginning of the listeners array. |
| * |
| * @param listener |
| */ |
| prependAny(listener) { |
| this._anyListeners = this._anyListeners || []; |
| this._anyListeners.unshift(listener); |
| return this; |
| } |
| /** |
| * Removes the listener that will be fired when any event is received. |
| * |
| * @example |
| * io.on("connection", (socket) => { |
| * const catchAllListener = (event, ...args) => { |
| * console.log(`got event ${event}`); |
| * } |
| * |
| * socket.onAny(catchAllListener); |
| * |
| * // remove a specific listener |
| * socket.offAny(catchAllListener); |
| * |
| * // or remove all listeners |
| * socket.offAny(); |
| * }); |
| * |
| * @param listener |
| */ |
| offAny(listener) { |
| if (!this._anyListeners) { |
| return this; |
| } |
| if (listener) { |
| const listeners = this._anyListeners; |
| for (let i = 0; i < listeners.length; i++) { |
| if (listener === listeners[i]) { |
| listeners.splice(i, 1); |
| return this; |
| } |
| } |
| } |
| else { |
| this._anyListeners = []; |
| } |
| return this; |
| } |
| /** |
| * Returns an array of listeners that are listening for any event that is specified. This array can be manipulated, |
| * e.g. to remove listeners. |
| */ |
| listenersAny() { |
| return this._anyListeners || []; |
| } |
| /** |
| * Adds a listener that will be fired when any event is sent. The event name is passed as the first argument to |
| * the callback. |
| * |
| * Note: acknowledgements sent to the client are not included. |
| * |
| * @example |
| * io.on("connection", (socket) => { |
| * socket.onAnyOutgoing((event, ...args) => { |
| * console.log(`sent event ${event}`); |
| * }); |
| * }); |
| * |
| * @param listener |
| */ |
| onAnyOutgoing(listener) { |
| this._anyOutgoingListeners = this._anyOutgoingListeners || []; |
| this._anyOutgoingListeners.push(listener); |
| return this; |
| } |
| /** |
| * Adds a listener that will be fired when any event is emitted. The event name is passed as the first argument to the |
| * callback. The listener is added to the beginning of the listeners array. |
| * |
| * @example |
| * io.on("connection", (socket) => { |
| * socket.prependAnyOutgoing((event, ...args) => { |
| * console.log(`sent event ${event}`); |
| * }); |
| * }); |
| * |
| * @param listener |
| */ |
| prependAnyOutgoing(listener) { |
| this._anyOutgoingListeners = this._anyOutgoingListeners || []; |
| this._anyOutgoingListeners.unshift(listener); |
| return this; |
| } |
| /** |
| * Removes the listener that will be fired when any event is sent. |
| * |
| * @example |
| * io.on("connection", (socket) => { |
| * const catchAllListener = (event, ...args) => { |
| * console.log(`sent event ${event}`); |
| * } |
| * |
| * socket.onAnyOutgoing(catchAllListener); |
| * |
| * // remove a specific listener |
| * socket.offAnyOutgoing(catchAllListener); |
| * |
| * // or remove all listeners |
| * socket.offAnyOutgoing(); |
| * }); |
| * |
| * @param listener - the catch-all listener |
| */ |
| offAnyOutgoing(listener) { |
| if (!this._anyOutgoingListeners) { |
| return this; |
| } |
| if (listener) { |
| const listeners = this._anyOutgoingListeners; |
| for (let i = 0; i < listeners.length; i++) { |
| if (listener === listeners[i]) { |
| listeners.splice(i, 1); |
| return this; |
| } |
| } |
| } |
| else { |
| this._anyOutgoingListeners = []; |
| } |
| return this; |
| } |
| /** |
| * Returns an array of listeners that are listening for any event that is specified. This array can be manipulated, |
| * e.g. to remove listeners. |
| */ |
| listenersAnyOutgoing() { |
| return this._anyOutgoingListeners || []; |
| } |
| /** |
| * Notify the listeners for each packet sent (emit or broadcast) |
| * |
| * @param packet |
| * |
| * @private |
| */ |
| notifyOutgoingListeners(packet) { |
| if (this._anyOutgoingListeners && this._anyOutgoingListeners.length) { |
| const listeners = this._anyOutgoingListeners.slice(); |
| for (const listener of listeners) { |
| listener.apply(this, packet.data); |
| } |
| } |
| } |
| newBroadcastOperator() { |
| const flags = Object.assign({}, this.flags); |
| this.flags = {}; |
| return new broadcast_operator_1.BroadcastOperator(this.adapter, new Set(), new Set([this.id]), flags); |
| } |
| } |
| exports.Socket = Socket; |