anear-js-api 0.3.26 → 0.3.29
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/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
|
|
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')
|
|
@@ -385,7 +385,7 @@ class AnearMessaging {
|
|
|
385
385
|
await this.closeParticipant(
|
|
386
386
|
anearEvent,
|
|
387
387
|
participantId,
|
|
388
|
-
(anearEvent, participant) => anearEvent.
|
|
388
|
+
(anearEvent, participant) => anearEvent.participantExit(participant)
|
|
389
389
|
)
|
|
390
390
|
}
|
|
391
391
|
|
|
@@ -451,7 +451,7 @@ class AnearMessaging {
|
|
|
451
451
|
}
|
|
452
452
|
|
|
453
453
|
async participantLeaveMessagingCallback(anearEvent, message) {
|
|
454
|
-
// this can be just a temporary leave (
|
|
454
|
+
// this can be just a temporary leave (a participant refreshing their browser for example), so pause any participant timers
|
|
455
455
|
const userId = message.clientId
|
|
456
456
|
const participantId = message.data.id
|
|
457
457
|
|
|
@@ -460,7 +460,11 @@ class AnearMessaging {
|
|
|
460
460
|
this.interruptParticipantTimer(participantId)
|
|
461
461
|
}
|
|
462
462
|
|
|
463
|
-
async closeParticipant(anearEvent, participantId, callback) {
|
|
463
|
+
async closeParticipant(anearEvent, participantId, callback = null) {
|
|
464
|
+
// closes out a single Participant. This is invoked when a single
|
|
465
|
+
// participant leaves an event, and the event may possibly continue,
|
|
466
|
+
// or possibly exit. Or this may be called by the event when exiting
|
|
467
|
+
// and cleaning out any remaining participants.
|
|
464
468
|
logger.debug(`closeParticipant(${participantId})`)
|
|
465
469
|
|
|
466
470
|
this.destroyParticipantTimer(participantId)
|
|
@@ -471,7 +475,9 @@ class AnearMessaging {
|
|
|
471
475
|
await this.detachParticipantPrivateChannel(anearEvent.id, participant)
|
|
472
476
|
|
|
473
477
|
await this.runExclusive("closeParticipant", async () => {
|
|
474
|
-
|
|
478
|
+
if (callback) {
|
|
479
|
+
await callback(anearEvent, participant)
|
|
480
|
+
}
|
|
475
481
|
await anearEvent.update()
|
|
476
482
|
})
|
|
477
483
|
}
|
package/lib/models/AnearEvent.js
CHANGED
|
@@ -140,8 +140,8 @@ class AnearEvent extends JsonApiResource {
|
|
|
140
140
|
// You may implement an async spectatorRefreshEventCallback() in your AnearEvent sub-class
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
async
|
|
144
|
-
throw new Error('You must implement an async
|
|
143
|
+
async participantExitEventCallback(participant) {
|
|
144
|
+
throw new Error('You must implement an async participantExitEventCallback() in your AnearEvent sub-class');
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
async participantActionEventCallback(participant, actionEventName, message) {
|
|
@@ -221,11 +221,11 @@ class AnearEvent extends JsonApiResource {
|
|
|
221
221
|
}
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
-
async
|
|
224
|
+
async participantExit(participant) {
|
|
225
|
+
// this informs the state machine that the participant has exited the event
|
|
226
|
+
// and removes that participant completely
|
|
227
|
+
this.anearStateMachine.sendParticipantExitEvent({ participant })
|
|
225
228
|
this.participants.purge(participant)
|
|
226
|
-
|
|
227
|
-
this.anearStateMachine.sendCloseEvent({ participant })
|
|
228
|
-
|
|
229
229
|
await participant.remove()
|
|
230
230
|
}
|
|
231
231
|
|
|
@@ -285,15 +285,29 @@ class AnearEvent extends JsonApiResource {
|
|
|
285
285
|
}
|
|
286
286
|
|
|
287
287
|
async transitionClosed() {
|
|
288
|
-
this.
|
|
288
|
+
await this.closeOutParticipants()
|
|
289
289
|
await this.transitionNextNext()
|
|
290
290
|
}
|
|
291
291
|
|
|
292
292
|
async transitionCanceled() {
|
|
293
|
-
this.
|
|
293
|
+
await this.closeOutParticipants()
|
|
294
294
|
await this.transitionEvent('cancel')
|
|
295
295
|
}
|
|
296
296
|
|
|
297
|
+
async closeOutParticipants() {
|
|
298
|
+
// upon exiting the event, this will clean up
|
|
299
|
+
// any participants remaining
|
|
300
|
+
return Promise.all(
|
|
301
|
+
this.participants.all.map(
|
|
302
|
+
async participant => {
|
|
303
|
+
await this.messaging.closeParticipant(this, participant.id)
|
|
304
|
+
await participant.remove()
|
|
305
|
+
this.participants.purge(participant)
|
|
306
|
+
}
|
|
307
|
+
)
|
|
308
|
+
)
|
|
309
|
+
}
|
|
310
|
+
|
|
297
311
|
eventChannelName () {
|
|
298
312
|
return this.getChannelName('event')
|
|
299
313
|
}
|
|
@@ -327,7 +341,4 @@ class AnearEvent extends JsonApiResource {
|
|
|
327
341
|
}
|
|
328
342
|
}
|
|
329
343
|
|
|
330
|
-
module.exports =
|
|
331
|
-
AnearEvent,
|
|
332
|
-
logger
|
|
333
|
-
}
|
|
344
|
+
module.exports = AnearEvent
|
package/lib/utils/AnearXstate.js
CHANGED
|
@@ -4,8 +4,9 @@ const { createMachine, interpret, State } = require('xstate')
|
|
|
4
4
|
const logger = require('../utils/Logger')
|
|
5
5
|
|
|
6
6
|
const JoinEvent = 'JOIN'
|
|
7
|
+
const ParticipantExitEvent = 'PARTICIPANT_EXIT'
|
|
7
8
|
const RefreshEvent = 'REFRESH'
|
|
8
|
-
const CloseEvent = '
|
|
9
|
+
const CloseEvent = 'EVENT_CLOSE'
|
|
9
10
|
const TimeoutEvent = 'TIMEOUT'
|
|
10
11
|
|
|
11
12
|
class AnearXstate {
|
|
@@ -50,6 +51,10 @@ class AnearXstate {
|
|
|
50
51
|
this.send(CloseEvent, params)
|
|
51
52
|
}
|
|
52
53
|
|
|
54
|
+
sendParticipantExitEvent(params) {
|
|
55
|
+
this.send(ParticipantExitEvent, params)
|
|
56
|
+
}
|
|
57
|
+
|
|
53
58
|
sendTimeoutEvent(params) {
|
|
54
59
|
this.send(TimeoutEvent, params)
|
|
55
60
|
}
|
|
@@ -35,8 +35,8 @@ const DefaultConfigFunc = (anearEvent) => {
|
|
|
35
35
|
REFRESH: {
|
|
36
36
|
target: 'refresh'
|
|
37
37
|
},
|
|
38
|
-
|
|
39
|
-
target: '
|
|
38
|
+
PARTICIPANT_EXIT: {
|
|
39
|
+
target: 'participant_exit'
|
|
40
40
|
},
|
|
41
41
|
TIMEOUT: {
|
|
42
42
|
target: 'timeout'
|
|
@@ -62,10 +62,10 @@ const DefaultConfigFunc = (anearEvent) => {
|
|
|
62
62
|
...PromiseResolveReject
|
|
63
63
|
}
|
|
64
64
|
},
|
|
65
|
-
|
|
65
|
+
participant_exit: {
|
|
66
66
|
invoke: {
|
|
67
|
-
id: '
|
|
68
|
-
src: '
|
|
67
|
+
id: 'participant_exit',
|
|
68
|
+
src: 'participantExitEventHandler',
|
|
69
69
|
...PromiseResolveReject
|
|
70
70
|
}
|
|
71
71
|
},
|
|
@@ -95,7 +95,7 @@ const DefaultOptionsFunc = anearEvent => {
|
|
|
95
95
|
services: {
|
|
96
96
|
joinEventHandler: (context, event) => anearEvent.participantEnterEventCallback(event.participant),
|
|
97
97
|
refreshEventHandler: (context, event) => anearEvent.participantRefreshEventCallback(event.participant, event.remainingTimeout),
|
|
98
|
-
|
|
98
|
+
participantExitEventHandler: (context, event) => anearEvent.participantExitEventCallback(event.participant),
|
|
99
99
|
timeoutEventHandler: (context, event) => anearEvent.participantTimedOutEventCallback(event.participant),
|
|
100
100
|
actionEventHandler: (context, event) => anearEvent.participantActionEventCallback(event.participant, event.type, event.payload)
|
|
101
101
|
}
|
package/package.json
CHANGED
package/tests/AnearEvent.test.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use strict"
|
|
2
2
|
const { assign } = require('xstate')
|
|
3
|
-
const
|
|
3
|
+
const AnearEvent = require('../lib/models/AnearEvent')
|
|
4
4
|
const AnearParticipant = require('../lib/models/AnearParticipant')
|
|
5
5
|
const MockMessaging = require('../lib/messaging/__mocks__/AnearMessaging')
|
|
6
6
|
|
|
7
7
|
const mockParticipantEnterHandler = jest.fn()
|
|
8
8
|
const mockParticipantRefreshHandler = jest.fn()
|
|
9
|
-
const
|
|
9
|
+
const mockParticipantExitHandler = jest.fn()
|
|
10
10
|
const mockParticipantActionHandler = jest.fn()
|
|
11
11
|
|
|
12
12
|
const TicTacToeMachineConfig = anearEvent => ({
|
|
@@ -30,8 +30,8 @@ const TicTacToeMachineConfig = anearEvent => ({
|
|
|
30
30
|
REFRESH: {
|
|
31
31
|
actions: 'refreshHandler'
|
|
32
32
|
},
|
|
33
|
-
|
|
34
|
-
actions: '
|
|
33
|
+
PARTICIPANT_EXIT: {
|
|
34
|
+
actions: 'participantExitHandler'
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
},
|
|
@@ -40,8 +40,8 @@ const TicTacToeMachineConfig = anearEvent => ({
|
|
|
40
40
|
BULLSEYE: {
|
|
41
41
|
actions: 'actionHandler'
|
|
42
42
|
},
|
|
43
|
-
|
|
44
|
-
actions: '
|
|
43
|
+
PARTICIPANT_EXIT: {
|
|
44
|
+
actions: 'participantExitHandler'
|
|
45
45
|
},
|
|
46
46
|
REFRESH: {
|
|
47
47
|
actions: 'refreshHandler'
|
|
@@ -59,8 +59,8 @@ const TicTacToeMachineOptions = anearEvent => ({
|
|
|
59
59
|
refreshHandler: (context, event) => {
|
|
60
60
|
anearEvent.myParticipantRefreshHandler(event.participant)
|
|
61
61
|
},
|
|
62
|
-
|
|
63
|
-
anearEvent.
|
|
62
|
+
participantExitHandler: (context, event) => {
|
|
63
|
+
anearEvent.myParticipantExitHandler(event.participant)
|
|
64
64
|
},
|
|
65
65
|
actionHandler: assign({score: (context, event) => context.score + event.payload.points}),
|
|
66
66
|
}
|
|
@@ -84,8 +84,8 @@ class TestEvent extends AnearEvent {
|
|
|
84
84
|
async myParticipantEnterHandler(...args) {
|
|
85
85
|
return mockParticipantEnterHandler(...args)
|
|
86
86
|
}
|
|
87
|
-
async
|
|
88
|
-
return
|
|
87
|
+
async myParticipantExitHandler(...args) {
|
|
88
|
+
return mockParticipantExitHandler(...args)
|
|
89
89
|
}
|
|
90
90
|
async myParticipantRefreshHandler(...args) {
|
|
91
91
|
return mockParticipantRefreshHandler(...args)
|
|
@@ -110,8 +110,8 @@ class TestEventWithDefaultXState extends AnearEvent {
|
|
|
110
110
|
return Promise.resolve()
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
participantExitEventCallback(participant) {
|
|
114
|
+
mockParticipantExitHandler(participant)
|
|
115
115
|
return Promise.resolve()
|
|
116
116
|
}
|
|
117
117
|
|
|
@@ -181,11 +181,11 @@ test('participant close with Default Xstate Config', async () => {
|
|
|
181
181
|
|
|
182
182
|
const p1 = new TestPlayer(chatParticipant1)
|
|
183
183
|
|
|
184
|
-
await t.
|
|
184
|
+
await t.participantExit(p1)
|
|
185
185
|
await t.update()
|
|
186
186
|
|
|
187
|
-
expect(
|
|
188
|
-
expect(
|
|
187
|
+
expect(mockParticipantExitHandler).toHaveBeenCalledWith(p1)
|
|
188
|
+
expect(mockParticipantExitHandler).toHaveBeenCalledTimes(1)
|
|
189
189
|
expect(t.participants.numActive(false)).toBe(0)
|
|
190
190
|
|
|
191
191
|
await t.remove()
|
|
@@ -252,14 +252,14 @@ test('can add participants, not hosted', async () => {
|
|
|
252
252
|
expect(t.participants.get(p2).name).toBe("bbondfl93")
|
|
253
253
|
expect(p2.userType).toBe("participant")
|
|
254
254
|
|
|
255
|
-
await t.
|
|
256
|
-
await t.
|
|
255
|
+
await t.participantExit(p1)
|
|
256
|
+
await t.participantExit(p2)
|
|
257
257
|
await t.update()
|
|
258
258
|
await t.remove()
|
|
259
259
|
|
|
260
|
-
expect(
|
|
261
|
-
expect(
|
|
262
|
-
expect(
|
|
260
|
+
expect(mockParticipantExitHandler).toHaveBeenCalledWith(p1)
|
|
261
|
+
expect(mockParticipantExitHandler).toHaveBeenCalledWith(p2)
|
|
262
|
+
expect(mockParticipantExitHandler).toHaveBeenCalledTimes(2)
|
|
263
263
|
expect(t.participants.numActive(false)).toBe(0)
|
|
264
264
|
})
|
|
265
265
|
|
|
@@ -291,14 +291,14 @@ test('can add participant, hosted', async () => {
|
|
|
291
291
|
expect(t.participants.numActive(false)).toBe(1)
|
|
292
292
|
expect(t.participants.get(p2).name).toBe('bbondfl93')
|
|
293
293
|
|
|
294
|
-
await t.
|
|
295
|
-
await t.
|
|
294
|
+
await t.participantExit(host)
|
|
295
|
+
await t.participantExit(p2)
|
|
296
296
|
await t.update()
|
|
297
297
|
await t.remove()
|
|
298
298
|
|
|
299
|
-
expect(
|
|
300
|
-
expect(
|
|
301
|
-
expect(
|
|
299
|
+
expect(mockParticipantExitHandler).toHaveBeenCalledWith(host)
|
|
300
|
+
expect(mockParticipantExitHandler).toHaveBeenCalledWith(p2)
|
|
301
|
+
expect(mockParticipantExitHandler).toHaveBeenCalledTimes(2)
|
|
302
302
|
expect(t.participants.numActive(false)).toBe(0)
|
|
303
303
|
})
|
|
304
304
|
|
|
@@ -327,8 +327,8 @@ test('can be retrieved back from storage with participants, not hosted', async (
|
|
|
327
327
|
expect(rehydratedPlayer1.context.name).toBe('machvee')
|
|
328
328
|
expect(rehydratedPlayer2.context.name).toBe('bbondfl93')
|
|
329
329
|
|
|
330
|
-
await rehydratedTestEvent.
|
|
331
|
-
await rehydratedTestEvent.
|
|
330
|
+
await rehydratedTestEvent.participantExit(rehydratedPlayer1)
|
|
331
|
+
await rehydratedTestEvent.participantExit(rehydratedPlayer2)
|
|
332
332
|
await rehydratedTestEvent.remove()
|
|
333
333
|
})
|
|
334
334
|
|
|
@@ -354,8 +354,9 @@ test('can update state machine context via Action events', async () => {
|
|
|
354
354
|
|
|
355
355
|
expect(t.anearStateMachine.context.score).toBe(92)
|
|
356
356
|
|
|
357
|
-
await t.
|
|
358
|
-
await t.
|
|
357
|
+
await t.participantExit(p1)
|
|
358
|
+
await t.participantExit(p2)
|
|
359
|
+
await t.closeOutParticipants()
|
|
359
360
|
await t.remove()
|
|
360
361
|
})
|
|
361
362
|
|
|
@@ -374,7 +375,7 @@ test('can reset All ParticipantTimers', async () => {
|
|
|
374
375
|
|
|
375
376
|
expect(resetMock).toHaveBeenCalledTimes(1)
|
|
376
377
|
|
|
377
|
-
await t.
|
|
378
|
-
await t.
|
|
378
|
+
await t.participantExit(p1)
|
|
379
|
+
await t.participantExit(p2)
|
|
379
380
|
await t.remove()
|
|
380
381
|
})
|