@inixiative/json-rules
v1.1.2
Published
TypeScript-first JSON rules engine with intuitive syntax and detailed error messages
Maintainers
Readme
@inixiative/json-rules
A powerful, type-safe JSON-based rules engine for TypeScript/JavaScript applications. Define complex validation and business logic rules using simple JSON structures.
Installation
npm install @inixiative/json-rules
# or
yarn add @inixiative/json-rules
# or
bun add @inixiative/json-rulesFeatures
- 🎯 Type-safe: Full TypeScript support with strict type checking
- 🔧 Flexible: 22 standard operators, 8 array operators, and 8 date operators
- 🌳 Composable: Nest rules with logical operators (all/any) and conditional logic (if-then-else)
- 📊 Array validation: Rich array validation with element-wise conditions
- 📅 Date handling: Comprehensive date comparison with timezone support
- 🔍 Path-based access: Reference values from anywhere in your data structure
- 💬 Custom errors: Every rule supports custom error messages
Installation
npm install json-rules
# or
yarn add json-rules
# or
bun add json-rulesQuick Start
import { check, Operator } from 'json-rules';
// Simple rule
const rule = {
field: 'age',
operator: Operator.greaterThanEqual,
value: 18,
error: 'Must be 18 or older'
};
const result = check(rule, { age: 21 }); // returns true
const result2 = check(rule, { age: 16 }); // returns "Must be 18 or older"Operators
Standard Operators (22)
Comparison
equal- Exact equality checknotEqual- Not equal checklessThan- Less than comparisonlessThanEqual- Less than or equalgreaterThan- Greater than comparisongreaterThanEqual- Greater than or equal
Range
between- Value within range (inclusive)notBetween- Value outside range
Membership
in- Value in arraynotIn- Value not in arraycontains- Array/string contains valuenotContains- Array/string doesn't contain value
String
startsWith- String starts with valueendsWith- String ends with value
Pattern
match- Regex pattern matchnotMatch- Regex pattern doesn't match
Existence
isEmpty- Check if value is empty (null, undefined, "", [], {})notEmpty- Check if value is not emptyexists- Field exists (not undefined)notExists- Field doesn't exist (undefined)
Array Operators (8)
all- All elements match conditionany- At least one element matchesnone- No elements matchatLeast- At least X elements matchatMost- At most X elements matchexactly- Exactly X elements matchempty- Array is emptynotEmpty- Array has elements
Date Operators (8)
before- Date is before comparison dateafter- Date is after comparison dateonOrBefore- Date is on or beforeonOrAfter- Date is on or afterbetween- Date is between two datesnotBetween- Date is outside rangedayIn- Day of week is in listdayNotIn- Day of week is not in list
Timezone Handling
Date comparisons are timezone-aware:
When condition value has no timezone (e.g.,
'2025-01-20'), it's interpreted in the field's timezone:// Field: Jan 20 10:00 AM Sydney time { eventDate: '2025-01-20T10:00:00+11:00' } // Condition: on or after Jan 20 (interpreted as Jan 20 in Sydney) { dateOperator: 'onOrAfter', value: '2025-01-20' } // ✓ passesWhen condition value has timezone (e.g.,
'2025-01-20T00:00:00Z'), it's used as-is:// Condition: after midnight UTC specifically { dateOperator: 'after', value: '2025-01-20T00:00:00Z' }Fields without timezone are treated as local time (UTC offset 0)
Rule Types
Basic Rule
{
field: 'status',
operator: Operator.equal,
value: 'active'
}Logical Operators
// All conditions must pass (AND)
{
all: [
{ field: 'age', operator: Operator.greaterThanEqual, value: 18 },
{ field: 'hasLicense', operator: Operator.equal, value: true }
]
}
// At least one must pass (OR)
{
any: [
{ field: 'role', operator: Operator.equal, value: 'admin' },
{ field: 'isOwner', operator: Operator.equal, value: true }
]
}Conditional Logic (If-Then-Else)
{
if: { field: 'type', operator: Operator.equal, value: 'premium' },
then: { field: 'discount', operator: Operator.greaterThan, value: 0 },
else: { field: 'discount', operator: Operator.equal, value: 0 }
}Array Validation
{
field: 'orders',
arrayOperator: ArrayOperator.all,
condition: {
field: 'total',
operator: Operator.lessThan,
value: 1000
}
}Date Validation
{
field: 'expiryDate',
dateOperator: DateOperator.after,
value: '2024-12-31'
}Advanced Features
Path-Based Value Resolution
Compare fields against each other using paths:
{
field: 'confirmPassword',
operator: Operator.equal,
path: 'password' // Compare against another field
}Array Element Context
Use $. prefix to reference the current array element:
{
field: 'items',
arrayOperator: ArrayOperator.all,
condition: {
field: 'price',
operator: Operator.lessThan,
path: '$.maxPrice' // Reference field on current array element
}
}Custom Error Messages
Every rule supports custom error messages:
{
field: 'email',
operator: Operator.match,
value: /^[^@]+@[^@]+\.[^@]+$/,
error: 'Please enter a valid email address'
}Complex Example
const rule = {
all: [
// User must be active
{ field: 'status', operator: Operator.equal, value: 'active' },
// Age requirement
{ field: 'age', operator: Operator.between, value: [18, 65] },
// Must have at least one verified email
{
field: 'emails',
arrayOperator: ArrayOperator.any,
condition: { field: 'verified', operator: Operator.equal, value: true }
},
// Conditional premium features
{
if: { field: 'subscription', operator: Operator.equal, value: 'premium' },
then: {
field: 'features',
arrayOperator: ArrayOperator.all,
condition: { field: 'enabled', operator: Operator.equal, value: true }
}
}
]
};
const userData = {
status: 'active',
age: 25,
emails: [
{ address: '[email protected]', verified: true },
{ address: '[email protected]', verified: false }
],
subscription: 'premium',
features: [
{ name: 'advanced', enabled: true },
{ name: 'analytics', enabled: true }
]
};
const result = check(rule, userData); // returns trueAPI Reference
check(condition: Condition, data: any, context?: any): boolean | string
The main validation function.
- condition: The rule to evaluate
- data: The data to validate against
- context: Optional context (defaults to data)
- Returns:
trueif validation passes, error string if it fails
Types
type Condition = Rule | ArrayRule | DateRule | All | Any | IfThenElse | boolean;
type Rule = {
field: string;
operator: Operator;
value?: any;
path?: string;
error?: string;
};
type ArrayRule = {
field: string;
arrayOperator: ArrayOperator;
condition?: Condition;
count?: number;
error?: string;
};
type DateRule = {
field: string;
dateOperator: DateOperator;
value?: any;
path?: string;
error?: string;
};Error Handling
The engine throws errors for:
- Invalid array fields when using array operators
- Missing required parameters (e.g., count for atLeast)
- Invalid dates in date comparisons
- Primitive arrays with array operators (use
containsorininstead)
License
MIT
