Skip to content

State Machine

The state machine module (vitamem/lifecycle/state-machine) provides pure functions for managing thread lifecycle transitions. These functions are used internally by the Vitamem facade but are also exported for direct use when you need low-level control.

All functions are imported from the main vitamem package:

import {
canTransition,
transition,
shouldCool,
shouldGoDormant,
reactivate,
InvalidTransitionError,
} from "vitamem";

The state machine enforces these transitions:

FromAllowed targets
activecooling
coolingactive, dormant
dormantclosed
closed(none)

These are defined by the VALID_TRANSITIONS constant:

const VALID_TRANSITIONS: Record<ThreadState, ThreadState[]> = {
active: ["cooling"],
cooling: ["active", "dormant"],
dormant: ["closed"],
closed: [],
};

Check whether a transition between two states is allowed without throwing.

function canTransition(from: ThreadState, to: ThreadState): boolean;
ParameterTypeDescription
fromThreadStateThe current state
toThreadStateThe desired target state

true if the transition is valid, false otherwise.

canTransition("active", "cooling"); // true
canTransition("active", "dormant"); // false -- must go through cooling
canTransition("closed", "active"); // false -- closed is terminal

Transition a thread to a new state. Returns a new Thread object with updated state and timestamps. The original thread object is not mutated.

function transition(thread: Thread, to: ThreadState): Thread;
ParameterTypeDescription
threadThreadThe thread to transition
toThreadStateThe target state

A new Thread object with:

  • state set to to
  • updatedAt set to the current time
  • State-specific timestamps updated:
    • Transition to cooling: sets coolingStartedAt
    • Transition to dormant: sets dormantAt, clears coolingStartedAt
    • Transition to closed: sets closedAt
    • Transition to active: clears coolingStartedAt

InvalidTransitionError if the transition is not allowed.

// Active -> Cooling
const cooled = transition(activeThread, "cooling");
console.log(cooled.state); // "cooling"
console.log(cooled.coolingStartedAt); // Date (just set)
// Cooling -> Dormant
const dormant = transition(cooled, "dormant");
console.log(dormant.state); // "dormant"
console.log(dormant.dormantAt); // Date (just set)
console.log(dormant.coolingStartedAt); // null (cleared)
// Invalid transition throws
try {
transition(activeThread, "dormant"); // active -> dormant not allowed
} catch (e) {
console.log(e.message); // "Invalid transition: active → dormant"
}

Determine whether an active thread should transition to cooling based on message inactivity.

function shouldCool(thread: Thread, coolingTimeoutMs: number): boolean;
ParameterTypeDescription
threadThreadThe thread to check
coolingTimeoutMsnumberMilliseconds of inactivity before cooling (default in facade: 6 hours)

true if all of these conditions are met:

  • The thread is in the active state
  • The thread has a lastMessageAt timestamp
  • The elapsed time since lastMessageAt is greater than or equal to coolingTimeoutMs

Returns false otherwise (including if the thread is not active or has no messages).

const SIX_HOURS = 6 * 60 * 60 * 1000;
// Thread with recent activity
shouldCool(recentThread, SIX_HOURS); // false
// Thread inactive for 7 hours
shouldCool(staleThread, SIX_HOURS); // true
// Thread already cooling
shouldCool(coolingThread, SIX_HOURS); // false (wrong state)

Determine whether a cooling thread should transition to dormant.

function shouldGoDormant(thread: Thread, dormantTimeoutMs: number): boolean;
ParameterTypeDescription
threadThreadThe thread to check
dormantTimeoutMsnumberMilliseconds in cooling before going dormant (default in facade: same as coolingTimeoutMs)

true if all of these conditions are met:

  • The thread is in the cooling state
  • The thread has a coolingStartedAt timestamp
  • The elapsed time since coolingStartedAt is greater than or equal to dormantTimeoutMs

Returns false otherwise.

const SIX_HOURS = 6 * 60 * 60 * 1000;
// Thread just started cooling
shouldGoDormant(justCooled, SIX_HOURS); // false
// Thread has been cooling for 7 hours
shouldGoDormant(longCooled, SIX_HOURS); // true

Reactivate a cooling thread back to active state. This is a convenience function equivalent to transition(thread, "active").

function reactivate(thread: Thread): Thread;
ParameterTypeDescription
threadThreadThe thread to reactivate (must be in cooling state)

A new Thread object with:

  • state set to "active"
  • coolingStartedAt cleared to null
  • updatedAt set to the current time

InvalidTransitionError if the thread is not in the cooling state.

// Thread is cooling, user sends a new message
const active = reactivate(coolingThread);
console.log(active.state); // "active"
console.log(active.coolingStartedAt); // null

Error class thrown when an invalid state transition is attempted.

PropertyTypeDescription
namestringAlways "InvalidTransitionError"
messagestring"Invalid transition: {from} → {to}"
import { transition, InvalidTransitionError } from "vitamem";
try {
transition(dormantThread, "active");
} catch (e) {
if (e instanceof InvalidTransitionError) {
console.log(e.message); // "Invalid transition: dormant → active"
}
}