npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

decision-core

v0.0.8-alpha.0

Published

The ultimate JSON-based rule engine for Node.js that turns complex business logic into declarative configurations. Built for backend developers who believe code should be expressive, not repetitive.

Readme


Transform complex business logic into elegant, maintainable JSON rules. Stop hardcoding decisions, start building intelligent systems.

// From this mess...
if (user.tier === 'vip' && order.total > 100 && user.country === 'US') {
  return { discount: 0.20, shipping: 'free' };
} else if (user.isNew && order.total > 50) {
  return { discount: 0.10, shipping: 'standard' };
} // ... 50 more lines

// To this elegance...
const result = await RuleEngine.evaluate(discountRules, { user, order });

🚀 Why Rule Engine?

Built for Modern Developers

  • 🎯 Zero Dependencies - No supply chain bloat, just pure JavaScript excellence
  • 🏎️ Lightning Fast - 17,000+ rule evaluations per second with complex JSONPath support at 55,000+ ops/sec
  • 🛡️ TypeScript Native - Built-in generics for bulletproof type safety
  • 🌐 Universal - Node.js, browsers, edge functions, Deno, Bun - everywhere JavaScript runs

Powerful Yet Intuitive

  • 🔍 JSONPath Support - Navigate complex objects: $.user.profile.settings.theme
  • 🔗 Self-Referencing - Dynamic field references: "value": "$.maxPrice"
  • 🧩 121+ Operators - From basic comparisons to advanced pattern matching
  • 🏗️ Fluent Builder - Construct rules programmatically with intuitive chains

Enterprise Ready

  • 🔧 Extensible Core - Plugin custom operators without touching internals
  • 📊 Rule Introspection - Reverse-engineer possible inputs from rule definitions
  • Performance Optimized - Optional validation bypass for trusted rules
  • 🎭 Data Mutations - Preprocess data before evaluation

🎬 Quick Start

npm install @usex/rule-engine

Your First Rule in 30 Seconds

import { RuleEngine } from '@usex/rule-engine';

// Define a discount rule
const discountRule = {
  conditions: [
    {
      // VIP customers get 20% off orders over $100
      and: [
        { field: "$.customer.tier", operator: "equals", value: "vip" },
        { field: "$.order.total", operator: "greater-than", value: 100 }
      ],
      result: { discount: 0.20, message: "VIP discount applied! 🎉" }
    },
    {
      // First-time buyers get 10% off orders over $50
      and: [
        { field: "$.customer.orderCount", operator: "equals", value: 0 },
        { field: "$.order.total", operator: "greater-than", value: 50 }
      ],
      result: { discount: 0.10, message: "Welcome! First order discount 🎁" }
    }
  ],
  default: { discount: 0, message: "No discount available" }
};

// Apply the rule
const orderData = {
  customer: { tier: "vip", orderCount: 5 },
  order: { total: 150, items: ["laptop", "mouse"] }
};

const result = await RuleEngine.evaluate(discountRule, orderData);
console.log(result);
// { value: { discount: 0.20, message: "VIP discount applied! 🎉" }, isPassed: true }

🏗️ Core Concepts

Rules Structure

Every rule follows this pattern:

interface Rule<T = any> {
  conditions: Condition<T> | Condition<T>[];  // What to check
  default?: T;                                // Fallback result
}

Conditions: Your Logic Building Blocks

interface Condition<T = any> {
  and?: Array<Constraint | Condition<T>>;   // ALL must match
  or?: Array<Constraint | Condition<T>>;    // ANY must match
  none?: Array<Constraint | Condition<T>>;  // NONE must match
  result?: T;                               // What to return when matched
}

Constraints: The Evaluation Units

interface Constraint {
  field: string;      // Path to the data (supports JSONPath)
  operator: string;   // How to compare
  value: any;         // What to compare against
  message?: string;   // Optional validation message
}

🛠️ API Reference

Static Methods (Recommended)

