@omerrhamza/rule-engine
v0.1.6
Published
A TypeScript rule engine library with JSON Schema Draft-07 compliance and JSONPath integration
Downloads
754
Readme
JSON Rule Engine
A pure, flexible, type-safe condition evaluation engine for complex conditional logic on dynamic JSON data. Built with TypeScript, designed for escalation policies, alert routing, and complex decision-making systems.
🚀 Features
- Type-Safe: Full TypeScript support with comprehensive type definitions
- Flexible Conditions: Support for nested AND/OR logic with unlimited depth
- JSONPath Support: Access any field in your JSON data using JSONPath expressions
- Extensible: Runtime operator registration - add custom operators on the fly
- Async Operations: Built-in support for async operators (API calls, DB queries)
- Debug Mode: Detailed logging and execution traces
- Schema Validation: Validate conditions and data against JSON Schema
- Performance: JSONPath caching and optimized evaluation
- Zero Config: Works out of the box with sensible defaults
- Universal: Works in both frontend and backend
📦 Installation
npm install @omerrhamza/rule-engine
# or
yarn add @omerrhamza/rule-engine
# or
pnpm add @omerrhamza/rule-engine
# or
bun add @omerrhamza/rule-engine🎯 Quick Start
import { RuleEngine } from "rule-engine";
import type { Condition, ConditionGroup } from "rule-engine";
// Your data
const alert = {
severity: "HIGH",
host: "production-database-01",
status: "PROBLEM",
tags: ["database", "production"],
};
// Define a condition
const criticalDbCondition: ConditionGroup = {
operator: "AND",
conditions: [
{ field: "$.severity", operator: "in", value: ["HIGH", "DISASTER"] },
{ field: "$.host", operator: "contains", value: "database" },
{ field: "$.status", operator: "eq", value: "PROBLEM" },
],
};
// Create engine and evaluate
const engine = new RuleEngine();
const result = await engine.evaluate(criticalDbCondition, alert);
console.log(`Matched: ${result.matched}`);
console.log(`Execution time: ${result.executionTime}ms`);Simple Condition
// Single condition
const condition: Condition = {
field: "$.severity",
operator: "eq",
value: "HIGH",
};
const result = await engine.evaluate(condition, data);
console.log(result.matched); // true or false📚 Core Concepts
Conditions
Two types of conditions:
- Single Condition
interface Condition {
field: string; // JSONPath expression (e.g., "$.user.age")
operator: string; // Operator name (e.g., "eq", "gt")
value: unknown; // Value to compare against
}- Condition Group
interface ConditionGroup {
operator: "AND" | "OR";
conditions: Array<Condition | ConditionGroup>; // Can nest infinitely
}Evaluation Result
interface EvaluationResult {
matched: boolean; // Did the condition match?
executionTime: number; // Execution time in milliseconds
trace?: Array<{
// Debug trace (if debug mode enabled)
path: string;
operator: string;
result: boolean;
}>;
}Operators
Built-in operator categories:
- Comparison:
eq,neq,gt,gte,lt,lte,in,notIn - String:
contains,startsWith,endsWith,matches - Array:
arrayContains,arrayContainsAny,arrayContainsAll,arrayLength,arrayEmpty,arrayNotEmpty - Existence:
exists,notExists,empty,notEmpty - Type:
isString,isNumber,isBoolean,isArray,isObject,isNull - DateTime:
before,after,between,withinLast,olderThan - Async:
asyncWebhook,asyncCustom
🔧 Advanced Features
Custom Operators
Register your own operators:
engine.getRegistry().register("divisibleBy", {
execute: (fieldValue, compareValue) => {
return (
typeof fieldValue === "number" &&
typeof compareValue === "number" &&
fieldValue % compareValue === 0
);
},
metadata: {
name: "Divisible By",
description: "Check if number is divisible by another",
category: "custom",
},
});
// Use it
const condition: Condition = {
field: "$.value",
operator: "divisibleBy",
value: 5,
};Async Operators
Handle async operations like API calls or database queries:
engine.getRegistry().register("userExists", {
execute: async (userId) => {
const user = await database.findUser(userId);
return user !== null;
},
metadata: {
name: "User Exists",
description: "Check if user exists in database",
category: "async",
},
});
const condition: Condition = {
field: "$.userId",
operator: "userExists",
value: null,
};Debug Mode
Enable detailed logging:
const engine = new RuleEngine({ debug: true });
const result = await engine.evaluate(condition, data);
// Access debug logs
const logger = engine.getLogger();
const logs = logger.getLogs();
console.log(logs);Schema Validation
Validate conditions and check fields against your provider schema:
import { SchemaValidator } from "rule-engine";
const validator = new SchemaValidator();
// Validate condition structure
const validation = validator.validateConditionGroup(condition);
if (!validation.valid) {
console.error("Invalid condition:", validation.errors);
}⚙️ Configuration
const engine = new RuleEngine({
debug: false, // Enable debug logging
cacheJSONPath: true, // Cache JSONPath resolutions for performance
});🧪 Development
# Type check
bun run tsc
# Lint
bun run lint
# Format
bun run format
# Lint & format (check and fix)
bun run check:fix
# Build
bun run build
bun run build:types