the-rule-engine
Version:
⚙️ A small fluent DSL for conditional logic (validation, gating, etc)
91 lines (86 loc) • 2.41 kB
JavaScript
// src/index.js
export function createRuleEngine() {
const rules = [];
let thenFn = null;
let elseFn = null;
const evaluateLogic = (context) => {
let result = null;
for (const rule of rules) {
const value = rule.fn(context);
switch (rule.type) {
case 'when': result = value; break;
case 'and': result = result && value; break;
case 'or': result = result || value; break;
case 'not': result = !value; break;
}
}
return result;
};
const wrapCondition = (fnOrEngine) => {
if (typeof fnOrEngine === 'function') return fnOrEngine;
if (fnOrEngine?.evaluateLogicOnly) {
return (ctx) => fnOrEngine.evaluateLogicOnly(ctx);
}
throw new Error('Condition must be a function or a RuleEngine instance');
};
const api = {
when(fnOrEngine) {
rules.push({ type: 'when', fn: wrapCondition(fnOrEngine) });
return api;
},
and(fnOrEngine) {
rules.push({ type: 'and', fn: wrapCondition(fnOrEngine) });
return api;
},
or(fnOrEngine) {
rules.push({ type: 'or', fn: wrapCondition(fnOrEngine) });
return api;
},
not(fnOrEngine) {
rules.push({ type: 'not', fn: wrapCondition(fnOrEngine) });
return api;
},
group(cb) {
const sub = createRuleEngine();
cb(sub);
rules.push({ type: rules.length === 0 ? 'when' : 'and', fn: (ctx) => sub.evaluateLogicOnly(ctx) });
return api;
},
orGroup(cb) {
const sub = createRuleEngine();
cb(sub);
rules.push({ type: 'or', fn: (ctx) => sub.evaluateLogicOnly(ctx) });
return api;
},
andGroup(cb) {
const sub = createRuleEngine();
cb(sub);
rules.push({ type: 'and', fn: (ctx) => sub.evaluateLogicOnly(ctx) });
return api;
},
notGroup(cb) {
const sub = createRuleEngine();
cb(sub);
rules.push({ type: rules.length === 0 ? 'when' : 'and', fn: (ctx) => !sub.evaluateLogicOnly(ctx) });
return api;
},
then(fn) {
thenFn = fn;
return api;
},
otherwise(fn) {
elseFn = fn;
return api;
},
evaluate(ctx) {
const matched = evaluateLogic(ctx);
if (matched && thenFn) return thenFn(ctx);
if (!matched && elseFn) return elseFn(ctx);
return null;
},
evaluateLogicOnly(ctx) {
return evaluateLogic(ctx);
}
};
return api;
}