| Method | Description | Returns | |--------|-------------|---------| | RuleEngine.evaluate(rule, data, trustRule?) | Full evaluation with metadata | Promise<EvaluationResult<T>> | | RuleEngine.checkIsPassed(rule, data, trustRule?) | Quick boolean check | Promise<boolean> | | RuleEngine.getEvaluateResult(rule, data, trustRule?) | Just the result value | Promise<T> | | RuleEngine.evaluateMany(rules, data, trustRule?) | Batch evaluation | Promise<EvaluationResult<T>[]> | | RuleEngine.validate(rule) | Validate rule structure | ValidationResult | | RuleEngine.introspect(rule) | Analyze rule requirements | IntrospectionResult | | RuleEngine.builder() | Get fluent builder | RuleBuilder |

Instance Methods

const engine = new RuleEngine();
// All static methods available as instance methods
await engine.evaluate(rule, data);

🔧 Operators Showcase

String & Text

// Basic string operations
{ field: "name", operator: "equals", value: "John" }
{ field: "email", operator: "like", value: "*@gmail.com" }
{ field: "description", operator: "matches", value: "^Product.*" }

// Validation
{ field: "email", operator: "email", value: true }
{ field: "url", operator: "url", value: true }
{ field: "uuid", operator: "uuid", value: true }

Numbers & Ranges

// Comparisons
{ field: "age", operator: "greater-than", value: 18 }
{ field: "price", operator: "between", value: [10, 100] }

// Types
{ field: "score", operator: "integer", value: true }
{ field: "rating", operator: "positive", value: true }

Arrays & Collections

// Membership
{ field: "roles", operator: "contains", value: "admin" }
{ field: "status", operator: "in", value: ["active", "pending"] }
{ field: "tags", operator: "contains-all", value: ["urgent", "review"] }
{ field: "features", operator: "contains-any", value: ["premium", "beta"] }

Date & Time

// Date comparisons
{ field: "birthDate", operator: "date-after", value: "1990-01-01" }
{ field: "expiryDate", operator: "date-before-now", value: true }
{ field: "createdAt", operator: "date-between", value: ["2023-01-01", "2023-12-31"] }

// Time comparisons
{ field: "openTime", operator: "time-after", value: "09:00" }
{ field: "closeTime", operator: "time-before", value: "17:30" }

Existence & Nullability

// Existence checks
{ field: "optional", operator: "exists", value: true }
{ field: "deprecated", operator: "not-exists", value: true }

// Null checks
{ field: "data", operator: "null-or-undefined", value: false }
{ field: "config", operator: "not-empty", value: true }

Length & Size

// String length
{ field: "password", operator: "min-length", value: 8 }
{ field: "username", operator: "max-length", value: 20 }
{ field: "code", operator: "string-length", value: 6 }
{ field: "description", operator: "length-between", value: [10, 500] }

🎯 Real-World Examples

🛒 E-commerce Pricing Engine

const pricingRules = {
  conditions: [
    {
      // Black Friday: 50% off everything
      and: [
        { field: "$.event.name", operator: "equals", value: "black-friday" },
        { field: "$.event.active", operator: "equals", value: true }
      ],
      result: {
        discount: 0.50,
        code: "BLACKFRIDAY50",
        expires: "2025-11-30T23:59:59Z"
      }
    },
    {
      // Bulk orders: tiered discounts
      or: [
        { field: "$.cart.quantity", operator: "greater-than", value: 50 },
        { field: "$.cart.value", operator: "greater-than", value: 1000 }
      ],
      result: {
        discount: 0.15,
        code: "BULK15",
        shipping: "free"
      }
    },
    {
      // New customer welcome
      and: [
        { field: "$.customer.orderHistory.length", operator: "equals", value: 0 },
        { field: "$.cart.value", operator: "greater-than", value: 50 }
      ],
      result: {
        discount: 0.10,
        code: "WELCOME10",
        message: "Welcome! Enjoy 10% off your first order 🎉"
      }
    }
  ],
  default: { discount: 0, message: "Regular pricing applies" }
};

🔐 Dynamic Access Control

