/*
lifecycle — component lifecycle state machine for the component runtime.
- Do not activate components — the lifecycle only defines states.
- RFC-1097: sweep — werkstatt-engine clean
Sweep batch 4: 73 Compass headers on headerless engine files (certification, component-runtime, isolation, evolution, testing), real KEY_DECISIONS on 75 files (kernel, cache, dht, swim, gitmesh, runtime), ~80 purpose expansions (CONTRACT-02/PURPOSE-02), non-goals on 13 CONTRACT-03 files, CS-07 history literal fix repo-wide (253 files). Policy: .template.ts/.template.astro excludedPaths. werkstatt-engine now 0 diagnostics.
*/
import type { ResolvedComponentIdentityV1 } from "@warpgogol/werkstatt-shared/component";
export type ComponentLifecycleState =
| "declared"
| "waiting"
| "loading"
| "active"
| "draining"
| "unloading"
| "disposed"
| "failed"
| "quarantined";
const VALID_TRANSITIONS: Record = {
declared: ["waiting", "failed"],
waiting: ["loading", "failed"],
loading: ["active", "failed"],
active: ["draining", "failed", "quarantined"],
draining: ["unloading", "failed", "quarantined"],
unloading: ["disposed", "failed", "quarantined"],
disposed: [],
failed: ["quarantined"],
quarantined: [],
};
export interface LifecycleTransitionResult {
readonly ok: true;
readonly from: ComponentLifecycleState;
readonly to: ComponentLifecycleState;
}
export interface LifecycleTransitionError {
readonly ok: false;
readonly from: ComponentLifecycleState;
readonly to: ComponentLifecycleState;
readonly rule: string;
readonly message: string;
}
export type LifecycleTransitionOutcome = LifecycleTransitionResult | LifecycleTransitionError;
export function isValidTransition(
from: ComponentLifecycleState,
to: ComponentLifecycleState,
): boolean {
const allowed = VALID_TRANSITIONS[from];
return allowed !== undefined && allowed.includes(to);
}
export function transition(
from: ComponentLifecycleState,
to: ComponentLifecycleState,
): LifecycleTransitionOutcome {
if (from === to) {
return {
ok: false,
from,
to,
rule: "LIFECYCLE-01",
message: `self-transition is not valid: ${from} -> ${to}`,
};
}
if (!isValidTransition(from, to)) {
return {
ok: false,
from,
to,
rule: "LIFECYCLE-02",
message: `invalid transition: ${from} -> ${to}`,
};
}
return { ok: true, from, to };
}
export function isTerminal(state: ComponentLifecycleState): boolean {
return state === "disposed" || state === "quarantined";
}
export function isActive(state: ComponentLifecycleState): boolean {
return state === "active";
}
export function isDraining(state: ComponentLifecycleState): boolean {
return state === "draining" || state === "unloading";
}
export function canAcceptNewCalls(state: ComponentLifecycleState): boolean {
return state === "active";
}
export function canCancel(state: ComponentLifecycleState): boolean {
return state === "active" || state === "draining";
}
export interface ComponentLifecycle {
readonly component: ResolvedComponentIdentityV1;
readonly state: ComponentLifecycleState;
}
export class LifecycleMachine {
private _state: ComponentLifecycleState;
readonly component: ResolvedComponentIdentityV1;
constructor(
component: ResolvedComponentIdentityV1,
initial: ComponentLifecycleState = "declared",
) {
this.component = component;
this._state = initial;
}
get state(): ComponentLifecycleState {
return this._state;
}
transitionTo(target: ComponentLifecycleState): LifecycleTransitionOutcome {
const result = transition(this._state, target);
if (result.ok) {
this._state = target;
}
return result;
}
get isTerminal(): boolean {
return isTerminal(this._state);
}
get canAcceptNewCalls(): boolean {
return canAcceptNewCalls(this._state);
}
get isDraining(): boolean {
return isDraining(this._state);
}
}