@f-o-t/rules-engine 3.0.0 → 3.0.5

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.
Files changed (105) hide show
  1. package/dist/analyzer/analysis.d.ts +72 -0
  2. package/dist/analyzer/analysis.d.ts.map +1 -0
  3. package/dist/builder/conditions.d.ts +29 -0
  4. package/dist/builder/conditions.d.ts.map +1 -0
  5. package/dist/builder/rule.d.ts +40 -0
  6. package/dist/builder/rule.d.ts.map +1 -0
  7. package/dist/cache/cache.d.ts +21 -0
  8. package/dist/cache/cache.d.ts.map +1 -0
  9. package/dist/cache/noop.d.ts +3 -0
  10. package/dist/cache/noop.d.ts.map +1 -0
  11. package/dist/core/evaluate.d.ts +21 -0
  12. package/dist/core/evaluate.d.ts.map +1 -0
  13. package/dist/core/filter.d.ts +8 -0
  14. package/dist/core/filter.d.ts.map +1 -0
  15. package/dist/core/group.d.ts +10 -0
  16. package/dist/core/group.d.ts.map +1 -0
  17. package/dist/core/sort.d.ts +14 -0
  18. package/dist/core/sort.d.ts.map +1 -0
  19. package/dist/engine/engine.d.ts +27 -0
  20. package/dist/engine/engine.d.ts.map +1 -0
  21. package/dist/engine/hooks.d.ts +16 -0
  22. package/dist/engine/hooks.d.ts.map +1 -0
  23. package/dist/engine/state.d.ts +19 -0
  24. package/dist/engine/state.d.ts.map +1 -0
  25. package/dist/index.d.ts +33 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +3083 -0
  28. package/dist/index.js.map +37 -0
  29. package/dist/optimizer/index-builder.d.ts +48 -0
  30. package/dist/optimizer/index-builder.d.ts.map +1 -0
  31. package/dist/serialization/serializer.d.ts +79 -0
  32. package/dist/serialization/serializer.d.ts.map +1 -0
  33. package/dist/simulation/simulator.d.ts +65 -0
  34. package/dist/simulation/simulator.d.ts.map +1 -0
  35. package/dist/types/config.d.ts +87 -0
  36. package/dist/types/config.d.ts.map +1 -0
  37. package/dist/types/consequence.d.ts +21 -0
  38. package/dist/types/consequence.d.ts.map +1 -0
  39. package/dist/types/evaluation.d.ts +70 -0
  40. package/dist/types/evaluation.d.ts.map +1 -0
  41. package/dist/types/rule.d.ts +114 -0
  42. package/dist/types/rule.d.ts.map +1 -0
  43. package/dist/types/state.d.ts +65 -0
  44. package/dist/types/state.d.ts.map +1 -0
  45. package/dist/utils/conditions.d.ts +24 -0
  46. package/dist/utils/conditions.d.ts.map +1 -0
  47. package/dist/utils/hash.d.ts +3 -0
  48. package/dist/utils/hash.d.ts.map +1 -0
  49. package/dist/utils/id.d.ts +2 -0
  50. package/dist/utils/id.d.ts.map +1 -0
  51. package/dist/utils/time.d.ts +8 -0
  52. package/dist/utils/time.d.ts.map +1 -0
  53. package/dist/validation/conflicts.d.ts +25 -0
  54. package/dist/validation/conflicts.d.ts.map +1 -0
  55. package/dist/validation/integrity.d.ts +38 -0
  56. package/dist/validation/integrity.d.ts.map +1 -0
  57. package/dist/validation/schema.d.ts +69 -0
  58. package/dist/validation/schema.d.ts.map +1 -0
  59. package/dist/versioning/version-store.d.ts +50 -0
  60. package/dist/versioning/version-store.d.ts.map +1 -0
  61. package/package.json +35 -31
  62. package/CHANGELOG.md +0 -168
  63. package/__tests__/builder.test.ts +0 -363
  64. package/__tests__/cache.test.ts +0 -130
  65. package/__tests__/config.test.ts +0 -35
  66. package/__tests__/engine.test.ts +0 -1213
  67. package/__tests__/evaluate.test.ts +0 -339
  68. package/__tests__/exports.test.ts +0 -30
  69. package/__tests__/filter-sort.test.ts +0 -303
  70. package/__tests__/integration.test.ts +0 -419
  71. package/__tests__/money-integration.test.ts +0 -149
  72. package/__tests__/validation.test.ts +0 -862
  73. package/biome.json +0 -39
  74. package/docs/MIGRATION-v3.md +0 -118
  75. package/fot.config.ts +0 -5
  76. package/src/analyzer/analysis.ts +0 -401
  77. package/src/builder/conditions.ts +0 -321
  78. package/src/builder/rule.ts +0 -192
  79. package/src/cache/cache.ts +0 -135
  80. package/src/cache/noop.ts +0 -20
  81. package/src/core/evaluate.ts +0 -185
  82. package/src/core/filter.ts +0 -85
  83. package/src/core/group.ts +0 -103
  84. package/src/core/sort.ts +0 -90
  85. package/src/engine/engine.ts +0 -462
  86. package/src/engine/hooks.ts +0 -235
  87. package/src/engine/state.ts +0 -322
  88. package/src/index.ts +0 -303
  89. package/src/optimizer/index-builder.ts +0 -381
  90. package/src/serialization/serializer.ts +0 -408
  91. package/src/simulation/simulator.ts +0 -359
  92. package/src/types/config.ts +0 -184
  93. package/src/types/consequence.ts +0 -38
  94. package/src/types/evaluation.ts +0 -87
  95. package/src/types/rule.ts +0 -112
  96. package/src/types/state.ts +0 -116
  97. package/src/utils/conditions.ts +0 -108
  98. package/src/utils/hash.ts +0 -30
  99. package/src/utils/id.ts +0 -6
  100. package/src/utils/time.ts +0 -42
  101. package/src/validation/conflicts.ts +0 -440
  102. package/src/validation/integrity.ts +0 -473
  103. package/src/validation/schema.ts +0 -386
  104. package/src/versioning/version-store.ts +0 -337
  105. package/tsconfig.json +0 -29
