@bimetal/rule-engine
v0.13.0
Published
Domain-agnostic rule engine: Rule<Cmd, Ctx>, RuleEngine, validation/severity. Foundation for @bimetal/calendar-rules, @bimetal/table-rules and other domain rule packs.
Maintainers
Readme
@bimetal/rule-engine
Domain-agnostic command-validation rule engine. Zero external dependencies, zero @bimetal/* dependencies. Foundation for rule packs like @bimetal/calendar-rules and any other domain rule packs.
Installation
npm install @bimetal/rule-engineWhat's Inside
Rule<Cmd, Ctx>— declarative rule withid,description, optionalappliesTofilter, andevaluate(command, context): RuleResultRuleContext<R>— context passed to rules; generic over the read-model typeRuleEngine<Cmd, Ctx>—evaluate(command, context): RuleEngineResultRuleResult—{ passed: true }or{ passed: false, severity, ruleId, message }RuleEngineResult— aggregated result witherrors[]andwarnings[]RuleSeverity—'error' | 'warning'CallerInfo— consumer-defined caller identitycreateRuleEngine(rules)— factory; filters rules byappliesToand runs them
Domain Filtering
Beyond appliesTo (command-type filter), this engine intentionally has no other filtering hooks. Domain-specific filtering (e.g. calendar's domainKey for matching events with a specific domain model) belongs in the rule's own evaluate function or in a domain-specific wrapper. See @bimetal/calendar-rules's domainRule factory for an example.
Example
import { createRuleEngine } from '@bimetal/rule-engine';
import type { Rule } from '@bimetal/rule-engine';
type CreateItem = { type: 'CreateItem'; id: string; payload: { name: string } };
type Cmd = CreateItem;
type Ctx = { readModel: { items: readonly { id: string; name: string }[] } };
const requireName: Rule<Cmd, Ctx> = {
id: 'item.requireName',
description: 'Item name must be non-empty',
appliesTo: ['CreateItem'],
evaluate(command) {
return command.payload.name.trim()
? { passed: true }
: { passed: false, severity: 'error', ruleId: 'item.requireName', message: 'name required' };
},
};
const engine = createRuleEngine<Cmd, Ctx>([requireName]);
const result = engine.evaluate(
{ type: 'CreateItem', id: 'c1', payload: { name: '' } },
{ readModel: { items: [] } },
);
console.log(result.passed); // falseReplaces
This package contains the domain-agnostic portion of the (removed) @bimetal/rules. Concrete calendar rules and withRules integration live in @bimetal/calendar-rules.
License
PolyForm Noncommercial License 1.0.0
