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); // trueMultiple 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 seniorExpression 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
priorityfor execution order. - Actions can be asynchronous, supporting external API calls.
- Fully extensible and lightweight, with zero dependencies.
