Skip to content

crafts69guy/rule-engine-js

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

93 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Rule Engine JS

npm version License: MIT Build Status Coverage Status

A powerful, flexible JavaScript rule engine for dynamic business logic evaluation. Build complex conditional logic with simple, readable syntax.

πŸš€ Why Rule Engine JS?

Stop hardcoding business logic. Rule Engine JS lets you define complex conditional logic as data, making your applications more flexible, maintainable, and business-friendly.

// Instead of this hardcoded logic...
if (user.age >= 18 && user.role === 'admin' && user.permissions.includes('write')) {
  return true;
}

// Write this declarative rule...
const rule = rules.and(
  rules.gte('age', 18),
  rules.eq('role', 'admin'),
  rules.in('write', 'permissions')
);

const result = engine.evaluateExpr(rule, user);

✨ Key Features

  • 🎯 Zero Dependencies - Lightweight with no external dependencies
  • ⚑ High Performance - Intelligent caching with LRU eviction
  • πŸ”’ Security First - Built-in protection against prototype pollution
  • 🧩 Dynamic Field Comparison - Compare values across different data paths
  • πŸ“ Type Safe - Full TypeScript support with comprehensive type definitions
  • πŸ”§ Extensible - Easy custom operator registration
  • πŸ“Š Monitoring - Built-in performance metrics and cache statistics

πŸ“¦ Installation

npm install rule-engine-js
yarn add rule-engine-js

🎯 Quick Start

import { createRuleEngine, createRuleHelpers } from 'rule-engine-js';

// Create engine and helpers
const engine = createRuleEngine();
const rules = createRuleHelpers();

// Your data
const user = {
  name: 'John Doe',
  age: 28,
  role: 'admin',
  email: '[email protected]',
  permissions: ['read', 'write', 'delete'],
};

// Simple rule
const isAdult = rules.gte('age', 18);
console.log(engine.evaluateExpr(isAdult, user).success); // true

// Complex rule
const canAccess = rules.and(
  rules.gte('age', 18),
  rules.eq('role', 'admin'),
  rules.validation.email('email'),
  rules.in('write', 'permissions')
);

console.log(engine.evaluateExpr(canAccess, user).success); // true

πŸ—οΈ Core Concepts

Rules are Data

Rules are simple JSON objects that describe conditions:

// This rule...
const rule = { and: [{ gte: ['age', 18] }, { eq: ['role', 'admin'] }] };

// Is equivalent to this helper syntax...
const rule = rules.and(rules.gte('age', 18), rules.eq('role', 'admin'));

Dynamic Field Comparison

Compare values from different paths in your data:

const formData = {
  password: 'secret123',
  confirmPassword: 'secret123',
  score: 85,
  maxScore: 100,
};

const rule = rules.and(
  rules.field.equals('password', 'confirmPassword'),
  rules.lt('score', 'maxScore')
);

Path Resolution

Access nested data with dot notation:

const user = {
  profile: {
    settings: {
      theme: 'dark',
      notifications: true,
    },
  },
};

const rule = rules.eq('profile.settings.theme', 'dark');

πŸ› οΈ Common Use Cases

πŸ” User Access Control
const accessRule = rules.and(
  // User must be active
  rules.isTrue('user.isActive'),

  // Either admin OR (department match AND has permission)
  rules.or(
    rules.eq('user.role', 'admin'),
    rules.and(
      rules.field.equals('user.department', 'resource.department'),
      rules.in('write', 'user.permissions')
    )
  )
);

const context = {
  user: {
    isActive: true,
    role: 'editor',
    department: 'engineering',
    permissions: ['read', 'write'],
  },
  resource: { department: 'engineering' },
};

const hasAccess = engine.evaluateExpr(accessRule, context);
πŸ’° Dynamic Pricing & Discounts
const discountRule = rules.or(
  // VIP customers with minimum order
  rules.and(rules.eq('customer.type', 'vip'), rules.gte('order.total', 100)),

  // High loyalty points
  rules.gte('customer.loyaltyPoints', 1000),

  // Large orders
  rules.gte('order.total', 200)
);

const orderData = {
  customer: { type: 'vip', loyaltyPoints: 500 },
  order: { total: 150 },
};

const eligible = engine.evaluateExpr(discountRule, orderData);
πŸ“ Form Validation
const validationRule = rules.and(
  rules.validation.required('firstName'),
  rules.validation.required('lastName'),
  rules.validation.email('email'),
  rules.validation.ageRange('age', 18, 120),
  rules.validation.minLength('password', 8),
  rules.validation.maxLength('username', 20),
  rules.field.equals('password', 'confirmPassword'),
  rules.isTrue('agreedToTerms')
);

const formData = {
  firstName: 'John',
  lastName: 'Doe',
  email: '[email protected]',
  age: 25,
  username: 'johndoe',
  password: 'secret123',
  confirmPassword: 'secret123',
  agreedToTerms: true,
};

const isValid = engine.evaluateExpr(validationRule, formData);
🏦 Loan Approval Logic
const approvalRule = rules.and(
  rules.gte('applicant.creditScore', 650),
  rules.gte('applicant.income', 50000),
  rules.lte('applicant.debtRatio', 0.4),
  rules.gte('applicant.employmentYears', 2),
  rules.between('applicant.age', [18, 70]),
  rules.in('loan.purpose', ['home', 'car', 'education'])
);

