@ivandt/json-rules
v2.2.0
Published
Rule parsing engine for JSON rules
Maintainers
Readme
| Statements | Functions | Lines |
| --------------------------------------------------------------------------- | -------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
| |
|
|
JsonRules Engine
A rule engine for JavaScript & TypeScript
Define business logic with JSON rules
Overview
JsonRules is a rule engine that allows you to define business logic as JSON and evaluate it against data. It provides a simple way to externalize decision-making logic from your application code.
Use Cases
- E-commerce: Pricing rules, discount eligibility, shipping calculations
- User Management: Access control, feature flags, user segmentation
- Content: Filtering, moderation, recommendations
- Workflows: Approval processes, notifications
- Configuration: A/B testing, feature rollouts
Features
- Type-safe with full TypeScript support
- 40+ built-in operators for common operations
- Support for complex nested conditions
- Template variables for dynamic rules
- Validation for rule structure and syntax
- Works in Node.js and browsers
Installation
npm install @ivandt/json-rulesDependencies
This library has one dependency:
validator- Used for advanced validation operators (email, URL, phone numbers, etc.)
Quick Start
import { JsonRules, Rule } from "@ivandt/json-rules";
// Define a rule
const rule: Rule = {
conditions: {
all: [
{ field: "age", operator: "is greater than or equal", value: 18 },
{ field: "country", operator: "is equal", value: "US" }
]
}
};
// Evaluate against data
const user = { age: 25, country: "US" };
const result = JsonRules.evaluate(rule, user);
console.log(result); // trueOperators
JsonRules provides operators for common data operations:
Equality & Comparison
| Operator | Description | Accepts | Example |
|----------|-------------|---------|---------|
| is equal | Equal to | string \| number \| boolean \| Date \| null | { field: "status", operator: "is equal", value: "active" } |
| is not equal | Not equal to | string \| number \| boolean \| Date \| null | { field: "status", operator: "is not equal", value: "banned" } |
| is greater than | Greater than comparison | string \| number \| Date | { field: "age", operator: "is greater than", value: 18 } |
| is less than | Less than comparison | string \| number \| Date | { field: "price", operator: "is less than", value: 100 } |
| is greater than or equal | Greater than or equal | string \| number \| Date | { field: "score", operator: "is greater than or equal", value: 80 } |
| is less than or equal | Less than or equal | string \| number \| Date | { field: "items", operator: "is less than or equal", value: 10 } |
Range & Between
| Operator | Description | Accepts | Example |
|----------|-------------|---------|---------|
| is between numbers | Number within range (inclusive) | [number, number] | { field: "age", operator: "is between numbers", value: [18, 65] } |
| is not between numbers | Number outside range | [number, number] | { field: "temperature", operator: "is not between numbers", value: [32, 100] } |
| is between dates | Date within range (inclusive) | [Date, Date] | { field: "eventDate", operator: "is between dates", value: [startDate, endDate] } |
| is not between dates | Date outside range | [Date, Date] | { field: "blackoutDate", operator: "is not between dates", value: [holiday1, holiday2] } |
Collection & Array
| Operator | Description | Accepts | Example |
|----------|-------------|---------|---------|
| in | Value exists in array | (string \| number \| boolean \| object \| null)[] | { field: "country", operator: "in", value: ["US", "CA", "UK"] } |
| not in | Value not in array | (string \| number \| boolean \| object \| null)[] | { field: "status", operator: "not in", value: ["banned", "suspended"] } |
| array contains | Array field contains value | string \| number \| boolean \| object \| null | { field: "skills", operator: "array contains", value: "javascript" } |
| array no contains | Array field doesn't contain value | string \| number \| boolean \| object \| null | { field: "permissions", operator: "array no contains", value: "admin" } |
String Operations
| Operator | Description | Accepts | Example |
|----------|-------------|---------|---------|
| contains | String contains substring | string | { field: "email", operator: "contains", value: "@company.com" } |
| not contains | String doesn't contain substring | string | { field: "message", operator: "not contains", value: "spam" } |
| contains any | String contains any substring | string[] | { field: "title", operator: "contains any", value: ["urgent", "critical"] } |
| not contains any | String contains none of substrings | string[] | { field: "content", operator: "not contains any", value: ["spam", "scam"] } |
| starts with | String starts with prefix | string | { field: "productCode", operator: "starts with", value: "PRD-" } |
| ends with | String ends with suffix | string | { field: "filename", operator: "ends with", value: ".pdf" } |
Pattern Matching
| Operator | Description | Accepts | Example |
|----------|-------------|---------|---------|
| matches | Matches regex pattern | { regex: string, flags?: string } | { field: "email", operator: "matches", value: { regex: "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$" } } |
| not matches | Doesn't match regex pattern | { regex: string, flags?: string } | { field: "username", operator: "not matches", value: { regex: "^(admin\|root)$", flags: "i" } } |
Date Operations
| Operator | Description | Accepts | Example |
|----------|-------------|---------|---------|
| is before | Date is before specified date | string \| number \| Date | { field: "expiry", operator: "is before", value: new Date('2024-12-31') } |
| is after | Date is after specified date | string \| number \| Date | { field: "startDate", operator: "is after", value: new Date('2024-01-01') } |
| is on or before | Date is on or before specified date | string \| number \| Date | { field: "deadline", operator: "is on or before", value: new Date() } |
| is on or after | Date is on or after specified date | string \| number \| Date | { field: "validFrom", operator: "is on or after", value: new Date() } |
Math & Number Validation
| Operator | Description | Accepts | Example |
|----------|-------------|---------|---------|
| is even | Number is even | none | { field: "quantity", operator: "is even" } |
| is odd | Number is odd | none | { field: "productId", operator: "is odd" } |
| is positive | Number is positive (> 0) | none | { field: "balance", operator: "is positive" } |
| is negative | Number is negative (< 0) | none | { field: "adjustment", operator: "is negative" } |
| is empty | Value is null, undefined, or empty string/array | none | { field: "optionalField", operator: "is empty" } |
| is not empty | Value is not empty | none | { field: "requiredField", operator: "is not empty" } |
Data Validation
| Operator | Description | Accepts | Example |
|----------|-------------|---------|---------|
| is valid email | Valid email address | EmailValidationConfig (optional) | { field: "email", operator: "is valid email" } |
| is valid phone | Valid phone number | PhoneValidationConfig | { field: "phone", operator: "is valid phone", value: { locale: "us" } } |
| is URL | Valid URL | URLValidationConfig | { field: "website", operator: "is URL", value: { requireTld: false } } |
| is UUID | Valid UUID | UUIDValidationConfig | { field: "id", operator: "is UUID", value: { version: 4 } } |
| is EAN | Valid EAN barcode | none | { field: "barcode", operator: "is EAN" } |
| is IMEI | Valid IMEI number | IMEIValidationConfig | { field: "deviceId", operator: "is IMEI", value: { allowHyphens: true } } |
| is unit | Valid unit of measurement | UnitType | { field: "distance", operator: "is unit", value: "length" } |
| is country | Valid country identifier | CountryValidationConfig | { field: "country", operator: "is country", value: { format: "iso2" } } |
| is domain | Valid domain name | DomainValidationConfig | { field: "domain", operator: "is domain", value: { requireTld: true } } |
Rule Structure
Basic Rule
interface Rule {
conditions: Condition | Condition[];
default?: any;
}Conditions
Rules support three logical operators:
| Type | Logic | Description |
|------|-------|-------------|
| all | AND | All constraints must be true |
| any | OR | At least one constraint must be true |
| none | NOT | No constraints should be true |
Constraints
{
field: string, // Property path (supports dot notation)
operator: string, // Comparison operator
value: any // Expected value or template reference
}Examples
Basic Example
const rule: Rule = {
conditions: {
all: [
{ field: "age", operator: "is greater than or equal", value: 21 },
{ field: "country", operator: "in", value: ["US", "CA"] }
]
}
};
const user = { age: 25, country: "US" };
const result = JsonRules.evaluate(rule, user); // trueComplex Conditions
const complexRule: Rule = {
conditions: {
any: [
{
all: [
{ field: "membershipTier", operator: "is equal", value: "premium" },
{ field: "accountAge", operator: "is greater than", value: 365 }
]
},
{
all: [
{ field: "totalSpent", operator: "is greater than", value: 1000 },
{ field: "lastPurchase", operator: "is after", value: new Date('2024-01-01') }
]
}
]
}
};Validation Operators
const validationRule: Rule = {
conditions: {
all: [
{ field: "email", operator: "is valid email", value: null },
{ field: "phone", operator: "is valid phone", value: { locale: "us" } },
{ field: "website", operator: "is URL", value: { protocols: ["https"] } }
]
}
};Template Variables
const dynamicRule: Rule = {
conditions: {
all: [
{ field: "endDate", operator: "is after", value: "{startDate}" },
{ field: "price", operator: "is less than", value: "{maxBudget}" }
]
}
};API Reference
JsonRules.evaluate()
static evaluate<T>(
rule: Rule,
criteria: object | object[],
trustRule?: boolean
): T | booleanJsonRules.validate()
static validate(rule: Rule): ValidationResultValidationResult
interface ValidationResult {
isValid: boolean;
error?: ValidationError;
}Phone Number Validation
To use phone validation, import the specific locale validators:
// Import specific locales
import "@ivandt/json-rules/validators/phone/us";
import "@ivandt/json-rules/validators/phone/gb";
import "@ivandt/json-rules/validators/phone/de";
const rule: Rule = {
conditions: {
all: [
{ field: "phone", operator: "is valid phone", value: { locale: "us" } }
]
}
};Contributing
- Fork the repository
- Create a feature branch:
git checkout -b feature/new-feature - Make your changes with tests
- Run the test suite:
npm test - Submit a pull request
License
MIT License - see LICENSE file for details.
