import type { SessionState } from "../types"; /** 合法状态迁移表 */ const TRANSITIONS: Record = { idle: ["connecting", "disconnected"], connecting: ["auth_pending", "error", "disconnected"], auth_pending: ["connected", "error", "disconnected"], connected: ["reconnecting", "disconnected", "error"], reconnecting: ["connected", "error", "disconnected"], disconnected: ["connecting", "idle"], error: ["connecting", "disconnected"], }; export function canTransition(from: SessionState, to: SessionState): boolean { return (TRANSITIONS[from] ?? []).includes(to); } export function assertTransition(from: SessionState, to: SessionState): void { if (!canTransition(from, to)) { throw new Error(`非法状态跳转: ${from} → ${to}`); } } /** * SessionMachine — 会话生命周期状态机。 * 纯状态逻辑,无任何副作用,零 DOM 依赖。 */ export class SessionMachine { private _state: SessionState = "idle"; private listeners = new Set<(state: SessionState) => void>(); get state(): SessionState { return this._state; } transition(to: SessionState): void { assertTransition(this._state, to); this._state = to; for (const fn of this.listeners) { fn(this._state); } } tryTransition(to: SessionState): boolean { if (!canTransition(this._state, to)) return false; this.transition(to); return true; } onChange(fn: (state: SessionState) => void): () => void { this.listeners.add(fn); return () => this.listeners.delete(fn); } reset(): void { this._state = "idle"; for (const fn of this.listeners) { fn(this._state); } } }