anear-js-api 0.4.1 → 0.4.2

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.
@@ -46,7 +46,7 @@ class AnearApi extends ApiService {
46
46
  const json = await this.post("transitions", {event_name: eventName}, relationships)
47
47
  const attrs = json.data.attributes
48
48
  logger.info(`API: newState is ${attrs.state}`)
49
- return json
49
+ return attrs
50
50
  } catch(err) {
51
51
  logger.error(err)
52
52
  }
package/lib/index.js CHANGED
@@ -2,7 +2,8 @@
2
2
 
3
3
  const JsonApiResource = require('./models/JsonApiResource')
4
4
  const JsonApiArrayResource = require('./models/JsonApiArrayResource')
5
- const {AnearEvent, logger} = require('./models/AnearEvent')
5
+ const AnearEvent = require('./models/AnearEvent')
6
+ const logger = require('./utils/Logger')
6
7
  const AnearParticipant = require('./models/AnearParticipant')
7
8
  const AnearMessaging = require('./messaging/AnearMessaging')
8
9
  const AnearApiService = require('./api/ApiService')
@@ -1,9 +1,7 @@
1
1
  "use strict"
2
2
  const Ably = require('ably/promises')
3
3
  const AnearApi = require('../api/AnearApi')
4
- const ParticipantTimer = require('../utils/ParticipantTimer')
5
4
  const logger = require('../utils/Logger')
6
- const { Mutex } = require('async-mutex')
7
5
 
8
6
  const AppId = process.env.ANEARAPP_APP_ID
9
7
 
@@ -29,7 +27,6 @@ class AnearMessaging {
29
27
  this.api = new AnearApi()
30
28
  this.AnearEventClass = AnearEventClass
31
29
  this.AnearParticipantClass = AnearParticipantClass
32
- this.mutex = new Mutex()
33
30
  this.anearEvents = {}
34
31
 
35
32
  const baseUrl = this.api.api_base_url
@@ -53,7 +50,6 @@ class AnearMessaging {
53
50
  }
54
51
 
55
52
  this.eventChannels = {}
56
- this.participantTimers = {}
57
53
 
58
54
  this.initRealtime(clientOptions)
59
55
  }
@@ -85,78 +81,10 @@ class AnearMessaging {
85
81
  return this.realtime.channels.get(channelName, channelParams)
86
82
  }
87
83
 
88
- ensureParticipantTimer(anearEvent, participant, timeoutMsecs) {
89
- // this is called when a new timer is being started for a privateMessage
90
- // sent to a participant, or public message to all participants
91
- // If the timer already exists and is paused, it is resumed with the timeRemaining
92
- // [a starter function, timeRemaining] is returned
93
- let timeRemaining = timeoutMsecs
94
- let timerStarter = () => {}
95
-
96
- if (timeoutMsecs > 0) {
97
- const timer = this.participantTimers[participant.id] || this.createTimer(anearEvent, participant, timeoutMsecs)
98
-
99
- if (timer.isRunning) {
100
- timeRemaining = timer.interrupt()
101
- } else {
102
- timerStarter = () => timer.start(timeoutMsecs)
103
- }
104
- }
105
- logger.debug(`ensureParticipantTimer(timeRemaining: ${timeRemaining})`)
106
-
107
- return [timerStarter, timeRemaining]
108
- }
109
-
110
- createTimer(anearEvent, participant, timeoutMsecs) {
111
- const timer = new ParticipantTimer(
112
- participant.id,
113
- async () => await this.timerExpired(anearEvent, participant, timeoutMsecs)
114
- )
115
- this.participantTimers[participant.id] = timer
116
-
117
- return timer
118
- }
119
-
120
- destroyParticipantTimer(participantId) {
121
- // participant will not be receiving any more display messages
122
- // so we close out and delete the ParticipantTimer
123
- const timer = this.participantTimers[participantId]
124
-
125
- if (timer) {
126
- timer.reset()
127
- delete this.participantTimers[participantId]
128
- }
129
- }
130
-
131
- interruptParticipantTimer(participantId) {
132
- const timer = this.participantTimers[participantId]
133
-
134
- if (timer && timer.isRunning) timer.interrupt()
135
- }
136
-
137
- resetParticipantTimer(participantId) {
138
- // called after participant takes Action before timer expires
139
- const timer = this.participantTimers[participantId]
140
- if (timer) timer.reset()
141
- }
142
-
143
- async timerExpired(anearEvent, participant, timeoutMsecs) {
144
- logger.debug(`participant (${anearEvent.id}, ${participant.id}) TIMED OUT after ${timeoutMsecs} msecs`)
145
-
146
- delete this.participantTimers[participant.id]
147
-
148
- await anearEvent.participantTimedOut(participant)
149
- await anearEvent.update()
150
- }
151
-
152
84
  async getAnearEventFromStorage(eventId) {
153
85
  return await this.AnearEventClass.getFromStorage(eventId, this)
154
86
  }