const application = {
  applicant: {
    creditScore: 720,
    income: 75000,
    debtRatio: 0.25,
    employmentYears: 3,
    age: 32,
  },
  loan: {
    amount: 250000,
    purpose: 'home',
  },
};

const approved = engine.evaluateExpr(approvalRule, application);

πŸ“š Available Operators

Category Operators Description
Comparison eq, neq, gt, gte, lt, lte Compare values with type coercion support
Logical and, or, not Combine multiple conditions
String contains, startsWith, endsWith, regex Text pattern matching
Array in, notIn Check membership in arrays
Special between, isNull, isNotNull Range and null checking
Validation email, required, ageRange, oneOf, minLength, maxLength, lengthRange, exactLength Common validation patterns

⚑ Performance Features

  • LRU Caching: Expression results and path resolutions are cached
  • Regex Compilation: Patterns are compiled once and reused
  • Metrics Tracking: Monitor performance with built-in metrics
  • Bundle Optimization: Multiple output formats (UMD, ESM, CommonJS)
// Get performance metrics
const metrics = engine.getMetrics();
console.log({
  evaluations: metrics.evaluations,
  cacheHits: metrics.cacheHits,
  avgTime: metrics.avgTime,
});

// Get cache statistics
const cacheStats = engine.getCacheStats();
console.log(cacheStats);

πŸ”’ Security Features

  • Prototype Pollution Protection: Automatically blocks dangerous paths
  • Function Access Prevention: Functions are blocked by default
  • Safe Path Resolution: Only accesses own properties
  • Configurable Security: Adjust security settings as needed
// Secure by default
const maliciousData = { __proto__: { isAdmin: true } };
engine.resolvePath(maliciousData, '__proto__.isAdmin'); // Returns undefined

// Configure security
const engine = createRuleEngine({
  allowPrototypeAccess: false, // Always false in production
  strict: true, // Enable strict type checking
  maxDepth: 10, // Prevent deep recursion
  maxOperators: 100, // Limit complexity
});

🎨 Custom Operators

Extend the engine with your own business logic:

// Register business-specific logic
engine.registerOperator('isBusinessHours', (args, context) => {
  const [timezone = 'UTC'] = args;
  const now = new Date();
  const hour = now.getUTCHours();
  return hour >= 9 && hour < 17; // 9 AM to 5 PM UTC
});

// Usage
const rule = rules.and(
  { isBusinessHours: ['America/New_York'] },
  rules.isTrue('support.available')
);

πŸ“– Documentation

πŸš€ Framework Integration

Express.js Middleware
import { createRuleEngine, createRuleHelpers } from 'rule-engine-js';

const engine = createRuleEngine();
const rules = createRuleHelpers();

function createAccessMiddleware(accessRule) {
  return (req, res, next) => {
    const result = engine.evaluateExpr(accessRule, req.user);
    if (result.success) {
      next();
    } else {
      res.status(403).json({ error: 'Access denied' });
    }
  };
}

// Usage
const adminRule = rules.eq('role', 'admin');
app.get('/admin/*', createAccessMiddleware(adminRule));
React Form Validation
import { createRuleEngine, createRuleHelpers } from 'rule-engine-js';

const engine = createRuleEngine();
const rules = createRuleHelpers();

function useFormValidation(validationRules) {
  const validateForm = (formData) => {
    const results = {};

    Object.entries(validationRules).forEach(([field, rule]) => {
      const result = engine.evaluateExpr(rule, formData);
      results[field] = {
        isValid: result.success,
        error: result.success ? null : result.error,
      };
    });

    return results;
  };

  return { validateForm };
}

// Usage
const validationRules = {
  email: rules.validation.email('email'),
  username: rules.validation.lengthRange('username', 3, 20),
  password: rules.and(
    rules.validation.minLength('password', 8),
    rules.regex('password', '(?=.*[0-9])(?=.*[a-zA-Z])')
  ),
};

πŸ§ͺ Testing

import { createRuleEngine, createRuleHelpers } from 'rule-engine-js';

describe('User Access Rules', () => {
  const engine = createRuleEngine();
  const rules = createRuleHelpers();

  test('admin has full access', () => {
    const user = { role: 'admin' };
    const rule = rules.eq('role', 'admin');

    const result = engine.evaluateExpr(rule, user);
    expect(result.success).toBe(true);
  });
});

πŸ“Š Benchmarks

Operation Speed Cache Hit Rate
Simple equality ~0.1ms 95%
Complex nested rules ~2ms 85%
Regex operations ~0.5ms 90%
Path resolution ~0.05ms 98%

Benchmarks run on Node.js 18, Intel i7, with 1000 rule evaluations

🀝 Contributing

We welcome contributions! Please see our Contributing Guide for details.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Write tests for your changes
  4. Ensure all tests pass (npm test)
  5. Commit your changes (git commit -m 'Add amazing feature')
  6. Push to the branch (git push origin feature/amazing-feature)
  7. Open a Pull Request

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

πŸ™ Acknowledgments

  • Inspired by business rule engines and decision tables
  • Built for modern JavaScript applications
  • Designed with security and performance in mind

πŸ“š Related Projects

  • JSON Schema - For data validation
  • Joi - Object schema validation
  • Yup - Schema builder for runtime value parsing

πŸ“– Read the Full Documentation | πŸš€ View Examples | πŸ’¬ Get Support

Made with ❀️ by Crafts69Guy

About

Flexible JavaScript rule engine for dynamic business logic, validation, and decision-making with JSON-based rules and built-in security.

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •