How-to
Add a phase
Adding a new FSM phase (anticipation, bonus, big-win).
Short version
/add-phase AnticipationPhase Long version
- Create the file.
src/flow/phases/AnticipationPhase.ts:import type { Phase, PhaseContext } from '../Phase'; export class AnticipationPhase implements Phase { readonly name = 'anticipation'; async enter(ctx: PhaseContext) { const teasers = ctx.stores.data.teasingReels; ctx.reels.setAnticipation(teasers); // wait for the anticipation animation to settle, then advance await ctx.fsm.transition('stopSpin'); } exit() { /* dispose anything this phase allocated */ } } - Register. In
src/composition.ts, addfsm.register(new AnticipationPhase())alongside the others. - Wire the transition in. Whichever phase precedes this one
now transitions to it. For anticipation, that's probably
SpinPhase— instead of going straight tostopSpin, it goes toanticipationwhen the server returnsteasingReels. - Test it.
tests/flow/AnticipationPhase.test.ts— instantiate the phase, mock the context, callenter, assert the presenter was called and the FSM transitioned. - Run:
pnpm typecheck && pnpm test && pnpm lint.
Don't
- Call
setTimeoutinside the phase. Usectx.ticker.schedule. - Import
pixi-reels. Talk toctx.reels(the presenter). - Loop inside the phase. Transition out and back in (or split into two phases).
- Call another phase's
enterdirectly. Usectx.fsm.transition.
The skip method
Implement skip() if the user should be able to short-circuit this phase
(Turbo / SuperTurbo). Usually that means "cancel the pending ticker handle and
transition immediately." See WinShowPhase.skip for the canonical shape.
The exit method
Implement exit() if the phase holds timers, event listeners, or transient
state. Called before the next phase's enter — and also on FSM teardown.
Make it idempotent.