A small, dependency-free rules engine for forms, checklists, intake flows, and approval workflows. Keep business rules outside UI components, make dependent changes traceable, and model exceptions without weakening policy.
It separates two concerns:
- Effects update dependent values and return an audit trace.
- Validation returns errors and warnings, including optional overrides that require an explanation.
import { applyEffects, validate, canComplete } from 'form-rulekit';
const effects = [{
id: 'digital-delivery',
when: { field: 'digital', equals: true },
then: [{ field: 'shippingRequired', set: false }],
}];
const result = applyEffects({ digital: true, shippingRequired: true }, effects);
console.log(result.values.shippingRequired); // false
console.log(result.trace[0].ruleId); // digital-deliveryRules are ordinary data and functions, so the library works in a browser, server route, worker, or CLI. Cyclic or contradictory effects fail explicitly instead of looping forever.
Form logic tends to disappear into event handlers: one answer clears another, a warning becomes a blocker, and an exception bypasses policy with no explanation. Form Rulekit separates that work into two deterministic stages:
- Effects stabilize dependent values and record every change.
- Validation produces structured issues and evaluates explicit overrides.
This public package distills patterns developed across a year of customer intake, operational checklists, estimating, approvals, and document workflows. Examples contain synthetic data only. See the field notes.
From a clone:
npm installRequires Node.js 20 or newer. The runtime has no dependencies and includes TypeScript declarations. Registry publication is intentionally outside the scope of this reference repository.
import { validate, canComplete } from 'form-rulekit';
const rules = [{
id: 'budget-floor',
severity: 'error',
override: { allowed: true, requiresReason: true },
check: ({ budget }) => budget < 1000 && {
field: 'budget',
message: 'Budget is below the delivery minimum',
},
}];
const issues = validate({ budget: 750 }, rules);
const ready = canComplete(
{ budget: 750 },
rules,
{ 'budget-floor': 'Approved as a pilot engagement' },
);Warnings never block completion. Errors block unless their rule permits an override and any required explanation is present.
{ field, equals }{ field, notEquals }{ field, in: [...] }{ field, exists: true | false }
Effects run until values stabilize. The returned trace contains the rule, field, previous value, and next value for each mutation. Inputs are never mutated.
flowchart LR
Values["Form values"] --> Effects["Stabilize effects"]
EffectRules["Effect rules"] --> Effects
Effects --> Stable["Stable values + audit trace"]
Stable --> Validation["Run validations"]
ValidationRules["Validation rules"] --> Validation
Overrides["Explained overrides"] --> Gate["Completion gate"]
Validation --> Gate
Read the architecture and run the complete intake example.
npm install
npm run verify
node examples/intake.jsThe test suite covers chained effects, all condition operators, immutability, cycle detection, severity summaries, and explained overrides.
MIT