const accessControlRules = {
  conditions: [
    {
      // Super admin: full access
      and: [
        { field: "role", operator: "equals", value: "super-admin" },
        { field: "status", operator: "equals", value: "active" }
      ],
      result: {
        permissions: ["read", "write", "delete", "admin"],
        level: "unlimited",
        expires: null
      }
    },
    {
      // Department manager: departmental access
      and: [
        { field: "role", operator: "equals", value: "manager" },
        { field: "department", operator: "exists", value: true },
        { field: "$.session.loginTime", operator: "date-after-now", value: "-8h" }
      ],
      result: {
        permissions: ["read", "write"],
        level: "department",
        scope: "$.department",
        expires: "$.session.loginTime + 8h"
      }
    },
    {
      // Regular user: read-only during business hours
      and: [
        { field: "role", operator: "equals", value: "user" },
        { field: "$.currentTime", operator: "time-between", value: ["09:00", "17:00"] },
        { field: "$.currentTime", operator: "date-between", value: ["monday", "friday"] }
      ],
      result: {
        permissions: ["read"],
        level: "limited",
        expires: "17:00"
      }
    }
  ],
  default: {
    permissions: [],
    level: "none",
    message: "Access denied"
  }
};

✅ Smart Form Validation

const registrationValidation = {
  conditions: {
    and: [
      // Email validation with custom message
      {
        field: "email",
        operator: "email",
        value: true,
        message: "Please enter a valid email address"
      },

      // Strong password requirements
      {
        and: [
          {
            field: "password",
            operator: "min-length",
            value: 8,
            message: "Password must be at least 8 characters long"
          },
          {
            field: "password",
            operator: "matches",
            value: ".*[A-Z].*",
            message: "Password must contain at least one uppercase letter"
          },
          {
            field: "password",
            operator: "matches",
            value: ".*[0-9].*",
            message: "Password must contain at least one number"
          }
        ]
      },

      // Age verification
      {
        field: "birthDate",
        operator: "date-before",
        value: "$.today - 18 years",
        message: "You must be 18 or older to register"
      },

      // Terms acceptance
      {
        field: "acceptTerms",
        operator: "equals",
        value: true,
        message: "You must accept our terms and conditions"
      },

      // Optional referral code validation
      {
        or: [
          { field: "referralCode", operator: "not-exists", value: true },
          { field: "referralCode", operator: "empty", value: true },
          {
            and: [
              { field: "referralCode", operator: "string-length", value: 8 },
              { field: "referralCode", operator: "alpha-numeric", value: true }
            ]
          }
        ],
        message: "Referral code must be 8 alphanumeric characters"
      }
    ]
  }
};

🎨 Advanced Features

🔗 Self-Referencing Magic

Compare fields against other fields dynamically:

const budgetRule = {
  conditions: {
    and: [
      // Actual cost must not exceed budget
      {
        field: "$.project.actualCost",
        operator: "less-than-or-equals",
        value: "$.project.approvedBudget"
      },
      // Start date must be before end date
      {
        field: "$.project.startDate",
        operator: "date-before",
        value: "$.project.endDate"
      },
      // Team size appropriate for project scope
      {
        field: "$.project.teamSize",
        operator: "greater-than-or-equals",
        value: "$.project.minimumTeamSize"
      }
    ]
  }
};

🏗️ Fluent Builder Pattern

Construct complex rules programmatically:

const complexRule = RuleEngine.builder()
  .add({
    and: [
      { field: "userType", operator: "equals", value: "premium" },
      { field: "subscriptionActive", operator: "equals", value: true }
    ],
    result: { access: "premium", features: ["analytics", "api", "support"] }
  })
  .add({
    and: [
      { field: "userType", operator: "equals", value: "basic" },
      { field: "trialExpired", operator: "equals", value: false }
    ],
    result: { access: "basic", features: ["dashboard"] }
  })
  .default({ access: "none", features: [] })
  .build(true); // Validate during build

🔧 Custom Operators (V2)

Extend the engine with your own operators:

import { registerCustomOperator, OperatorCategory, BaseOperatorStrategy } from '@usex/rule-engine';

class CreditCardOperator extends BaseOperatorStrategy<string, void> {
  readonly metadata = {
    name: "credit-card",
    displayName: "Credit Card Number",
    category: OperatorCategory.PATTERN,
    description: "Validates credit card numbers using Luhn algorithm",
    acceptedFieldTypes: ["string"],
    expectedValueType: "void",
    requiresValue: false,
  };

  evaluate(context) {
    const { fieldValue } = context;
    return this.isValidCreditCard(fieldValue);
  }

