Overview
Rule Helpers provide a fluent API to build rules programmatically instead of writing JSON.
import { createRuleHelpers } from 'rule-engine-js';
const rules = createRuleHelpers();
Comparison Operators
eq()
rules.eq(field, value)
// { eq: [field, value] }
neq()
rules.neq(field, value)
// { neq: [field, value] }
Numeric Operators
gt(), gte(), lt(), lte()
rules.gt('age', 18)
// { gt: ['age', 18] }
rules.gte('score', 70)
rules.lt('price', 100)
rules.lte('quantity', 50)
Logical Operators
and()
rules.and(...conditions)
// { and: [conditions] }
Example:
rules.and(
rules.gte('age', 18),
rules.eq('role', 'admin'),
rules.in('write', 'permissions')
)
// {
// and: [
// { gte: ['age', 18] },
// { eq: ['role', 'admin'] },
// { in: ['write', 'permissions'] }
// ]
// }
or()
rules.or(...conditions)
// { or: [conditions] }
not()
rules.not(condition)
// { not: [condition] }
String Operators
contains()
rules.contains('email', '@company.com')
// { contains: ['email', '@company.com'] }
startsWith()
rules.startsWith('filename', 'report_')
// { startsWith: ['filename', 'report_'] }
endsWith()
rules.endsWith('filename', '.pdf')
// { endsWith: ['filename', '.pdf'] }
regex()
rules.regex('email', '^[a-z]+@example\\.com$')
// { regex: ['email', '^[a-z]+@example\\.com$'] }
Array Operators
in()
rules.in('role', ['admin', 'moderator', 'user'])
// { in: ['role', ['admin', 'moderator', 'user']] }
// Or check value in array field
rules.in('write', 'permissions')
// { in: ['write', 'permissions'] }
notIn()
rules.notIn('status', ['banned', 'suspended'])
// { notIn: ['status', ['banned', 'suspended']] }
Special Operators
between()
rules.between('age', [18, 65])
// { between: ['age', [18, 65]] }
isNull()
rules.isNull('middleName')
// { isNull: ['middleName'] }
isNotNull()
rules.isNotNull('email')
// { isNotNull: ['email'] }
State Operators
changed()
rules.changed('temperature')
// { changed: ['temperature'] }
changedBy()
rules.changedBy('price', 10)
// { changedBy: ['price', 10] }
changedFrom(), changedTo()
rules.changedFrom('status', 'pending')
rules.changedTo('status', 'completed')
increased(), decreased()
rules.increased('score')
rules.decreased('stock')
Field Comparison
Compare two fields:
rules.field.equals('password', 'confirmPassword')
// { eq: ['password', 'confirmPassword'] }
rules.field.lessThan('price', 'maxPrice')
// { lt: ['price', 'maxPrice'] }
rules.field.greaterThan('score', 'minScore')
// { gt: ['score', 'minScore'] }
Validation Helpers
email()
rules.validation.email('email')
// { regex: ['email', '^[\\w\\.-]+@[\\w\\.-]+\\.[a-zA-Z]{2,}$'] }
required()
rules.validation.required('username')
// { isNotNull: ['username'] }
ageRange()
rules.validation.ageRange('age', 18, 120)
// { between: ['age', [18, 120]] }
isTrue(), isFalse()
rules.isTrue('acceptedTerms')
// { eq: ['acceptedTerms', true] }
rules.isFalse('isBlocked')
// { eq: ['isBlocked', false] }
Complete Example
import { createRuleEngine, createRuleHelpers } from 'rule-engine-js';
const engine = createRuleEngine();
const rules = createRuleHelpers();
// Build complex rule
const canAccessAdminPanel = rules.and(
// Must be adult
rules.gte('age', 18),
// Must be admin or moderator
rules.or(
rules.eq('role', 'admin'),
rules.eq('role', 'moderator')
),
// Must have valid email
rules.validation.email('email'),
// Must have write permission
rules.in('write', 'permissions'),
// Must not be banned
rules.not(
rules.eq('status', 'banned')
)
);
// Evaluate
const user = {
age: 25,
role: 'admin',
email: 'admin@company.com',
permissions: ['read', 'write', 'delete'],
status: 'active'
};
const result = engine.evaluateExpr(canAccessAdminPanel, user);
console.log(result.success); // true
Comparison: JSON vs Helpers
// Verbose JSON
const rule = {
and: [
{ gte: ['age', 18] },
{ eq: ['role', 'admin'] },
{
or: [
{ contains: ['email', '@company.com'] },
{ contains: ['email', '@admin.com'] }
]
}
]
};
// Cleaner with helpers
const rule = rules.and(
rules.gte('age', 18),
rules.eq('role', 'admin'),
rules.or(
rules.contains('email', '@company.com'),
rules.contains('email', '@admin.com')
)
);
TypeScript
Basic Usage
import { createRuleHelpers } from 'rule-engine-js';
import type { RuleExpression } from 'rule-engine-js';
const rules = createRuleHelpers();
const rule: RuleExpression = rules.and(
rules.gte('age', 18),
rules.eq('role', 'admin')
);
Typed Path Autocomplete v1.0.7+
Get IDE autocomplete for object paths by passing a type parameter:
// 1. Define your context type
interface UserContext {
user: {
name: string;
age: number;
email: string;
};
order: {
total: number;
status: 'pending' | 'paid' | 'shipped';
};
}
// 2. Pass type to createRuleHelpers
const rules = createRuleHelpers<UserContext>();
// 3. Get autocomplete!
const rule = rules.and(
rules.gte('user.age', 18), // IDE suggests: user.age, order.total
rules.eq('order.status', 'paid') // IDE suggests: pending, paid, shipped
);
How it works: The generic type T enables TypeScript to infer all valid paths in your object structure and provide autocomplete suggestions.
Type-Safe Operators
Numeric operators only accept numeric paths:
interface Context {
user: { name: string; age: number };
score: number;
}
const rules = createRuleHelpers<Context>();
rules.gte('user.age', 18); // ✓ age is number
rules.gte('score', 100); // ✓ score is number
rules.gte('user.name', 5); // ✗ TypeScript error: name is string
Dynamic Array Lookup
Use string paths to reference arrays in your context:
interface Context {
value: string;
allowedValues: string[];
}
const rules = createRuleHelpers<Context>();
// Check if 'value' exists in 'allowedValues' array
rules.in('value', 'allowedValues');
Backward Compatibility
Untyped usage works exactly as before:
// No type parameter = any path allowed
const rules = createRuleHelpers();
rules.eq('any.path.here', 'value'); // ✓ works