SkillAgentSearch skills...

Rulepilot

A simple yet powerful JSON rule processing engine for JavaScript

Install / Use

/learn @andrewbrg/Rulepilot

README

<img src=".github/logo.png" width="190" alt="RulePilot" />

npm version

| Statements | Functions | Lines | | --------------------------------------------------------------------------- | -------------------------------------------------------------------------- | ---------------------------------------------------------------------- | | Statements | Functions | Lines |

Overview

RulePilot is a fast and lightweight rule engine for JavaScript. It is designed to be simple to use and easy to integrate into your application.

The rule engine evaluates human-readable JSON rules against a set of criteria. The rules are evaluated in a top-down fashion.

Simple rules can be written to evaluate to a boolean value (indicating whether the criteria tested passes the rule).

Otherwise, granular rules can be created, where each condition of the rule can evaluate to a boolean, number, string, object or array. This is particularly useful when you want to evaluate a rule and return a result based on each condition's evaluation.

Features

  • Simple to use
  • Written in TypeScript
  • Human-readable JSON rules
  • Runs in both Node & the browser
  • Lightweight with zero dependencies & Fast (10,000 rule evaluations in ~35-40ms)
  • Fluent rule builder tool (ORM style)
  • Both Simple & Granular rule evaluation
  • Sub-rules & infinitely nested conditions
  • Dynamic criteria mutation
  • Supports Criteria objects with nested properties
  • Rule validation & debugging tools
  • Supports Any, All, and None type conditions
  • Supports Equal, NotEqual, GreaterThan, GreaterThanOrEqual, LessThan, LessThanOrEqual, In, NotIn, Contains, Not Contains, ContainsAny, Not ContainsAny, Matches and Not Matches operators

Usage

Installation

npm install rulepilot
yarn add rulepilot

Importing

import { RulePilot } from "rulepilot";

For TypeScript users, you can import the Rule interface to get type definitions for the rule JSON.

import { RulePilot, Rule } from "rulepilot";

const rule: Rule = {
  // ...
};

Static vs Instantiated Approach

The RulePilot library can be used in two ways, either as a static class or as an instance.

The static class is great for most cases as long as you do not intend to use different mutations in multiple places in your codebase, (more on what mutations are further down).

Otherwise, you can create an instance of the RulePilot class and use it to evaluate rules.

import { RulePilot } from "rulepilot";

let result;

// Static
result = await RulePilot.evaluate(rule, criteria);

// Instance
const rulePilot = new RulePilot();
result = await rulePilot.evaluate(rule, criteria);

Basic Example

Here we are defining a rule which will evaluate to true or false based on the criteria provided. In this example, we are checking whether a user is allowed to benefit from a discount at checkout of not.

For the discount to be applied, the user must be from either the UK or Finland, have a coupon and the total checkout price must be greater than or equal to 120.00.

import { RulePilot, Rule } from "rulepilot";

// Define a rule which caters for your needs
const rule: Rule = {
  conditions: {
    all: [
      { field: "country", operator: "in", value: ["GB", "FI"] },
      { field: "hasCoupon", operator: "==", value: true },
      { field: "totalCheckoutPrice", operator: ">=", value: 120.0 },
    ],
  },
};

// Define the criteria which will be evaluated against the rule
const criteria = {
  country: "GB",
  totalCheckoutPrice: 125.0,
  hasCoupon: true,
};

/** Evaluate the criteria against the rule */
let result = await RulePilot.evaluate<boolean>(rule, criteria);
// result == true

// However, if any of the criteria do not pass the check, the result will be false
criteria.totalCheckoutPrice = 25.0;

/** Evaluate the new criteria against the rule */
result = await RulePilot.evaluate<boolean>(rule, criteria); // result == false

We can add additional conditions to the rule, for example apart from the above-mentioned conditions, we can also say that if the user is either over 18 years old or has a valid student card then we will evaluate to true.

Take note of how the conditions property is now an array of objects.

import { RulePilot, Rule } from "rulepilot";

// Define a rule which caters for your needs
const rule: Rule = {
  conditions: [
    {
      all: [
        { field: "country", operator: "in", value: ["GB", "FI"] },
        { field: "hasCoupon", operator: "==", value: true },
        { field: "totalCheckoutPrice", operator: ">=", value: 120.0 },
      ],
    },
    {
      any: [
        { field: "age", operator: ">=",  value: 18 },
        { field: "hasStudentCard", operator: "==", value: true },
      ],
    },
  ],
};