  private isValidCreditCard(cardNumber: string): boolean {
    // Luhn algorithm implementation
    const digits = cardNumber.replace(/\D/g, '');
    let sum = 0;
    let isEven = false;

    for (let i = digits.length - 1; i >= 0; i--) {
      let digit = parseInt(digits[i]);

      if (isEven) {
        digit *= 2;
        if (digit > 9) digit -= 9;
      }

      sum += digit;
      isEven = !isEven;
    }

    return sum % 10 === 0;
  }
}

// Register and use
registerCustomOperator(CreditCardOperator);

const paymentRule = {
  conditions: {
    and: [
      { field: "cardNumber", operator: "credit-card" },
      { field: "cvv", operator: "string-length", value: 3 }
    ]
  }
};

🎭 Data Mutations

Preprocess data before evaluation:

const engine = new RuleEngine();

// Add mutations for data preprocessing
engine.addMutation('normalizeEmail', (data) => {
  if (data.email) {
    data.email = data.email.toLowerCase().trim();
  }
  return data;
});

engine.addMutation('calculateAge', (data) => {
  if (data.birthDate) {
    const today = new Date();
    const birth = new Date(data.birthDate);
    data.age = today.getFullYear() - birth.getFullYear();
  }
  return data;
});

// Mutations are applied automatically before evaluation
const result = await engine.evaluate(rule, {
  email: "  [email protected]  ",
  birthDate: "1990-01-01"
});

📊 Rule Introspection

Understand what your rules need:

const insights = RuleEngine.introspect(complexRule);
console.log(insights);
// {
//   fields: ["userType", "subscriptionActive", "trialExpired"],
//   operators: ["equals"],
//   possibleResults: [
//     { access: "premium", features: ["analytics", "api", "support"] },
//     { access: "basic", features: ["dashboard"] },
//     { access: "none", features: [] }
//   ],
//   complexity: "medium",
//   estimatedPerformance: "fast"
// }

🏎️ Performance & Optimization

Real-World Benchmarks

Performance data from actual benchmark runs (10,000 iterations each on modern hardware)

Core Rule Evaluation

| Operation | Hz (ops/sec) | Avg Time | Performance Grade | |-----------|-------------|----------|-------------------| | Simple Rules (3-5 conditions) | ~16,900 | 0.059ms | 🚀 Lightning Fast | | Complex Rules (nested evaluation) | ~17,400 | 0.057ms | 🔥 Blazing | | Complex Rules (priority-based) | ~8,000 | 0.126ms | ⚡ Very Fast | | Array Operations | ~45,400 | 0.022ms | 🚀 Ultra Fast |

Advanced Features

| Feature | Hz (ops/sec) | Avg Time | Performance Grade | |---------|-------------|----------|-------------------| | JSONPath Resolution (simple) | ~55,000 | 0.018ms | 🔥 Blazing Fast | | JSONPath Deep Nested Access | ~54,000 | 0.019ms | 🔥 Blazing Fast | | JSONPath Array Processing | ~49,500 | 0.020ms | 🚀 Ultra Fast | | Self-Referencing (complex) | ~33,600 | 0.030ms | 🚀 Excellent | | Self-Referencing (simple) | ~30,900 | 0.032ms | 🚀 Excellent |

Data Processing & Validation

| Operation | Hz (ops/sec) | Avg Time | Use Case | |-----------|-------------|----------|----------| | Rule Builder (simple) | ~12,000,000 | 0.0001ms | Rule Construction | | Rule Builder (complex) | ~94,300 | 0.011ms | Complex Rule Building | | Rule Validation | ~67,000 | 0.015ms | Schema Validation | | Data Mutations (simple) | ~16,300 | 0.061ms | Data Preprocessing | | Data Mutations (complex) | ~34,100 | 0.029ms | Advanced Transformations | | Text Interpolation | ~796,000 | 0.0013ms | Dynamic Messages |

Error Handling & Edge Cases

| Operation | Hz (ops/sec) | Performance Grade | |-----------|-------------|-------------------| | Unknown Operator Handling | ~1,661,000 | ⚡ Instant Response | | Invalid Ruleset Handling | ~214,000 | 🚀 Very Fast | | Exists/NotExists Operators | ~41,400 | 🔥 Blazing |

All benchmarks run on 10,000 iterations with statistical accuracy. Performance may vary based on hardware and rule complexity.

Bundle Size

| Build Type | Size | |------------|------| | Minified | 102.7kB | | Minified + Gzipped | 18.4kB |

