Rule the form. Play the field.
Umpire is a declarative field-availability engine. Define fields, declare rules between them, and Umpire tells you which fields are in play β and which stale values just fell out. It answers a structural question, not a validation question: given the current values and conditions, what should be enabled right now?
Forms are the most common use case, but Umpire works anywhere state fits a plain object with interdependent options β game boards, config panels, pricing calculators, permission matrices. If it has fields and rules, Umpire can call the game.
import { requires, umpire } from '@umpire/core'
const ump = umpire({
fields: {
password: {},
confirmPassword: {},
},
rules: [requires('confirmPassword', 'password')],
})
ump.check({ password: '', confirmPassword: '' }).confirmPassword
// { enabled: false, satisfied: false, fair: true, required: false, reason: 'requires password', reasons: ['requires password'] }
ump.check({ password: 'hunter2', confirmPassword: '' }).confirmPassword
// { enabled: true, satisfied: false, fair: true, required: false, reason: null, reasons: [] }For a larger example with conditions, plan-gated fields, and play() transitions, see the Signup example in the docs.
| Package | Purpose |
|---|---|
@umpire/core |
Pure logic engine with zero runtime dependencies |
@umpire/dsl |
Pure expression DSL (expr.*, compileExpr, getExprFieldRefs) |
@umpire/react |
useUmpire() hook for React |
@umpire/solid |
useUmpire() hook for Solid |
@umpire/signals |
Signal adapter via SignalProtocol (Jotai, Preact, Alien Signals, TC39) |
@umpire/store |
Generic store adapter β bring your own getState() + subscribe(next, prev) |
@umpire/zustand |
Zustand adapter (satisfies the store contract natively) |
@umpire/redux |
Redux / Redux Toolkit adapter |
@umpire/tanstack-store |
TanStack Store adapter |
@umpire/pinia |
Pinia adapter |
@umpire/vuex |
Vuex 4 adapter |
@umpire/zod |
Availability-aware Zod schemas β disabled fields produce no errors |
@umpire/reads |
Derived read tables and read-backed rule bridges |
@umpire/write |
Create/patch write-policy checks against Umpire availability |
@umpire/drizzle |
Drizzle table hydration for Umpire fields plus write-policy re-exports |
@umpire/testing |
Invariant probes for rule configurations |
@umpire/devtools |
In-app inspector panel β scorecard, traces, foul log, graph view |
- Pure logic, zero dependencies.
- Declarative rules:
requires,disables,enabledWhen,fairWhen,oneOf. - Recommendations, not mutations:
play()suggests resets, you decide when to apply them. - Adapters for React, Solid, Zustand, Redux, TanStack Store, Pinia, Vuex, and signals.
- Debuggable:
challenge()traces why any field was ruled out,@umpire/devtoolssurfaces it visually.
npm install @umpire/corePublished packages do not require Bun to consume.
Local repo work expects:
- Node 24+
- Yarn 4
- Bun 1.2+
yarn test and yarn test:coverage use Bun under the hood, so those commands will fail if Bun is not installed.
Full docs, concepts, and examples live at https://umpire.tools/
Each published package ships a tight AGENTS.md file for cross-agent discoverability, with .claude/rules/* included as a Claude-specific compatibility surface. In this repo, AGENTS.md is canonical and CLAUDE.md plus .cursor/rules/ are compatibility symlinks.
Stable β v1.0.0
MIT