155
87
 
156
- async getAnearParticipantFromStorage(participantId) {
157
- return await this.AnearParticipantClass.getFromStorage(participantId)
158
- }
159
-
160
88
  async initEventRealtimeMessaging(anearEvent) {
161
89
 
162
90
  if (this.eventChannels.hasOwnProperty(anearEvent.id)) {
@@ -195,41 +123,35 @@ class AnearMessaging {
195
123
  // if we are getting this event create message from history after a quick restart,
196
124
  // we just return if the event already exists
197
125
  //
198
- await this.loadOrPersistEventAndInitialize(anearEvent)
199
-
200
- } catch(err) {
201
- logger.error(err)
202
- }
203
- }
204
-
205
- async loadOrPersistEventAndInitialize(anearEvent) {
206
- const eventExists = await anearEvent.exists()
126
+ const eventExists = await anearEvent.exists()
127
+
128
+ logger.info(`Event ${anearEvent.id} ${eventExists ? "already exists" : "does not exist"} in Storage`)
129
+
130
+ let loadedEvent = anearEvent
131
+
132
+ if (!eventExists) {
133
+ await anearEvent.runExclusive(`createEventCallback ${anearEvent.id}`, async () => {
134
+ await anearEvent.createdEventCallback()
135
+ await anearEvent.persist()
136
+ // start the state machine before initialiing Realtime Messaging
137
+ // as REFRESH events come in and the state machine should be ready
138
+ // to handle those XState events
139
+ anearEvent.startStateMachine()
140
+ await this.initEventRealtimeMessaging(anearEvent)
141
+ })
142
+ } else {
143
+ loadedEvent = await this.getAnearEventFromStorage(anearEvent.id)
144
+ await this.initEventRealtimeMessaging(loadedEvent)
145
+ loadedEvent.startStateMachine()
146
+ }
207
147
 
208
- logger.info(`Event ${anearEvent.id} ${eventExists ? "already exists" : "does not exist"} in Storage`)
148
+ logger.info(`New ${loadedEvent.constructor.name} Event: `, loadedEvent.toJSON())
209
149
 
210
- let loadedEvent = anearEvent
150
+ this.anearEvents[loadedEvent.id] = loadedEvent
211
151
 
212
- if (!eventExists) {
213
- await this.runExclusive("createEventCallback", async () => {
214
- await anearEvent.createdEventCallback()
215
- await anearEvent.persist()
216
- // start the state machine before initialiing Realtime Messaging
217
- // as REFRESH events come in and the state machine should be ready
218
- // to handle those XState events
219
- anearEvent.startStateMachine()
220
- await this.initEventRealtimeMessaging(anearEvent)
221
- })
222
- } else {
223
- loadedEvent = await this.getAnearEventFromStorage(anearEvent.id)
224
- await this.initEventRealtimeMessaging(loadedEvent)
225
- loadedEvent.startStateMachine()
152
+ } catch(err) {
153
+ logger.error(err)
226
154
  }
227
-
228
- logger.info(`New ${loadedEvent.constructor.name} Event: `, loadedEvent.toJSON())
229
-
230
- this.anearEvents[loadedEvent.id] = loadedEvent
231
-
232
- return loadedEvent
233
155
  }
234
156
 
235
157
  async reloadAnyEventsInProgress(appId) {
@@ -242,11 +164,16 @@ class AnearMessaging {
242
164
  for (const eventData of events) {
243
165
  const eventJson = await this.api.getEvent(eventData.id)
244
166
  const anearEvent = new this.AnearEventClass(eventJson, this)
245
- // THIS does NOT work yet. This is logic that restarte events in progress
246
- // if the nodeJS process crahsed during play
167
+ await this.initEventRealtimeMessaging(anearEvent)
168
+
169
+ const attachedParticipants = this.getPresentParticipants(anearEvent)
170
+ // TBD: might want to change the attach and presence logic on
171
+ // the actions channel. The Ably docs show subscribing to the
172
+ // presence events on the actions channel, and instead of using History,
173
+ // it does a get() to fetch all of the current members. This behavior
174
+ // is useful for both event start, and event restart within this function
175
+ // anearEvent.startStateMachine()
247
176
  //
248
- // const loadedEvent = await this.loadOrPersistEventAndInitialize(anearEvent)
249
- // await this.refreshActiveParticipants(loadedEvent) DOES NOT WORK YET
250
177
  }
251
178
  }
252
179
  } catch (err) {
@@ -254,20 +181,6 @@ class AnearMessaging {
254
181
  }
255
182
  }
256
183
 
257
- async refreshActiveParticipants(anearEvent) {
258
- const allParticipants = anearEvent.participants.active
259
-
260
- return Promise.all(
261
- allParticipants.map(
262
- participant => this.processParticipantEnter(
263
- anearEvent,
264
- participant.id,
265
- participant.geoLocation
266
- )
267
- )
268
- )
269
- }
270
-
271
184
  async setupCreateEventChannel() {
272
185
  logger.info(`attaching to channel ${AnearCreateEventChannelName}`)
273
186
 
@@ -307,6 +220,21 @@ class AnearMessaging {
307
220
  )
308
221
  }
309
222
 
223
+ async getSpectatorCount(anearEvent) {
224
+ if (!anearEvent.allowsSpectators()) return 0
225
+
226
+ const channel = this.eventChannels[anearEvent.id].spectators
227
+ const members = await channel.presence.get()
228
+ return members.length
229
+ }
230
+
231
+ async getPresentParticipants(anearEvent) {
232
+ // returns the participant presence data for each member who is present on
233
+ // the event's actions channel
234
+ const channel = this.eventChannels[anearEvent.id].actions
235
+ return await channel.presence.get()
236
+ }
237
+
310
238
  async setupActionsChannel(anearEvent) {
311
239
  const actionsChannel = this.getChannel(anearEvent.actionsChannelName())
312
240
 
@@ -372,7 +300,9 @@ class AnearMessaging {
372
300
  await this.closeParticipant(
373
301
  anearEvent,
374
302
  participantId,
375
- (anearEvent, participant) => anearEvent.participantClose(participant)
303
+ async (anearEvent, participant) => {
304
+ await anearEvent.participantExit(participant)
305
+ }
376
306
  )
377
307
  }
378
308
 
@@ -391,7 +321,7 @@ class AnearMessaging {
391
321
  await this.processParticipantEnter(anearEvent, participantId, geoLocation)
392
322
  }
393
323
 
394
- async processParticipantEnter(anearEvent, participantId, geoLocation) {
324
+ async processParticipantEnter(anearEvent, participantId, geoLocation = null) {
395
325
 
396
326
  logger.debug(`processing Participant Enter for event: ${anearEvent.id}, participant: ${participantId}`)
397
327
  //
@@ -401,17 +331,17 @@ class AnearMessaging {
401
331
  //
402
332
  try {
403
333
  const participantJson = await this.api.getEventParticipantJson(participantId)
404
- const participant = new this.AnearParticipantClass(participantJson)
334
+ const participant = new this.AnearParticipantClass(participantJson, anearEvent)
405
335
 
406
- participant.geoLocation = geoLocation
407
-
408
- const persistedAnearParticipant = await this.AnearParticipantClass.getFromStorage(participantId)
336
+ const persistedAnearParticipant = await this.AnearParticipantClass.getFromStorage(participantId, anearEvent)
409
337
 
410
338
  if (persistedAnearParticipant) {
411
339
  participant.context = persistedAnearParticipant.context
412
340
  }
413
341
 
414
- await this.runExclusive("participantEnterCallback", async () => {
342
+ await anearEvent.runExclusive(`participantEnterCallback ${participant.id}`, async () => {
343
+ participant.geoLocation = geoLocation
344
+
415
345
  await this.setupPrivatePublishingChannel(participant)
416
346
  await anearEvent.participantEnter(participant)
417
347
  await anearEvent.update()
@@ -438,27 +368,32 @@ class AnearMessaging {
438
368
  }
439
369
 
440
370
  async participantLeaveMessagingCallback(anearEvent, message) {
441
- // this can be just a temporary leave (refresh browser for example), so pause any participant timers
371
+ // this can be just a temporary leave from a participant refreshing their browser.
372
+ // currently, no action taken
442
373
  const userId = message.clientId
443
374
  const participantId = message.data.id
444
375
 
445
376
  logger.debug(`**** LEAVE PARTICIPANT **** participantLeaveMessagingCallback(participant: ${participantId})`)
446
-
447
- this.interruptParticipantTimer(participantId)
448
377
  }
449
378
 
450
- async closeParticipant(anearEvent, participantId, callback) {
379
+ async closeParticipant(anearEvent, participantId, callback = null) {
380
+ // closes out a single Participant. This is invoked when a single
381
+ // participant leaves an event, and the event may possibly continue,
382
+ // or possibly exit. Or this may be called by the event when exiting
383
+ // and cleaning out any remaining participants.
451
384
  logger.debug(`closeParticipant(${participantId})`)
452
385
 
453
- this.destroyParticipantTimer(participantId)
454
-
455
- const participant = await this.getAnearParticipantFromStorage(participantId)
386
+ const participant = anearEvent.participants.getById(participantId)
456
387
 
457
388
  if (participant) {
389
+ participant.destroyTimer()
390
+
458
391
  await this.detachParticipantPrivateChannel(anearEvent.id, participant)
459
392
 
460
- await this.runExclusive("closeParticipant", async () => {
461
- await callback(anearEvent, participant)
393
+ await anearEvent.runExclusive(`closeParticipant ${participant.id}`, async () => {
394
+ if (callback) {
395
+ await callback(anearEvent, participant)
396
+ }
462
397
  await anearEvent.update()
463
398
  })
464
399
  }
@@ -487,16 +422,16 @@ class AnearMessaging {
487
422
  const participantId = message.data.participantId
488
423
  const payload = message.data.payload
489
424
 
490
- this.resetParticipantTimer(participantId) // participant responded in time, reset any running timer
425
+ const participant = anearEvent.participants.getById(participantId)
426
+
427
+ participant.resetTimer() // participant responded in time, reset any running timer
491
428
 
492
429
  logger.debug(`participantActionMessagingCallback(${anearEvent.id}, ${participantId})`)
493
430
 
494
431
  const actionJSON = JSON.parse(payload)
495
432
  const [actionEventName, actionPayload] = Object.entries(actionJSON)[0]
496
433
 
497
- const participant = await this.getAnearParticipantFromStorage(participantId)
498
-
499
- await this.runExclusive("participantActionCallback", async () => {
434
+ await anearEvent.runExclusive(`participantActionCallback ${participant.id}`, async () => {
500
435
  await anearEvent.participantAction(participant, actionEventName, actionPayload)
501
436
  await anearEvent.update()
502
437
  await participant.update()
@@ -526,18 +461,6 @@ class AnearMessaging {
526
461
  }
527
462
  }
528
463
 
529
- async runExclusive(name, callback) {
530
- logger.debug(`waiting for ${name} mutex`)
531
-
532
- await this.mutex.runExclusive(
533
- async () => {
534
- logger.debug(`mutex ${name} locked!`)
535
- await callback()
536
- }
537
- )
538
- logger.debug(`mutex ${name} released!`)
539
- }
540
-
541
464
  subscribeEventMessages(channel, messageType, callback) {
542
465
  channel.subscribe(messageType, callback)
543
466
  logger.debug(`subscribed to ${messageType} messages on ${channel.name}`)
@@ -579,14 +502,14 @@ class AnearMessaging {
579
502
  )
580
503
  }
581
504
 
582
- setMultipleParticipantTimers(anearEvent, participants, timeoutMsecs) {
505
+ setMultipleParticipantTimers(participants, timeoutMsecs) {
583
506
  if (timeoutMsecs === 0) return [() => {}, 0]
584
507
 
585
508
  const participantTimers = []
586
509
 
587
510
  participants.forEach(
588
511
  participant => {
589
- const [startTimer, _timeRemaining] = this.ensureParticipantTimer(anearEvent, participant, timeoutMsecs)
512
+ const [startTimer, _timeRemaining] = participant.ensureTimer(timeoutMsecs)
590
513
  participantTimers.push(startTimer)
591
514
  }
592
515
  )
@@ -598,7 +521,7 @@ class AnearMessaging {
598
521
  const eventId = anearEvent.id
599
522
  const channel = this.eventChannels[eventId].participants
600
523
 
601
- const [startTimers, timeRemaining] = this.setMultipleParticipantTimers(eventId, participants, timeoutMsecs)
524
+ const [startTimers, timeRemaining] = this.setMultipleParticipantTimers(participants, timeoutMsecs)
602
525
 
603
526
  await this.publishMessage(
604
527
  channel,
@@ -623,7 +546,7 @@ class AnearMessaging {
623
546
  const channel = this.eventChannels[anearEvent.id].privates[userId]
624
547
  if (!channel) throw new Error(`private channel not found. invalid user id ${userId}`)
625
548
 
626
- const [startTimer, timeRemaining] = this.ensureParticipantTimer(anearEvent, participant, timeoutMsecs)
549
+ const [startTimer, timeRemaining] = participant.ensureTimer(timeoutMsecs)
627
550
 
628
551
  await this.publishMessage(
629
552
  channel,
@@ -659,10 +582,9 @@ class AnearMessaging {
659
582
  )
660
583
  }
661
584
 
662
- async publishEventTransitionMessage(anearEvent, eventJson) {
585
+ async publishEventTransitionMessage(anearEvent, newState) {
663
586
  const channel = this.eventChannels[anearEvent.id].events
664
- const newState = eventJson.attributes.state
665
- const payload = {content: {state: newState, event: eventJson}}
587
+ const payload = {content: {state: newState}}
666
588
 
667
589
  logger.debug(`publishEventTransitionMessage: event ${anearEvent.id} transitioning to ${newState}`)
668
590
 
@@ -13,6 +13,10 @@ class AnearMessaging {
13
13
  async publishEventTransitionMessage(eventId, newState, callback) {
14
14
  return
15
15
  }
16
+
17
+ resetAllParticipantTimers() {
18
+ return
19
+ }
16
20
  }
17
21
 
18
22
  module.exports = AnearMessaging
@@ -1,5 +1,7 @@
1
1
  "use strict"
2
2
 
3
+ const { Mutex } = require('async-mutex')
4
+
3
5
  const AnearXstate = require('../utils/AnearXstate')
4
6
  const { DefaultConfigFunc, DefaultOptionsFunc } = require('../utils/AnearXstateDefaults')
5
7
 
@@ -13,19 +15,19 @@ class AnearEvent extends JsonApiResource {
13
15
 
14
16
  constructor(json, messaging) {
15
17
  super(json)
16
-
17
- this.messaging = messaging
18
18
  this.zone = this.findIncluded(this.relationships.zone)
19
19
  this.app = this.findIncluded(this.zone.relationships.app)
20
- this.participants = new Participants(json.participants)
20
+ this.messaging = messaging
21
21
  this.anearStateMachine = this.initStateMachine(json.previousState)
22
+ this.participants = new Participants(this, json.participants)
23
+ this.mutex = new Mutex()
22
24
  }
23
25
 
24
26
  toJSON() {
25
27
  return {
26
28
  ...super.toJSON(),
27
- context: this.stateMachineContext,
28
29
  participants: this.participants.toJSON(),
30
+ context: this.stateMachineContext,
29
31
  previousState: this.anearStateMachine.currentState
30
32
  }
31
33
  }
@@ -120,6 +122,10 @@ class AnearEvent extends JsonApiResource {
120
122
  return !this.hasFlag("no_spectators")
121
123
  }
122
124
 
125
+ async spectatorCount() {
126
+ return await this.messaging.getSpectatorCount(this)
127
+ }
128
+
123
129
  async createdEventCallback(participantCreator) {
124
130
  // You may implement createdEventCallback() in your AnearEvent sub-class
125
131
  }
@@ -136,8 +142,8 @@ class AnearEvent extends JsonApiResource {
136
142
  // You may implement an async spectatorRefreshEventCallback() in your AnearEvent sub-class
137
143
  }
138
144
 
139
- async participantCloseEventCallback(participant) {
140
- throw new Error('You must implement an async participantCloseEventCallback() in your AnearEvent sub-class');
145
+ async participantExitEventCallback(participant) {
146
+ throw new Error('You must implement an async participantExitEventCallback() in your AnearEvent sub-class');
141
147
  }
142
148
 
143
149
  async participantActionEventCallback(participant, actionEventName, message) {
@@ -173,6 +179,18 @@ class AnearEvent extends JsonApiResource {
173
179
  )
174
180
  }
175
181
 
182
+ async runExclusive(name, callback) {
183
+ logger.debug(`waiting for ${name} mutex`)
184
+
185
+ await this.mutex.runExclusive(
186
+ async () => {
187
+ logger.debug(`mutex ${name} locked!`)
188
+ await callback()
189
+ }
190
+ )
191
+ logger.debug(`mutex ${name} released!`)
192
+ }
193
+
176
194
  async publishEventSpectatorsMessage(message) {
177
195
  await this.messaging.publishEventSpectatorsMessage(this, this.css, message)
178
196
  }
@@ -203,13 +221,13 @@ class AnearEvent extends JsonApiResource {
203
221
  if (this.participants.exists(participant)) {
204
222
  logger.info(`AnearEvent: participant ${participant.id} exists. Refreshing...`)
205
223
 
206
- this.participants.add(this, participant) // update the participants entry
224
+ this.participants.add(participant) // update the participants entry
207
225
 
208
226
  this.anearStateMachine.sendRefreshEvent({ participant })
209
227
 
210
228
  await participant.update()
211
229
  } else {
212
- this.participants.add(this, participant) // add the participants entry
230
+ this.participants.add(participant) // add the participants entry
213
231
 
214
232
  this.anearStateMachine.sendJoinEvent({ participant })
215
233
 
@@ -217,11 +235,15 @@ class AnearEvent extends JsonApiResource {
217
235
  }
218
236
  }
219
237
 
220
- async participantClose(participant) {
221
- this.participants.purge(participant)
222
-
223
- this.anearStateMachine.sendCloseEvent({ participant })
238
+ async participantExit(participant) {
239
+ // this informs the state machine that the participant has exited the event
240
+ // and removes that participant completely
241
+ this.anearStateMachine.sendParticipantExitEvent({ participant })
242
+ await this.participantPurge(participant)
243
+ }
224
244
 
245
+ async participantPurge(participant) {
246
+ this.participants.purge(participant)
225
247
  await participant.remove()
226
248
  }
227
249
 
@@ -250,11 +272,10 @@ class AnearEvent extends JsonApiResource {
250
272
  logger.debug(`AnearEvent: transitionEvent(${eventName})`)
251
273
 
252
274
  try {
253
- const eventJson = await this.messaging.api.transitionEvent(this.id, eventName)
254
- const newState = eventJson.attributes.state
255
-
256
- await this.messaging.publishEventTransitionMessage(this, eventJson)
275
+ const responseAttributes = await this.messaging.api.transitionEvent(this.id, eventName)
276
+ const newState = responseAttributes.state
257
277
  this.attributes.state = newState
278
+ await this.messaging.publishEventTransitionMessage(this, newState)
258
279
  } catch(err) {
259
280
  logger.error(`AnearEvent: transitionEvent error: ${err}`)
260
281
  }
@@ -285,6 +306,34 @@ class AnearEvent extends JsonApiResource {
285
306
  await this.transitionEvent('cancel')
286
307
  }
287
308
 
309
+ async closeOutParticipants() {
310
+ // upon exiting the event, this will clean up any participants remaining
311
+ return Promise.all(
312
+ this.participants.all.map(
313
+ async p => {
314
+ const participant = await this.messaging.getAnearParticipantFromStorage(p.id)
315
+ await this.messaging.closeParticipant(
316
+ this,
317
+ participant.id,
318
+ async (anearEvent, participant) => {
319
+ await anearEvent.participantPurge(participant)
320
+ }
321
+ )
322
+ }
323
+ )
324
+ )
325
+ }
326
+
327
+ async purgeParticipants() {
328
+ // remove participants from Participants class and from Storage
329
+ const all = this.participants.all
330
+ if (this.participants.host) all.push(this.participants.host)
331
+
332
+ await Promise.all(
333
+ all.map(p => this.participantPurge(p))
334
+ )
335
+ }
336
+
288
337
  eventChannelName () {
289
338
  return this.getChannelName('event')
290
339
  }
@@ -305,6 +354,11 @@ class AnearEvent extends JsonApiResource {
305
354
  return this.attributes[`${key}-channel-name`]
306
355
  }
307
356
 
357
+ async closeEvent() {
358
+ await this.closeOutParticipants()
359
+ await this.closeMessaging()
360
+ }
361
+
308
362
  async closeMessaging () {
309
363
  await this.messaging.detachAll(this.id)
310
364
  }
@@ -318,7 +372,4 @@ class AnearEvent extends JsonApiResource {
318
372
  }
319
373
  }
320
374
 
321
- module.exports = {
322
- AnearEvent,
323
- logger
324
- }
375
+ module.exports = AnearEvent