A fast, serializable rules engine for TypeScript & JavaScript β move business logic where you need it.
Table of Contents
- Quick Start
- What is Rules Machine?
- Why Use a Rules Engine?
- Installation
- Core Concepts
- API Reference
- Examples
- Performance & Security
- Use Cases
- Contributing
- License
Rules Machine is a high-performance, general-purpose rules engine for TypeScript and JavaScript. It executes business logic defined as JSON-serializable rules, making it perfect for:
- Serverless & Edge β Deploy rules to CloudFlare Workers, Lambda@Edge, or any V8 isolate
- Shared Validation β Use the same rules on client, server, and in transit
- Dynamic Logic β Store, sync, and update rules without redeploying code
- Complex Decision Trees β Organize pricing, discounts, approvals, and workflows
pnpm add @elite-libs/rules-machine
# or
npm install @elite-libs/rules-machine
# or
yarn add @elite-libs/rules-machineimport { ruleFactory } from '@elite-libs/rules-machine';
const calculateDiscount = ruleFactory([
{ if: { and: ['price >= 25', 'price <= 50'] }, then: 'discount = 5' },
{ if: 'price > 50', then: 'discount = 10' },
{ return: 'discount' },
]);
calculateDiscount({ price: 40 }); // 5
calculateDiscount({ price: 60 }); // 10Rules engines shine when you need to:
- Store & Sync Rules β Keep rules in JSON/YAML, databases, or config services
- Manage Complexity β Isolate business rules from core application logic
- Trace & Debug β Get step-by-step execution traces for audits and debugging
- Enable Non-Devs β Let product, QA, or business teams review rule logic
- E-commerce: Tiered discounts, tax calculations, shipping rules
- Finance: Loan approvals, risk assessments, compliance checks
- Healthcare: Eligibility rules, coverage determinations, pricing tiers
- SaaS: Feature flags, usage limits, subscription tiers
| App Logic | Business Rules |
|---|---|
| Changes infrequently | Changes frequently |
| Core application behavior | Business policies & decisions |
| "Validate cart total > 0" | "Premium users get 25% off" |
| "One discount per order" | "NY residents pay NY tax" |
When to use Rules Machine:
- Rules change based on business needs (pricing, discounts, approvals)
- You need to store or sync rules across environments
- Audit trails and execution traces are required
- Non-technical stakeholders need to review logic
Creates a rule executor from JSON-serializable rules.
import { ruleFactory } from '@elite-libs/rules-machine';
const engine = ruleFactory(rules, options?);
const result = engine(input);Options:
| Option | Type | Default | Description |
|---|---|---|---|
trace |
boolean |
false |
Enable execution tracing |
ignoreMissingKeys |
boolean |
true |
Skip undefined keys vs. throw |
Returns: Function that accepts input object and returns result (or trace object if enabled).
ruleFactory([
{ if: 'score > 90', then: 'grade = "A"' },
{ if: 'score > 80', then: 'grade = "B"' },
{ else: 'grade = "C"' },
]);// Object form (short-circuits)
{ if: { and: ['age >= 18', 'age <= 65'] }, then: 'eligible = true' }
// Inline form
{ if: 'age >= 18 AND age <= 65', then: 'eligible = true' }// Map: double each item
ruleFactory([
{
map: 'numbers',
run: '$item * 2',
set: 'doubled',
},
]);
// Filter: keep only multiples of 3
ruleFactory([
{
filter: 'numbers',
run: '$item % 3 == 0',
set: 'results',
},
{ return: 'results' },
]);
// Every: check if all items match
ruleFactory([
{
every: 'numbers',
run: '$item % 3 == 0',
set: 'allMatch',
},
{ return: 'allMatch' },
]);Special variables available in array rules: $item, $index, $array.
Ends execution and returns a value.
ruleFactory([{ if: 'valid', then: 'result = "ok"' }, { return: 'result' }]);ruleFactory([
{ try: 'riskyOperation()', catch: 'status = "failed"' },
{ return: 'status' },
]);Comparison: =, ==, !=, <>, <, <=, >, >=, ~=
Arithmetic: +, -, *, /, %, ^
Logical: AND, OR (note: inline forms don't short-circuit; object forms do)
Rules Machine includes 80+ built-in functions:
AVERAGE(), CEIL(), FLOOR(), ROUND(), TRUNC(), SUM()
ADD(), SUB(), DIV(), MUL(), NEG(), MOD(), GCD()
ABS(), SQRT(), CUBEROOT(), SIGN()
ISPRIME(), ISNAN(), NOT()
LENGTH(), SLICE(), SORT(), REVERSE()
MIN(), MAX(), HEAD(), LAST(), TAIL()
TAKE(), DROP(), TAKEWHILE(), DROPWHILE()
FILTER(), MAP(), REDUCE()
CONCAT(), CONS(), JOIN(), RANGE()
CONTAINS(), INDEX()
DICT(), KEYS(), VALUES(), UNZIP(), UNZIPDICT()
CONTAINS(), COUNT_KEYS(), OMIT()
ZIP()
LOWER(), UPPER(), SPLIT(), CHAR(), CODE()
STRING_CONTAINS(), STRING_STARTS_WITH(), STRING_ENDS_WITH()
BIN2DEC(), DEC2BIN(), DEC2HEX(), HEX2DEC()
DEC2STR(), STR2DEC(), CHARARRAY()
IF(), GET(), FILTER_VALUES(), REMOVE_VALUES()
THROW()
Trigonometry & advanced math: SIN(), COS(), TAN(), ASIN(), ACOS(), ATAN(), EXP(), LN(), LOG(), LOG2(), DEGREES(), RADIANS(), and more.
[
{ "if": { "and": ["price >= 25", "price <= 50"] }, "then": "discount = 5" },
{ "if": "price > 50", "then": "discount = 10" },
{ "return": "discount" }
][
{ "if": "user.plan == \"premium\"", "then": "discount = 15" },
{ "if": "user.employee == true", "then": "discount = 15" },
{ "return": "discount" }
][
{
"if": "price <= 100",
"then": ["discount = 5", "user.discountApplied = true"]
},
{
"if": { "and": ["price >= 90", "user.discountApplied != true"] },
"then": "discount = 20"
},
{ "return": "discount" }
]const processOrders = ruleFactory([
{
filter: 'orders',
run: '$item.total > 100',
set: 'largeOrders',
},
{
map: 'largeOrders',
run: '$item.total * 0.9',
set: 'discountedTotals',
},
{
return: 'discountedTotals',
},
]);
processOrders({ orders: [{ total: 50 }, { total: 150 }, { total: 200 }] });
// [135, 180]const engine = ruleFactory(rules, { trace: true });
const result = engine({ price: 45 });
console.log(result.trace);
// [
// { operation: 'begin', startTime: ... },
// { operation: 'if', rule: 'price >= 25 AND price <= 50', result: true },
// { operation: 'evalRule', rule: 'discount = 5', value: 5 },
// { operation: 'complete', runTime: 0.42 }
// ]- No
eval()β Uses safe expression parsing viaexpressionparser - Protected keys β Blocks dangerous keys:
__proto__,constructor,prototype, etc. - Reserved fields β Prevents use of
$item,$index,$arrayin input
- Optimized for high-throughput scenarios
- Minimal overhead vs. native JavaScript
- Suitable for edge computing and serverless environments
| Industry | Use Case |
|---|---|
| E-commerce | Dynamic pricing, discount stacks, tax calculations, shipping rules |
| Finance | Loan approvals, risk scoring, compliance checks, fraud detection |
| Healthcare | Eligibility verification, coverage rules, claim adjudication |
| SaaS | Feature flags, usage limits, tier upgrades, billing logic |
| Gaming | Achievement unlocks, loot tables, match-making rules |
- Why Use a Rules Engine? β When and why to use Rules Machine
- Getting Started Guide β Step-by-step tutorial
- Examples β Real-world patterns and use cases
- Fork the repo
- Create a feature branch:
git checkout -b feature/my-feature - Commit changes:
git commit -am 'Add new feature' - Push:
git push origin feature/my-feature - Submit a PR
pnpm install
pnpm build
pnpm test
pnpm test:coverageBSD-3-Clause β See LICENSE for details.
Related Projects: