Skip to main content

Overview

The StatefulRuleEngine extends the base RuleEngine with state tracking capabilities, enabling you to detect changes in data over time and trigger actions based on state transitions.
Perfect for monitoring systems, event-driven workflows, and reactive business logic.

Key Features

State Tracking

Maintains previous states for comparison across evaluations

Event System

Subscribe to rule state changes with event listeners

Change Detection

Specialized operators for detecting value changes

History Management

Optional storage of evaluation history

Creating a Stateful Engine

import { createRuleEngine, StatefulRuleEngine } from 'rule-engine-js';

const baseEngine = createRuleEngine();
const statefulEngine = new StatefulRuleEngine(baseEngine, {
  triggerOnEveryChange: false,  // Trigger only on false → true
  storeHistory: true,            // Keep evaluation history
  maxHistorySize: 100,           // Limit history entries
});

Configuration Options

triggerOnEveryChange
boolean
default:"false"
When false, triggers only on false → true transitions. When true, triggers on any state change.
storeHistory
boolean
default:"false"
Enable storage of evaluation history for analysis and debugging.
maxHistorySize
number
default:"100"
Maximum number of history entries to store per rule.

Event System

Subscribe to rule state changes:
statefulEngine.on('triggered', (event) => {
  console.log(`Rule ${event.ruleId} was triggered!`);
  console.log('Previous state:', event.previousState);
  console.log('Current state:', event.currentState);
  console.log('Context:', event.context);
});

State Change Operators

const rule = { changed: ['user.email'] };

// First evaluation
statefulEngine.evaluate('email-change', rule, { user: { email: 'old@example.com' } });

// Second evaluation - triggers because email changed
statefulEngine.evaluate('email-change', rule, { user: { email: 'new@example.com' } });
const rule = { changedBy: ['temperature', 5] };

// First evaluation
statefulEngine.evaluate('temp', rule, { temperature: 20 });

// Triggers when temperature changes by 5 or more
statefulEngine.evaluate('temp', rule, { temperature: 26 }); // true
const rule = { changedFrom: ['status', 'pending'] };

// Triggers when status changes from 'pending' to anything else
statefulEngine.evaluate('status', rule, { status: 'pending' });
statefulEngine.evaluate('status', rule, { status: 'approved' }); // triggers
const rule = { changedTo: ['status', 'completed'] };

// Triggers when status changes to 'completed'
statefulEngine.evaluate('status', rule, { status: 'processing' });
statefulEngine.evaluate('status', rule, { status: 'completed' }); // triggers
const rule = { increased: ['stock'] };

// Triggers when stock quantity increases
statefulEngine.evaluate('stock', rule, { stock: 100 });
statefulEngine.evaluate('stock', rule, { stock: 150 }); // triggers
const rule = { decreased: ['balance'] };

// Triggers when balance decreases
statefulEngine.evaluate('balance', rule, { balance: 1000 });
statefulEngine.evaluate('balance', rule, { balance: 800 }); // triggers

Real-World Example

// Order processing workflow
const orderRules = {
  'payment-received': { changedTo: ['order.paymentStatus', 'paid'] },
  'inventory-low': {
    and: [
      { decreased: ['product.stock'] },
      { lte: ['product.stock', 10] }
    ]
  },
  'price-drop': {
    and: [
      { decreased: ['product.price'] },
      { changedBy: ['product.price', 5] }
    ]
  },
};

// Event handlers
statefulEngine.on('triggered', (event) => {
  switch (event.ruleId) {
    case 'payment-received':
      processOrder(event.context);
      break;
    case 'inventory-low':
      reorderStock(event.context.product);
      break;
    case 'price-drop':
      notifyCustomers(event.context.product);
      break;
  }
});

// Evaluate all rules
const orderData = {
  order: { paymentStatus: 'paid' },
  product: { stock: 8, price: 95 },
};

statefulEngine.evaluateBatch(orderRules, orderData);

Methods

evaluate()

Evaluate a single rule with state tracking:
const result = statefulEngine.evaluate(ruleId, rule, context);

evaluateBatch()

Evaluate multiple rules at once:
const results = statefulEngine.evaluateBatch(rulesObject, context);

getRuleState()

Get current state of a specific rule:
const state = statefulEngine.getRuleState(ruleId);

clearRuleState()

Clear state for a specific rule:
statefulEngine.clearRuleState(ruleId);

getHistory()

Get evaluation history (if enabled):
const history = statefulEngine.getHistory(ruleId);

Best Practices

Use Meaningful IDs

Use descriptive rule IDs that indicate the rule’s purpose

Batch Evaluations

Use evaluateBatch() for multiple related rules

Clean Up State

Periodically clear state for inactive rules

Limit History

Set appropriate maxHistorySize to manage memory

Next Steps