// Define the criteria which will be evaluated against the rule
const criteria = {
  country: "GB",
  totalCheckoutPrice: 20.0,
  hasCoupon: true,
};

/** Evaluate the criteria against the rule */
let result = await RulePilot.evaluate<boolean>(rule, criteria); // result == false

criteria.hasStudentCard = true;

/** Evaluate the new criteria against the rule */
result = await RulePilot.evaluate<boolean>(rule, criteria); // result == true

If we want to add additional requirements to the rule, we can do so by adding another any or all condition.

For example, we can add a requirement that a discount will also be given to all users from Sweden as long as they are 18+ or have a valid student card (irrelevant of any other conditions set).

const rule: Rule = {
  conditions: [
    {
      any: [
        {
          all: [
            { field: "country", operator: "in", value: ["GB", "FI"] },
            { field: "hasCoupon", operator: "==", value: true },
            { field: "totalCheckoutPrice", operator: ">=", value: 120.0 },
          ],
        },
        { field: "country",  operator: "==", value: "SE" },
      ],
    },
    {
      any: [
        { field: "age", operator: ">=",  value: 18 },
        { field: "hasStudentCard", operator: "==", value: true },
      ],
    },
  ],
};

The criteria can be narrowed down further by specifying Swedish users cannot be from Stockholm or Gothenburg otherwise they must spend more than 200.00 at checkout.

const rule: Rule = {
  conditions: [
    {
      any: [
        {
          all: [
            { field: "country", operator: "in", value: ["GB", "FI"] },
            { field: "hasCoupon", operator: "==", value: true },
            { field: "totalCheckoutPrice", operator: ">=", value: 120.0 },
          ],
        },
        {
          any: [
            {
              all: [
                { field: "country", operator: "==", value: "SE" },
                { field: "city", operator: "not in", value: ["Stockholm", "Gothenburg"] },
              ],
            },
            {
              all: [
                { field: "country",  operator: "==", value: "SE" },
                { field: "city", operator: "totalCheckoutPrice",  value: 200 },
              ],
            },
          ],
        },
      ],
    },
    {
      any: [
        { field: "age", operator: ">=",  value: 18 },
        { field: "hasStudentCard", operator: "==", value: true },
      ],
    },
  ],
};

Granular Example

It might be the case that we want to give different discounts to people based on the criteria they meet. For example, we want to give a 10% discount to all users who 18+ or have a student card and a 5% discount to the rest of the users who meet the other criteria.

To accomplish this, we can assign a result to each condition which will be used to calculate the discount.

const rule: Rule = {
  conditions: [
    {
      any: [
        {
          all: [
            { field: "country", operator: "in", value: ["GB", "FI"] },
            { field: "hasCoupon", operator: "==", value: true },
            { field: "totalCheckoutPrice", operator: ">=", value: 120.0 },
          ],
        },
        {
          any: [
            {
              all: [
                { field: "country", operator: "==", value: "SE" },
                { field: "city", operator: "not in", value: ["Stockholm", "Gothenburg"] },
              ],
            },
            {
              all: [
                { field: "country",  operator: "==", value: "SE" },
                { field: "city", operator: "totalCheckoutPrice",  value: 200 },
              ],
            },
          ],
        },
      ],
      result: 5,
    },
    {
      any: [
        { field: "age", operator: ">=",  value: 18 },
        { field: "hasStudentCard", operator: "==", value: true },
      ],
      result: 10,
    },
  ],
};

In such a setup the result of our evaluation will be the value of the result property in condition which was met first.

import { RulePilot } from "rulepilot";

// Define the criteria which will be evaluated against the rule
const criteria = {
  country: "GB",
  totalCheckoutPrice: 340.22,
  hasCoupon: true,
};

/** Evaluate the criteria against the rule */
let result = await RulePilot.evaluate<number>(rule, criteria); // result = 5

criteria.country = "SE";
criteria.city = "Linköping";

/** Evaluate the new criteria against the rule */
result = await RulePilot.evaluate<number>(rule, criteria); // result = 10

criteria.country = "IT";
criteria.
View on GitHub
GitHub Stars51
CategoryDevelopment
Updated1mo ago
Forks9

Languages

TypeScript

Security Score

100/100

Audited on Feb 27, 2026

No findings