Zero dependencies = predictable bundle size with tree-shaking support

Optimization Tips

1. Trust Mode for Validated Rules

// Skip validation for 20% performance boost
const result = await RuleEngine.evaluate(rule, data, true);

2. Reuse Engine Instances

const engine = new RuleEngine();
// Reuse for better performance with mutations

3. Batch Processing

// Process multiple records at once
const results = await RuleEngine.evaluate(rule, arrayOfData);

4. Operator Selection

// Prefer specific operators over general ones
{ operator: "equals" }        // ✅ Fast
{ operator: "matches" }       // ⚠️ Slower for simple cases

🎓 TypeScript Support

Full type safety with intelligent inference:

interface UserPermissions {
  canRead: boolean;
  canWrite: boolean;
  canDelete: boolean;
  level: 'admin' | 'user' | 'guest';
}

// Type-safe rule definition
const accessRule: Rule<UserPermissions> = {
  conditions: [
    {
      and: [
        { field: "role", operator: "equals", value: "admin" },
        { field: "active", operator: "equals", value: true }
      ],
      result: {
        canRead: true,
        canWrite: true,
        canDelete: true,
        level: "admin"
      }
    }
  ],
  default: {
    canRead: false,
    canWrite: false,
    canDelete: false,
    level: "guest"
  }
};

// Type-safe evaluation
const result = await RuleEngine.evaluate<UserPermissions>(accessRule, userData);
// result.value is typed as UserPermissions ✅

Generic Builder Pattern

const typedRule = RuleEngine.builder<UserPermissions>()
  .add({
    and: [{ field: "role", operator: "equals", value: "admin" }],
    result: { canRead: true, canWrite: true, canDelete: true, level: "admin" }
  })
  .default({ canRead: false, canWrite: false, canDelete: false, level: "guest" })
  .build();

🧪 Testing Your Rules

import { describe, it, expect } from 'vitest';
import { RuleEngine } from '@usex/rule-engine';

describe('Discount Rules', () => {
  it('should apply VIP discount for qualifying orders', async () => {
    const result = await RuleEngine.evaluate(discountRule, {
      customer: { tier: 'vip', orderCount: 5 },
      order: { total: 150 }
    });

    expect(result.isPassed).toBe(true);
    expect(result.value.discount).toBe(0.20);
    expect(result.value.message).toContain('VIP');
  });

  it('should validate rule structure', () => {
    const validation = RuleEngine.validate(discountRule);
    expect(validation.isValid).toBe(true);
    expect(validation.errors).toHaveLength(0);
  });

  it('should handle edge cases gracefully', async () => {
    const result = await RuleEngine.evaluate(discountRule, {});
    expect(result.value).toEqual({ discount: 0, message: "No discount available" });
  });
});

📚 Documentation & Resources

🤝 Contributing

We love contributions! Whether it's:

  • 🐛 Bug reports and fixes
  • ✨ New operators or features
  • 📖 Documentation improvements
  • 🎨 Examples and tutorials

See our Contributing Guide for details.

Development Setup

# Clone and setup
git clone https://github.com/ali-master/rule-engine.git
cd rule-engine
pnpm install

# Run tests
pnpm test

# Run benchmarks
pnpm test:bench

# Build package
pnpm build

# Watch mode for development
pnpm dev

🆚 Why Choose this lib Over Alternatives?

| Feature | @usex/rule-engine | json-rules-engine | node-rules | |---------|-------------------|-------------------|------------| | Zero Dependencies | ✅ | ❌ | ❌ | | TypeScript Native | ✅ | ⚠️ Partial | ❌ | | JSONPath Support | ✅ | ❌ | ❌ | | Self-Referencing | ✅ | ❌ | ❌ | | Custom Operators | ✅ | ⚠️ Limited | ❌ | | Performance (ops/sec) | 17k+ (55k+ JSONPath) | 45k | 30k | | Bundle Size | 18.4KB | 45KB | 38KB | | Browser Support | ✅ | ✅ | ❌ | | Rule Introspection | ✅ | ❌ | ❌ | | Fluent Builder | ✅ | ❌ | ❌ |

📄 License

MIT © Ali Torki


Built with ❤️ by Ali Torki, for developers

⭐ Star us on GitHub🐛 Report Issues💬 Discussions