@venizia/ignis-helpers 0.2.0-4 → 0.2.0-5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/modules/socket/websocket/server/delivery.d.ts +48 -0
- package/dist/cjs/modules/socket/websocket/server/delivery.d.ts.map +1 -0
- package/dist/cjs/modules/socket/websocket/server/delivery.js +206 -0
- package/dist/cjs/modules/socket/websocket/server/delivery.js.map +1 -0
- package/dist/cjs/modules/socket/websocket/server/helper.d.ts +2 -7
- package/dist/cjs/modules/socket/websocket/server/helper.d.ts.map +1 -1
- package/dist/cjs/modules/socket/websocket/server/helper.js +14 -181
- package/dist/cjs/modules/socket/websocket/server/helper.js.map +1 -1
- package/dist/cjs/utilities/index.d.ts +1 -0
- package/dist/cjs/utilities/index.d.ts.map +1 -1
- package/dist/cjs/utilities/index.js +1 -0
- package/dist/cjs/utilities/index.js.map +1 -1
- package/dist/cjs/utilities/slug.utility.d.ts +7 -0
- package/dist/cjs/utilities/slug.utility.d.ts.map +1 -0
- package/dist/cjs/utilities/slug.utility.js +21 -0
- package/dist/cjs/utilities/slug.utility.js.map +1 -0
- package/dist/esm/modules/socket/websocket/server/delivery.d.ts +48 -0
- package/dist/esm/modules/socket/websocket/server/delivery.d.ts.map +1 -0
- package/dist/esm/modules/socket/websocket/server/delivery.js +202 -0
- package/dist/esm/modules/socket/websocket/server/delivery.js.map +1 -0
- package/dist/esm/modules/socket/websocket/server/helper.d.ts +2 -7
- package/dist/esm/modules/socket/websocket/server/helper.d.ts.map +1 -1
- package/dist/esm/modules/socket/websocket/server/helper.js +14 -181
- package/dist/esm/modules/socket/websocket/server/helper.js.map +1 -1
- package/dist/esm/utilities/index.d.ts +1 -0
- package/dist/esm/utilities/index.d.ts.map +1 -1
- package/dist/esm/utilities/index.js +1 -0
- package/dist/esm/utilities/index.js.map +1 -1
- package/dist/esm/utilities/slug.utility.d.ts +7 -0
- package/dist/esm/utilities/slug.utility.d.ts.map +1 -0
- package/dist/esm/utilities/slug.utility.js +17 -0
- package/dist/esm/utilities/slug.utility.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { BaseHelper } from '../../../../modules/base';
|
|
2
|
+
import { IBunServer, IWebSocketClient, TWebSocketOutboundTransformer } from '../common';
|
|
3
|
+
export interface IWebSocketDeliveryHelperOptions<MetadataType extends Record<string, unknown> = Record<string, unknown>> {
|
|
4
|
+
server: IBunServer;
|
|
5
|
+
/** Shared by reference with WebSocketServerHelper - never copy, both sides must see the same live maps. */
|
|
6
|
+
clients: Map<string, IWebSocketClient<MetadataType>>;
|
|
7
|
+
users: Map<string, Set<string>>;
|
|
8
|
+
rooms: Map<string, Set<string>>;
|
|
9
|
+
outboundTransformer?: TWebSocketOutboundTransformer<unknown, MetadataType>;
|
|
10
|
+
encryptedBatchLimit: number;
|
|
11
|
+
}
|
|
12
|
+
/** Local delivery and fan-out for the WebSocket server: single client, per-user, per-room, and broadcast. */
|
|
13
|
+
export declare class WebSocketDeliveryHelper<MetadataType extends Record<string, unknown> = Record<string, unknown>> extends BaseHelper {
|
|
14
|
+
private server;
|
|
15
|
+
private clients;
|
|
16
|
+
private users;
|
|
17
|
+
private rooms;
|
|
18
|
+
private outboundTransformer?;
|
|
19
|
+
private encryptedBatchLimit;
|
|
20
|
+
constructor(opts: IWebSocketDeliveryHelperOptions<MetadataType>);
|
|
21
|
+
sendToClient(opts: {
|
|
22
|
+
clientId: string;
|
|
23
|
+
event: string;
|
|
24
|
+
data: unknown;
|
|
25
|
+
doLog?: boolean;
|
|
26
|
+
}): void;
|
|
27
|
+
private sendToClientAsync;
|
|
28
|
+
private deliverToSocket;
|
|
29
|
+
sendToUser(opts: {
|
|
30
|
+
userId: string;
|
|
31
|
+
event: string;
|
|
32
|
+
data: unknown;
|
|
33
|
+
}): void;
|
|
34
|
+
private sendToRoomExcluding;
|
|
35
|
+
sendToRoom(opts: {
|
|
36
|
+
room: string;
|
|
37
|
+
event: string;
|
|
38
|
+
data: unknown;
|
|
39
|
+
exclude?: string[];
|
|
40
|
+
}): void;
|
|
41
|
+
private broadcastExcluding;
|
|
42
|
+
broadcast(opts: {
|
|
43
|
+
event: string;
|
|
44
|
+
data: unknown;
|
|
45
|
+
exclude?: string[];
|
|
46
|
+
}): void;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=delivery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery.d.ts","sourceRoot":"","sources":["../../../../../../src/modules/socket/websocket/server/delivery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAG5C,OAAO,EACL,UAAU,EACV,gBAAgB,EAEhB,6BAA6B,EAG9B,MAAM,WAAW,CAAC;AAEnB,MAAM,WAAW,+BAA+B,CAC9C,YAAY,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAEtE,MAAM,EAAE,UAAU,CAAC;IACnB,2GAA2G;IAC3G,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,gBAAgB,CAAC,YAAY,CAAC,CAAC,CAAC;IACrD,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IAChC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IAChC,mBAAmB,CAAC,EAAE,6BAA6B,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IAC3E,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,6GAA6G;AAC7G,qBAAa,uBAAuB,CAClC,YAAY,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CACtE,SAAQ,UAAU;IAClB,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,OAAO,CAA8C;IAC7D,OAAO,CAAC,KAAK,CAA2B;IACxC,OAAO,CAAC,KAAK,CAA2B;IACxC,OAAO,CAAC,mBAAmB,CAAC,CAAuD;IACnF,OAAO,CAAC,mBAAmB,CAAS;gBAExB,IAAI,EAAE,+BAA+B,CAAC,YAAY,CAAC;IAW/D,YAAY,CAAC,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;YAiCxE,iBAAiB;IAuB/B,OAAO,CAAC,eAAe;IA8BvB,UAAU,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE;IAYjE,OAAO,CAAC,mBAAmB;IA2C3B,UAAU,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE;IAoCnF,OAAO,CAAC,kBAAkB;IAiB1B,SAAS,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE;CAiCrE"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WebSocketDeliveryHelper = void 0;
|
|
4
|
+
const base_1 = require("../../../../modules/base");
|
|
5
|
+
const utilities_1 = require("../../../../utilities");
|
|
6
|
+
const promise_utility_1 = require("../../../../utilities/promise.utility");
|
|
7
|
+
const common_1 = require("../common");
|
|
8
|
+
/** Local delivery and fan-out for the WebSocket server: single client, per-user, per-room, and broadcast. */
|
|
9
|
+
class WebSocketDeliveryHelper extends base_1.BaseHelper {
|
|
10
|
+
constructor(opts) {
|
|
11
|
+
super({ scope: WebSocketDeliveryHelper.name });
|
|
12
|
+
this.server = opts.server;
|
|
13
|
+
this.clients = opts.clients;
|
|
14
|
+
this.users = opts.users;
|
|
15
|
+
this.rooms = opts.rooms;
|
|
16
|
+
this.outboundTransformer = opts.outboundTransformer;
|
|
17
|
+
this.encryptedBatchLimit = opts.encryptedBatchLimit;
|
|
18
|
+
}
|
|
19
|
+
sendToClient(opts) {
|
|
20
|
+
const logger = this.logger.for(this.sendToClient.name);
|
|
21
|
+
const { clientId, event, data, doLog } = opts;
|
|
22
|
+
const client = this.clients.get(clientId);
|
|
23
|
+
if (!client) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
// Async path - transformer intercepts before socket.send()
|
|
27
|
+
const outboundTransformer = this.outboundTransformer;
|
|
28
|
+
if (outboundTransformer && client.encrypted) {
|
|
29
|
+
Promise.resolve()
|
|
30
|
+
.then(() => outboundTransformer({ client, event, data }))
|
|
31
|
+
.then(transformed => {
|
|
32
|
+
const outboundMessage = transformed ?? { event, data };
|
|
33
|
+
this.deliverToSocket({
|
|
34
|
+
client,
|
|
35
|
+
payload: JSON.stringify(outboundMessage),
|
|
36
|
+
doLog,
|
|
37
|
+
event,
|
|
38
|
+
data,
|
|
39
|
+
});
|
|
40
|
+
})
|
|
41
|
+
.catch(error => {
|
|
42
|
+
logger.error('Outbound transformer error | id: %s | error: %s', clientId, error);
|
|
43
|
+
});
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
// Sync path (unchanged, zero overhead when no transformer)
|
|
47
|
+
this.deliverToSocket({ client, payload: JSON.stringify({ event, data }), doLog, event, data });
|
|
48
|
+
}
|
|
49
|
+
async sendToClientAsync(opts) {
|
|
50
|
+
const { clientId, event, data } = opts;
|
|
51
|
+
const client = this.clients.get(clientId);
|
|
52
|
+
if (!client) {
|
|
53
|
+
return Promise.resolve();
|
|
54
|
+
}
|
|
55
|
+
if (!this.outboundTransformer || !client.encrypted) {
|
|
56
|
+
this.deliverToSocket({ client, payload: JSON.stringify({ event, data }), event, data });
|
|
57
|
+
return Promise.resolve();
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
const transformed = await Promise.resolve(this.outboundTransformer({ client, event, data }));
|
|
61
|
+
const outboundMessage = transformed ?? { event, data };
|
|
62
|
+
this.deliverToSocket({ client, payload: JSON.stringify(outboundMessage), event, data });
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
this.logger
|
|
66
|
+
.for(this.sendToClientAsync.name)
|
|
67
|
+
.error('Outbound transformer error | id: %s | error: %s', clientId, error);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
deliverToSocket(opts) {
|
|
71
|
+
const logger = this.logger.for(this.deliverToSocket.name);
|
|
72
|
+
const { client, payload, doLog, event, data } = opts;
|
|
73
|
+
try {
|
|
74
|
+
const result = client.socket.send(payload);
|
|
75
|
+
if (result === 0) {
|
|
76
|
+
logger.warn('Message dropped (socket closed) | id: %s', client.id);
|
|
77
|
+
}
|
|
78
|
+
if (result === -1) {
|
|
79
|
+
client.backpressured = true;
|
|
80
|
+
logger.warn('Backpressure detected | id: %s', client.id);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
logger.error('Failed to send | id: %s | error: %s', client.id, error);
|
|
85
|
+
}
|
|
86
|
+
if (doLog) {
|
|
87
|
+
logger.info('Message sent | id: %s | event: %s | data: %j', client.id, event, data);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
sendToUser(opts) {
|
|
91
|
+
const { userId, event, data } = opts;
|
|
92
|
+
const clientIds = this.users.get(userId);
|
|
93
|
+
if (!clientIds) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
for (const clientId of clientIds) {
|
|
97
|
+
this.sendToClient({ clientId, event, data });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
sendToRoomExcluding(opts) {
|
|
101
|
+
const { room, event, data, exclude } = opts;
|
|
102
|
+
const excludeSet = new Set(exclude);
|
|
103
|
+
const roomClientIds = this.rooms.get(room);
|
|
104
|
+
if (!roomClientIds) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
// Serialised ONCE when there is no transformer, because the payload is then identical for every
|
|
108
|
+
// recipient - `sendToClient` would otherwise run `JSON.stringify` per client on a message that
|
|
109
|
+
// never differs. With a transformer each client's payload really is different, so that path
|
|
110
|
+
// still goes through `sendToClient`. The non-excluding fan-out already works this way.
|
|
111
|
+
if (!this.outboundTransformer) {
|
|
112
|
+
const payload = JSON.stringify({ event, data });
|
|
113
|
+
for (const clientId of roomClientIds) {
|
|
114
|
+
if (excludeSet.has(clientId)) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const client = this.clients.get(clientId);
|
|
118
|
+
if (client) {
|
|
119
|
+
this.deliverToSocket({ client, payload, event, data });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
for (const clientId of roomClientIds) {
|
|
125
|
+
if (excludeSet.has(clientId)) {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
this.sendToClient({ clientId, event, data });
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
sendToRoom(opts) {
|
|
132
|
+
const { room, event, data, exclude } = opts;
|
|
133
|
+
// When exclude is present, must iterate - can't exclude from Bun native pub/sub
|
|
134
|
+
if (exclude?.length) {
|
|
135
|
+
this.sendToRoomExcluding({ room, event, data, exclude });
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
// No encryption - Bun native pub/sub O(1) C++ fan-out
|
|
139
|
+
if (!this.outboundTransformer) {
|
|
140
|
+
const payload = JSON.stringify({ event, data });
|
|
141
|
+
this.server.publish(room, payload);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
// Encryption enabled - iterate all clients individually with concurrency limit
|
|
145
|
+
const roomClientIds = this.rooms.get(room);
|
|
146
|
+
if (!roomClientIds) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const tasks = [];
|
|
150
|
+
for (const clientId of roomClientIds) {
|
|
151
|
+
tasks.push(() => this.sendToClientAsync({ clientId, event, data }));
|
|
152
|
+
}
|
|
153
|
+
if (tasks.length) {
|
|
154
|
+
(0, promise_utility_1.voidExecution)({
|
|
155
|
+
logger: this.logger,
|
|
156
|
+
scope: this.sendToRoom.name,
|
|
157
|
+
execution: (0, utilities_1.executePromiseWithLimit)({ tasks, limit: this.encryptedBatchLimit }),
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
broadcastExcluding(opts) {
|
|
162
|
+
const { event, data, exclude } = opts;
|
|
163
|
+
const excludeSet = new Set(exclude);
|
|
164
|
+
for (const [clientId, client] of this.clients) {
|
|
165
|
+
if (excludeSet.has(clientId)) {
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
// Only broadcast to authenticated clients (consistent with non-exclude path which uses BROADCAST_TOPIC)
|
|
169
|
+
if (client.state !== common_1.WebSocketClientStates.AUTHENTICATED) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
this.sendToClient({ clientId, event, data });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
broadcast(opts) {
|
|
176
|
+
const { event, data, exclude } = opts;
|
|
177
|
+
// When exclude is present, must iterate - can't exclude from Bun native pub/sub
|
|
178
|
+
if (exclude?.length) {
|
|
179
|
+
this.broadcastExcluding({ event, data, exclude });
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
// No encryption - Bun native pub/sub O(1) C++ fan-out
|
|
183
|
+
if (!this.outboundTransformer) {
|
|
184
|
+
const payload = JSON.stringify({ event, data });
|
|
185
|
+
this.server.publish(common_1.WebSocketDefaults.BROADCAST_TOPIC, payload);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
// Encryption enabled - iterate all clients individually with concurrency limit
|
|
189
|
+
const tasks = [];
|
|
190
|
+
for (const [clientId, client] of this.clients) {
|
|
191
|
+
if (client.state !== common_1.WebSocketClientStates.AUTHENTICATED) {
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
tasks.push(() => this.sendToClientAsync({ clientId, event, data }));
|
|
195
|
+
}
|
|
196
|
+
if (tasks.length) {
|
|
197
|
+
(0, promise_utility_1.voidExecution)({
|
|
198
|
+
logger: this.logger,
|
|
199
|
+
scope: this.broadcast.name,
|
|
200
|
+
execution: (0, utilities_1.executePromiseWithLimit)({ tasks, limit: this.encryptedBatchLimit }),
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
exports.WebSocketDeliveryHelper = WebSocketDeliveryHelper;
|
|
206
|
+
//# sourceMappingURL=delivery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery.js","sourceRoot":"","sources":["../../../../../../src/modules/socket/websocket/server/delivery.ts"],"names":[],"mappings":";;;AAAA,yCAA4C;AAC5C,2CAAsD;AACtD,iEAA4D;AAC5D,sCAOmB;AAcnB,6GAA6G;AAC7G,MAAa,uBAEX,SAAQ,iBAAU;IAQlB,YAAY,IAAmD;QAC7D,KAAK,CAAC,EAAE,KAAK,EAAE,uBAAuB,CAAC,IAAI,EAAE,CAAC,CAAC;QAE/C,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAC5B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACxB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACxB,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC,mBAAmB,CAAC;QACpD,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC,mBAAmB,CAAC;IACtD,CAAC;IAED,YAAY,CAAC,IAAyE;QACpF,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QACvD,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;QAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC1C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO;QACT,CAAC;QAED,2DAA2D;QAC3D,MAAM,mBAAmB,GAAG,IAAI,CAAC,mBAAmB,CAAC;QACrD,IAAI,mBAAmB,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YAC5C,OAAO,CAAC,OAAO,EAAE;iBACd,IAAI,CAAC,GAAG,EAAE,CAAC,mBAAmB,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;iBACxD,IAAI,CAAC,WAAW,CAAC,EAAE;gBAClB,MAAM,eAAe,GAAG,WAAW,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;gBACvD,IAAI,CAAC,eAAe,CAAC;oBACnB,MAAM;oBACN,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC;oBACxC,KAAK;oBACL,KAAK;oBACL,IAAI;iBACL,CAAC,CAAC;YACL,CAAC,CAAC;iBACD,KAAK,CAAC,KAAK,CAAC,EAAE;gBACb,MAAM,CAAC,KAAK,CAAC,iDAAiD,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;YACnF,CAAC,CAAC,CAAC;YACL,OAAO;QACT,CAAC;QAED,2DAA2D;QAC3D,IAAI,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjG,CAAC;IAEO,KAAK,CAAC,iBAAiB,CAAC,IAAwD;QACtF,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;QACvC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC1C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAC3B,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,mBAAmB,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;YACnD,IAAI,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YACxF,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAC3B,CAAC;QAED,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,mBAAmB,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;YAC7F,MAAM,eAAe,GAAG,WAAW,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;YACvD,IAAI,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1F,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM;iBACR,GAAG,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC;iBAChC,KAAK,CAAC,iDAAiD,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;QAC/E,CAAC;IACH,CAAC;IAEO,eAAe,CAAC,IAMvB;QACC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;QAC1D,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;QAErD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAE3C,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;gBACjB,MAAM,CAAC,IAAI,CAAC,0CAA0C,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;YACrE,CAAC;YAED,IAAI,MAAM,KAAK,CAAC,CAAC,EAAE,CAAC;gBAClB,MAAM,CAAC,aAAa,GAAG,IAAI,CAAC;gBAC5B,MAAM,CAAC,IAAI,CAAC,gCAAgC,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;YAC3D,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,CAAC,KAAK,CAAC,qCAAqC,EAAE,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QACxE,CAAC;QAED,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,CAAC,IAAI,CAAC,8CAA8C,EAAE,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QACtF,CAAC;IACH,CAAC;IAED,UAAU,CAAC,IAAsD;QAC/D,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC;QACrC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACzC,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,OAAO;QACT,CAAC;QAED,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC,YAAY,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAEO,mBAAmB,CAAC,IAK3B;QACC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;QAE5C,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;QACpC,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC3C,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,OAAO;QACT,CAAC;QAED,gGAAgG;QAChG,+FAA+F;QAC/F,4FAA4F;QAC5F,uFAAuF;QACvF,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC9B,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YAEhD,KAAK,MAAM,QAAQ,IAAI,aAAa,EAAE,CAAC;gBACrC,IAAI,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAC7B,SAAS;gBACX,CAAC;gBAED,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;gBAC1C,IAAI,MAAM,EAAE,CAAC;oBACX,IAAI,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;gBACzD,CAAC;YACH,CAAC;YAED,OAAO;QACT,CAAC;QAED,KAAK,MAAM,QAAQ,IAAI,aAAa,EAAE,CAAC;YACrC,IAAI,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC7B,SAAS;YACX,CAAC;YACD,IAAI,CAAC,YAAY,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,UAAU,CAAC,IAAwE;QACjF,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;QAE5C,gFAAgF;QAChF,IAAI,OAAO,EAAE,MAAM,EAAE,CAAC;YACpB,IAAI,CAAC,mBAAmB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;YACzD,OAAO;QACT,CAAC;QAED,sDAAsD;QACtD,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC9B,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,EAA8B,CAAC,CAAC;YAC5E,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YACnC,OAAO;QACT,CAAC;QAED,+EAA+E;QAC/E,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC3C,IAAI,CAAC,aAAa,EAAE,CAAC;YACnB,OAAO;QACT,CAAC;QAED,MAAM,KAAK,GAA+B,EAAE,CAAC;QAC7C,KAAK,MAAM,QAAQ,IAAI,aAAa,EAAE,CAAC;YACrC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACtE,CAAC;QAED,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YACjB,IAAA,+BAAa,EAAC;gBACZ,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,IAAI;gBAC3B,SAAS,EAAE,IAAA,mCAAuB,EAAC,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,mBAAmB,EAAE,CAAC;aAC/E,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAEO,kBAAkB,CAAC,IAAyD;QAClF,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;QAEtC,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;QACpC,KAAK,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC9C,IAAI,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC7B,SAAS;YACX,CAAC;YACD,wGAAwG;YACxG,IAAI,MAAM,CAAC,KAAK,KAAK,8BAAqB,CAAC,aAAa,EAAE,CAAC;gBACzD,SAAS;YACX,CAAC;YAED,IAAI,CAAC,YAAY,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,SAAS,CAAC,IAA0D;QAClE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;QAEtC,gFAAgF;QAChF,IAAI,OAAO,EAAE,MAAM,EAAE,CAAC;YACpB,IAAI,CAAC,kBAAkB,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;YAClD,OAAO;QACT,CAAC;QAED,sDAAsD;QACtD,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC9B,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,EAA8B,CAAC,CAAC;YAC5E,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,0BAAiB,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;YAChE,OAAO;QACT,CAAC;QAED,+EAA+E;QAC/E,MAAM,KAAK,GAA+B,EAAE,CAAC;QAC7C,KAAK,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC9C,IAAI,MAAM,CAAC,KAAK,KAAK,8BAAqB,CAAC,aAAa,EAAE,CAAC;gBACzD,SAAS;YACX,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACtE,CAAC;QAED,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YACjB,IAAA,+BAAa,EAAC;gBACZ,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI;gBAC1B,SAAS,EAAE,IAAA,mCAAuB,EAAC,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,mBAAmB,EAAE,CAAC;aAC/E,CAAC,CAAC;QACL,CAAC;IACH,CAAC;CACF;AAxPD,0DAwPC"}
|
|
@@ -12,11 +12,11 @@ export declare class WebSocketServerHelper<AuthDataType extends Record<string, u
|
|
|
12
12
|
*/
|
|
13
13
|
private static toRoomList;
|
|
14
14
|
private path;
|
|
15
|
-
private server;
|
|
16
15
|
private serverId;
|
|
17
16
|
private clients;
|
|
18
17
|
private users;
|
|
19
18
|
private rooms;
|
|
19
|
+
private delivery;
|
|
20
20
|
private redisPub;
|
|
21
21
|
private redisSub;
|
|
22
22
|
private authenticateFn;
|
|
@@ -24,7 +24,6 @@ export declare class WebSocketServerHelper<AuthDataType extends Record<string, u
|
|
|
24
24
|
private onClientConnected?;
|
|
25
25
|
private onClientDisconnected?;
|
|
26
26
|
private messageHandler?;
|
|
27
|
-
private outboundTransformer?;
|
|
28
27
|
private handshakeFn?;
|
|
29
28
|
private defaultRooms;
|
|
30
29
|
private serverOptions;
|
|
@@ -32,7 +31,6 @@ export declare class WebSocketServerHelper<AuthDataType extends Record<string, u
|
|
|
32
31
|
private heartbeatInterval;
|
|
33
32
|
private heartbeatTimeout;
|
|
34
33
|
private heartbeatTimer;
|
|
35
|
-
private encryptedBatchLimit;
|
|
36
34
|
private requireEncryption;
|
|
37
35
|
constructor(opts: IWebSocketServerOptions<AuthDataType, MetadataType>);
|
|
38
36
|
private initRedisClients;
|
|
@@ -82,27 +80,24 @@ export declare class WebSocketServerHelper<AuthDataType extends Record<string, u
|
|
|
82
80
|
private handleAuthenticate;
|
|
83
81
|
private handleJoin;
|
|
84
82
|
private handleLeave;
|
|
83
|
+
/** Local delivery and fan-out live in WebSocketDeliveryHelper - kept public here by delegation. */
|
|
85
84
|
sendToClient(opts: {
|
|
86
85
|
clientId: string;
|
|
87
86
|
event: string;
|
|
88
87
|
data: unknown;
|
|
89
88
|
doLog?: boolean;
|
|
90
89
|
}): void;
|
|
91
|
-
private sendToClientAsync;
|
|
92
|
-
private deliverToSocket;
|
|
93
90
|
sendToUser(opts: {
|
|
94
91
|
userId: string;
|
|
95
92
|
event: string;
|
|
96
93
|
data: unknown;
|
|
97
94
|
}): void;
|
|
98
|
-
private sendToRoomExcluding;
|
|
99
95
|
sendToRoom(opts: {
|
|
100
96
|
room: string;
|
|
101
97
|
event: string;
|
|
102
98
|
data: unknown;
|
|
103
99
|
exclude?: string[];
|
|
104
100
|
}): void;
|
|
105
|
-
private broadcastExcluding;
|
|
106
101
|
broadcast(opts: {
|
|
107
102
|
event: string;
|
|
108
103
|
data: unknown;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../../../../../../src/modules/socket/websocket/server/helper.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../../../../../../src/modules/socket/websocket/server/helper.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,9 +5,9 @@ const common_1 = require("../../../../common");
|
|
|
5
5
|
const base_1 = require("../../../../modules/base");
|
|
6
6
|
const error_1 = require("../../../../modules/error");
|
|
7
7
|
const redis_1 = require("../../../../modules/redis");
|
|
8
|
-
const utilities_1 = require("../../../../utilities");
|
|
9
8
|
const promise_utility_1 = require("../../../../utilities/promise.utility");
|
|
10
9
|
const common_2 = require("../common");
|
|
10
|
+
const delivery_1 = require("./delivery");
|
|
11
11
|
class WebSocketServerHelper extends base_1.BaseHelper {
|
|
12
12
|
/**
|
|
13
13
|
* Narrows an untrusted room payload to the strings it actually contains.
|
|
@@ -31,14 +31,12 @@ class WebSocketServerHelper extends base_1.BaseHelper {
|
|
|
31
31
|
this.heartbeatTimer = null;
|
|
32
32
|
this.identifier = opts.identifier;
|
|
33
33
|
this.path = opts.path ?? common_2.WebSocketDefaults.PATH;
|
|
34
|
-
this.server = opts.server;
|
|
35
34
|
this.serverId = crypto.randomUUID();
|
|
36
35
|
this.authenticateFn = opts.authenticateFn;
|
|
37
36
|
this.validateRoomFn = opts.validateRoomFn;
|
|
38
37
|
this.onClientConnected = opts.clientConnectedFn;
|
|
39
38
|
this.onClientDisconnected = opts.clientDisconnectedFn;
|
|
40
39
|
this.messageHandler = opts.messageHandler;
|
|
41
|
-
this.outboundTransformer = opts.outboundTransformer;
|
|
42
40
|
this.handshakeFn = opts.handshakeFn;
|
|
43
41
|
this.defaultRooms = opts.defaultRooms ?? [
|
|
44
42
|
common_2.WebSocketDefaults.ROOM,
|
|
@@ -53,8 +51,15 @@ class WebSocketServerHelper extends base_1.BaseHelper {
|
|
|
53
51
|
this.authTimeout = opts.authTimeout ?? common_2.WebSocketDefaults.AUTH_TIMEOUT;
|
|
54
52
|
this.heartbeatInterval = opts.heartbeatInterval ?? common_2.WebSocketDefaults.HEARTBEAT_INTERVAL;
|
|
55
53
|
this.heartbeatTimeout = opts.heartbeatTimeout ?? common_2.WebSocketDefaults.HEARTBEAT_TIMEOUT;
|
|
56
|
-
this.encryptedBatchLimit = opts.encryptedBatchLimit ?? common_2.WebSocketDefaults.ENCRYPTED_BATCH_LIMIT;
|
|
57
54
|
this.requireEncryption = opts.requireEncryption ?? false;
|
|
55
|
+
this.delivery = new delivery_1.WebSocketDeliveryHelper({
|
|
56
|
+
server: opts.server,
|
|
57
|
+
clients: this.clients,
|
|
58
|
+
users: this.users,
|
|
59
|
+
rooms: this.rooms,
|
|
60
|
+
outboundTransformer: opts.outboundTransformer,
|
|
61
|
+
encryptedBatchLimit: opts.encryptedBatchLimit ?? common_2.WebSocketDefaults.ENCRYPTED_BATCH_LIMIT,
|
|
62
|
+
});
|
|
58
63
|
this.initRedisClients(opts.redisConnection);
|
|
59
64
|
}
|
|
60
65
|
initRedisClients(redisConnection) {
|
|
@@ -644,190 +649,18 @@ class WebSocketServerHelper extends base_1.BaseHelper {
|
|
|
644
649
|
}
|
|
645
650
|
logger.info('Left rooms | id: %s | rooms: %j', clientId, validRooms);
|
|
646
651
|
}
|
|
652
|
+
/** Local delivery and fan-out live in WebSocketDeliveryHelper - kept public here by delegation. */
|
|
647
653
|
sendToClient(opts) {
|
|
648
|
-
|
|
649
|
-
const { clientId, event, data, doLog } = opts;
|
|
650
|
-
const client = this.clients.get(clientId);
|
|
651
|
-
if (!client) {
|
|
652
|
-
return;
|
|
653
|
-
}
|
|
654
|
-
// Async path - transformer intercepts before socket.send()
|
|
655
|
-
const outboundTransformer = this.outboundTransformer;
|
|
656
|
-
if (outboundTransformer && client.encrypted) {
|
|
657
|
-
Promise.resolve()
|
|
658
|
-
.then(() => outboundTransformer({ client, event, data }))
|
|
659
|
-
.then(transformed => {
|
|
660
|
-
const outboundMessage = transformed ?? { event, data };
|
|
661
|
-
this.deliverToSocket({
|
|
662
|
-
client,
|
|
663
|
-
payload: JSON.stringify(outboundMessage),
|
|
664
|
-
doLog,
|
|
665
|
-
event,
|
|
666
|
-
data,
|
|
667
|
-
});
|
|
668
|
-
})
|
|
669
|
-
.catch(error => {
|
|
670
|
-
logger.error('Outbound transformer error | id: %s | error: %s', clientId, error);
|
|
671
|
-
});
|
|
672
|
-
return;
|
|
673
|
-
}
|
|
674
|
-
// Sync path (unchanged, zero overhead when no transformer)
|
|
675
|
-
this.deliverToSocket({ client, payload: JSON.stringify({ event, data }), doLog, event, data });
|
|
676
|
-
}
|
|
677
|
-
async sendToClientAsync(opts) {
|
|
678
|
-
const { clientId, event, data } = opts;
|
|
679
|
-
const client = this.clients.get(clientId);
|
|
680
|
-
if (!client) {
|
|
681
|
-
return Promise.resolve();
|
|
682
|
-
}
|
|
683
|
-
if (!this.outboundTransformer || !client.encrypted) {
|
|
684
|
-
this.deliverToSocket({ client, payload: JSON.stringify({ event, data }), event, data });
|
|
685
|
-
return Promise.resolve();
|
|
686
|
-
}
|
|
687
|
-
try {
|
|
688
|
-
const transformed = await Promise.resolve(this.outboundTransformer({ client, event, data }));
|
|
689
|
-
const outboundMessage = transformed ?? { event, data };
|
|
690
|
-
this.deliverToSocket({ client, payload: JSON.stringify(outboundMessage), event, data });
|
|
691
|
-
}
|
|
692
|
-
catch (error) {
|
|
693
|
-
this.logger
|
|
694
|
-
.for(this.sendToClientAsync.name)
|
|
695
|
-
.error('Outbound transformer error | id: %s | error: %s', clientId, error);
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
deliverToSocket(opts) {
|
|
699
|
-
const logger = this.logger.for(this.deliverToSocket.name);
|
|
700
|
-
const { client, payload, doLog, event, data } = opts;
|
|
701
|
-
try {
|
|
702
|
-
const result = client.socket.send(payload);
|
|
703
|
-
if (result === 0) {
|
|
704
|
-
logger.warn('Message dropped (socket closed) | id: %s', client.id);
|
|
705
|
-
}
|
|
706
|
-
if (result === -1) {
|
|
707
|
-
client.backpressured = true;
|
|
708
|
-
logger.warn('Backpressure detected | id: %s', client.id);
|
|
709
|
-
}
|
|
710
|
-
}
|
|
711
|
-
catch (error) {
|
|
712
|
-
logger.error('Failed to send | id: %s | error: %s', client.id, error);
|
|
713
|
-
}
|
|
714
|
-
if (doLog) {
|
|
715
|
-
logger.info('Message sent | id: %s | event: %s | data: %j', client.id, event, data);
|
|
716
|
-
}
|
|
654
|
+
this.delivery.sendToClient(opts);
|
|
717
655
|
}
|
|
718
656
|
sendToUser(opts) {
|
|
719
|
-
|
|
720
|
-
const clientIds = this.users.get(userId);
|
|
721
|
-
if (!clientIds) {
|
|
722
|
-
return;
|
|
723
|
-
}
|
|
724
|
-
for (const clientId of clientIds) {
|
|
725
|
-
this.sendToClient({ clientId, event, data });
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
sendToRoomExcluding(opts) {
|
|
729
|
-
const { room, event, data, exclude } = opts;
|
|
730
|
-
const excludeSet = new Set(exclude);
|
|
731
|
-
const roomClientIds = this.rooms.get(room);
|
|
732
|
-
if (!roomClientIds) {
|
|
733
|
-
return;
|
|
734
|
-
}
|
|
735
|
-
// Serialised ONCE when there is no transformer, because the payload is then identical for every
|
|
736
|
-
// recipient - `sendToClient` would otherwise run `JSON.stringify` per client on a message that
|
|
737
|
-
// never differs. With a transformer each client's payload really is different, so that path
|
|
738
|
-
// still goes through `sendToClient`. The non-excluding fan-out already works this way.
|
|
739
|
-
if (!this.outboundTransformer) {
|
|
740
|
-
const payload = JSON.stringify({ event, data });
|
|
741
|
-
for (const clientId of roomClientIds) {
|
|
742
|
-
if (excludeSet.has(clientId)) {
|
|
743
|
-
continue;
|
|
744
|
-
}
|
|
745
|
-
const client = this.clients.get(clientId);
|
|
746
|
-
if (client) {
|
|
747
|
-
this.deliverToSocket({ client, payload, event, data });
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
return;
|
|
751
|
-
}
|
|
752
|
-
for (const clientId of roomClientIds) {
|
|
753
|
-
if (excludeSet.has(clientId)) {
|
|
754
|
-
continue;
|
|
755
|
-
}
|
|
756
|
-
this.sendToClient({ clientId, event, data });
|
|
757
|
-
}
|
|
657
|
+
this.delivery.sendToUser(opts);
|
|
758
658
|
}
|
|
759
659
|
sendToRoom(opts) {
|
|
760
|
-
|
|
761
|
-
// When exclude is present, must iterate - can't exclude from Bun native pub/sub
|
|
762
|
-
if (exclude?.length) {
|
|
763
|
-
this.sendToRoomExcluding({ room, event, data, exclude });
|
|
764
|
-
return;
|
|
765
|
-
}
|
|
766
|
-
// No encryption - Bun native pub/sub O(1) C++ fan-out
|
|
767
|
-
if (!this.outboundTransformer) {
|
|
768
|
-
const payload = JSON.stringify({ event, data });
|
|
769
|
-
this.server.publish(room, payload);
|
|
770
|
-
return;
|
|
771
|
-
}
|
|
772
|
-
// Encryption enabled - iterate all clients individually with concurrency limit
|
|
773
|
-
const roomClientIds = this.rooms.get(room);
|
|
774
|
-
if (!roomClientIds) {
|
|
775
|
-
return;
|
|
776
|
-
}
|
|
777
|
-
const tasks = [];
|
|
778
|
-
for (const clientId of roomClientIds) {
|
|
779
|
-
tasks.push(() => this.sendToClientAsync({ clientId, event, data }));
|
|
780
|
-
}
|
|
781
|
-
if (tasks.length) {
|
|
782
|
-
(0, promise_utility_1.voidExecution)({
|
|
783
|
-
logger: this.logger,
|
|
784
|
-
scope: this.sendToRoom.name,
|
|
785
|
-
execution: (0, utilities_1.executePromiseWithLimit)({ tasks, limit: this.encryptedBatchLimit }),
|
|
786
|
-
});
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
broadcastExcluding(opts) {
|
|
790
|
-
const { event, data, exclude } = opts;
|
|
791
|
-
const excludeSet = new Set(exclude);
|
|
792
|
-
for (const [clientId, client] of this.clients) {
|
|
793
|
-
if (excludeSet.has(clientId)) {
|
|
794
|
-
continue;
|
|
795
|
-
}
|
|
796
|
-
// Only broadcast to authenticated clients (consistent with non-exclude path which uses BROADCAST_TOPIC)
|
|
797
|
-
if (client.state !== common_2.WebSocketClientStates.AUTHENTICATED) {
|
|
798
|
-
continue;
|
|
799
|
-
}
|
|
800
|
-
this.sendToClient({ clientId, event, data });
|
|
801
|
-
}
|
|
660
|
+
this.delivery.sendToRoom(opts);
|
|
802
661
|
}
|
|
803
662
|
broadcast(opts) {
|
|
804
|
-
|
|
805
|
-
// When exclude is present, must iterate - can't exclude from Bun native pub/sub
|
|
806
|
-
if (exclude?.length) {
|
|
807
|
-
this.broadcastExcluding({ event, data, exclude });
|
|
808
|
-
return;
|
|
809
|
-
}
|
|
810
|
-
// No encryption - Bun native pub/sub O(1) C++ fan-out
|
|
811
|
-
if (!this.outboundTransformer) {
|
|
812
|
-
const payload = JSON.stringify({ event, data });
|
|
813
|
-
this.server.publish(common_2.WebSocketDefaults.BROADCAST_TOPIC, payload);
|
|
814
|
-
return;
|
|
815
|
-
}
|
|
816
|
-
// Encryption enabled - iterate all clients individually with concurrency limit
|
|
817
|
-
const tasks = [];
|
|
818
|
-
for (const [clientId, client] of this.clients) {
|
|
819
|
-
if (client.state !== common_2.WebSocketClientStates.AUTHENTICATED) {
|
|
820
|
-
continue;
|
|
821
|
-
}
|
|
822
|
-
tasks.push(() => this.sendToClientAsync({ clientId, event, data }));
|
|
823
|
-
}
|
|
824
|
-
if (tasks.length) {
|
|
825
|
-
(0, promise_utility_1.voidExecution)({
|
|
826
|
-
logger: this.logger,
|
|
827
|
-
scope: this.broadcast.name,
|
|
828
|
-
execution: (0, utilities_1.executePromiseWithLimit)({ tasks, limit: this.encryptedBatchLimit }),
|
|
829
|
-
});
|
|
830
|
-
}
|
|
663
|
+
this.delivery.broadcast(opts);
|
|
831
664
|
}
|
|
832
665
|
send(opts) {
|
|
833
666
|
const logger = this.logger.for(this.send.name);
|