Kumbatio
Quickstarts

Core

Create an energy engine, set and cycle levels, subscribe to changes, and resolve strategies - no framework required.

The core engine is a small stateful coordinator: it holds the current EnergyState, notifies subscribers on change, and resolves strategies against the current level. It runs in any JS/TS runtime.

Create an engine

import { createEnergyEngine } from '@kumbatio/energy-system'

const engine = createEnergyEngine({ initialLevel: 75 })

engine.getState()
// { level: 75, timestamp: ..., source: 'manual', revision: 0, origin: '...' }

All options are optional:

Prop

Type

Set and cycle levels

engine.setLevel(50) // source defaults to 'manual'
engine.setLevel(25, 'scheduled') // or 'inferred'

engine.cycleLevel() // 100 → 75 → 50 → 25 → 0 → 100

setLevel() is synchronous for in-memory subscribers. If a persistence adapter is configured, saving happens in the background with bounded exponential backoff.

Subscribe to changes

const unsubscribe = engine.subscribe((state, prev) => {
  console.log(`Energy: ${prev.level} -> ${state.level}`)
})

// later
unsubscribe()

Listeners receive both the new and previous state. A listener that throws is logged and does not break the others.

Resolve strategies

A strategy is a pure (level) => config mapping. engine.resolve() applies it to the current level:

import {
  uiVisibilityStrategy,
  taskComplexityStrategy,
} from '@kumbatio/energy-system'

const ui = engine.resolve(uiVisibilityStrategy)
if (!ui.sidebar) hideSidebar()

const tasks = engine.resolve(taskComplexityStrategy)
tasks.maxComplexity // 'routine' at level 50

See Strategies for the built-ins and Authoring strategies to write your own.

Persistence and cleanup

import { localStoragePersistence } from '@kumbatio/energy-system/persistence'

const engine = createEnergyEngine({
  initialLevel: 75,
  persistence: localStoragePersistence(),
})

engine.setLevel(50)
await engine.flush() // wait for durable storage (optional)

engine.dispose() // release subscriptions and observation resources

flush() rejects if the engine is disposed, or if the unchanged initial state can't be safely written because the hydration read failed - the engine never overwrites storage it couldn't read. Details in Persistence.

Deterministic testing

Inject a clock so tests control time:

let now = 0
const engine = createEnergyEngine({
  initialLevel: 100,
  clock: () => now,
  originId: 'test-engine',
})

engine.setLevel(50)
now += 60_000
engine.setLevel(25)

Full engine reference: /docs/api/core.