Recipe
State Management Patterns
Predictable state containers for Meridian loaders, licensing flows, and dashboard widgets.
1. Atomic License State
Wrap KeyAuth session tokens in a single immutable snapshot. Every mutation produces a new object — no partial updates leak between the loader heartbeat and the UI.
type LicenseState = Readonly<{
status: 'idle' | 'validating' | 'active' | 'expired'
token: string | null
expiry: number
}>
function reduce(state: LicenseState, event: LicenseEvent): LicenseState {
switch (event.type) {
case 'VALIDATE_START':
return { ...state, status: 'validating' }
case 'VALIDATE_OK':
return { status: 'active', token: event.token, expiry: event.expiry }
case 'VALIDATE_FAIL':
return { ...state, status: 'expired', token: null }
default:
return state
}
}2. Offline Grace Cache
When the license server is unreachable, fall back to an HMAC-signed local blob. The cache is valid for 72 hours and self-destructs if the machine fingerprint changes.
interface GraceEntry {
fingerprint: string
issued: number
ttl: number
signature: Uint8Array
}
function verifyGrace(entry: GraceEntry, key: Uint8Array): boolean {
const payload = entry.fingerprint + ':' + entry.issued + ':' + entry.ttl
return nacl.sign.detached.verify(
new TextEncoder().encode(payload),
entry.signature,
key
)
}3. Dashboard Widget Bus
Each widget subscribes to a typed event stream. The bus enforces at-most-once delivery and backpressure via a ring buffer. No global store — every widget owns its slice.
type WidgetEvent =
| { type: 'HW_FINGERPRINT_READY'; id: string }
| { type: 'LICENSE_SYNC'; status: LicenseState['status'] }
| { type: 'HEARTBEAT_TICK'; latency: number }
class WidgetBus {
private listeners = new Map<string, Set<(e: WidgetEvent) => void>>()
on(widgetId: string, fn: (e: WidgetEvent) => void) {
if (!this.listeners.has(widgetId)) this.listeners.set(widgetId, new Set())
this.listeners.get(widgetId)!.add(fn)
}
emit(e: WidgetEvent) {
for (const [, fns] of this.listeners) {
for (const fn of fns) fn(e)
}
}
}