Overview
State change operators detect changes between evaluations. Requires StatefulRuleEngine or manual _previous context.
changedBy
Change by threshold
changedFrom
Changed from value
changedTo
Changed to value
increased
Numeric increase
decreased
Numeric decrease
Requirements
StatefulRuleEngine Required: These operators need previous state. Use StatefulRuleEngine or pass _previous context manually.
import { createRuleEngine, StatefulRuleEngine } from 'rule-engine-js';
// Create stateful engine
const baseEngine = createRuleEngine();
const statefulEngine = new StatefulRuleEngine(baseEngine);
// Use state change operators
const rule = { changed: ['temperature'] };
statefulEngine.evaluate('temp-rule', rule, { temperature: 25 });
Source: src/operators/state.js | Tests: tests/unit/operators/state.test.js
Operators
changed - Any Change
Detects any value change.
{ changed: ['fieldPath'] }
Example:
// First evaluation
statefulEngine.evaluate('rule1', { changed: ['status'] }, { status: 'pending' });
// Result: { success: false } - no previous state
// Second evaluation
statefulEngine.evaluate('rule1', { changed: ['status'] }, { status: 'active' });
// Result: { success: true } - "pending" → "active"
// Third evaluation (same value)
statefulEngine.evaluate('rule1', { changed: ['status'] }, { status: 'active' });
// Result: { success: false } - no change
changedBy - Threshold Change
Detects numeric change by minimum amount (absolute value).
{ changedBy: ['fieldPath', threshold] }
Example:
// Temperature changed by at least 5 degrees
const rule = { changedBy: ['temperature', 5] };
// Previous: 20, Current: 23 → change = 3
statefulEngine.evaluate('temp', rule, { temperature: 23 });
// Result: { success: false } - changed by 3 (< 5)
// Previous: 23, Current: 29 → change = 6
statefulEngine.evaluate('temp', rule, { temperature: 29 });
// Result: { success: true } - changed by 6 (≥ 5)
changedFrom - From Specific Value
Detects transition from a specific value.
{ changedFrom: ['fieldPath', fromValue] }
Example:
// Detect order leaving "pending" status
const rule = { changedFrom: ['order.status', 'pending'] };
// Previous: "pending", Current: "processing"
statefulEngine.evaluate('order', rule, { order: { status: 'processing' } });
// Result: { success: true }
// Previous: "processing", Current: "shipped"
statefulEngine.evaluate('order', rule, { order: { status: 'shipped' } });
// Result: { success: false } - didn't change FROM pending
changedTo - To Specific Value
Detects transition to a specific value.
{ changedTo: ['fieldPath', toValue] }
Example:
// Detect order completion
const rule = { changedTo: ['order.status', 'completed'] };
// Previous: "processing", Current: "completed"
statefulEngine.evaluate('order', rule, { order: { status: 'completed' } });
// Result: { success: true }
// Previous: "completed", Current: "completed"
statefulEngine.evaluate('order', rule, { order: { status: 'completed' } });
// Result: { success: false } - already was completed
increased - Numeric Increase
Detects any numeric increase.
{ increased: ['fieldPath'] }
Example:
const rule = { increased: ['score'] };
// Previous: 85, Current: 90
statefulEngine.evaluate('score', rule, { score: 90 });
// Result: { success: true }
// Previous: 90, Current: 90
statefulEngine.evaluate('score', rule, { score: 90 });
// Result: { success: false } - no change
// Previous: 90, Current: 85
statefulEngine.evaluate('score', rule, { score: 85 });
// Result: { success: false } - decreased, not increased
decreased - Numeric Decrease
Detects any numeric decrease.
{ decreased: ['fieldPath'] }
Example:
const rule = { decreased: ['stock'] };
// Previous: 100, Current: 95
statefulEngine.evaluate('inventory', rule, { stock: 95 });
// Result: { success: true }
// Previous: 95, Current: 95
statefulEngine.evaluate('inventory', rule, { stock: 95 });
// Result: { success: false } - no change
Common Use Cases
Temperature Monitoring
Order Status Tracking
Inventory Management
Price Monitoring
// Alert on significant temp change
const tempAlert = {
and: [
{ gte: ['temperature', 25] },
{ increased: ['temperature'] }
]
};
// Or threshold-based
const significantChange = {
changedBy: ['temperature', 5]
};
StatefulRuleEngine Usage
Basic Setup
import { createRuleEngine, StatefulRuleEngine } from 'rule-engine-js';
const engine = createRuleEngine();
const statefulEngine = new StatefulRuleEngine(engine);
// Define rule
const rule = {
and: [
{ gte: ['temperature', 25] },
{ increased: ['temperature'] }
]
};
// First evaluation
statefulEngine.evaluate('temp-rule', rule, { temperature: 20 });
// Result: { success: false, triggered: false }
// Second evaluation - temp increased and now >= 25
statefulEngine.evaluate('temp-rule', rule, { temperature: 26 });
// Result: { success: true, triggered: true }
// Event 'triggered' emitted
Event System
// Listen for state changes
statefulEngine.on('triggered', (event) => {
console.log(`Rule ${event.ruleId} triggered!`);
console.log('Context:', event.context);
});
statefulEngine.on('changed', (event) => {
console.log(`Rule ${event.ruleId} state changed`);
});
Batch Evaluation
const rules = {
'temp-increased': { increased: ['temperature'] },
'temp-critical': { gte: ['temperature', 30] },
'temp-changed': { changedBy: ['temperature', 5] }
};
const results = statefulEngine.evaluateBatch(rules, { temperature: 28 });
// Returns results for all rules
Manual Previous Context
Without StatefulRuleEngine, pass _previous manually:
const context = {
temperature: 26,
_previous: {
temperature: 20
}
};
engine.evaluateExpr({ increased: ['temperature'] }, context);
// Result: { success: true }
Quick Reference
| Operator | Args | Detects | Example |
changed | 1 | Any change | { changed: ['status'] } |
changedBy | 2 | Change ≥ threshold | { changedBy: ['temp', 5] } |
changedFrom | 2 | From specific value | { changedFrom: ['status', 'pending'] } |
changedTo | 2 | To specific value | { changedTo: ['status', 'done'] } |
increased | 1 | Numeric increase | { increased: ['score'] } |
decreased | 1 | Numeric decrease | { decreased: ['stock'] } |
Error Handling
// First evaluation - no previous state
const result = statefulEngine.evaluate('rule', { changed: ['value'] }, { value: 10 });
// Result: { success: false } - returns false, not error
Non-Numeric for changedBy
const context = {
name: 'John',
_previous: { name: 'Jane' }
};
const result = engine.evaluateExpr({ changedBy: ['name', 5] }, context);
// Error: "CHANGED_BY requires numeric values"
API Reference