-
Notifications
You must be signed in to change notification settings - Fork 10.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Previously, broadcasting to a given room (by calling `io.to()`) would mutate the io instance, which could lead to surprising behaviors, like: ```js io.to("room1"); io.to("room2").emit(...); // also sent to room1 // or with async/await io.to("room3").emit("details", await fetchDetails()); // random behavior: maybe in room3, maybe to all clients ``` Calling `io.to()` (or any other broadcast modifier) will now return an immutable instance. Related: - #3431 - #3444
- Loading branch information
1 parent
7de2e87
commit ac9e8ca
Showing
6 changed files
with
269 additions
and
136 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
import type { BroadcastFlags, Room, SocketId } from "socket.io-adapter"; | ||
import { RESERVED_EVENTS } from "./socket"; | ||
import { PacketType } from "socket.io-parser"; | ||
import type { Adapter } from "socket.io-adapter"; | ||
|
||
export class BroadcastOperator { | ||
constructor( | ||
private readonly adapter: Adapter, | ||
private readonly rooms: Set<Room> = new Set<Room>(), | ||
private readonly exceptRooms: Set<Room> = new Set<Room>(), | ||
private readonly flags: BroadcastFlags = {} | ||
) {} | ||
|
||
/** | ||
* Targets a room when emitting. | ||
* | ||
* @param room | ||
* @return a new BroadcastOperator instance | ||
* @public | ||
*/ | ||
public to(room: Room): BroadcastOperator { | ||
return new BroadcastOperator( | ||
this.adapter, | ||
new Set([...this.rooms, room]), | ||
this.exceptRooms, | ||
this.flags | ||
); | ||
} | ||
|
||
/** | ||
* Targets a room when emitting. | ||
* | ||
* @param room | ||
* @return a new BroadcastOperator instance | ||
* @public | ||
*/ | ||
public in(room: Room): BroadcastOperator { | ||
return this.to(room); | ||
} | ||
|
||
/** | ||
* Excludes a room when emitting. | ||
* | ||
* @param room | ||
* @return a new BroadcastOperator instance | ||
* @public | ||
*/ | ||
public except(room: Room): BroadcastOperator { | ||
return new BroadcastOperator( | ||
this.adapter, | ||
this.rooms, | ||
new Set([...this.exceptRooms, room]), | ||
this.flags | ||
); | ||
} | ||
|
||
/** | ||
* Sets the compress flag. | ||
* | ||
* @param compress - if `true`, compresses the sending data | ||
* @return a new BroadcastOperator instance | ||
* @public | ||
*/ | ||
public compress(compress: boolean): BroadcastOperator { | ||
const flags = Object.assign({}, this.flags, { compress }); | ||
return new BroadcastOperator( | ||
this.adapter, | ||
this.rooms, | ||
this.exceptRooms, | ||
flags | ||
); | ||
} | ||
|
||
/** | ||
* 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). | ||
* | ||
* @return a new BroadcastOperator instance | ||
* @public | ||
*/ | ||
public get volatile(): BroadcastOperator { | ||
const flags = Object.assign({}, this.flags, { volatile: true }); | ||
return new BroadcastOperator( | ||
this.adapter, | ||
this.rooms, | ||
this.exceptRooms, | ||
flags | ||
); | ||
} | ||
|
||
/** | ||
* Sets a modifier for a subsequent event emission that the event data will only be broadcast to the current node. | ||
* | ||
* @return a new BroadcastOperator instance | ||
* @public | ||
*/ | ||
public get local(): BroadcastOperator { | ||
const flags = Object.assign({}, this.flags, { local: true }); | ||
return new BroadcastOperator( | ||
this.adapter, | ||
this.rooms, | ||
this.exceptRooms, | ||
flags | ||
); | ||
} | ||
|
||
/** | ||
* Emits to all clients. | ||
* | ||
* @return Always true | ||
* @public | ||
*/ | ||
public emit(ev: string | Symbol, ...args: any[]): true { | ||
if (RESERVED_EVENTS.has(ev)) { | ||
throw new Error(`"${ev}" is a reserved event name`); | ||
} | ||
// set up packet object | ||
args.unshift(ev); | ||
const packet = { | ||
type: PacketType.EVENT, | ||
data: args, | ||
}; | ||
|
||
if ("function" == typeof args[args.length - 1]) { | ||
throw new Error("Callbacks are not supported when broadcasting"); | ||
} | ||
|
||
this.adapter.broadcast(packet, { | ||
rooms: this.rooms, | ||
except: this.exceptRooms, | ||
flags: this.flags, | ||
}); | ||
|
||
return true; | ||
} | ||
|
||
/** | ||
* Gets a list of clients. | ||
* | ||
* @public | ||
*/ | ||
public allSockets(): Promise<Set<SocketId>> { | ||
if (!this.adapter) { | ||
throw new Error( | ||
"No adapter for this namespace, are you trying to get the list of clients of a dynamic namespace?" | ||
); | ||
} | ||
return this.adapter.sockets(this.rooms); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.