Skip to content
xevrionPublic

About

Programmable rule engine and policy evaluator for Node.js

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

66 Commits

Folders and files

Repository files navigation

ruleflow

A programmable rule engine and policy evaluator for Node.js. Define rules with conditions and actions, group them into policies, and evaluate them against JSON fact objects.

Features

  • Rule DSL: TypeScript-native rule definitions with operators, conditions, priority, and chaining
  • Policy Compiler: Compiles rules into an optimized evaluation plan with condition reordering
  • Execution Engine: Evaluates facts against compiled policies with short-circuit logic
  • Conflict Detector: Finds contradictory or redundant rules before deployment
  • Coverage Tracker: Reports which rules fired/didn't across a batch of facts
  • Tracer: Records full evaluation traces for debugging
  • Diff Engine: Compares two policy versions to understand what changed
  • CLI: Validate, run, and diff policy files from the command line
  • Plugin System: Register custom operators for domain-specific logic
  • Cache: LRU cache for compiled policies keyed by content hash

Installation

npm install ruleflow

Quick Start

import { PolicyCompiler } from 'ruleflow/compiler';
import { evaluate } from 'ruleflow/engine';
import type { Policy } from 'ruleflow/types';

const policy: Policy = {
  meta: {
    name: 'User Access Policy',
    version: '1.0.0',
    createdAt: new Date().toISOString(),
    updatedAt: new Date().toISOString(),
  },
  rules: [
    {
      id: 'rule-adult-premium',
      name: 'Adult Premium Access',
      priority: 10,
      conditions: {
        all: [
          { field: 'user.age', operator: 'gte', value: 18 },
          { field: 'user.subscription', operator: 'eq', value: 'premium' },
        ],
      },
      actions: [{ type: 'flag', target: 'access', payload: { level: 'full' } }],
    },
  ],
};

const compiler = new PolicyCompiler();
const compiled = compiler.compile(policy);

const result = evaluate(compiled, { user: { age: 25, subscription: 'premium' } });
console.log(result.matchedRules); // [{ ruleId: 'rule-adult-premium', ... }]

CLI Usage

# Validate a policy file
ruleflow validate policy.json

# Run a policy against facts
ruleflow run policy.json facts.json

# Run with trace output
ruleflow run policy.json facts.json --trace

# Diff two policy versions
ruleflow diff policy-v1.json policy-v2.json

# Coverage report
ruleflow coverage policy.json facts.json

Rule DSL

Conditions

Conditions use dot-path field access and a rich set of operators:

const conditions: ConditionGroup = {
  all: [
    { field: 'user.age', operator: 'gte', value: 18 },
    {
      any: [
        { field: 'user.role', operator: 'in', value: ['admin', 'moderator'] },
        { field: 'user.verified', operator: 'eq', value: true },
      ],
    },
    { field: 'user.banned', operator: 'eq', value: false, negate: false },
  ],
};

Built-in Operators

Operator Description Example
eq Strict equality { field: 'status', operator: 'eq', value: 'active' }
neq Not equal { field: 'status', operator: 'neq', value: 'banned' }
gt Greater than { field: 'age', operator: 'gt', value: 18 }
gte Greater than or equal { field: 'score', operator: 'gte', value: 100 }
lt Less than { field: 'price', operator: 'lt', value: 50 }
lte Less than or equal { field: 'qty', operator: 'lte', value: 10 }
in Value in array { field: 'role', operator: 'in', value: ['a', 'b'] }
nin Value not in array { field: 'tag', operator: 'nin', value: ['spam'] }
contains String/array contains { field: 'name', operator: 'contains', value: 'foo' }
startsWith String starts with { field: 'email', operator: 'startsWith', value: 'admin' }
endsWith String ends with { field: 'email', operator: 'endsWith', value: '.com' }
matches Regex match { field: 'id', operator: 'matches', value: '^[A-Z]{3}' }
between Inclusive range { field: 'age', operator: 'between', value: [18, 65] }
exists Field exists { field: 'user.profile', operator: 'exists', value: null }
isNull Field is null { field: 'deletedAt', operator: 'isNull', value: null }

Custom Operators

import { globalRegistry } from 'ruleflow/operators';

globalRegistry.register('divisibleBy', (actual, expected) => {
  if (typeof actual !== 'number' || typeof expected !== 'number') return false;
  return actual % expected === 0;
});

Policy Files (JSON)

{
  "meta": {
    "name": "My Policy",
    "version": "2.0.0",
    "author": "Yash Bavadiya",
    "createdAt": "2026-01-15T00:00:00.000Z",
    "updatedAt": "2026-05-20T00:00:00.000Z"
  },
  "rules": [
    {
      "id": "rule-001",
      "name": "High Value Order",
      "priority": 100,
      "conditions": {
        "all": [
          { "field": "order.total", "operator": "gte", "value": 1000 },
          { "field": "order.currency", "operator": "eq", "value": "USD" }
        ]
      },
      "actions": [
        { "type": "flag", "target": "review", "payload": { "reason": "high_value" } }
      ],
      "tags": ["finance", "review"],
      "enabled": true
    }
  ]
}

Conflict Detection

import { detectConflicts } from 'ruleflow/conflict';

const conflicts = detectConflicts(policy);
// [
//   { type: 'contradiction', ruleA: 'rule-001', ruleB: 'rule-002', severity: 'error', ... },
//   { type: 'redundancy', ruleA: 'rule-003', ruleB: 'rule-004', severity: 'warning', ... },
// ]

Coverage Tracking

import { runBatch } from 'ruleflow/engine';
import { computeCoverage } from 'ruleflow/coverage';

const batchResult = runBatch(compiled, facts);
const coverage = computeCoverage(compiled, batchResult);
// { totalRules: 5, firedRules: 3, coveragePercent: 60, neverFiredRules: ['rule-d', 'rule-e'] }

Policy Diffing

import { diffPolicies } from 'ruleflow/diff';

const diff = diffPolicies(policyV1, policyV2);
// { addedRules: [...], removedRules: [...], changedRules: [...], metaChanges: [...] }

Architecture

Policy JSON
    │
    ▼
policyParser ──► Policy (validated)
    │
    ▼
PolicyCompiler
    ├── conditionCompiler  (builds CompiledConditionGroup)
    ├── ruleCompiler       (validates + compiles each Rule)
    └── optimizer          (reorders by cost, prunes disabled)
    │
    ▼
CompiledPolicy (cached by SHA-256)
    │
    ▼
evaluator.evaluate(fact)
    ├── shortCircuit       (AND/OR/NONE with early exit)
    └── tracer             (optional trace recording)
    │
    ▼
EvalResult

License

MIT © Yash Bavadiya

About

Programmable rule engine and policy evaluator for Node.js

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages