Getting Started
Get up and running with Inval in under 5 minutes.
Installation
Install via your preferred package manager:
npm install @blu3ph4ntom/inval
# or
bun add @blu3ph4ntom/inval
# or
pnpm add @blu3ph4ntom/inval Or use the IIFE bundle directly in a script tag:
<script src="https://unpkg.com/@blu3ph4ntom/inval/dist/inval.iife.js"></script>
<script>
const { input, node, why } = Inval
// ...
</script> Inval requires Node.js 18+ and has zero runtime dependencies.
Quick Start
Let's build a simple dependency graph. We'll compute the area of a rectangle and see how Inval handles changes.
Step 1: Import the API
import { input, node } from '@blu3ph4ntom/inval' Step 2: Create input nodes
Input nodes are leaf nodes. They hold values that external code sets.
const width = input(800)
const height = input(600)
width.get() // 800
height.get() // 600 Step 3: Create a computed node
Computed nodes declare their dependencies and compute lazily.
const area = node({
dependsOn: { w: width, h: height },
compute: ({ w, h }) => w * h
})
area.get() // 480000 — computes on first access Step 4: Change an input and observe
area.get() // 480000 — cached, zero cost
width.set(1000)
area.get() // 600000 — recomputes because width changed Tip
The key insight: area.get() returns the cached value if nothing changed. It only recomputes when a dependency is dirty.
Your First Graph
Let's build something more realistic — a dashboard layout with dependent widgets.
import { input, node, batch } from '@blu3ph4ntom/inval'
// External inputs
const dashboardWidth = input(1200)
const sidebarWidth = input(300)
// Computed: content area width
const contentWidth = node({
dependsOn: { dash: dashboardWidth, sidebar: sidebarWidth },
compute: ({ dash, sidebar }) => dash - sidebar
})
// Computed: chart width (60% of content)
const chartWidth = node({
dependsOn: { content: contentWidth },
compute: ({ content }) => content * 0.6
})
// Computed: table width (40% of content)
const tableWidth = node({
dependsOn: { content: contentWidth },
compute: ({ content }) => content * 0.4
})
// Initial values
chartWidth.get() // 540 (900 * 0.6)
tableWidth.get() // 360 (900 * 0.4)
// Change sidebar — chart and table both update
sidebarWidth.set(400)
chartWidth.get() // 480 (800 * 0.6)
tableWidth.get() // 320 (800 * 0.4)
// Atomic update: change both at once
const changed = batch(() => {
dashboardWidth.set(1400)
sidebarWidth.set(350)
})
// changed = Set of all dirtied nodes Warning
Always use batch() when setting multiple inputs at once. This ensures a single atomic update instead of cascading invalidations.