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

scriba-rule-engine

v1.0.9

Published

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.

Downloads

983

Readme

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.