zebra-report: CallFSM — mirror
This commit is contained in:
parent
0520971ea6
commit
910f92c52a
1 changed files with 87 additions and 2 deletions
|
|
@ -971,6 +971,91 @@ const remoteTileSpec = {
|
|||
},
|
||||
};
|
||||
|
||||
/* ==================================================================
|
||||
* CallFSM — the user's overall lifecycle in a space. Orchestrates
|
||||
* (but doesn't replace) the per-leg FSMs above: a Joined call has
|
||||
* exactly one SubscribeFSM and zero-to-three PublishFSMs running
|
||||
* underneath; the runtime starts/stops them on entry/exit of joined.
|
||||
*
|
||||
* idle ──ENTER──▶ connecting ──WELCOME──▶ joined ──LEAVE──▶ leaving ──DONE──▶ idle
|
||||
* ▲ │ FAILED │ ▲
|
||||
* │ ▼ │ WS_DROPPED │
|
||||
* │ idle ▼ │
|
||||
* │ reconnecting ──WELCOME──▶ joined │
|
||||
* │ │ LEAVE / FAILED │
|
||||
* │ ▼ │
|
||||
* │ leaving ─────────────────────────────┘
|
||||
* │ ▲
|
||||
* │ │ ACK
|
||||
* └─────────────────────────────────── booted ◀── BOOTED ── (any live state)
|
||||
*
|
||||
* Role lives in ctx (host / cohost / speaker / listener). ROLE_CHANGE
|
||||
* fires re-entry of joined so observers can swap mesh/sub/publish
|
||||
* topology without inventing a new state per role permutation. */
|
||||
const callSpec = {
|
||||
initial: 'idle',
|
||||
context: { code: '', handle: '', uuid: '', role: '', bootedBy: null, lastError: null },
|
||||
states: {
|
||||
idle: {
|
||||
entry: (ctx) => { ctx.uuid = ''; ctx.role = ''; ctx.bootedBy = null; },
|
||||
on: {
|
||||
ENTER: {
|
||||
target: 'connecting',
|
||||
action: (ctx, ev) => { if (ev.payload){ ctx.code = ev.payload.code || ''; ctx.handle = ev.payload.handle || ''; } },
|
||||
},
|
||||
},
|
||||
},
|
||||
connecting: {
|
||||
on: {
|
||||
WELCOME: {
|
||||
target: 'joined',
|
||||
action: (ctx, ev) => { if (ev.payload){ ctx.uuid = ev.payload.uuid || ''; ctx.role = ev.payload.role || ''; } },
|
||||
},
|
||||
FAILED: { target: 'idle', action: (ctx, ev) => { ctx.lastError = ev.payload && ev.payload.error; } },
|
||||
BOOTED: { target: 'booted', action: (ctx, ev) => { ctx.bootedBy = ev.payload && ev.payload.by; } },
|
||||
LEAVE: 'idle',
|
||||
},
|
||||
},
|
||||
joined: {
|
||||
on: {
|
||||
/* ROLE_CHANGE re-enters joined so observers get an event to act
|
||||
* on (swap mesh peers, start/stop publish, etc.) */
|
||||
ROLE_CHANGE: {
|
||||
target: 'joined',
|
||||
action: (ctx, ev) => { if (ev.payload && ev.payload.role) ctx.role = ev.payload.role; },
|
||||
},
|
||||
WS_DROPPED: 'reconnecting',
|
||||
LEAVE: 'leaving',
|
||||
BOOTED: { target: 'booted', action: (ctx, ev) => { ctx.bootedBy = ev.payload && ev.payload.by; } },
|
||||
},
|
||||
},
|
||||
reconnecting: {
|
||||
/* signal-server WebSocket dropped; the SubscribeFSM + publish
|
||||
* legs stay alive because WebRTC PCs are independent of the WS.
|
||||
* On WELCOME (a re-issued one after the WS comes back) we land
|
||||
* back in joined and ROLE_CHANGE may follow. */
|
||||
on: {
|
||||
WELCOME: {
|
||||
target: 'joined',
|
||||
action: (ctx, ev) => { if (ev.payload && ev.payload.role) ctx.role = ev.payload.role; },
|
||||
},
|
||||
FAILED: { target: 'idle', action: (ctx, ev) => { ctx.lastError = ev.payload && ev.payload.error; } },
|
||||
LEAVE: 'leaving',
|
||||
BOOTED: { target: 'booted', action: (ctx, ev) => { ctx.bootedBy = ev.payload && ev.payload.by; } },
|
||||
},
|
||||
},
|
||||
leaving: {
|
||||
on: { DONE: 'idle' },
|
||||
},
|
||||
booted: {
|
||||
/* user has been removed from the room. ACK transitions back to
|
||||
* idle so the entry screen comes back; the room's auto-rejoin
|
||||
* sessionStorage key is cleared by the runtime when entering booted. */
|
||||
on: { ACK: 'idle' },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/* ==================================================================
|
||||
* identity — ed25519 keypair, persisted in localStorage as JWK.
|
||||
*
|
||||
|
|
@ -3039,8 +3124,8 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');
|
|||
|
||||
<footer style="margin:2.2rem auto 0;font-size:0.65rem;color:#999;line-height:1.7;word-break:break-all;font-family:monospace">
|
||||
<span id="pi-seal" style="color:#777;cursor:default;user-select:none" title="">page integrity</span> · built <span class="stamp-date">2026-06-02</span><br>
|
||||
md5 <span class="stamp-md5">a3266c98f3e438ced8d92ede2203211b</span><br>
|
||||
sha256 <span class="stamp-sha">8b48fd39877e1a3a2d0dd155998489a09ca62f72eb0a59889ce9a0de4affe26a</span><br>
|
||||
md5 <span class="stamp-md5">a871b9e9438f857dfd47cbaa578c676d</span><br>
|
||||
sha256 <span class="stamp-sha">bdfaeba6af0fd5765508ab816cf3bde5ab405a0864368320ba26d952e0218a72</span><br>
|
||||
<span style="color:#bbb">hashes are of this page with these two fields zeroed — to verify, blank them and re-hash</span><br>
|
||||
<span style="color:#bbb">one self-contained file — <strong>save a copy</strong> and verify against these hashes; point at your own servers with ?signal= and ?turncred=, or <a href="host-your-own.html" style="color:#999">host your own community</a></span>
|
||||
</footer>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue