@eleven-am/pondsocket 0.1.43 → 0.1.45

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/client/channel.js CHANGED
@@ -8,19 +8,18 @@ class Channel {
8
8
  this._name = name;
9
9
  this._queue = [];
10
10
  this._presence = [];
11
- this._finished = false;
12
11
  this._joinParams = params;
13
12
  this._publisher = publisher;
14
13
  this._clientState = clientState;
15
14
  this._receiver = new subjectUtils_1.SimpleSubject();
16
- this._joinState = new subjectUtils_1.SimpleBehaviorSubject(false);
15
+ this._joinState = new subjectUtils_1.SimpleBehaviorSubject(enums_1.ChannelState.IDLE);
17
16
  this._presenceSub = this._init(receiver);
18
17
  }
19
18
  /**
20
19
  * @desc Connects to the channel.
21
20
  */
22
21
  join() {
23
- if (this._finished) {
22
+ if (this._joinState.value === enums_1.ChannelState.CLOSED) {
24
23
  throw new Error('This channel has been closed');
25
24
  }
26
25
  const joinMessage = {
@@ -29,6 +28,7 @@ class Channel {
29
28
  event: enums_1.ClientActions.JOIN_CHANNEL,
30
29
  payload: this._joinParams,
31
30
  };
31
+ this._joinState.next(enums_1.ChannelState.JOINING);
32
32
  if (this._clientState.value === enums_1.PondState.OPEN) {
33
33
  this._publisher(joinMessage);
34
34
  }
@@ -47,7 +47,7 @@ class Channel {
47
47
  payload: {},
48
48
  };
49
49
  this._publish(leaveMessage);
50
- this._finished = true;
50
+ this._joinState.next(enums_1.ChannelState.CLOSED);
51
51
  this._presenceSub();
52
52
  }
53
53
  /**
@@ -143,15 +143,9 @@ class Channel {
143
143
  /**
144
144
  * @desc Gets the current connection state of the channel.
145
145
  */
146
- isConnected() {
146
+ get channelState() {
147
147
  return this._joinState.value;
148
148
  }
149
- /**
150
- * @desc check is the channel has been closed.
151
- */
152
- hasClosed() {
153
- return this._finished;
154
- }
155
149
  /**
156
150
  * @desc Gets the current presence of the channel.
157
151
  */
@@ -163,7 +157,7 @@ class Channel {
163
157
  * @param callback - The callback to call when the presence changes.
164
158
  */
165
159
  onUsersChange(callback) {
166
- return this._subscribeToPresence((event, payload) => callback(payload.presence));
160
+ return this._subscribeToPresence((_event, payload) => callback(payload.presence));
167
161
  }
168
162
  _send(event, payload, receivers = 'all_users') {
169
163
  const message = {
@@ -176,8 +170,10 @@ class Channel {
176
170
  this._publish(message);
177
171
  }
178
172
  _publish(data) {
179
- if (this._joinState.value && this._clientState.value !== enums_1.PondState.OPEN) {
180
- this._publisher(data);
173
+ if (this._clientState.value === enums_1.PondState.OPEN) {
174
+ if (this._joinState.value === enums_1.ChannelState.JOINED || this._joinState.value === enums_1.ChannelState.JOINING) {
175
+ this._publisher(data);
176
+ }
181
177
  return;
182
178
  }
183
179
  this._queue.push(data);
@@ -192,10 +188,10 @@ class Channel {
192
188
  _init(receiver) {
193
189
  const unsubMessages = receiver.subscribe((data) => {
194
190
  if (data.channelName === this._name) {
195
- if (!this._joinState.value) {
196
- this._joinState.publish(true);
191
+ if (this._joinState.value !== enums_1.ChannelState.JOINED) {
192
+ this._joinState.next(enums_1.ChannelState.JOINED);
197
193
  }
198
- this._receiver.publish(data);
194
+ this._receiver.next(data);
199
195
  }
200
196
  });
201
197
  const unsubStateChange = this._clientState.subscribe((state) => {
@@ -212,11 +208,11 @@ class Channel {
212
208
  .forEach((message) => {
213
209
  this._publisher(message);
214
210
  });
215
- this._joinState.publish(true);
211
+ this._joinState.next(enums_1.ChannelState.JOINED);
216
212
  this._queue = [];
217
213
  }
218
214
  else if (state !== enums_1.PondState.OPEN) {
219
- this._joinState.publish(false);
215
+ this._joinState.next(enums_1.ChannelState.STALLED);
220
216
  }
221
217
  });
222
218
  const unsubPresence = this._subscribeToPresence((_, payload) => {
@@ -0,0 +1,528 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const channel_1 = require("./channel");
4
+ const enums_1 = require("../enums");
5
+ const channelEngine_1 = require("../server/channel/channelEngine");
6
+ const subjectUtils_1 = require("../server/utils/subjectUtils");
7
+ const createChannel = (params) => {
8
+ const publisher = jest.fn();
9
+ const state = new subjectUtils_1.SimpleBehaviorSubject(enums_1.PondState.OPEN);
10
+ const receiver = new subjectUtils_1.SimpleSubject();
11
+ const channel = new channel_1.Channel(publisher, state, 'test', receiver, params);
12
+ return {
13
+ channel,
14
+ publisher,
15
+ state,
16
+ receiver,
17
+ };
18
+ };
19
+ describe('Channel', () => {
20
+ it('should correctly send a join message', () => {
21
+ const { channel, publisher, state } = createChannel();
22
+ expect(publisher).not.toHaveBeenCalled();
23
+ expect(state.value).toBe(enums_1.PondState.OPEN);
24
+ channel.join();
25
+ expect(publisher).toHaveBeenCalledWith({
26
+ action: enums_1.ClientActions.JOIN_CHANNEL,
27
+ channelName: 'test',
28
+ event: enums_1.ClientActions.JOIN_CHANNEL,
29
+ payload: {},
30
+ });
31
+ const { channel: channel2, publisher: publisher2, state: state2 } = createChannel();
32
+ state2.next(enums_1.PondState.CLOSED);
33
+ expect(state2.value).toBe(enums_1.PondState.CLOSED);
34
+ expect(publisher2).not.toHaveBeenCalled();
35
+ expect(channel2['_queue']).toEqual([]);
36
+ channel2.join();
37
+ expect(publisher2).not.toHaveBeenCalled();
38
+ expect(channel2['_queue']).toEqual([
39
+ {
40
+ action: enums_1.ClientActions.JOIN_CHANNEL,
41
+ channelName: 'test',
42
+ event: enums_1.ClientActions.JOIN_CHANNEL,
43
+ payload: {},
44
+ },
45
+ ]);
46
+ state2.next(enums_1.PondState.OPEN);
47
+ expect(publisher2).toHaveBeenCalledWith({
48
+ action: enums_1.ClientActions.JOIN_CHANNEL,
49
+ channelName: 'test',
50
+ event: enums_1.ClientActions.JOIN_CHANNEL,
51
+ payload: {},
52
+ });
53
+ expect(channel2['_queue']).toEqual([]);
54
+ const { channel: channel3 } = createChannel();
55
+ channel3.leave();
56
+ expect(() => channel3.join())
57
+ .toThrowError('This channel has been closed');
58
+ });
59
+ it('should correctly send a leave message', () => {
60
+ const { channel, publisher, receiver } = createChannel();
61
+ expect(channel.channelState).toBe(enums_1.ChannelState.IDLE);
62
+ channel.join();
63
+ expect(channel.channelState).toBe(enums_1.ChannelState.JOINING);
64
+ // once the server responds with a join message, the channel should be connected
65
+ receiver.next({
66
+ action: 'SYSTEM',
67
+ channelName: 'test',
68
+ event: 'SYSTEM',
69
+ payload: {
70
+ event: 'JOIN',
71
+ },
72
+ });
73
+ expect(channel.channelState).toBe(enums_1.ChannelState.JOINED);
74
+ channel.leave();
75
+ expect(channel.channelState).toBe(enums_1.ChannelState.CLOSED);
76
+ expect(publisher).toHaveBeenCalledWith({
77
+ action: enums_1.ClientActions.LEAVE_CHANNEL,
78
+ channelName: 'test',
79
+ event: enums_1.ClientActions.LEAVE_CHANNEL,
80
+ payload: {},
81
+ });
82
+ });
83
+ it('should notify subscribers when the channel state changes', () => {
84
+ const { channel, receiver } = createChannel();
85
+ const stateListener = jest.fn();
86
+ channel.onConnectionChange(stateListener);
87
+ expect(stateListener).toHaveBeenCalledWith(enums_1.ChannelState.IDLE);
88
+ channel.join();
89
+ receiver.next({
90
+ action: 'SYSTEM',
91
+ channelName: 'test',
92
+ event: 'SYSTEM',
93
+ payload: {
94
+ event: 'JOIN',
95
+ },
96
+ });
97
+ expect(stateListener).toHaveBeenCalledWith(enums_1.ChannelState.JOINED);
98
+ channel.leave();
99
+ expect(stateListener).toHaveBeenCalledWith(enums_1.ChannelState.CLOSED);
100
+ });
101
+ it('should correctly send a message', () => {
102
+ const { channel, publisher, state } = createChannel();
103
+ expect(channel.channelState).toBe(enums_1.ChannelState.IDLE);
104
+ expect(publisher).not.toHaveBeenCalled();
105
+ expect(channel['_queue']).toEqual([]);
106
+ // when the socket is connected but the channel is not joined, the message would not be queued
107
+ channel.broadcast('test', { test: true });
108
+ expect(publisher).not.toHaveBeenCalled();
109
+ expect(channel['_queue']).toEqual([]);
110
+ channel.join();
111
+ publisher.mockClear();
112
+ // however once the channel is joined, the message should be sent
113
+ channel.broadcast('test', { test: true });
114
+ expect(publisher).toHaveBeenCalledWith({
115
+ action: enums_1.ClientActions.BROADCAST,
116
+ addresses: 'all_users',
117
+ channelName: 'test',
118
+ event: 'test',
119
+ payload: {
120
+ test: true,
121
+ },
122
+ });
123
+ // if for some reason the socket is disconnected, the message should be queued
124
+ publisher.mockClear();
125
+ state.next(enums_1.PondState.CLOSED);
126
+ expect(state.value).toBe(enums_1.PondState.CLOSED);
127
+ channel.broadcast('test', { test: true });
128
+ expect(publisher).not.toHaveBeenCalled();
129
+ expect(channel['_queue']).toEqual([
130
+ {
131
+ action: enums_1.ClientActions.BROADCAST,
132
+ addresses: 'all_users',
133
+ channelName: 'test',
134
+ event: 'test',
135
+ payload: {
136
+ test: true,
137
+ },
138
+ },
139
+ ]);
140
+ // once the socket is reconnected, a join message should be sent and the queued messages should be sent
141
+ publisher.mockClear();
142
+ state.next(enums_1.PondState.OPEN);
143
+ expect(publisher).toHaveBeenCalledWith({
144
+ action: enums_1.ClientActions.JOIN_CHANNEL,
145
+ channelName: 'test',
146
+ event: enums_1.ClientActions.JOIN_CHANNEL,
147
+ payload: {},
148
+ });
149
+ expect(publisher).toHaveBeenCalledWith({
150
+ action: enums_1.ClientActions.BROADCAST,
151
+ addresses: 'all_users',
152
+ channelName: 'test',
153
+ event: 'test',
154
+ payload: {
155
+ test: true,
156
+ },
157
+ });
158
+ expect(channel['_queue']).toEqual([]);
159
+ // if the channel is closed, the message should not be sent, and should not be queued
160
+ publisher.mockClear();
161
+ channel.leave();
162
+ // the leave message should be sent
163
+ expect(publisher).toHaveBeenCalledWith({
164
+ action: enums_1.ClientActions.LEAVE_CHANNEL,
165
+ channelName: 'test',
166
+ event: enums_1.ClientActions.LEAVE_CHANNEL,
167
+ payload: {},
168
+ });
169
+ expect(channel.channelState).toBe(enums_1.ChannelState.CLOSED);
170
+ channel.broadcast('test', { test: true });
171
+ expect(channel['_queue']).toEqual([]);
172
+ expect(publisher).toBeCalledTimes(1);
173
+ });
174
+ // The presence system tests
175
+ it('should notify subscribers when a user joins the channel', () => {
176
+ const { channel, receiver } = createChannel();
177
+ const presenceListener = jest.fn();
178
+ channel.onJoin(presenceListener);
179
+ expect(presenceListener).not.toHaveBeenCalled();
180
+ // usually the server wouldn't send a presence if the channel is not joined
181
+ // but for testing purposes, we'll just send it anyway
182
+ receiver.next({
183
+ action: channelEngine_1.ServerActions.PRESENCE,
184
+ event: enums_1.PresenceEventTypes.JOIN,
185
+ channelName: 'test',
186
+ payload: {
187
+ changed: {
188
+ id: 'test',
189
+ status: 'online',
190
+ },
191
+ presence: [
192
+ {
193
+ id: 'test',
194
+ status: 'online',
195
+ },
196
+ {
197
+ id: 'test2',
198
+ status: 'online',
199
+ },
200
+ ],
201
+ },
202
+ });
203
+ expect(presenceListener).toHaveBeenCalledWith({
204
+ id: 'test',
205
+ status: 'online',
206
+ });
207
+ presenceListener.mockClear();
208
+ receiver.next({
209
+ action: channelEngine_1.ServerActions.PRESENCE,
210
+ event: enums_1.PresenceEventTypes.UPDATE,
211
+ channelName: 'test',
212
+ payload: {
213
+ changed: {
214
+ id: 'test',
215
+ status: 'online',
216
+ },
217
+ presence: [
218
+ {
219
+ id: 'test',
220
+ status: 'online',
221
+ },
222
+ {
223
+ id: 'test2',
224
+ status: 'online',
225
+ },
226
+ ],
227
+ },
228
+ });
229
+ expect(presenceListener).not.toHaveBeenCalled();
230
+ // also, if a presence event is received for a different channel, it should not be sent to the listener
231
+ receiver.next({
232
+ action: channelEngine_1.ServerActions.PRESENCE,
233
+ event: enums_1.PresenceEventTypes.JOIN,
234
+ channelName: 'test2',
235
+ payload: {
236
+ changed: {
237
+ id: 'test',
238
+ status: 'online',
239
+ },
240
+ presence: [
241
+ {
242
+ id: 'test',
243
+ status: 'online',
244
+ },
245
+ {
246
+ id: 'test2',
247
+ status: 'online',
248
+ },
249
+ ],
250
+ },
251
+ });
252
+ expect(presenceListener).not.toHaveBeenCalled();
253
+ });
254
+ it('should notify subscribers when a user leaves the channel', () => {
255
+ const { channel, receiver } = createChannel();
256
+ const presenceListener = jest.fn();
257
+ channel.onLeave(presenceListener);
258
+ expect(presenceListener).not.toHaveBeenCalled();
259
+ // usually the server wouldn't send a presence if the channel is not joined
260
+ // but for testing purposes, we'll just send it anyway
261
+ receiver.next({
262
+ action: channelEngine_1.ServerActions.PRESENCE,
263
+ event: enums_1.PresenceEventTypes.LEAVE,
264
+ channelName: 'test',
265
+ payload: {
266
+ changed: {
267
+ id: 'test',
268
+ status: 'online',
269
+ },
270
+ presence: [
271
+ {
272
+ id: 'test',
273
+ status: 'online',
274
+ },
275
+ {
276
+ id: 'test2',
277
+ status: 'online',
278
+ },
279
+ ],
280
+ },
281
+ });
282
+ expect(presenceListener).toHaveBeenCalledWith({
283
+ id: 'test',
284
+ status: 'online',
285
+ });
286
+ presenceListener.mockClear();
287
+ receiver.next({
288
+ action: channelEngine_1.ServerActions.PRESENCE,
289
+ event: enums_1.PresenceEventTypes.UPDATE,
290
+ channelName: 'test',
291
+ payload: {
292
+ changed: {
293
+ id: 'test',
294
+ status: 'online',
295
+ },
296
+ presence: [
297
+ {
298
+ id: 'test',
299
+ status: 'online',
300
+ },
301
+ {
302
+ id: 'test2',
303
+ status: 'online',
304
+ },
305
+ ],
306
+ },
307
+ });
308
+ expect(presenceListener).not.toHaveBeenCalled();
309
+ // also, if a presence event is received for a different channel, it should not be sent to the listener
310
+ receiver.next({
311
+ action: channelEngine_1.ServerActions.PRESENCE,
312
+ event: enums_1.PresenceEventTypes.LEAVE,
313
+ channelName: 'test2',
314
+ payload: {
315
+ changed: {
316
+ id: 'test',
317
+ status: 'online',
318
+ },
319
+ presence: [
320
+ {
321
+ id: 'test',
322
+ status: 'online',
323
+ },
324
+ {
325
+ id: 'test2',
326
+ status: 'online',
327
+ },
328
+ ],
329
+ },
330
+ });
331
+ expect(presenceListener).not.toHaveBeenCalled();
332
+ });
333
+ it('should notify subscribers when a user updates their presence', () => {
334
+ const { channel, receiver } = createChannel();
335
+ const presenceListener = jest.fn();
336
+ channel.onUsersChange(presenceListener);
337
+ expect(presenceListener).not.toHaveBeenCalled();
338
+ // usually the server wouldn't send a presence if the channel is not joined
339
+ // but for testing purposes, we'll just send it anyway
340
+ receiver.next({
341
+ action: channelEngine_1.ServerActions.PRESENCE,
342
+ event: enums_1.PresenceEventTypes.UPDATE,
343
+ channelName: 'test',
344
+ payload: {
345
+ changed: {
346
+ id: 'test',
347
+ status: 'online',
348
+ },
349
+ presence: [
350
+ {
351
+ id: 'test',
352
+ status: 'online',
353
+ },
354
+ {
355
+ id: 'test2',
356
+ status: 'online',
357
+ },
358
+ ],
359
+ },
360
+ });
361
+ expect(presenceListener).toHaveBeenCalledWith([
362
+ {
363
+ id: 'test',
364
+ status: 'online',
365
+ },
366
+ {
367
+ id: 'test2',
368
+ status: 'online',
369
+ },
370
+ ]);
371
+ expect(channel.getPresence()).toBe(presenceListener.mock.calls[0][0]);
372
+ presenceListener.mockClear();
373
+ // if we receive a leave or join event, it should be sent to the listener
374
+ // this is because we are listening for all presence events
375
+ receiver.next({
376
+ action: channelEngine_1.ServerActions.PRESENCE,
377
+ event: enums_1.PresenceEventTypes.JOIN,
378
+ channelName: 'test',
379
+ payload: {
380
+ changed: {
381
+ id: 'test',
382
+ status: 'online',
383
+ },
384
+ presence: [
385
+ {
386
+ id: 'test',
387
+ status: 'online',
388
+ },
389
+ ],
390
+ },
391
+ });
392
+ expect(presenceListener).toHaveBeenCalledWith([
393
+ {
394
+ id: 'test',
395
+ status: 'online',
396
+ },
397
+ ]);
398
+ expect(channel.getPresence()).toBe(presenceListener.mock.calls[0][0]);
399
+ presenceListener.mockClear();
400
+ // also, if a presence event is received for a different channel, it should not be sent to the listener
401
+ receiver.next({
402
+ action: channelEngine_1.ServerActions.PRESENCE,
403
+ event: enums_1.PresenceEventTypes.UPDATE,
404
+ channelName: 'test2',
405
+ payload: {
406
+ changed: {
407
+ id: 'test',
408
+ status: 'online',
409
+ },
410
+ presence: [
411
+ {
412
+ id: 'test',
413
+ status: 'online',
414
+ },
415
+ {
416
+ id: 'test2',
417
+ status: 'online',
418
+ },
419
+ ],
420
+ },
421
+ });
422
+ expect(presenceListener).not.toHaveBeenCalled();
423
+ // we once again send a presence event for the same channel, but this time it should be sent to the listener
424
+ receiver.next({
425
+ action: channelEngine_1.ServerActions.PRESENCE,
426
+ event: enums_1.PresenceEventTypes.UPDATE,
427
+ channelName: 'test',
428
+ payload: {
429
+ changed: {
430
+ id: 'test',
431
+ status: 'away',
432
+ },
433
+ presence: [
434
+ {
435
+ id: 'test',
436
+ status: 'away',
437
+ },
438
+ {
439
+ id: 'test2',
440
+ status: 'online',
441
+ },
442
+ ],
443
+ },
444
+ });
445
+ expect(presenceListener).toHaveBeenCalledWith([
446
+ {
447
+ id: 'test',
448
+ status: 'away',
449
+ },
450
+ {
451
+ id: 'test2',
452
+ status: 'online',
453
+ },
454
+ ]);
455
+ expect(channel.getPresence()).toBe(presenceListener.mock.calls[0][0]);
456
+ });
457
+ // message events
458
+ it('should notify subscribers when a message is received', () => {
459
+ const { channel, receiver } = createChannel();
460
+ const messageListener = jest.fn();
461
+ channel.onMessage(messageListener);
462
+ expect(messageListener).not.toHaveBeenCalled();
463
+ receiver.next({
464
+ action: channelEngine_1.ServerActions.BROADCAST,
465
+ event: 'message',
466
+ channelName: 'test',
467
+ payload: {
468
+ id: 'test',
469
+ message: 'test',
470
+ },
471
+ });
472
+ expect(messageListener).toHaveBeenCalledWith('message', {
473
+ id: 'test',
474
+ message: 'test',
475
+ });
476
+ // if a message event is received for a different channel, it should not be sent to the listener
477
+ receiver.next({
478
+ action: channelEngine_1.ServerActions.BROADCAST,
479
+ event: 'message',
480
+ channelName: 'test2',
481
+ payload: {
482
+ id: 'test',
483
+ message: 'test',
484
+ },
485
+ });
486
+ expect(messageListener).toHaveBeenCalledTimes(1);
487
+ // we can also subscribe to a specific message event
488
+ const specificMessageListener = jest.fn();
489
+ channel.onMessageEvent('test', specificMessageListener);
490
+ expect(specificMessageListener).not.toHaveBeenCalled();
491
+ receiver.next({
492
+ action: channelEngine_1.ServerActions.BROADCAST,
493
+ event: 'test',
494
+ channelName: 'test',
495
+ payload: {
496
+ id: 'test',
497
+ message: 'test',
498
+ },
499
+ });
500
+ expect(specificMessageListener).toHaveBeenCalledWith({
501
+ id: 'test',
502
+ message: 'test',
503
+ });
504
+ specificMessageListener.mockClear();
505
+ // if a message event is received for the same channel, but a different event, it should not be sent to the listener
506
+ receiver.next({
507
+ action: channelEngine_1.ServerActions.BROADCAST,
508
+ event: 'test2',
509
+ channelName: 'test',
510
+ payload: {
511
+ id: 'test',
512
+ message: 'test',
513
+ },
514
+ });
515
+ expect(specificMessageListener).not.toHaveBeenCalled();
516
+ // if a message event is received for a different channel, it should not be sent to the listener
517
+ receiver.next({
518
+ action: channelEngine_1.ServerActions.BROADCAST,
519
+ event: 'test',
520
+ channelName: 'test2',
521
+ payload: {
522
+ id: 'test',
523
+ message: 'test',
524
+ },
525
+ });
526
+ expect(specificMessageListener).not.toHaveBeenCalled();
527
+ });
528
+ });
package/client.js CHANGED
@@ -30,14 +30,14 @@ class PondClient {
30
30
  connect(backoff = 1) {
31
31
  const socket = new WebSocket(this._address.toString());
32
32
  socket.onopen = () => {
33
- this._connectionState.publish(enums_1.PondState.OPEN);
33
+ this._connectionState.next(enums_1.PondState.OPEN);
34
34
  };
35
35
  socket.onmessage = (message) => {
36
36
  const data = JSON.parse(message.data);
37
- this._broadcaster.publish(data);
37
+ this._broadcaster.next(data);
38
38
  };
39
39
  socket.onerror = () => {
40
- this._connectionState.publish(enums_1.PondState.CLOSED);
40
+ this._connectionState.next(enums_1.PondState.CLOSED);
41
41
  setTimeout(() => {
42
42
  this.connect(backoff * 2);
43
43
  }, backoff * 1000);
@@ -65,7 +65,8 @@ class PondClient {
65
65
  * @param params - The params to send to the server.
66
66
  */
67
67
  createChannel(name, params) {
68
- if (this._channels[name] && !this._channels[name].hasClosed()) {
68
+ var _a;
69
+ if (((_a = this._channels[name]) === null || _a === void 0 ? void 0 : _a.channelState) !== enums_1.ChannelState.CLOSED) {
69
70
  return this._channels[name];
70
71
  }
71
72
  const publisher = this._createPublisher();