@f-o-t/rules-engine 2.0.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +168 -0
- package/LICENSE.md +16 -4
- package/README.md +106 -23
- package/__tests__/builder.test.ts +363 -0
- package/__tests__/cache.test.ts +130 -0
- package/__tests__/config.test.ts +35 -0
- package/__tests__/engine.test.ts +1213 -0
- package/__tests__/evaluate.test.ts +339 -0
- package/__tests__/exports.test.ts +30 -0
- package/__tests__/filter-sort.test.ts +303 -0
- package/__tests__/integration.test.ts +419 -0
- package/__tests__/money-integration.test.ts +149 -0
- package/__tests__/validation.test.ts +862 -0
- package/biome.json +39 -0
- package/docs/MIGRATION-v3.md +118 -0
- package/fot.config.ts +5 -0
- package/package.json +31 -67
- package/src/analyzer/analysis.ts +401 -0
- package/src/builder/conditions.ts +321 -0
- package/src/builder/rule.ts +192 -0
- package/src/cache/cache.ts +135 -0
- package/src/cache/noop.ts +20 -0
- package/src/core/evaluate.ts +185 -0
- package/src/core/filter.ts +85 -0
- package/src/core/group.ts +103 -0
- package/src/core/sort.ts +90 -0
- package/src/engine/engine.ts +462 -0
- package/src/engine/hooks.ts +235 -0
- package/src/engine/state.ts +322 -0
- package/src/index.ts +303 -0
- package/src/optimizer/index-builder.ts +381 -0
- package/src/serialization/serializer.ts +408 -0
- package/src/simulation/simulator.ts +359 -0
- package/src/types/config.ts +184 -0
- package/src/types/consequence.ts +38 -0
- package/src/types/evaluation.ts +87 -0
- package/src/types/rule.ts +112 -0
- package/src/types/state.ts +116 -0
- package/src/utils/conditions.ts +108 -0
- package/src/utils/hash.ts +30 -0
- package/src/utils/id.ts +6 -0
- package/src/utils/time.ts +42 -0
- package/src/validation/conflicts.ts +440 -0
- package/src/validation/integrity.ts +473 -0
- package/src/validation/schema.ts +386 -0
- package/src/versioning/version-store.ts +337 -0
- package/tsconfig.json +29 -0
- package/dist/index.cjs +0 -3088
- package/dist/index.d.cts +0 -1317
- package/dist/index.d.ts +0 -1317
- package/dist/index.js +0 -3072
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [3.0.0] - 2026-01-31
|
|
9
|
+
|
|
10
|
+
### ⚠️ BREAKING CHANGES
|
|
11
|
+
|
|
12
|
+
- Engine now requires `evaluator` or `operators` configuration
|
|
13
|
+
- `evaluateRule()` function signature changed to accept evaluator parameter
|
|
14
|
+
- `evaluateRules()` function signature changed to accept evaluator parameter
|
|
15
|
+
|
|
16
|
+
### 🎉 Features
|
|
17
|
+
|
|
18
|
+
- **Custom operators support**: Use custom operators from any library (e.g., `@f-o-t/money/operators`)
|
|
19
|
+
- **Plugin system integration**: Full integration with `@f-o-t/condition-evaluator` plugin system
|
|
20
|
+
- **Better extensibility**: Easily compose multiple operator sets
|
|
21
|
+
- **Type-safe operators**: Better TypeScript support for custom operator types
|
|
22
|
+
|
|
23
|
+
### 📚 Documentation
|
|
24
|
+
|
|
25
|
+
- Added migration guide for v2 → v3
|
|
26
|
+
- Updated README with custom operator examples
|
|
27
|
+
- Added examples for money operators integration
|
|
28
|
+
|
|
29
|
+
### 🔧 Migration
|
|
30
|
+
|
|
31
|
+
See [MIGRATION-v3.md](./docs/MIGRATION-v3.md) for detailed migration instructions.
|
|
32
|
+
|
|
33
|
+
**Quick migration:**
|
|
34
|
+
```typescript
|
|
35
|
+
// Before (v2.x)
|
|
36
|
+
const engine = createEngine({ consequences: MyConsequences });
|
|
37
|
+
|
|
38
|
+
// After (v3.x)
|
|
39
|
+
const engine = createEngine({
|
|
40
|
+
consequences: MyConsequences,
|
|
41
|
+
evaluator: createEvaluator() // Add this
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## [2.0.2] - 2026-01-25
|
|
48
|
+
|
|
49
|
+
### Changed
|
|
50
|
+
|
|
51
|
+
- Updated dependencies to latest versions
|
|
52
|
+
|
|
53
|
+
## [2.0.1] - 2025-12-31
|
|
54
|
+
|
|
55
|
+
### Changed
|
|
56
|
+
|
|
57
|
+
- Removed unused imports and dead code
|
|
58
|
+
- Removed unused `ConditionGroup` type import from index-builder
|
|
59
|
+
- Removed unused `ConflictResolutionStrategySchema` import from config types
|
|
60
|
+
- Removed unused `_cacheStats` variable from engine stats
|
|
61
|
+
- Removed unused `_collectAllConditionIds` helper function from integrity validation
|
|
62
|
+
|
|
63
|
+
## [2.0.0] - 2025-12-24
|
|
64
|
+
|
|
65
|
+
### Added
|
|
66
|
+
|
|
67
|
+
- **Zod Schema-First Types**: All configuration types now built from Zod schemas
|
|
68
|
+
- `CacheConfigSchema`, `ValidationConfigSchema`, `VersioningConfigSchema`, `LogLevelSchema`
|
|
69
|
+
- `ConflictResolutionStrategySchema`, `EvaluateOptionsSchema`, `EvaluateConfigSchema`
|
|
70
|
+
- `RuleStatsSchema`, `CacheStatsSchema`, `EngineStatsSchema`
|
|
71
|
+
- `ValidationErrorSchema`, `ValidationResultSchema`, `ValidationOptionsSchema`
|
|
72
|
+
- **Hook Error Handling**: New `onHookError` callback for capturing hook errors
|
|
73
|
+
- Previously hooks failed silently; now errors are reported via callback
|
|
74
|
+
- **Hook Timeout Protection**: New `hookTimeoutMs` config option
|
|
75
|
+
- Prevents slow hooks from blocking engine execution indefinitely
|
|
76
|
+
- Timeout errors are reported through `onHookError`
|
|
77
|
+
- **Orphaned Reference Detection**: `ImportResult` now includes `orphanedReferences`
|
|
78
|
+
- Detects when imported RuleSets reference non-existent rule IDs
|
|
79
|
+
- New `OrphanedReference` type exported
|
|
80
|
+
- **Shared Conditions Utility**: New internal `src/utils/conditions.ts`
|
|
81
|
+
- `collectConditionFields()`, `collectConditionOperators()`, `countConditions()`
|
|
82
|
+
- `calculateMaxDepth()`, `countConditionGroups()`
|
|
83
|
+
- **Config Helper Functions**: New functions replace constants
|
|
84
|
+
- `getDefaultCacheConfig()`, `getDefaultValidationConfig()`, `getDefaultVersioningConfig()`
|
|
85
|
+
- `getDefaultLogLevel()`, `getDefaultConflictResolution()`
|
|
86
|
+
- `parseCacheConfig()`, `parseValidationConfig()`, `parseVersioningConfig()`
|
|
87
|
+
|
|
88
|
+
### Changed
|
|
89
|
+
|
|
90
|
+
- **Cache Eviction Performance**: O(n) → O(1) for oldest entry eviction
|
|
91
|
+
- Now uses ES6 Map insertion order for FIFO eviction
|
|
92
|
+
- **Strict Mode Behavior**: `strictMode: true` now implicitly enables consequence validation
|
|
93
|
+
- Previously required both `strictMode: true` and `validateConsequences: true`
|
|
94
|
+
|
|
95
|
+
### Removed
|
|
96
|
+
|
|
97
|
+
- **BREAKING**: Removed FP utility exports from `src/utils/pipe.ts`
|
|
98
|
+
- `pipe()`, `compose()`, `identity()`, `always()`, `tap()`
|
|
99
|
+
- These generic utilities are not domain-specific; use lodash/ramda instead
|
|
100
|
+
- **BREAKING**: Removed `delay()` from `src/utils/time.ts`
|
|
101
|
+
- Low value utility; use `Bun.sleep()` or inline `new Promise(resolve => setTimeout(resolve, ms))`
|
|
102
|
+
- **BREAKING**: Removed `DEFAULT_*` constant exports
|
|
103
|
+
- `DEFAULT_CACHE_CONFIG` → use `getDefaultCacheConfig()`
|
|
104
|
+
- `DEFAULT_VALIDATION_CONFIG` → use `getDefaultValidationConfig()`
|
|
105
|
+
- `DEFAULT_VERSIONING_CONFIG` → use `getDefaultVersioningConfig()`
|
|
106
|
+
- `DEFAULT_ENGINE_CONFIG` → removed (use schema defaults)
|
|
107
|
+
- **BREAKING**: Removed internal mutable type exports
|
|
108
|
+
- `MutableEngineState`, `MutableOptimizerState`, `MutableRuleStats`
|
|
109
|
+
- These are internal implementation details
|
|
110
|
+
|
|
111
|
+
### Fixed
|
|
112
|
+
|
|
113
|
+
- **Silent Hook Errors**: All 11 hook execution functions now report errors via `onHookError`
|
|
114
|
+
- **Redundant Sorting**: Removed unnecessary sort in `evaluate()` (rules already sorted on add/update)
|
|
115
|
+
|
|
116
|
+
## [1.0.0] - 2025-12-10
|
|
117
|
+
|
|
118
|
+
### Added
|
|
119
|
+
|
|
120
|
+
- Initial release of the rules engine library
|
|
121
|
+
- **Engine**: Stateful rule management with `createEngine()`
|
|
122
|
+
- Add, update, remove, enable/disable rules
|
|
123
|
+
- Rule sets for grouping related rules
|
|
124
|
+
- Configurable caching with TTL and max size
|
|
125
|
+
- Lifecycle hooks (onBeforeEvaluation, onAfterEvaluation, onRuleMatch, onRuleError, onCacheHit)
|
|
126
|
+
- Conflict resolution strategies: "all", "first-match", "highest-priority"
|
|
127
|
+
- **Fluent Builders**: Chainable APIs for building rules and conditions
|
|
128
|
+
- `rule()` builder with full configuration options
|
|
129
|
+
- Shorthand condition helpers: `num()`, `str()`, `bool()`, `date()`, `arr()`
|
|
130
|
+
- Logical operators: `all()`, `any()`, `and()`, `or()`
|
|
131
|
+
- **Core Evaluation**: Built on `@f-o-t/condition-evaluator`
|
|
132
|
+
- `evaluateRule()` and `evaluateRules()` functions
|
|
133
|
+
- Filter rules by tags, category, enabled status
|
|
134
|
+
- Sort rules by priority, name, created/updated date
|
|
135
|
+
- Group rules by category, priority, enabled status, or custom function
|
|
136
|
+
- **Validation**: Comprehensive rule validation
|
|
137
|
+
- Schema validation with Zod
|
|
138
|
+
- Conflict detection (duplicate IDs, overlapping conditions, priority collisions, unreachable rules)
|
|
139
|
+
- Integrity checks (negative priority, missing fields, invalid operators)
|
|
140
|
+
- **Simulation**: Test rules without side effects
|
|
141
|
+
- `simulate()` for single context testing
|
|
142
|
+
- `batchSimulate()` for multiple contexts
|
|
143
|
+
- `whatIf()` for comparing rule set changes
|
|
144
|
+
- **Versioning**: Track rule changes with rollback support
|
|
145
|
+
- Version store with full history
|
|
146
|
+
- Rollback to any previous version
|
|
147
|
+
- Prune old versions
|
|
148
|
+
- **Indexing & Optimization**: Fast rule lookups
|
|
149
|
+
- Build indexes by field, tag, category, priority
|
|
150
|
+
- Optimization suggestions for rule sets
|
|
151
|
+
- **Analysis**: Rule set analytics
|
|
152
|
+
- Complexity analysis per rule
|
|
153
|
+
- Field, operator, and consequence usage statistics
|
|
154
|
+
- Find most complex rules
|
|
155
|
+
- **Serialization**: Import/export capabilities
|
|
156
|
+
- JSON export/import with optional ID regeneration
|
|
157
|
+
- Clone rules
|
|
158
|
+
- Merge and diff rule sets
|
|
159
|
+
- **Utilities**: Functional programming helpers
|
|
160
|
+
- `pipe()`, `compose()`, `identity()`, `always()`, `tap()`
|
|
161
|
+
- `measureTime()`, `measureTimeAsync()`, `withTimeout()`, `delay()`
|
|
162
|
+
- `generateId()`, `hashContext()`, `hashRules()`
|
|
163
|
+
|
|
164
|
+
### Changed
|
|
165
|
+
|
|
166
|
+
- **Internal refactor**: Removed 12 internal barrel files
|
|
167
|
+
- All imports now use direct file paths instead of barrel re-exports
|
|
168
|
+
- No public API changes - only internal module structure improvements
|
package/LICENSE.md
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c)
|
|
3
|
+
Copyright (c) 2026 FOT (F-O-T)
|
|
4
4
|
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
6
11
|
|
|
7
|
-
The above copyright notice and this permission notice shall be included in all
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
8
14
|
|
|
9
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -23,35 +23,65 @@ bun add @f-o-t/rules-engine
|
|
|
23
23
|
## Quick Start
|
|
24
24
|
|
|
25
25
|
```typescript
|
|
26
|
-
import { createEngine,
|
|
26
|
+
import { createEngine, createEvaluator } from "@f-o-t/rules-engine";
|
|
27
|
+
import { z } from "zod";
|
|
27
28
|
|
|
28
|
-
//
|
|
29
|
-
const
|
|
29
|
+
// Define your consequence types
|
|
30
|
+
const MyConsequences = {
|
|
31
|
+
send_email: z.object({
|
|
32
|
+
to: z.string().email(),
|
|
33
|
+
subject: z.string(),
|
|
34
|
+
}),
|
|
35
|
+
apply_discount: z.object({
|
|
36
|
+
percentage: z.number(),
|
|
37
|
+
}),
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Create engine with built-in operators
|
|
41
|
+
const engine = createEngine({
|
|
42
|
+
consequences: MyConsequences,
|
|
43
|
+
evaluator: createEvaluator(), // Required!
|
|
44
|
+
});
|
|
30
45
|
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
str("customerType", "eq", "premium")
|
|
39
|
-
)
|
|
40
|
-
)
|
|
41
|
-
.then("apply_discount", { percentage: 15 })
|
|
42
|
-
.withPriority(100)
|
|
43
|
-
.tagged("pricing", "discount")
|
|
44
|
-
.build()
|
|
45
|
-
);
|
|
46
|
+
// Or with custom operators
|
|
47
|
+
import { moneyOperators } from "@f-o-t/money/operators";
|
|
48
|
+
|
|
49
|
+
const engine = createEngine({
|
|
50
|
+
consequences: MyConsequences,
|
|
51
|
+
operators: moneyOperators, // Convenience: engine creates evaluator
|
|
52
|
+
});
|
|
46
53
|
|
|
47
|
-
//
|
|
54
|
+
// Add rules
|
|
55
|
+
engine.addRule({
|
|
56
|
+
name: "high-value-customer",
|
|
57
|
+
conditions: {
|
|
58
|
+
id: "g1",
|
|
59
|
+
operator: "AND",
|
|
60
|
+
conditions: [
|
|
61
|
+
{
|
|
62
|
+
id: "c1",
|
|
63
|
+
type: "number",
|
|
64
|
+
field: "totalPurchases",
|
|
65
|
+
operator: "gt",
|
|
66
|
+
value: 1000,
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
consequences: [
|
|
71
|
+
{
|
|
72
|
+
type: "apply_discount",
|
|
73
|
+
payload: { percentage: 10 },
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Evaluate
|
|
48
79
|
const result = await engine.evaluate({
|
|
49
|
-
|
|
50
|
-
customerType: "premium",
|
|
80
|
+
totalPurchases: 1500,
|
|
51
81
|
});
|
|
52
82
|
|
|
53
|
-
console.log(result.matchedRules);
|
|
54
|
-
console.log(result.consequences);
|
|
83
|
+
console.log(result.matchedRules); // Rules that matched
|
|
84
|
+
console.log(result.consequences); // Actions to take
|
|
55
85
|
```
|
|
56
86
|
|
|
57
87
|
## Building Conditions
|
|
@@ -125,6 +155,59 @@ const myRule = rule()
|
|
|
125
155
|
.build();
|
|
126
156
|
```
|
|
127
157
|
|
|
158
|
+
## Custom Operators
|
|
159
|
+
|
|
160
|
+
Use custom operators from libraries like `@f-o-t/money`:
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
import { createEngine } from "@f-o-t/rules-engine";
|
|
164
|
+
import { moneyOperators } from "@f-o-t/money/operators";
|
|
165
|
+
|
|
166
|
+
const engine = createEngine({
|
|
167
|
+
operators: moneyOperators,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
engine.addRule({
|
|
171
|
+
name: "large-transaction",
|
|
172
|
+
conditions: {
|
|
173
|
+
id: "g1",
|
|
174
|
+
operator: "AND",
|
|
175
|
+
conditions: [
|
|
176
|
+
{
|
|
177
|
+
id: "c1",
|
|
178
|
+
type: "custom",
|
|
179
|
+
field: "amount",
|
|
180
|
+
operator: "money_gt",
|
|
181
|
+
value: { amount: "5000.00", currency: "BRL" },
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
},
|
|
185
|
+
consequences: [
|
|
186
|
+
{ type: "flag_for_review", payload: {} },
|
|
187
|
+
],
|
|
188
|
+
});
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Create your own custom operators:
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
import { createEngine, createOperator } from "@f-o-t/rules-engine";
|
|
195
|
+
|
|
196
|
+
const customOperator = createOperator({
|
|
197
|
+
name: "is_valid_cpf",
|
|
198
|
+
type: "custom",
|
|
199
|
+
description: "Validate Brazilian CPF",
|
|
200
|
+
evaluate: (actual, expected) => {
|
|
201
|
+
// Your validation logic
|
|
202
|
+
return validateCPF(actual as string);
|
|
203
|
+
},
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
const engine = createEngine({
|
|
207
|
+
operators: { is_valid_cpf: customOperator },
|
|
208
|
+
});
|
|
209
|
+
```
|
|
210
|
+
|
|
128
211
|
## Engine API
|
|
129
212
|
|
|
130
213
|
### Rule Management
|