Skip to content

A flexible rule engine (zero dependencies) for evaluating dynamic conditions and executing actions on records.

Notifications You must be signed in to change notification settings

johanlabs/rule-engine

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

7 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Scriba Rule Engine

A flexible rule engine (zero dependencies) for evaluating dynamic conditions and executing actions on records. Ideal for validation, automation, and configurable workflows in TypeScript/Node.js projects.


πŸš€ Features

Feature Description
Simple & Composite Conditions Supports and, or, not logical operators.
Dynamic Expressions Use placeholders like {{field}} in conditions and actions.
Built-in Actions throw, log, setField, callFunction, callWebhook.
Extensibility Register custom conditions and actions.
Async Support Handle asynchronous API calls or external tasks.
Zero Dependencies Works out-of-the-box without extra packages.

πŸ“¦ Installation

# Using npm
npm install scriba-rule-engine

# Using yarn
yarn add scriba-rule-engine

βš™οΈ Core Types

type Condition = {
  type?: string;
  field?: string;
  value?: any;
  valueFrom?: string;
  expression?: string;
  and?: Condition[];
  or?: Condition[];
  not?: Condition;
};

type ActionPayload = Record<string, any>;
type ActionFn = (params: { record: any; payload: ActionPayload }) => Promise<void> | void;

type Rule = {
  conditions: Condition | Condition[];
  actions: Array<{ type: string } & ActionPayload>;
  priority?: number;
};

πŸ’‘ Usage Example

import { RuleEngine, defaultConditions, defaultActions } from "scriba-rule-engine";

const engine = new RuleEngine({
  conditions: defaultConditions,
  actions: defaultActions,
});

const record = { name: "Alice", age: 25, status: "active" };

const rules = [
  {
    conditions: { type: "greaterThan", field: "age", value: 18 },
    actions: [
      { type: "log", message: "User {{name}} is an adult" },
      { type: "setField", field: "verified", value: true }
    ]
  },
  {
    conditions: { type: "fieldExists", field: "status" },
    actions: [
      { type: "callFunction", fn: async (r) => console.log("Processing", r.name) }
    ]
  }
];

await engine.applyRules(record, rules);
console.log(record);
// Output: { name: "Alice", age: 25, status: "active", verified: true }

πŸ› οΈ Adding Custom Conditions or Actions

// Custom Condition
engine.registerCondition("isEven", (record, cond) => record[cond.field!] % 2 === 0);

// Custom Action
engine.registerAction("customAction", async ({ record }) => {
  console.log("Custom action executed for:", record);
});

πŸ”— Built-in Conditions

Condition Description
greaterThan Checks if a field is greater than a value.
lessThan Checks if a field is less than a value.
equals Checks equality with a value.
notEquals Checks inequality with a value.
fieldExists Checks if a field exists.
fieldNull Checks if a field is null or undefined.
inList Checks if the field value exists in an array.
notInList Checks if the field value does NOT exist in an array.
matchesRegex Checks if a field matches a regex.
expression Evaluates a dynamic JavaScript expression with placeholders.
beforeDate Checks if a date is before a given date.
afterDate Checks if a date is after a given date.
startsWith Checks if a string starts with a value.
endsWith Checks if a string ends with a value.
contains Checks if a string contains a value.
allInList Checks if all values in condition array exist in record field array.
anyInList Checks if any value in condition array exists in record field array.

πŸ”— Built-in Actions

Action Description
throw Throws an error with a dynamic message.
log Logs a message to the console.
setField Sets a record field to a value or expression result.
callFunction Calls a custom function with the record and arguments.
callWebhook Sends a POST request to a webhook with dynamic payload.

πŸ“– Advanced Examples

Conditional AND / OR

const rec = { age: 30, status: 'inactive' };
const rules = [
  {
    conditions: {
      and: [
        { type: 'greaterThan', field: 'age', value: 18 },
        { not: { type: 'equals', field: 'status', value: 'active' } }
      ]
    },
    actions: [{ type: 'setField', field: 'canAccess', value: true }]
  }
];
await engine.applyRules(rec, rules);
console.log(rec.canAccess); // true

Multiple Actions

const rec = { age: 70, profile: {} };
const rules = [
  {
    conditions: { type: 'greaterThan', field: 'age', value: 65 },
    actions: [
      { type: 'setField', field: 'status', value: 'blocked' },
      { type: 'setField', field: 'profile.category', value: 'senior' }
    ]
  }
];
await engine.applyRules(rec, rules);
console.log(rec.status, rec.profile.category); // blocked senior

Expression Example

const rec = { score: 5 };
const rules = [
  { conditions: { expression: '{{score}} > 0' }, actions: [{ type: 'setField', field: 'bonus', expression: '{{score}} * 10' }] }
];
await engine.applyRules(rec, rules);
console.log(rec.bonus); // 50

πŸ“š Notes

  • Expressions use double curly braces {{field}} to dynamically evaluate record values.
  • Rules can have optional priority for execution order.
  • Actions can be asynchronous, supporting external API calls.
  • Fully extensible and lightweight, with zero dependencies.

About

A flexible rule engine (zero dependencies) for evaluating dynamic conditions and executing actions on records.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •