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
- // A N E A R P A R T I C I P A N T M A C H I N E
4
- //
5
- // Incoming Messages
6
- // - Response (ACTION) Messages routed from AnearEventMachine
7
- // - route to appParticipantMachine
8
- //
9
- // Timeouts
10
- // 1. PARTICIPANT_DISCONNECT. Possibly a brief outage and will return. Give them 60 seconds
11
- // to PARTICIPANT_RECONNECT otherwise cleanupAndExit
12
- // 2. ACTION timeout (> 0 msecs)
13
- // - participants channel - group response timeout ... all participants should have responded
14
- // by this time. The participants response is likely nullified, or could be fatal and the participant is terminated
15
- // - participant private channel - waiting for a players turn in a game. The App may decide to
16
- // nullify the participants turn and continue, or consider it fatal and end the event/participant is terminated
17
- // 3. idle. If participant doesn't voluntarily interact with the event with an ACTION, or a client update presence, the
18
- // participant may be deemed idle/missing and terminated. This whe a client holds the game open in browser, but does
19
- // not interact. This can be used to keep the game free of uninterested participants
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')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anear-js-api",
3
- "version": "0.5.8",
3
+ "version": "0.5.9",
4
4
  "description": "Javascript Developer API for Anear Apps",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {