Skip to content

Commit

Permalink
feat: add support for catch-all listeners for outgoing packets
Browse files Browse the repository at this point in the history
This is similar to `onAny()`, but for outgoing packets.

Syntax:

```js
socket.onAnyOutgoing((event, ...args) => {
  console.log(event);
});
```

Related: socketio/socket.io@531104d
  • Loading branch information
darrachequesne committed Apr 23, 2022
1 parent 692d54e commit 74e3e60
Show file tree
Hide file tree
Showing 2 changed files with 168 additions and 1 deletion.
115 changes: 114 additions & 1 deletion lib/socket.ts
Expand Up @@ -76,6 +76,7 @@ export class Socket<
private flags: Flags = {};
private subs?: Array<VoidFunction>;
private _anyListeners: Array<(...args: any[]) => void>;
private _anyOutgoingListeners: Array<(...args: any[]) => void>;

/**
* `Socket` constructor.
Expand Down Expand Up @@ -193,6 +194,7 @@ export class Socket<
if (discardPacket) {
debug("discard packet as the transport is not currently writable");
} else if (this.connected) {
this.notifyOutgoingListeners(packet);
this.packet(packet);
} else {
this.sendBuffer.push(packet);
Expand Down Expand Up @@ -440,7 +442,10 @@ export class Socket<
this.receiveBuffer.forEach((args) => this.emitEvent(args));
this.receiveBuffer = [];

this.sendBuffer.forEach((packet) => this.packet(packet));
this.sendBuffer.forEach((packet) => {
this.notifyOutgoingListeners(packet);
this.packet(packet);
});
this.sendBuffer = [];
}

Expand Down Expand Up @@ -606,6 +611,114 @@ export class Socket<
public listenersAny() {
return this._anyListeners || [];
}

/**
* Adds a listener that will be fired when any event is emitted. The event name is passed as the first argument to the
* callback.
*
* @param listener
*
* <pre><code>
*
* socket.onAnyOutgoing((event, ...args) => {
* console.log(event);
* });
*
* </pre></code>
*
* @public
*/
public onAnyOutgoing(listener: (...args: any[]) => void): this {
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.
*
* @param listener
*
* <pre><code>
*
* socket.prependAnyOutgoing((event, ...args) => {
* console.log(event);
* });
*
* </pre></code>
*
* @public
*/
public prependAnyOutgoing(listener: (...args: any[]) => void): this {
this._anyOutgoingListeners = this._anyOutgoingListeners || [];
this._anyOutgoingListeners.unshift(listener);
return this;
}

/**
* Removes the listener that will be fired when any event is emitted.
*
* @param listener
*
* <pre><code>
*
* const handler = (event, ...args) => {
* console.log(event);
* }
*
* socket.onAnyOutgoing(handler);
*
* // then later
* socket.offAnyOutgoing(handler);
*
* </pre></code>
*
* @public
*/
public offAnyOutgoing(listener?: (...args: any[]) => void): this {
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.
*
* @public
*/
public listenersAnyOutgoing() {
return this._anyOutgoingListeners || [];
}

/**
* Notify the listeners for each packet sent
*
* @param packet
*
* @private
*/
private notifyOutgoingListeners(packet: Packet) {
if (this._anyOutgoingListeners && this._anyOutgoingListeners.length) {
const listeners = this._anyOutgoingListeners.slice();
for (const listener of listeners) {
listener.apply(this, packet.data);
}
}
}
}

export namespace Socket {
Expand Down
54 changes: 54 additions & 0 deletions test/socket.ts
Expand Up @@ -369,6 +369,60 @@ describe("socket", function () {
});
});

describe("onAnyOutgoing", () => {
it("should call listener", (done) => {
const socket = io("/abc");

socket.on("connect", () => {
socket.onAnyOutgoing((event, arg1) => {
expect(event).to.be("my-event");
expect(arg1).to.be("123");

success(done, socket);
});

socket.emit("my-event", "123");
});
});

it("should prepend listener", (done) => {
const socket = io("/abc");

let count = 0;

socket.onAnyOutgoing(() => {
expect(count).to.be(2);

success(done, socket);
});

socket.prependAnyOutgoing(() => {
expect(count++).to.be(1);
});

socket.prependAnyOutgoing(() => {
expect(count++).to.be(0);
});

socket.emit("my-event", "123");
});

it("should remove listener", (done) => {
const socket = io("/abc");
const fail = () => done(new Error("fail"));

socket.onAnyOutgoing(fail);
socket.offAnyOutgoing(fail);
expect(socket.listenersAnyOutgoing.length).to.be(0);

socket.onAnyOutgoing(() => {
success(done, socket);
});

socket.emit("my-event", "123");
});
});

describe("timeout", () => {
it("should timeout after the given delay when socket is not connected", (done) => {
const socket = io("/", {
Expand Down

0 comments on commit 74e3e60

Please sign in to comment.