first commit
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
import type { SessionState } from "../types";
|
||||
|
||||
/** 合法状态迁移表 */
|
||||
const TRANSITIONS: Record<SessionState, SessionState[]> = {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user