@@ -1,185 +0,0 @@
1
- import {
2
- createEvaluator,
3
- type GroupEvaluationResult,
4
- } from "@f-o-t/condition-evaluator";
5
- import type {
6
- AggregatedConsequence,
7
- ConsequenceDefinitions,
8
- DefaultConsequences,
9
- } from "../types/consequence";
10
- import type {
11
- EvaluateConfig,
12
- EvaluationContext,
13
- RuleEvaluationResult,
14
- } from "../types/evaluation";
15
- import type { Rule } from "../types/rule";
16
- import { measureTime } from "../utils/time";
17
-
18
- export type EvaluateRuleOptions = {
19
- readonly skipDisabled?: boolean;
20
- };
21
-
22
- export const evaluateRule = <
23
- TContext = unknown,
24
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
25
- >(
26
- rule: Rule<TContext, TConsequences>,
27
- context: EvaluationContext<TContext>,
28
- evaluator: ReturnType<typeof createEvaluator>,
29
- options: EvaluateRuleOptions = {},
30
- ): RuleEvaluationResult<TContext, TConsequences> => {
31
- if (options.skipDisabled && !rule.enabled) {
32
- return {
33
- ruleId: rule.id,
34
- ruleName: rule.name,
35
- matched: false,
36
- conditionResult: createEmptyGroupResult(rule.conditions.id),
37
- consequences: [],
38
- evaluationTimeMs: 0,
39
- skipped: true,
40
- skipReason: "Rule is disabled",
41
- };
42
- }
43
-
44
- const { result: conditionResult, durationMs } = measureTime(() => {
45
- try {
46
- const evalContext = {
47
- data: context.data as Record<string, unknown>,
48
- metadata: context.metadata as Record<string, unknown> | undefined,
49
- };
50
- // Use the provided evaluator instead of hardcoded evaluateConditionGroup
51
- return evaluator.evaluateConditionGroup(rule.conditions, evalContext);
52
- } catch (error) {
53
- return {
54
- error,
55
- result: createEmptyGroupResult(rule.conditions.id),
56
- };
57
- }
58
- });
59
-
60
- if ("error" in conditionResult) {
61
- return {
62
- ruleId: rule.id,
63
- ruleName: rule.name,
64
- matched: false,
65
- conditionResult: conditionResult.result,
66
- consequences: [],
67
- evaluationTimeMs: durationMs,
68
- skipped: false,
69
- error:
70
- conditionResult.error instanceof Error
71
- ? conditionResult.error
72
- : new Error(String(conditionResult.error)),
73
- };
74
- }
75
-
76
- const matched = conditionResult.passed;
77
-
78
- const consequences: AggregatedConsequence<TConsequences>[] = matched
79
- ? rule.consequences.map((consequence) => ({
80
- type: consequence.type,
81
- payload: consequence.payload,
82
- ruleId: rule.id,
83
- ruleName: rule.name,
84
- priority: rule.priority,
85
- }))
86
- : [];
87
-
88
- return {
89
- ruleId: rule.id,
90
- ruleName: rule.name,
91
- matched,
92
- conditionResult,
93
- consequences,
94
- evaluationTimeMs: durationMs,
95
- skipped: false,
96
- };
97
- };
98
-
99
- const createEmptyGroupResult = (groupId: string): GroupEvaluationResult => ({
100
- groupId,
101
- operator: "AND",
102
- passed: false,
103
- results: [],
104
- });
105
-
106
- export type EvaluateRulesOptions<
107
- TContext = unknown,
108
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
109
- > = {
110
- readonly config?: Partial<EvaluateConfig>;
111
- readonly onRuleEvaluated?: (
112
- result: RuleEvaluationResult<TContext, TConsequences>,
113
- ) => void;
114
- };
115
-
116
- export type EvaluateRulesResult<
117
- TContext = unknown,
118
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
119
- > = {
120
- readonly results: ReadonlyArray<
121
- RuleEvaluationResult<TContext, TConsequences>
122
- >;
123
- readonly matchedRules: ReadonlyArray<Rule<TContext, TConsequences>>;
124
- readonly consequences: ReadonlyArray<AggregatedConsequence<TConsequences>>;
125
- readonly stoppedEarly: boolean;
126
- readonly stoppedByRuleId?: string;
127
- };
128
-
129
- export const evaluateRules = <
130
- TContext = unknown,
131
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
132
- >(
133
- rules: ReadonlyArray<Rule<TContext, TConsequences>>,
134
- context: EvaluationContext<TContext>,
135
- evaluator: ReturnType<typeof createEvaluator>,
136
- options: EvaluateRulesOptions<TContext, TConsequences> = {},
137
- ): EvaluateRulesResult<TContext, TConsequences> => {
138
- const config: EvaluateConfig = {
139
- conflictResolution: options.config?.conflictResolution ?? "priority",
140
- continueOnError: options.config?.continueOnError ?? true,
141
- collectAllConsequences: options.config?.collectAllConsequences ?? true,
142
- };
143
-
144
- const results: RuleEvaluationResult<TContext, TConsequences>[] = [];
145
- const matchedRules: Rule<TContext, TConsequences>[] = [];
146
- const consequences: AggregatedConsequence<TConsequences>[] = [];
147
- let stoppedEarly = false;
148
- let stoppedByRuleId: string | undefined;
149
-
150
- for (const rule of rules) {
151
- const result = evaluateRule(rule, context, evaluator, { skipDisabled: true });
152
- results.push(result);
153
-
154
- options.onRuleEvaluated?.(result);
155
-
156
- if (result.error && !config.continueOnError) {
157
- break;
158
- }
159
-
160
- if (result.matched) {
161
- matchedRules.push(rule);
162
- consequences.push(...result.consequences);
163
-
164
- if (rule.stopOnMatch) {
165
- stoppedEarly = true;
166
- stoppedByRuleId = rule.id;
167
- break;
168
- }
169
-
170
- if (config.conflictResolution === "first-match") {
171
- stoppedEarly = true;
172
- stoppedByRuleId = rule.id;
173
- break;
174
- }
175
- }
176
- }
177
-
178
- return {
179
- results,
180
- matchedRules,
181
- consequences,
182
- stoppedEarly,
183
- stoppedByRuleId,
184
- };
185
- };
@@ -1,85 +0,0 @@
1
- import type {
2
- ConsequenceDefinitions,
3
- DefaultConsequences,
4
- } from "../types/consequence";
5
- import type { Rule, RuleFilters } from "../types/rule";
6
-
7
- export const filterRules = <
8
- TContext = unknown,
9
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
10
- >(
11
- filters: RuleFilters,
12
- ) => {
13
- return (
14
- rules: ReadonlyArray<Rule<TContext, TConsequences>>,
15
- ): ReadonlyArray<Rule<TContext, TConsequences>> => {
16
- return rules.filter((rule) => {
17
- if (
18
- filters.enabled !== undefined &&
19
- rule.enabled !== filters.enabled
20
- ) {
21
- return false;
22
- }
23
-
24
- if (filters.tags && filters.tags.length > 0) {
25
- const hasMatchingTag = filters.tags.some((tag) =>
26
- rule.tags.includes(tag),
27
- );
28
- if (!hasMatchingTag) {
29
- return false;
30
- }
31
- }
32
-
33
- if (
34
- filters.category !== undefined &&
35
- rule.category !== filters.category
36
- ) {
37
- return false;
38
- }
39
-
40
- if (filters.ids && filters.ids.length > 0) {
41
- if (!filters.ids.includes(rule.id)) {
42
- return false;
43
- }
44
- }
45
-
46
- return true;
47
- });
48
- };
49
- };
50
-
51
- export const filterByEnabled = <
52
- TContext = unknown,
53
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
54
- >(
55
- enabled: boolean,
56
- ) => {
57
- return filterRules<TContext, TConsequences>({ enabled });
58
- };
59
-
60
- export const filterByTags = <
61
- TContext = unknown,
62
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
63
- >(
64
- tags: ReadonlyArray<string>,
65
- ) => {
66
- return filterRules<TContext, TConsequences>({ tags });
67
- };
68
-
69
- export const filterByCategory = <
70
- TContext = unknown,
71
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
72
- >(
73
- category: string,
74
- ) => {
75
- return filterRules<TContext, TConsequences>({ category });
76
- };
77
-
78
- export const filterByIds = <
79
- TContext = unknown,
80
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
81
- >(
82
- ids: ReadonlyArray<string>,
83
- ) => {
84
- return filterRules<TContext, TConsequences>({ ids });
85
- };
package/src/core/group.ts DELETED
@@ -1,103 +0,0 @@
1
- import type {
2
- ConsequenceDefinitions,
3
- DefaultConsequences,
4
- } from "../types/consequence";
5
- import type { Rule } from "../types/rule";
6
-
7
- export type GroupByField = "category" | "priority" | "enabled";
8
-
9
- export type GroupedRules<
10
- TContext = unknown,
11
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
12
- > = ReadonlyMap<
13
- string | number | boolean,
14
- ReadonlyArray<Rule<TContext, TConsequences>>
15
- >;
16
-
17
- export const groupRules = <
18
- TContext = unknown,
19
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
20
- >(
21
- field: GroupByField,
22
- ) => {
23
- return (
24
- rules: ReadonlyArray<Rule<TContext, TConsequences>>,
25
- ): GroupedRules<TContext, TConsequences> => {
26
- const groups = new Map<
27
- string | number | boolean,
28
- Rule<TContext, TConsequences>[]
29
- >();
30
-
31
- for (const rule of rules) {
32
- let key: string | number | boolean;
33
-
34
- switch (field) {
35
- case "category":
36
- key = rule.category ?? "uncategorized";
37
- break;
38
- case "priority":
39
- key = rule.priority;
40
- break;
41
- case "enabled":
42
- key = rule.enabled;
43
- break;
44
- }
45
-
46
- const existing = groups.get(key);
47
- if (existing) {
48
- existing.push(rule);
49
- } else {
50
- groups.set(key, [rule]);
51
- }
52
- }
53
-
54
- return groups;
55
- };
56
- };
57
-
58
- export const groupByCategory = <
59
- TContext = unknown,
60
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
61
- >() => {
62
- return groupRules<TContext, TConsequences>("category");
63
- };
64
-
65
- export const groupByPriority = <
66
- TContext = unknown,
67
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
68
- >() => {
69
- return groupRules<TContext, TConsequences>("priority");
70
- };
71
-
72
- export const groupByEnabled = <
73
- TContext = unknown,
74
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
75
- >() => {
76
- return groupRules<TContext, TConsequences>("enabled");
77
- };
78
-
79
- export const groupByCustom = <
80
- TContext = unknown,
81
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
82
- TKey extends string | number | boolean = string,
83
- >(
84
- keyFn: (rule: Rule<TContext, TConsequences>) => TKey,
85
- ) => {
86
- return (
87
- rules: ReadonlyArray<Rule<TContext, TConsequences>>,
88
- ): ReadonlyMap<TKey, ReadonlyArray<Rule<TContext, TConsequences>>> => {
89
- const groups = new Map<TKey, Rule<TContext, TConsequences>[]>();
90
-
91
- for (const rule of rules) {
92
- const key = keyFn(rule);
93
- const existing = groups.get(key);
94
- if (existing) {
95
- existing.push(rule);
96
- } else {
97
- groups.set(key, [rule]);
98
- }
99
- }
100
-
101
- return groups;
102
- };
103
- };
package/src/core/sort.ts DELETED
@@ -1,90 +0,0 @@
1
- import type {
2
- ConsequenceDefinitions,
3
- DefaultConsequences,
4
- } from "../types/consequence";
5
- import type { Rule } from "../types/rule";
6
-
7
- export type SortField = "priority" | "name" | "createdAt" | "updatedAt";
8
- export type SortDirection = "asc" | "desc";
9
-
10
- export type SortOptions = {
11
- readonly field: SortField;
12
- readonly direction?: SortDirection;
13
- };
14
-
15
- export const sortRules = <
16
- TContext = unknown,
17
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
18
- >(
19
- options: SortField | SortOptions,
20
- ) => {
21
- const normalizedOptions: SortOptions =
22
- typeof options === "string"
23
- ? { field: options, direction: "desc" }
24
- : options;
25
-
26
- const { field, direction = "desc" } = normalizedOptions;
27
-
28
- return (
29
- rules: ReadonlyArray<Rule<TContext, TConsequences>>,
30
- ): ReadonlyArray<Rule<TContext, TConsequences>> => {
31
- const sorted = [...rules].sort((a, b) => {
32
- let comparison = 0;
33
-
34
- switch (field) {
35
- case "priority":
36
- comparison = a.priority - b.priority;
37
- break;
38
- case "name":
39
- comparison = a.name.localeCompare(b.name);
40
- break;
41
- case "createdAt":
42
- comparison = a.createdAt.getTime() - b.createdAt.getTime();
43
- break;
44
- case "updatedAt":
45
- comparison = a.updatedAt.getTime() - b.updatedAt.getTime();
46
- break;
47
- }
48
-
49
- return direction === "desc" ? -comparison : comparison;
50
- });
51
-
52
- return sorted;
53
- };
54
- };
55
-
56
- export const sortByPriority = <
57
- TContext = unknown,
58
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
59
- >(
60
- direction: SortDirection = "desc",
61
- ) => {
62
- return sortRules<TContext, TConsequences>({ field: "priority", direction });
63
- };
64
-
65
- export const sortByName = <
66
- TContext = unknown,
67
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
68
- >(
69
- direction: SortDirection = "asc",
70
- ) => {
71
- return sortRules<TContext, TConsequences>({ field: "name", direction });
72
- };
73
-
74
- export const sortByCreatedAt = <
75
- TContext = unknown,
76
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
77
- >(
78
- direction: SortDirection = "desc",
79
- ) => {
80
- return sortRules<TContext, TConsequences>({ field: "createdAt", direction });
81
- };
82
-
83
- export const sortByUpdatedAt = <
84
- TContext = unknown,
85
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
86
- >(
87
- direction: SortDirection = "desc",
88
- ) => {
89
- return sortRules<TContext, TConsequences>({ field: "updatedAt", direction });
90
- };