/
mentoster
/
vk-stilus
Обзор
Документация
Войти
/
mentoster
/
vk-stilus
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
3
CI/CD
Аналитика
Безопасность
master
src/core/lifecycle-controller.ts
113 строк
4 KB
mentoster
refactor(core): migrate bounded primitives to strict TypeScript
20 июл 2026, 19:32
20 июл 2026, 19:32
1be3bfa
Код
Авторство
О чём код?
import { verifyInvariant } from './invariant.js'; import { LIMITS } from './limits.js'; import { err, ok, type Result } from './result.js'; export const LIFECYCLE_STATES = Object.freeze({ CREATED: 'created', INITIALIZED: 'initialized', ACTIVE: 'active', INACTIVE: 'inactive', DISPOSED: 'disposed', } as const); export type LifecycleState = (typeof LIFECYCLE_STATES)[keyof typeof LIFECYCLE_STATES]; export interface LifecycleTransition { readonly previous: LifecycleState; readonly next: LifecycleState; } export interface LifecycleFailure { readonly code: string; readonly owner?: string; readonly current?: LifecycleState; readonly next?: LifecycleState; readonly context?: unknown; } const ALLOWED_TRANSITIONS = Object.freeze({ created: Object.freeze([LIFECYCLE_STATES.INITIALIZED]), initialized: Object.freeze([ LIFECYCLE_STATES.ACTIVE, LIFECYCLE_STATES.INACTIVE, LIFECYCLE_STATES.DISPOSED, ]), active: Object.freeze([LIFECYCLE_STATES.INACTIVE, LIFECYCLE_STATES.DISPOSED]), inactive: Object.freeze([LIFECYCLE_STATES.ACTIVE, LIFECYCLE_STATES.DISPOSED]), disposed: Object.freeze([] as LifecycleState[]), }) satisfies Readonly<Record<LifecycleState, readonly LifecycleState[]>>; const LIFECYCLE_CONTEXT = Object.freeze({ owner: 'LifecycleController' }); /** @assertion-exception Pure lifecycle failure factory owns no mutable state. */ function transitionError( owner: string, current: LifecycleState, next: LifecycleState, ): LifecycleFailure { return Object.freeze({ code: 'LIFECYCLE_TRANSITION_REJECTED', owner, current, next }); } /** * @classdesc Enforces deterministic lifecycle states for one controller. * @responsibility Validate transitions, retain bounded history, and expose current state. * @nonresponsibility Initialize, activate, deactivate, or dispose feature resources itself. * @trustBoundary Receives requested transitions from controller orchestration. * @lifecycle created to initialized, active/inactive, then terminal disposed. * @invariant Current state is declared and history never exceeds 50 entries. * @resourceBudget 50 transition history entries. * @sideEffects Mutates only private lifecycle state and bounded history. */ export class LifecycleController { owner: string; current: LifecycleState; transitions: LifecycleTransition[]; constructor(owner: string) { this.owner = owner; this.current = LIFECYCLE_STATES.CREATED; this.transitions = []; const created = verifyInvariant( this.current === LIFECYCLE_STATES.CREATED, 'lifecycle.construct.created', LIFECYCLE_CONTEXT, ); const empty = verifyInvariant( this.transitions.length === 0, 'lifecycle.construct.empty', LIFECYCLE_CONTEXT, ); if (!created.ok || !empty.ok) { this.current = LIFECYCLE_STATES.DISPOSED; this.transitions.length = 0; } } transition(next: LifecycleState): Result<LifecycleState, LifecycleFailure> { const allowed: readonly LifecycleState[] = ALLOWED_TRANSITIONS[this.current]; if (!allowed.includes(next)) { return err(transitionError(this.owner, this.current, next)); } if (this.transitions.length >= LIMITS.HISTORY_ENTRIES) this.transitions.shift(); const previous = this.current; this.current = next; this.transitions.push(Object.freeze({ previous, next })); const current = verifyInvariant( this.current === next, 'lifecycle.transition.current', LIFECYCLE_CONTEXT, ); const history = verifyInvariant( this.transitions.length <= LIMITS.HISTORY_ENTRIES, 'lifecycle.transition.history', LIFECYCLE_CONTEXT, ); if (!current.ok) return err(current.error); return history.ok ? ok(this.current) : err(history.error); } state(): LifecycleState { return this.current; } history(): readonly LifecycleTransition[] { return Object.freeze([...this.transitions]); } }