Skip to content

Commit 74e3e60

Browse files
feat: add support for catch-all listeners for outgoing packets
This is similar to `onAny()`, but for outgoing packets. Syntax: ```js socket.onAnyOutgoing((event, ...args) => { console.log(event); }); ``` Related: socketio/socket.io@531104d
1 parent 692d54e commit 74e3e60

File tree

2 files changed

+168
-1
lines changed

2 files changed

+168
-1
lines changed

lib/socket.ts

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ export class Socket<
7676
private flags: Flags = {};
7777
private subs?: Array<VoidFunction>;
7878
private _anyListeners: Array<(...args: any[]) => void>;
79+
private _anyOutgoingListeners: Array<(...args: any[]) => void>;
7980

8081
/**
8182
* `Socket` constructor.
@@ -193,6 +194,7 @@ export class Socket<
193194
if (discardPacket) {
194195
debug("discard packet as the transport is not currently writable");
195196
} else if (this.connected) {
197+
this.notifyOutgoingListeners(packet);
196198
this.packet(packet);
197199
} else {
198200
this.sendBuffer.push(packet);
@@ -440,7 +442,10 @@ export class Socket<
440442
this.receiveBuffer.forEach((args) => this.emitEvent(args));
441443
this.receiveBuffer = [];
442444

443-
this.sendBuffer.forEach((packet) => this.packet(packet));
445+
this.sendBuffer.forEach((packet) => {
446+
this.notifyOutgoingListeners(packet);
447+
this.packet(packet);
448+
});
444449
this.sendBuffer = [];
445450
}
446451

@@ -606,6 +611,114 @@ export class Socket<
606611
public listenersAny() {
607612
return this._anyListeners || [];
608613
}
614+
615+
/**
616+
* Adds a listener that will be fired when any event is emitted. The event name is passed as the first argument to the
617+
* callback.
618+
*
619+
* @param listener
620+
*
621+
* <pre><code>
622+
*
623+
* socket.onAnyOutgoing((event, ...args) => {
624+
* console.log(event);
625+
* });
626+
*
627+
* </pre></code>
628+
*
629+
* @public
630+
*/
631+
public onAnyOutgoing(listener: (...args: any[]) => void): this {
632+
this._anyOutgoingListeners = this._anyOutgoingListeners || [];
633+
this._anyOutgoingListeners.push(listener);
634+
return this;
635+
}
636+
637+
/**
638+
* Adds a listener that will be fired when any event is emitted. The event name is passed as the first argument to the
639+
* callback. The listener is added to the beginning of the listeners array.
640+
*
641+
* @param listener
642+
*
643+
* <pre><code>
644+
*
645+
* socket.prependAnyOutgoing((event, ...args) => {
646+
* console.log(event);
647+
* });
648+
*
649+
* </pre></code>
650+
*
651+
* @public
652+
*/
653+
public prependAnyOutgoing(listener: (...args: any[]) => void): this {
654+
this._anyOutgoingListeners = this._anyOutgoingListeners || [];
655+
this._anyOutgoingListeners.unshift(listener);
656+
return this;
657+
}
658+
659+
/**
660+
* Removes the listener that will be fired when any event is emitted.
661+
*
662+
* @param listener
663+
*
664+
* <pre><code>
665+
*
666+
* const handler = (event, ...args) => {
667+
* console.log(event);
668+
* }
669+
*
670+
* socket.onAnyOutgoing(handler);
671+
*
672+
* // then later
673+
* socket.offAnyOutgoing(handler);
674+
*
675+
* </pre></code>
676+
*
677+
* @public
678+
*/
679+
public offAnyOutgoing(listener?: (...args: any[]) => void): this {
680+
if (!this._anyOutgoingListeners) {
681+
return this;
682+
}
683+
if (listener) {
684+
const listeners = this._anyOutgoingListeners;
685+
for (let i = 0; i < listeners.length; i++) {
686+
if (listener === listeners[i]) {
687+
listeners.splice(i, 1);
688+
return this;
689+
}
690+
}
691+
} else {
692+
this._anyOutgoingListeners = [];
693+
}
694+
return this;
695+
}
696+
697+
/**
698+
* Returns an array of listeners that are listening for any event that is specified. This array can be manipulated,
699+
* e.g. to remove listeners.
700+
*
701+
* @public
702+
*/
703+
public listenersAnyOutgoing() {
704+
return this._anyOutgoingListeners || [];
705+
}
706+
707+
/**
708+
* Notify the listeners for each packet sent
709+
*
710+
* @param packet
711+
*
712+
* @private
713+
*/
714+
private notifyOutgoingListeners(packet: Packet) {
715+
if (this._anyOutgoingListeners && this._anyOutgoingListeners.length) {
716+
const listeners = this._anyOutgoingListeners.slice();
717+
for (const listener of listeners) {
718+
listener.apply(this, packet.data);
719+
}
720+
}
721+
}
609722
}
610723

611724
export namespace Socket {

test/socket.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,60 @@ describe("socket", function () {
369369
});
370370
});
371371

372+
describe("onAnyOutgoing", () => {
373+
it("should call listener", (done) => {
374+
const socket = io("/abc");
375+
376+
socket.on("connect", () => {
377+
socket.onAnyOutgoing((event, arg1) => {
378+
expect(event).to.be("my-event");
379+
expect(arg1).to.be("123");
380+
381+
success(done, socket);
382+
});
383+
384+
socket.emit("my-event", "123");
385+
});
386+
});
387+
388+
it("should prepend listener", (done) => {
389+
const socket = io("/abc");
390+
391+
let count = 0;
392+
393+
socket.onAnyOutgoing(() => {
394+
expect(count).to.be(2);
395+
396+
success(done, socket);
397+
});
398+
399+
socket.prependAnyOutgoing(() => {
400+
expect(count++).to.be(1);
401+
});
402+
403+
socket.prependAnyOutgoing(() => {
404+
expect(count++).to.be(0);
405+
});
406+
407+
socket.emit("my-event", "123");
408+
});
409+
410+
it("should remove listener", (done) => {
411+
const socket = io("/abc");
412+
const fail = () => done(new Error("fail"));
413+
414+
socket.onAnyOutgoing(fail);
415+
socket.offAnyOutgoing(fail);
416+
expect(socket.listenersAnyOutgoing.length).to.be(0);
417+
418+
socket.onAnyOutgoing(() => {
419+
success(done, socket);
420+
});
421+
422+
socket.emit("my-event", "123");
423+
});
424+
});
425+
372426
describe("timeout", () => {
373427
it("should timeout after the given delay when socket is not connected", (done) => {
374428
const socket = io("/", {

0 commit comments

Comments
 (0)