anear-js-api 0.5.8 → 0.5.9
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.
|
@@ -1,10 +1,49 @@
|
|
|
1
1
|
"use strict"
|
|
2
|
+
/**
|
|
3
|
+
* AnearCoreServiceMachine (ACSM)
|
|
4
|
+
*
|
|
5
|
+
* This is the top-level state machine for a single Anear App (identified by appId).
|
|
6
|
+
* It owns the lifecycle of:
|
|
7
|
+
*
|
|
8
|
+
* - Bootstrapping the app configuration from the Anear API (ANAPI)
|
|
9
|
+
* - Publishing / updating static assets (images, fonts, CSS) for that app
|
|
10
|
+
* - Loading and caching compiled Pug templates in memory
|
|
11
|
+
* - Initializing realtime messaging (Ably) for the app
|
|
12
|
+
* - Subscribing to CREATE_EVENT / LOAD_EVENT lifecycle messages for Anear Events
|
|
13
|
+
* - Spawning and tracking one AnearEventMachine (AEM) per active Anear Event
|
|
14
|
+
*
|
|
15
|
+
* High-level lifecycle:
|
|
16
|
+
*
|
|
17
|
+
* 1. Waits for UPDATE_CONTEXT to receive its own interpreter reference
|
|
18
|
+
* (so realtime callbacks can send events back into the machine).
|
|
19
|
+
*
|
|
20
|
+
* 2. Fetches app metadata from the Anear API with retry + backoff.
|
|
21
|
+
* On success, appData is cached in context.
|
|
22
|
+
*
|
|
23
|
+
* 3. Initializes realtime messaging for the given appId, and once connected:
|
|
24
|
+
* - Creates the "event creation" channel for this app
|
|
25
|
+
* - Subscribes to CREATE_EVENT and LOAD_EVENT messages
|
|
26
|
+
*
|
|
27
|
+
* 4. Runs the static-assets pipeline:
|
|
28
|
+
* - Uploads / refreshes image assets for the app
|
|
29
|
+
* - Uploads / refreshes font assets
|
|
30
|
+
* - Minifies app CSS and uploads it (using the published image/font URLs)
|
|
31
|
+
* This ensures the app's visual resources are up-to-date and available to clients.
|
|
32
|
+
*
|
|
33
|
+
* 5. Loads and compiles all Pug templates into memory, indexed by path,
|
|
34
|
+
* so AnearEventMachines can render templates quickly on state transitions.
|
|
35
|
+
*
|
|
36
|
+
* 6. Enters the steady-state "waitAnearEventLifecycleCommand" mode where it:
|
|
37
|
+
* - Listens for CREATE_EVENT / LOAD_EVENT messages from ANAPI via Ably
|
|
38
|
+
* - Starts a new AnearEventMachine per event (or rehydrates on LOAD_EVENT)
|
|
39
|
+
* - Cleans up AnearEventMachines when they signal EVENT_MACHINE_EXIT
|
|
40
|
+
*
|
|
41
|
+
* In other words: the ACSM is the always-on, per-app supervisor that makes sure
|
|
42
|
+
* the app’s assets are published, templates are loaded, realtime is connected,
|
|
43
|
+
* and that each Anear Event has its own AnearEventMachine (AEM) managing the
|
|
44
|
+
* app developer’s AppEventMachine (AppM) and any participant machines.
|
|
45
|
+
*/
|
|
2
46
|
|
|
3
|
-
// The AnearCoreServiceMachine is the highest parent state machine in the hierarchy
|
|
4
|
-
// of the AnearService HSM. It is responsible for the realtime-messaging (ably.io)
|
|
5
|
-
// and all AnearEventMachines created for each request to create a new
|
|
6
|
-
// AnearEvent for the appId provided. The developer provides their AppEventMachineFactory
|
|
7
|
-
// for App XState Machine instantiation and execution as each new Event is created
|
|
8
47
|
const { assign, createMachine, interpret } = require('xstate')
|
|
9
48
|
const logger = require('../utils/Logger')
|
|
10
49
|
const { version: anearJsApiVersion } = require('../../package.json')
|
|
@@ -1,28 +1,76 @@
|
|
|
1
1
|
"use strict"
|
|
2
|
+
/**
|
|
3
|
+
* AnearEventMachine (AEM)
|
|
4
|
+
*
|
|
5
|
+
* One AnearEventMachine instance is created per Anear Event.
|
|
6
|
+
* It is owned and started by the AnearCoreServiceMachine (ACSM) whenever the
|
|
7
|
+
* ACSM receives a CREATE_EVENT or LOAD_EVENT lifecycle command for a given app.
|
|
8
|
+
*
|
|
9
|
+
* High-level responsibilities:
|
|
10
|
+
* - Own the full lifecycle of a single event instance (from creation/load
|
|
11
|
+
* until completion or timeout).
|
|
12
|
+
* - Wire up and manage event-scoped Ably channels for:
|
|
13
|
+
* - Participant presence (join/leave)
|
|
14
|
+
* - Participant actions (button clicks, answers, moves, etc.)
|
|
15
|
+
* - Host / moderator controls (start, pause, advance, reset, end)
|
|
16
|
+
* - Public / spectator updates (rendered views, state snapshots, scores)
|
|
17
|
+
* - Drive the app developer’s AppEventMachine (AppM) using the incoming
|
|
18
|
+
* realtime messages and event state transitions.
|
|
19
|
+
* - Render Pug templates into HTML for the appropriate audience (participants,
|
|
20
|
+
* spectators, merchant/public displays) and publish them over Ably.
|
|
21
|
+
* - Persist and/or rehydrate event state via the Anear API (ANAPI) as needed.
|
|
22
|
+
*
|
|
23
|
+
* Relationship to AnearCoreServiceMachine:
|
|
24
|
+
* - Constructed by ACSM with:
|
|
25
|
+
* - `anearEvent` → the event JSON model (id, appId, metadata)
|
|
26
|
+
* - `appEventMachineFactory`
|
|
27
|
+
* - `appParticipantMachineFactory` (optional)
|
|
28
|
+
* - ACSM context fields:
|
|
29
|
+
* - `appData` (app metadata from ANAPI)
|
|
30
|
+
* - `pugTemplates` (preloaded, compiled templates)
|
|
31
|
+
* - `imageAssetsUrl` (base URL for image assets)
|
|
32
|
+
* - `fontAssetsUrl` (base URL for font assets)
|
|
33
|
+
* - `coreServiceMachine` (parent interpreter, to send events back up)
|
|
34
|
+
* - Registers itself in `context.anearEventMachines[eventId]` so the ACSM can:
|
|
35
|
+
* - Keep track of all active events for this app
|
|
36
|
+
* - Clean up this machine when it finishes
|
|
37
|
+
*
|
|
38
|
+
* CREATE vs LOAD behavior:
|
|
39
|
+
* - CREATE_EVENT:
|
|
40
|
+
* - AEM is started for a brand-new event instance.
|
|
41
|
+
* - Initializes the AppEventMachine in its initial state.
|
|
42
|
+
* - Sets up all Ably channels and subscribes to participant / host actions.
|
|
43
|
+
* - Optionally notifies ANAPI that the event has been created.
|
|
44
|
+
*
|
|
45
|
+
* - LOAD_EVENT (rehydration):
|
|
46
|
+
* - ACSM instantiates AEM with `options.rehydrate === true`.
|
|
47
|
+
* - AEM fetches or receives persisted state for this event from ANAPI.
|
|
48
|
+
* - Restores the AppEventMachine to its last known state/context.
|
|
49
|
+
* - Re-attaches Ably channel subscriptions so the event can resume live play.
|
|
50
|
+
*
|
|
51
|
+
* Event lifecycle (high-level):
|
|
52
|
+
* 1. AEM is created and started by the ACSM in response to CREATE_EVENT or LOAD_EVENT.
|
|
53
|
+
* 2. AEM configures its event-scoped Ably channels and subscriptions.
|
|
54
|
+
* 3. AEM wires realtime messages (presence, actions, host commands) into the
|
|
55
|
+
* AppEventMachine and optional participant machines.
|
|
56
|
+
* 4. On state changes, AEM:
|
|
57
|
+
* - Renders the appropriate Pug templates using `context.pugTemplates`.
|
|
58
|
+
* - Publishes updated views and state snapshots out via Ably (participants,
|
|
59
|
+
* spectators, merchant/public).
|
|
60
|
+
* - Persists any required event state back to ANAPI.
|
|
61
|
+
* 5. When the event logically ends (completed, cancelled, or timed out), AEM:
|
|
62
|
+
* - Cleans up its Ably subscriptions / channels as needed.
|
|
63
|
+
* - Sends `EVENT_MACHINE_EXIT` (with `eventId`) back to the ACSM so the
|
|
64
|
+
* parent can remove this AEM from `context.anearEventMachines`.
|
|
65
|
+
*
|
|
66
|
+
* Note:
|
|
67
|
+
* - This module is intentionally scoped to a single event and should not hold
|
|
68
|
+
* any global state across events. All per-app concerns (asset URLs, Pug cache,
|
|
69
|
+
* app metadata, etc.) live in the AnearCoreServiceMachine and are passed in
|
|
70
|
+
* via the context/options when the AEM is constructed.
|
|
71
|
+
*/
|
|
2
72
|
const logger = require('../utils/Logger')
|
|
3
73
|
|
|
4
|
-
//
|
|
5
|
-
// A N E A R E V E N T M A C H I N E
|
|
6
|
-
// Incoming Messages
|
|
7
|
-
// - Event Messages
|
|
8
|
-
// - route to appEventMachine
|
|
9
|
-
// - Participant Enter / Leave Presence Messages
|
|
10
|
-
// - route to appEventMachine
|
|
11
|
-
// - Participant Leave Presence
|
|
12
|
-
// - participant intentionally/accidentally exited event via UI or client code
|
|
13
|
-
// - Participant Actions
|
|
14
|
-
// - route to appEventMachine
|
|
15
|
-
// - route to participants[participantId].machine
|
|
16
|
-
// - route to appParticipantMachine
|
|
17
|
-
// Participant Action Timeout
|
|
18
|
-
// - route to appEventMachine
|
|
19
|
-
//
|
|
20
|
-
// Outgoing Messages
|
|
21
|
-
// - All Participants Display
|
|
22
|
-
// - All Spectators Display
|
|
23
|
-
// - Public (Merchant Location) Display
|
|
24
|
-
//
|
|
25
|
-
|
|
26
74
|
const { assign, createMachine, interpret } = require('xstate')
|
|
27
75
|
const C = require('../utils/Constants')
|
|
28
76
|
|
|
@@ -1,22 +1,99 @@
|
|
|
1
1
|
"use strict"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
2
|
+
/**
|
|
3
|
+
* AnearParticipantMachine (APM)
|
|
4
|
+
*
|
|
5
|
+
* One AnearParticipantMachine instance is created per participant in a given
|
|
6
|
+
* Anear Event. It is owned by the AnearEventMachine (AEM) and represents the
|
|
7
|
+
* server-side lifecycle and realtime plumbing for a single participant.
|
|
8
|
+
*
|
|
9
|
+
* High-level responsibilities:
|
|
10
|
+
*
|
|
11
|
+
* - Manage the participant’s event-scoped private Ably channel:
|
|
12
|
+
* - Creates and attaches to a participant-specific private channel
|
|
13
|
+
* (e.g. for per-player UI updates, prompts, and boot/shutdown messages).
|
|
14
|
+
* - Publishes PRIVATE_DISPLAY / FORCE_SHUTDOWN payloads to the client.
|
|
15
|
+
*
|
|
16
|
+
* - Bridge between the AnearEventMachine and the app’s optional
|
|
17
|
+
* appParticipantMachine:
|
|
18
|
+
* - Receives ACTION / display-related events and presence events from the AEM.
|
|
19
|
+
* - Forwards ACTION and timeout signals into the appParticipantMachine
|
|
20
|
+
* when present, so the app can implement per-participant logic.
|
|
21
|
+
* - Notifies the AEM when this participant’s machine has completed
|
|
22
|
+
* (PARTICIPANT_MACHINE_EXIT).
|
|
23
|
+
*
|
|
24
|
+
* - Enforce participant-level timeouts and activity rules:
|
|
25
|
+
* - Action timeouts:
|
|
26
|
+
* - After a PRIVATE_DISPLAY / RENDER_DISPLAY that expects input,
|
|
27
|
+
* starts a per-participant response timer.
|
|
28
|
+
* - If the timer fires before an ACTION arrives, emits PARTICIPANT_TIMEOUT
|
|
29
|
+
* back to the AEM and the appParticipantMachine.
|
|
30
|
+
* - Disconnect / reconnect:
|
|
31
|
+
* - On PARTICIPANT_DISCONNECT, enters a reconnect-grace window.
|
|
32
|
+
* - If the participant reconnects (PARTICIPANT_RECONNECT) in time,
|
|
33
|
+
* resumes either idle or the pending response state.
|
|
34
|
+
* - If not, either times out the current turn or exits the participant
|
|
35
|
+
* from the event.
|
|
36
|
+
* - Idle / cleanup:
|
|
37
|
+
* - Tracks lastSeen and can be used by the AEM/app logic to remove
|
|
38
|
+
* idle/non-responsive participants and keep the event “clean”.
|
|
39
|
+
*
|
|
40
|
+
* - Handle participant exit / removal:
|
|
41
|
+
* - PARTICIPANT_EXIT:
|
|
42
|
+
* - Voluntary exit from the participant (e.g. closing the app, leaving).
|
|
43
|
+
* - Drives cleanup of the private channel and transitions to a final state.
|
|
44
|
+
* - BOOT_PARTICIPANT:
|
|
45
|
+
* - Involuntary removal (e.g. host kicks the player).
|
|
46
|
+
* - Publishes a shutdown/boot message and then proceeds to cleanup.
|
|
47
|
+
*
|
|
48
|
+
* Relationship to other Anear components:
|
|
49
|
+
*
|
|
50
|
+
* - AnearEventMachine (AEM):
|
|
51
|
+
* - The AEM creates one APM per participant and passes:
|
|
52
|
+
* - `anearEvent` → the parent event/state machine service
|
|
53
|
+
* - `anearParticipant` → the participant model (ids, privateChannelName, etc.)
|
|
54
|
+
* - `appParticipantMachineFactory` (optional)
|
|
55
|
+
* - APM reports its termination back to the AEM via
|
|
56
|
+
* `PARTICIPANT_MACHINE_EXIT` so the AEM can clean up references.
|
|
57
|
+
*
|
|
58
|
+
* - App-level participant state machine (appParticipantMachine):
|
|
59
|
+
* - Created only if `appParticipantMachineFactory` is provided.
|
|
60
|
+
* - Encapsulates app-specific per-participant state (roles, inventory,
|
|
61
|
+
* per-player storyline, etc.)
|
|
62
|
+
* - The APM routes ACTION and timeout-related events into this machine so
|
|
63
|
+
* the app developer can implement custom behavior without worrying about
|
|
64
|
+
* Ably wiring or lifecycle plumbing.
|
|
65
|
+
*
|
|
66
|
+
* Incoming events (from AEM / system) – high level:
|
|
67
|
+
* - RENDER_DISPLAY / PRIVATE_DISPLAY:
|
|
68
|
+
* - Prompt the participant with a new view or interaction on their private channel.
|
|
69
|
+
* - Optionally include an action timeout; APM transitions into a wait state.
|
|
70
|
+
*
|
|
71
|
+
* - ACTION:
|
|
72
|
+
* - Participant’s response to a prompt (button click, answer, move, etc.).
|
|
73
|
+
* - APM clears any pending timeout, updates `lastSeen`, and forwards the event
|
|
74
|
+
* into the appParticipantMachine (if defined).
|
|
75
|
+
*
|
|
76
|
+
* - PARTICIPANT_DISCONNECT / PARTICIPANT_RECONNECT:
|
|
77
|
+
* - Presence events indicating temporary loss or restoration of connection.
|
|
78
|
+
* - APM pauses/resumes any active action timeout and decides whether to
|
|
79
|
+
* time out the participant or allow them to continue.
|
|
80
|
+
*
|
|
81
|
+
* - PARTICIPANT_EXIT / BOOT_PARTICIPANT:
|
|
82
|
+
* - Signals that the participant is done with the event (voluntarily or not).
|
|
83
|
+
* - Triggers cleanup, private channel detach, and notification back to the AEM.
|
|
84
|
+
*
|
|
85
|
+
* Lifecycle (condensed):
|
|
86
|
+
* 1. APM is created by the AEM for a specific participant.
|
|
87
|
+
* 2. It creates and attaches to the participant’s private Ably channel, then
|
|
88
|
+
* signals PARTICIPANT_MACHINE_READY back to the AEM.
|
|
89
|
+
* 3. It enters the live state, handling:
|
|
90
|
+
* - PRIVATE_DISPLAY / RENDER_DISPLAY → publish UI, optionally start timeout.
|
|
91
|
+
* - ACTION → forward to appParticipantMachine, clear timeout, go idle.
|
|
92
|
+
* - DISCONNECT / RECONNECT → manage reconnect windows and possible timeouts.
|
|
93
|
+
* - EXIT / BOOT → send shutdown messaging and clean up.
|
|
94
|
+
* 4. On final cleanup, it detaches from the private channel and notifies the AEM
|
|
95
|
+
* via PARTICIPANT_MACHINE_EXIT, then ignores any further incoming events.
|
|
96
|
+
*/
|
|
20
97
|
const logger = require('../utils/Logger')
|
|
21
98
|
const { assign, createMachine, interpret } = require('xstate')
|
|
22
99
|
const C = require('../utils/Constants')
|