@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
package/dist/index.js ADDED
@@ -0,0 +1,3083 @@
1
+ // @bun
2
+ // src/index.ts
3
+ import {
4
+ ConditionGroup,
5
+ isConditionGroup as isConditionGroup6,
6
+ createEvaluator as createEvaluator3,
7
+ createOperator
8
+ } from "@f-o-t/condition-evaluator";
9
+
10
+ // src/analyzer/analysis.ts
11
+ import {
12
+ isConditionGroup as isConditionGroup2
13
+ } from "@f-o-t/condition-evaluator";
14
+
15
+ // src/utils/conditions.ts
16
+ import {
17
+ isConditionGroup
18
+ } from "@f-o-t/condition-evaluator";
19
+ var collectConditionFields = (condition) => {
20
+ const fields = new Set;
21
+ const traverse = (c) => {
22
+ if (isConditionGroup(c)) {
23
+ for (const child of c.conditions) {
24
+ traverse(child);
25
+ }
26
+ } else {
27
+ fields.add(c.field);
28
+ }
29
+ };
30
+ traverse(condition);
31
+ return fields;
32
+ };
33
+ var countConditions = (condition) => {
34
+ if (isConditionGroup(condition)) {
35
+ return condition.conditions.reduce((sum, c) => sum + countConditions(c), 0);
36
+ }
37
+ return 1;
38
+ };
39
+ var calculateMaxDepth = (condition, currentDepth = 1) => {
40
+ if (isConditionGroup(condition)) {
41
+ if (condition.conditions.length === 0)
42
+ return currentDepth;
43
+ return Math.max(...condition.conditions.map((c) => calculateMaxDepth(c, currentDepth + 1)));
44
+ }
45
+ return currentDepth;
46
+ };
47
+ var countConditionGroups = (condition) => {
48
+ if (isConditionGroup(condition)) {
49
+ return 1 + condition.conditions.reduce((sum, c) => sum + countConditionGroups(c), 0);
50
+ }
51
+ return 0;
52
+ };
53
+
54
+ // src/analyzer/analysis.ts
55
+ var collectOperators = (condition) => {
56
+ const operators = new Set;
57
+ const traverse = (c) => {
58
+ if (isConditionGroup2(c)) {
59
+ operators.add(c.operator);
60
+ for (const child of c.conditions) {
61
+ traverse(child);
62
+ }
63
+ } else {
64
+ operators.add(c.operator);
65
+ }
66
+ };
67
+ traverse(condition);
68
+ return operators;
69
+ };
70
+ var calculateComplexityScore = (totalConditions, maxDepth, groupCount, uniqueFields) => {
71
+ return totalConditions * 1 + maxDepth * 2 + groupCount * 1.5 + uniqueFields * 0.5;
72
+ };
73
+ var analyzeRuleComplexity = (rule) => {
74
+ const totalConditions = countConditions(rule.conditions);
75
+ const maxDepth = calculateMaxDepth(rule.conditions);
76
+ const groupCount = countConditionGroups(rule.conditions);
77
+ const uniqueFields = collectConditionFields(rule.conditions).size;
78
+ const uniqueOperators = collectOperators(rule.conditions).size;
79
+ const consequenceCount = rule.consequences.length;
80
+ const complexityScore = calculateComplexityScore(totalConditions, maxDepth, groupCount, uniqueFields);
81
+ return {
82
+ ruleId: rule.id,
83
+ ruleName: rule.name,
84
+ totalConditions,
85
+ maxDepth,
86
+ groupCount,
87
+ uniqueFields,
88
+ uniqueOperators,
89
+ consequenceCount,
90
+ complexityScore
91
+ };
92
+ };
93
+ var analyzeRuleSet = (rules) => {
94
+ const complexities = rules.map(analyzeRuleComplexity);
95
+ const allFields = new Set;
96
+ const allOperators = new Set;
97
+ const allConsequenceTypes = new Set;
98
+ const allCategories = new Set;
99
+ const allTags = new Set;
100
+ let totalConditions = 0;
101
+ let totalConsequences = 0;
102
+ let minPriority = Number.POSITIVE_INFINITY;
103
+ let maxPriority = Number.NEGATIVE_INFINITY;
104
+ let enabledCount = 0;
105
+ for (const rule of rules) {
106
+ totalConditions += countConditions(rule.conditions);
107
+ totalConsequences += rule.consequences.length;
108
+ if (rule.priority < minPriority)
109
+ minPriority = rule.priority;
110
+ if (rule.priority > maxPriority)
111
+ maxPriority = rule.priority;
112
+ if (rule.enabled)
113
+ enabledCount++;
114
+ for (const field of collectConditionFields(rule.conditions)) {
115
+ allFields.add(field);
116
+ }
117
+ for (const operator of collectOperators(rule.conditions)) {
118
+ allOperators.add(operator);
119
+ }
120
+ for (const consequence of rule.consequences) {
121
+ allConsequenceTypes.add(consequence.type);
122
+ }
123
+ if (rule.category)
124
+ allCategories.add(rule.category);
125
+ for (const tag of rule.tags)
126
+ allTags.add(tag);
127
+ }
128
+ const averageComplexity = complexities.length > 0 ? complexities.reduce((sum, c) => sum + c.complexityScore, 0) / complexities.length : 0;
129
+ const complexityDistribution = {
130
+ low: complexities.filter((c) => c.complexityScore < 5).length,
131
+ medium: complexities.filter((c) => c.complexityScore >= 5 && c.complexityScore < 15).length,
132
+ high: complexities.filter((c) => c.complexityScore >= 15).length
133
+ };
134
+ return {
135
+ ruleCount: rules.length,
136
+ enabledCount,
137
+ disabledCount: rules.length - enabledCount,
138
+ totalConditions,
139
+ totalConsequences,
140
+ uniqueFields: [...allFields].sort(),
141
+ uniqueOperators: [...allOperators].sort(),
142
+ uniqueConsequenceTypes: [...allConsequenceTypes].sort(),
143
+ uniqueCategories: [...allCategories].sort(),
144
+ uniqueTags: [...allTags].sort(),
145
+ priorityRange: {
146
+ min: minPriority === Number.POSITIVE_INFINITY ? 0 : minPriority,
147
+ max: maxPriority === Number.NEGATIVE_INFINITY ? 0 : maxPriority
148
+ },
149
+ averageComplexity,
150
+ complexityDistribution,
151
+ ruleComplexities: complexities
152
+ };
153
+ };
154
+ var analyzeFieldUsage = (rules) => {
155
+ const fieldMap = new Map;
156
+ for (const rule of rules) {
157
+ const traverse = (c) => {
158
+ if (isConditionGroup2(c)) {
159
+ for (const child of c.conditions) {
160
+ traverse(child);
161
+ }
162
+ } else {
163
+ const existing = fieldMap.get(c.field) ?? {
164
+ types: new Set,
165
+ operators: new Set,
166
+ rules: []
167
+ };
168
+ existing.types.add(c.type);
169
+ existing.operators.add(c.operator);
170
+ if (!existing.rules.some((r) => r.id === rule.id)) {
171
+ existing.rules.push({ id: rule.id, name: rule.name });
172
+ }
173
+ fieldMap.set(c.field, existing);
174
+ }
175
+ };
176
+ traverse(rule.conditions);
177
+ }
178
+ return [...fieldMap.entries()].map(([field, data]) => ({
179
+ field,
180
+ count: data.rules.length,
181
+ types: [...data.types].sort(),
182
+ operators: [...data.operators].sort(),
183
+ rules: data.rules
184
+ })).sort((a, b) => b.count - a.count);
185
+ };
186
+ var analyzeOperatorUsage = (rules) => {
187
+ const operatorMap = new Map;
188
+ for (const rule of rules) {
189
+ const traverse = (c) => {
190
+ if (isConditionGroup2(c)) {
191
+ for (const child of c.conditions) {
192
+ traverse(child);
193
+ }
194
+ } else {
195
+ const key = `${c.type}:${c.operator}`;
196
+ const existing = operatorMap.get(key) ?? {
197
+ type: c.type,
198
+ rules: []
199
+ };
200
+ if (!existing.rules.some((r) => r.id === rule.id)) {
201
+ existing.rules.push({ id: rule.id, name: rule.name });
202
+ }
203
+ operatorMap.set(key, existing);
204
+ }
205
+ };
206
+ traverse(rule.conditions);
207
+ }
208
+ return [...operatorMap.entries()].map(([key, data]) => {
209
+ const parts = key.split(":");
210
+ return {
211
+ operator: parts[1] ?? "",
212
+ type: data.type,
213
+ count: data.rules.length,
214
+ rules: data.rules
215
+ };
216
+ }).sort((a, b) => b.count - a.count);
217
+ };
218
+ var analyzeConsequenceUsage = (rules) => {
219
+ const consequenceMap = new Map;
220
+ for (const rule of rules) {
221
+ for (const consequence of rule.consequences) {
222
+ const type = consequence.type;
223
+ const existing = consequenceMap.get(type) ?? [];
224
+ if (!existing.some((r) => r.id === rule.id)) {
225
+ existing.push({ id: rule.id, name: rule.name });
226
+ }
227
+ consequenceMap.set(type, existing);
228
+ }
229
+ }
230
+ return [...consequenceMap.entries()].map(([type, rules2]) => ({
231
+ type,
232
+ count: rules2.length,
233
+ rules: rules2
234
+ })).sort((a, b) => b.count - a.count);
235
+ };
236
+ var findMostComplexRules = (rules, limit = 10) => {
237
+ return rules.map(analyzeRuleComplexity).sort((a, b) => b.complexityScore - a.complexityScore).slice(0, limit);
238
+ };
239
+ var findLeastUsedFields = (rules, limit = 10) => {
240
+ return [...analyzeFieldUsage(rules)].sort((a, b) => a.count - b.count).slice(0, limit);
241
+ };
242
+ var formatRuleSetAnalysis = (analysis) => {
243
+ const lines = [
244
+ "=== Rule Set Analysis ===",
245
+ "",
246
+ `Rules: ${analysis.ruleCount} (${analysis.enabledCount} enabled, ${analysis.disabledCount} disabled)`,
247
+ `Total Conditions: ${analysis.totalConditions}`,
248
+ `Total Consequences: ${analysis.totalConsequences}`,
249
+ "",
250
+ `Unique Fields: ${analysis.uniqueFields.length}`,
251
+ ` ${analysis.uniqueFields.join(", ") || "(none)"}`,
252
+ "",
253
+ `Unique Operators: ${analysis.uniqueOperators.length}`,
254
+ ` ${analysis.uniqueOperators.join(", ") || "(none)"}`,
255
+ "",
256
+ `Consequence Types: ${analysis.uniqueConsequenceTypes.length}`,
257
+ ` ${analysis.uniqueConsequenceTypes.join(", ") || "(none)"}`,
258
+ "",
259
+ `Categories: ${analysis.uniqueCategories.length}`,
260
+ ` ${analysis.uniqueCategories.join(", ") || "(none)"}`,
261
+ "",
262
+ `Tags: ${analysis.uniqueTags.length}`,
263
+ ` ${analysis.uniqueTags.join(", ") || "(none)"}`,
264
+ "",
265
+ `Priority Range: ${analysis.priorityRange.min} - ${analysis.priorityRange.max}`,
266
+ "",
267
+ `Average Complexity: ${analysis.averageComplexity.toFixed(2)}`,
268
+ `Complexity Distribution:`,
269
+ ` Low (< 5): ${analysis.complexityDistribution.low}`,
270
+ ` Medium (5-15): ${analysis.complexityDistribution.medium}`,
271
+ ` High (> 15): ${analysis.complexityDistribution.high}`
272
+ ];
273
+ return lines.join(`
274
+ `);
275
+ };
276
+ // src/builder/conditions.ts
277
+ var conditionIdCounter = 0;
278
+ var groupIdCounter = 0;
279
+ var generateConditionId = () => `cond-${++conditionIdCounter}`;
280
+ var generateGroupId = () => `group-${++groupIdCounter}`;
281
+ var resetBuilderIds = () => {
282
+ conditionIdCounter = 0;
283
+ groupIdCounter = 0;
284
+ };
285
+ var createConditionBuilder = (state = { conditions: [] }, operator = "AND") => {
286
+ const addCondition = (condition) => {
287
+ return createConditionBuilder({ conditions: [...state.conditions, condition] }, operator);
288
+ };
289
+ return {
290
+ number: (field, op, value) => addCondition({
291
+ id: generateConditionId(),
292
+ type: "number",
293
+ field,
294
+ operator: op,
295
+ value
296
+ }),
297
+ string: (field, op, value) => addCondition({
298
+ id: generateConditionId(),
299
+ type: "string",
300
+ field,
301
+ operator: op,
302
+ value
303
+ }),
304
+ boolean: (field, op, value) => addCondition({
305
+ id: generateConditionId(),
306
+ type: "boolean",
307
+ field,
308
+ operator: op,
309
+ value
310
+ }),
311
+ date: (field, op, value) => {
312
+ const normalizedValue = value instanceof Date ? value.toISOString() : Array.isArray(value) ? value.map((v) => v instanceof Date ? v.toISOString() : v) : value;
313
+ return addCondition({
314
+ id: generateConditionId(),
315
+ type: "date",
316
+ field,
317
+ operator: op,
318
+ value: normalizedValue
319
+ });
320
+ },
321
+ array: (field, op, value) => addCondition({
322
+ id: generateConditionId(),
323
+ type: "array",
324
+ field,
325
+ operator: op,
326
+ value
327
+ }),
328
+ ref: (field, op, valueRef) => addCondition({
329
+ id: generateConditionId(),
330
+ type: "number",
331
+ field,
332
+ operator: op,
333
+ valueRef
334
+ }),
335
+ and: (builderFn) => {
336
+ const nestedBuilder = createConditionBuilder({ conditions: [] }, "AND");
337
+ const result = builderFn(nestedBuilder);
338
+ return addCondition(result.build());
339
+ },
340
+ or: (builderFn) => {
341
+ const nestedBuilder = createConditionBuilder({ conditions: [] }, "OR");
342
+ const result = builderFn(nestedBuilder);
343
+ return addCondition(result.build());
344
+ },
345
+ raw: (condition) => addCondition(condition),
346
+ build: () => ({
347
+ id: generateGroupId(),
348
+ operator,
349
+ conditions: state.conditions
350
+ }),
351
+ getConditions: () => state.conditions
352
+ };
353
+ };
354
+ var conditions = () => createConditionBuilder({ conditions: [] }, "AND");
355
+ var and = (builderFn) => {
356
+ const builder = createConditionBuilder({ conditions: [] }, "AND");
357
+ return builderFn(builder).build();
358
+ };
359
+ var or = (builderFn) => {
360
+ const builder = createConditionBuilder({ conditions: [] }, "OR");
361
+ return builderFn(builder).build();
362
+ };
363
+ var all = (...items) => ({
364
+ id: generateGroupId(),
365
+ operator: "AND",
366
+ conditions: items
367
+ });
368
+ var any = (...items) => ({
369
+ id: generateGroupId(),
370
+ operator: "OR",
371
+ conditions: items
372
+ });
373
+ var num = (field, operator, value) => ({
374
+ id: generateConditionId(),
375
+ type: "number",
376
+ field,
377
+ operator,
378
+ value
379
+ });
380
+ var str = (field, operator, value) => ({
381
+ id: generateConditionId(),
382
+ type: "string",
383
+ field,
384
+ operator,
385
+ value
386
+ });
387
+ var bool = (field, operator, value) => ({
388
+ id: generateConditionId(),
389
+ type: "boolean",
390
+ field,
391
+ operator,
392
+ value
393
+ });
394
+ var date = (field, operator, value) => {
395
+ const normalizedValue = value instanceof Date ? value.toISOString() : Array.isArray(value) ? value.map((v) => v instanceof Date ? v.toISOString() : v) : value;
396
+ return {
397
+ id: generateConditionId(),
398
+ type: "date",
399
+ field,
400
+ operator,
401
+ value: normalizedValue
402
+ };
403
+ };
404
+ var arr = (field, operator, value) => ({
405
+ id: generateConditionId(),
406
+ type: "array",
407
+ field,
408
+ operator,
409
+ value
410
+ });
411
+ // src/builder/rule.ts
412
+ var createRuleBuilder = (state = {
413
+ consequences: [],
414
+ priority: 0,
415
+ enabled: true,
416
+ stopOnMatch: false,
417
+ tags: []
418
+ }) => {
419
+ const update = (updates) => {
420
+ return createRuleBuilder({ ...state, ...updates });
421
+ };
422
+ return {
423
+ id: (id) => update({ id }),
424
+ named: (name) => update({ name }),
425
+ describedAs: (description) => update({ description }),
426
+ when: (conditionsOrBuilder) => {
427
+ if (typeof conditionsOrBuilder === "function") {
428
+ const builder = conditions();
429
+ const result = conditionsOrBuilder(builder);
430
+ return update({ conditions: result.build() });
431
+ }
432
+ return update({ conditions: conditionsOrBuilder });
433
+ },
434
+ then: (type, payload) => {
435
+ return update({
436
+ consequences: [
437
+ ...state.consequences,
438
+ { type, payload }
439
+ ]
440
+ });
441
+ },
442
+ withPriority: (priority) => update({ priority }),
443
+ enabled: (enabled = true) => update({ enabled }),
444
+ disabled: () => update({ enabled: false }),
445
+ stopOnMatch: (stop = true) => update({ stopOnMatch: stop }),
446
+ tagged: (...tags) => update({ tags: [...state.tags, ...tags] }),
447
+ inCategory: (category) => update({ category }),
448
+ withMetadata: (metadata) => update({ metadata: { ...state.metadata, ...metadata } }),
449
+ build: () => {
450
+ if (!state.name) {
451
+ throw new Error("Rule must have a name");
452
+ }
453
+ if (!state.conditions) {
454
+ throw new Error("Rule must have conditions");
455
+ }
456
+ if (state.consequences.length === 0) {
457
+ throw new Error("Rule must have at least one consequence");
458
+ }
459
+ return {
460
+ id: state.id,
461
+ name: state.name,
462
+ description: state.description,
463
+ conditions: state.conditions,
464
+ consequences: state.consequences,
465
+ priority: state.priority,
466
+ enabled: state.enabled,
467
+ stopOnMatch: state.stopOnMatch,
468
+ tags: state.tags,
469
+ category: state.category,
470
+ metadata: state.metadata
471
+ };
472
+ },
473
+ getState: () => state
474
+ };
475
+ };
476
+ var rule = () => {
477
+ return createRuleBuilder();
478
+ };
479
+ var createRule = (builderFn) => {
480
+ const builder = createRuleBuilder();
481
+ return builderFn(builder).build();
482
+ };
483
+ // src/cache/cache.ts
484
+ var createCache = (options) => {
485
+ const entries = new Map;
486
+ let hits = 0;
487
+ let misses = 0;
488
+ let evictions = 0;
489
+ const isExpired = (entry) => {
490
+ return Date.now() > entry.expiresAt;
491
+ };
492
+ const evictExpired = () => {
493
+ const now = Date.now();
494
+ for (const [key, entry] of entries) {
495
+ if (now > entry.expiresAt) {
496
+ entries.delete(key);
497
+ evictions++;
498
+ options.onEvict?.(key, entry.value);
499
+ }
500
+ }
501
+ };
502
+ const evictOldest = () => {
503
+ if (entries.size === 0)
504
+ return;
505
+ const oldestKey = entries.keys().next().value;
506
+ if (oldestKey !== undefined) {
507
+ const entry = entries.get(oldestKey);
508
+ entries.delete(oldestKey);
509
+ evictions++;
510
+ if (entry) {
511
+ options.onEvict?.(oldestKey, entry.value);
512
+ }
513
+ }
514
+ };
515
+ const get = (key) => {
516
+ const entry = entries.get(key);
517
+ if (!entry) {
518
+ misses++;
519
+ return;
520
+ }
521
+ if (isExpired(entry)) {
522
+ entries.delete(key);
523
+ evictions++;
524
+ options.onEvict?.(key, entry.value);
525
+ misses++;
526
+ return;
527
+ }
528
+ hits++;
529
+ return entry.value;
530
+ };
531
+ const set = (key, value) => {
532
+ evictExpired();
533
+ while (entries.size >= options.maxSize) {
534
+ evictOldest();
535
+ }
536
+ const now = Date.now();
537
+ entries.set(key, {
538
+ value,
539
+ expiresAt: now + options.ttl,
540
+ createdAt: now
541
+ });
542
+ };
543
+ const has = (key) => {
544
+ const entry = entries.get(key);
545
+ if (!entry)
546
+ return false;
547
+ if (isExpired(entry)) {
548
+ entries.delete(key);
549
+ evictions++;
550
+ options.onEvict?.(key, entry.value);
551
+ return false;
552
+ }
553
+ return true;
554
+ };
555
+ const deleteEntry = (key) => {
556
+ return entries.delete(key);
557
+ };
558
+ const clear = () => {
559
+ entries.clear();
560
+ };
561
+ const getStats = () => {
562
+ const totalRequests = hits + misses;
563
+ return {
564
+ size: entries.size,
565
+ maxSize: options.maxSize,
566
+ hits,
567
+ misses,
568
+ hitRate: totalRequests > 0 ? hits / totalRequests : 0,
569
+ evictions
570
+ };
571
+ };
572
+ return {
573
+ get,
574
+ set,
575
+ has,
576
+ delete: deleteEntry,
577
+ clear,
578
+ getStats
579
+ };
580
+ };
581
+ // src/cache/noop.ts
582
+ var createNoopCache = () => {
583
+ return {
584
+ get: () => {
585
+ return;
586
+ },
587
+ set: () => {},
588
+ has: () => false,
589
+ delete: () => false,
590
+ clear: () => {},
591
+ getStats: () => ({
592
+ size: 0,
593
+ maxSize: 0,
594
+ hits: 0,
595
+ misses: 0,
596
+ hitRate: 0,
597
+ evictions: 0
598
+ })
599
+ };
600
+ };
601
+ // src/utils/time.ts
602
+ var measureTime = (fn) => {
603
+ const start = performance.now();
604
+ const result = fn();
605
+ const durationMs = performance.now() - start;
606
+ return { result, durationMs };
607
+ };
608
+ var measureTimeAsync = async (fn) => {
609
+ const start = performance.now();
610
+ const result = await fn();
611
+ const durationMs = performance.now() - start;
612
+ return { result, durationMs };
613
+ };
614
+ var withTimeout = (promise, timeoutMs, errorMessage = "Operation timed out") => {
615
+ return new Promise((resolve, reject) => {
616
+ const timer = setTimeout(() => {
617
+ reject(new Error(errorMessage));
618
+ }, timeoutMs);
619
+ promise.then((result) => {
620
+ clearTimeout(timer);
621
+ resolve(result);
622
+ }).catch((error) => {
623
+ clearTimeout(timer);
624
+ reject(error);
625
+ });
626
+ });
627
+ };
628
+
629
+ // src/core/evaluate.ts
630
+ var evaluateRule = (rule2, context, evaluator, options = {}) => {
631
+ if (options.skipDisabled && !rule2.enabled) {
632
+ return {
633
+ ruleId: rule2.id,
634
+ ruleName: rule2.name,
635
+ matched: false,
636
+ conditionResult: createEmptyGroupResult(rule2.conditions.id),
637
+ consequences: [],
638
+ evaluationTimeMs: 0,
639
+ skipped: true,
640
+ skipReason: "Rule is disabled"
641
+ };
642
+ }
643
+ const { result: conditionResult, durationMs } = measureTime(() => {
644
+ try {
645
+ const evalContext = {
646
+ data: context.data,
647
+ metadata: context.metadata
648
+ };
649
+ return evaluator.evaluateConditionGroup(rule2.conditions, evalContext);
650
+ } catch (error) {
651
+ return {
652
+ error,
653
+ result: createEmptyGroupResult(rule2.conditions.id)
654
+ };
655
+ }
656
+ });
657
+ if ("error" in conditionResult) {
658
+ return {
659
+ ruleId: rule2.id,
660
+ ruleName: rule2.name,
661
+ matched: false,
662
+ conditionResult: conditionResult.result,
663
+ consequences: [],
664
+ evaluationTimeMs: durationMs,
665
+ skipped: false,
666
+ error: conditionResult.error instanceof Error ? conditionResult.error : new Error(String(conditionResult.error))
667
+ };
668
+ }
669
+ const matched = conditionResult.passed;
670
+ const consequences = matched ? rule2.consequences.map((consequence) => ({
671
+ type: consequence.type,
672
+ payload: consequence.payload,
673
+ ruleId: rule2.id,
674
+ ruleName: rule2.name,
675
+ priority: rule2.priority
676
+ })) : [];
677
+ return {
678
+ ruleId: rule2.id,
679
+ ruleName: rule2.name,
680
+ matched,
681
+ conditionResult,
682
+ consequences,
683
+ evaluationTimeMs: durationMs,
684
+ skipped: false
685
+ };
686
+ };
687
+ var createEmptyGroupResult = (groupId) => ({
688
+ groupId,
689
+ operator: "AND",
690
+ passed: false,
691
+ results: []
692
+ });
693
+ var evaluateRules = (rules, context, evaluator, options = {}) => {
694
+ const config = {
695
+ conflictResolution: options.config?.conflictResolution ?? "priority",
696
+ continueOnError: options.config?.continueOnError ?? true,
697
+ collectAllConsequences: options.config?.collectAllConsequences ?? true
698
+ };
699
+ const results = [];
700
+ const matchedRules = [];
701
+ const consequences = [];
702
+ let stoppedEarly = false;
703
+ let stoppedByRuleId;
704
+ for (const rule2 of rules) {
705
+ const result = evaluateRule(rule2, context, evaluator, { skipDisabled: true });
706
+ results.push(result);
707
+ options.onRuleEvaluated?.(result);
708
+ if (result.error && !config.continueOnError) {
709
+ break;
710
+ }
711
+ if (result.matched) {
712
+ matchedRules.push(rule2);
713
+ consequences.push(...result.consequences);
714
+ if (rule2.stopOnMatch) {
715
+ stoppedEarly = true;
716
+ stoppedByRuleId = rule2.id;
717
+ break;
718
+ }
719
+ if (config.conflictResolution === "first-match") {
720
+ stoppedEarly = true;
721
+ stoppedByRuleId = rule2.id;
722
+ break;
723
+ }
724
+ }
725
+ }
726
+ return {
727
+ results,
728
+ matchedRules,
729
+ consequences,
730
+ stoppedEarly,
731
+ stoppedByRuleId
732
+ };
733
+ };
734
+ // src/core/filter.ts
735
+ var filterRules = (filters) => {
736
+ return (rules) => {
737
+ return rules.filter((rule2) => {
738
+ if (filters.enabled !== undefined && rule2.enabled !== filters.enabled) {
739
+ return false;
740
+ }
741
+ if (filters.tags && filters.tags.length > 0) {
742
+ const hasMatchingTag = filters.tags.some((tag) => rule2.tags.includes(tag));
743
+ if (!hasMatchingTag) {
744
+ return false;
745
+ }
746
+ }
747
+ if (filters.category !== undefined && rule2.category !== filters.category) {
748
+ return false;
749
+ }
750
+ if (filters.ids && filters.ids.length > 0) {
751
+ if (!filters.ids.includes(rule2.id)) {
752
+ return false;
753
+ }
754
+ }
755
+ return true;
756
+ });
757
+ };
758
+ };
759
+ var filterByEnabled = (enabled) => {
760
+ return filterRules({ enabled });
761
+ };
762
+ var filterByTags = (tags) => {
763
+ return filterRules({ tags });
764
+ };
765
+ var filterByCategory = (category) => {
766
+ return filterRules({ category });
767
+ };
768
+ var filterByIds = (ids) => {
769
+ return filterRules({ ids });
770
+ };
771
+ // src/core/group.ts
772
+ var groupRules = (field) => {
773
+ return (rules) => {
774
+ const groups = new Map;
775
+ for (const rule2 of rules) {
776
+ let key;
777
+ switch (field) {
778
+ case "category":
779
+ key = rule2.category ?? "uncategorized";
780
+ break;
781
+ case "priority":
782
+ key = rule2.priority;
783
+ break;
784
+ case "enabled":
785
+ key = rule2.enabled;
786
+ break;
787
+ }
788
+ const existing = groups.get(key);
789
+ if (existing) {
790
+ existing.push(rule2);
791
+ } else {
792
+ groups.set(key, [rule2]);
793
+ }
794
+ }
795
+ return groups;
796
+ };
797
+ };
798
+ var groupByCategory = () => {
799
+ return groupRules("category");
800
+ };
801
+ var groupByPriority = () => {
802
+ return groupRules("priority");
803
+ };
804
+ var groupByEnabled = () => {
805
+ return groupRules("enabled");
806
+ };
807
+ var groupByCustom = (keyFn) => {
808
+ return (rules) => {
809
+ const groups = new Map;
810
+ for (const rule2 of rules) {
811
+ const key = keyFn(rule2);
812
+ const existing = groups.get(key);
813
+ if (existing) {
814
+ existing.push(rule2);
815
+ } else {
816
+ groups.set(key, [rule2]);
817
+ }
818
+ }
819
+ return groups;
820
+ };
821
+ };
822
+ // src/core/sort.ts
823
+ var sortRules = (options) => {
824
+ const normalizedOptions = typeof options === "string" ? { field: options, direction: "desc" } : options;
825
+ const { field, direction = "desc" } = normalizedOptions;
826
+ return (rules) => {
827
+ const sorted = [...rules].sort((a, b) => {
828
+ let comparison = 0;
829
+ switch (field) {
830
+ case "priority":
831
+ comparison = a.priority - b.priority;
832
+ break;
833
+ case "name":
834
+ comparison = a.name.localeCompare(b.name);
835
+ break;
836
+ case "createdAt":
837
+ comparison = a.createdAt.getTime() - b.createdAt.getTime();
838
+ break;
839
+ case "updatedAt":
840
+ comparison = a.updatedAt.getTime() - b.updatedAt.getTime();
841
+ break;
842
+ }
843
+ return direction === "desc" ? -comparison : comparison;
844
+ });
845
+ return sorted;
846
+ };
847
+ };
848
+ var sortByPriority = (direction = "desc") => {
849
+ return sortRules({ field: "priority", direction });
850
+ };
851
+ var sortByName = (direction = "asc") => {
852
+ return sortRules({ field: "name", direction });
853
+ };
854
+ var sortByCreatedAt = (direction = "desc") => {
855
+ return sortRules({ field: "createdAt", direction });
856
+ };
857
+ var sortByUpdatedAt = (direction = "desc") => {
858
+ return sortRules({ field: "updatedAt", direction });
859
+ };
860
+ // src/engine/engine.ts
861
+ import { createEvaluator } from "@f-o-t/condition-evaluator";
862
+
863
+ // src/types/config.ts
864
+ import { z } from "zod";
865
+ var LogLevelSchema = z.enum([
866
+ "none",
867
+ "error",
868
+ "warn",
869
+ "info",
870
+ "debug"
871
+ ]);
872
+ var CacheConfigSchema = z.object({
873
+ enabled: z.boolean().default(true),
874
+ ttl: z.number().int().positive().default(60000),
875
+ maxSize: z.number().int().positive().default(1000)
876
+ });
877
+ var ValidationConfigSchema = z.object({
878
+ enabled: z.boolean().default(true),
879
+ strict: z.boolean().default(false)
880
+ });
881
+ var VersioningConfigSchema = z.object({
882
+ enabled: z.boolean().default(false),
883
+ maxVersions: z.number().int().positive().default(10)
884
+ });
885
+ var parseCacheConfig = (input) => CacheConfigSchema.parse(input ?? {});
886
+ var parseValidationConfig = (input) => ValidationConfigSchema.parse(input ?? {});
887
+ var parseVersioningConfig = (input) => VersioningConfigSchema.parse(input ?? {});
888
+ var getDefaultCacheConfig = () => CacheConfigSchema.parse({});
889
+ var getDefaultValidationConfig = () => ValidationConfigSchema.parse({});
890
+ var getDefaultVersioningConfig = () => VersioningConfigSchema.parse({});
891
+ var getDefaultLogLevel = () => "warn";
892
+ var getDefaultConflictResolution = () => "priority";
893
+
894
+ // src/types/state.ts
895
+ import { z as z2 } from "zod";
896
+ var RuleStatsSchema = z2.object({
897
+ evaluations: z2.number().int().nonnegative(),
898
+ matches: z2.number().int().nonnegative(),
899
+ errors: z2.number().int().nonnegative(),
900
+ totalTimeMs: z2.number().nonnegative(),
901
+ avgTimeMs: z2.number().nonnegative(),
902
+ lastEvaluated: z2.date().optional()
903
+ });
904
+ var CacheStatsSchema = z2.object({
905
+ size: z2.number().int().nonnegative(),
906
+ maxSize: z2.number().int().positive(),
907
+ hits: z2.number().int().nonnegative(),
908
+ misses: z2.number().int().nonnegative(),
909
+ hitRate: z2.number().min(0).max(1),
910
+ evictions: z2.number().int().nonnegative()
911
+ });
912
+ var EngineStatsSchema = z2.object({
913
+ totalRules: z2.number().int().nonnegative(),
914
+ enabledRules: z2.number().int().nonnegative(),
915
+ disabledRules: z2.number().int().nonnegative(),
916
+ totalRuleSets: z2.number().int().nonnegative(),
917
+ totalEvaluations: z2.number().int().nonnegative(),
918
+ totalMatches: z2.number().int().nonnegative(),
919
+ totalErrors: z2.number().int().nonnegative(),
920
+ avgEvaluationTimeMs: z2.number().nonnegative(),
921
+ cacheHits: z2.number().int().nonnegative(),
922
+ cacheMisses: z2.number().int().nonnegative(),
923
+ cacheHitRate: z2.number().min(0).max(1)
924
+ });
925
+ var createInitialState = () => ({
926
+ rules: new Map,
927
+ ruleSets: new Map,
928
+ ruleOrder: []
929
+ });
930
+ var createInitialOptimizerState = () => ({
931
+ ruleStats: new Map,
932
+ lastOptimized: undefined
933
+ });
934
+ var createInitialRuleStats = () => ({
935
+ evaluations: 0,
936
+ matches: 0,
937
+ errors: 0,
938
+ totalTimeMs: 0,
939
+ avgTimeMs: 0,
940
+ lastEvaluated: undefined
941
+ });
942
+
943
+ // src/utils/hash.ts
944
+ var hashContext = (context) => {
945
+ const str2 = JSON.stringify(context, (_, value) => {
946
+ if (value instanceof Date) {
947
+ return value.toISOString();
948
+ }
949
+ if (value instanceof Map) {
950
+ return Object.fromEntries(value);
951
+ }
952
+ if (value instanceof Set) {
953
+ return Array.from(value);
954
+ }
955
+ return value;
956
+ });
957
+ if (typeof Bun !== "undefined" && Bun.hash) {
958
+ return Bun.hash(str2).toString(16);
959
+ }
960
+ let hash = 0;
961
+ for (let i = 0;i < str2.length; i++) {
962
+ const char = str2.charCodeAt(i);
963
+ hash = (hash << 5) - hash + char;
964
+ hash = hash & hash;
965
+ }
966
+ return Math.abs(hash).toString(16);
967
+ };
968
+ var hashRules = (ruleIds) => {
969
+ return hashContext(ruleIds.slice().sort());
970
+ };
971
+
972
+ // src/utils/id.ts
973
+ var generateId = () => {
974
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
975
+ return crypto.randomUUID();
976
+ }
977
+ return `${Date.now().toString(36)}-${Math.random().toString(36).substring(2, 11)}`;
978
+ };
979
+
980
+ // src/engine/hooks.ts
981
+ var toError = (error) => error instanceof Error ? error : new Error(String(error));
982
+ var executeWithTimeout = async (hookName, hookFn, hooks, timeoutMs) => {
983
+ try {
984
+ const promise = Promise.resolve(hookFn());
985
+ if (timeoutMs !== undefined && timeoutMs > 0) {
986
+ await withTimeout(promise, timeoutMs, `Hook '${hookName}' timed out after ${timeoutMs}ms`);
987
+ } else {
988
+ await promise;
989
+ }
990
+ } catch (error) {
991
+ hooks.onHookError?.(hookName, toError(error));
992
+ }
993
+ };
994
+ var executeBeforeEvaluation = async (hooks, context, rules, timeoutMs) => {
995
+ if (!hooks.beforeEvaluation)
996
+ return;
997
+ await executeWithTimeout("beforeEvaluation", () => hooks.beforeEvaluation?.(context, rules), hooks, timeoutMs);
998
+ };
999
+ var executeAfterEvaluation = async (hooks, result, timeoutMs) => {
1000
+ if (!hooks.afterEvaluation)
1001
+ return;
1002
+ await executeWithTimeout("afterEvaluation", () => hooks.afterEvaluation?.(result), hooks, timeoutMs);
1003
+ };
1004
+ var executeBeforeRuleEvaluation = async (hooks, rule2, context, timeoutMs) => {
1005
+ if (!hooks.beforeRuleEvaluation)
1006
+ return;
1007
+ await executeWithTimeout("beforeRuleEvaluation", () => hooks.beforeRuleEvaluation?.(rule2, context), hooks, timeoutMs);
1008
+ };
1009
+ var executeAfterRuleEvaluation = async (hooks, rule2, result, timeoutMs) => {
1010
+ if (!hooks.afterRuleEvaluation)
1011
+ return;
1012
+ await executeWithTimeout("afterRuleEvaluation", () => hooks.afterRuleEvaluation?.(rule2, result), hooks, timeoutMs);
1013
+ };
1014
+ var executeOnRuleMatch = async (hooks, rule2, context, timeoutMs) => {
1015
+ if (!hooks.onRuleMatch)
1016
+ return;
1017
+ await executeWithTimeout("onRuleMatch", () => hooks.onRuleMatch?.(rule2, context), hooks, timeoutMs);
1018
+ };
1019
+ var executeOnRuleSkip = async (hooks, rule2, reason, timeoutMs) => {
1020
+ if (!hooks.onRuleSkip)
1021
+ return;
1022
+ await executeWithTimeout("onRuleSkip", () => hooks.onRuleSkip?.(rule2, reason), hooks, timeoutMs);
1023
+ };
1024
+ var executeOnRuleError = async (hooks, rule2, ruleError, timeoutMs) => {
1025
+ if (!hooks.onRuleError)
1026
+ return;
1027
+ await executeWithTimeout("onRuleError", () => hooks.onRuleError?.(rule2, ruleError), hooks, timeoutMs);
1028
+ };
1029
+ var executeOnConsequenceCollected = async (hooks, rule2, consequence, timeoutMs) => {
1030
+ if (!hooks.onConsequenceCollected)
1031
+ return;
1032
+ await executeWithTimeout("onConsequenceCollected", () => hooks.onConsequenceCollected?.(rule2, consequence), hooks, timeoutMs);
1033
+ };
1034
+ var executeOnCacheHit = async (hooks, key, result, timeoutMs) => {
1035
+ if (!hooks.onCacheHit)
1036
+ return;
1037
+ await executeWithTimeout("onCacheHit", () => hooks.onCacheHit?.(key, result), hooks, timeoutMs);
1038
+ };
1039
+ var executeOnCacheMiss = async (hooks, key, timeoutMs) => {
1040
+ if (!hooks.onCacheMiss)
1041
+ return;
1042
+ await executeWithTimeout("onCacheMiss", () => hooks.onCacheMiss?.(key), hooks, timeoutMs);
1043
+ };
1044
+ var executeOnSlowRule = async (hooks, rule2, timeMs, threshold, timeoutMs) => {
1045
+ if (!hooks.onSlowRule)
1046
+ return;
1047
+ await executeWithTimeout("onSlowRule", () => hooks.onSlowRule?.(rule2, timeMs, threshold), hooks, timeoutMs);
1048
+ };
1049
+
1050
+ // src/engine/state.ts
1051
+ var addRule = (state, input) => {
1052
+ const now = new Date;
1053
+ const rule2 = {
1054
+ id: input.id ?? generateId(),
1055
+ name: input.name,
1056
+ description: input.description,
1057
+ conditions: input.conditions,
1058
+ consequences: input.consequences.map((c) => ({
1059
+ type: c.type,
1060
+ payload: c.payload
1061
+ })),
1062
+ priority: input.priority ?? 0,
1063
+ enabled: input.enabled ?? true,
1064
+ stopOnMatch: input.stopOnMatch ?? false,
1065
+ tags: input.tags ?? [],
1066
+ category: input.category,
1067
+ metadata: input.metadata,
1068
+ createdAt: now,
1069
+ updatedAt: now
1070
+ };
1071
+ state.rules.set(rule2.id, rule2);
1072
+ if (!state.ruleOrder.includes(rule2.id)) {
1073
+ state.ruleOrder.push(rule2.id);
1074
+ sortRuleOrder(state);
1075
+ }
1076
+ return rule2;
1077
+ };
1078
+ var addRules = (state, inputs) => {
1079
+ return inputs.map((input) => addRule(state, input));
1080
+ };
1081
+ var removeRule = (state, ruleId) => {
1082
+ const deleted = state.rules.delete(ruleId);
1083
+ if (deleted) {
1084
+ const index = state.ruleOrder.indexOf(ruleId);
1085
+ if (index !== -1) {
1086
+ state.ruleOrder.splice(index, 1);
1087
+ }
1088
+ }
1089
+ return deleted;
1090
+ };
1091
+ var updateRule = (state, ruleId, updates) => {
1092
+ const existing = state.rules.get(ruleId);
1093
+ if (!existing)
1094
+ return;
1095
+ const updated = {
1096
+ ...existing,
1097
+ ...updates.name !== undefined && { name: updates.name },
1098
+ ...updates.description !== undefined && {
1099
+ description: updates.description
1100
+ },
1101
+ ...updates.conditions !== undefined && {
1102
+ conditions: updates.conditions
1103
+ },
1104
+ ...updates.consequences !== undefined && {
1105
+ consequences: updates.consequences.map((c) => ({
1106
+ type: c.type,
1107
+ payload: c.payload
1108
+ }))
1109
+ },
1110
+ ...updates.priority !== undefined && { priority: updates.priority },
1111
+ ...updates.enabled !== undefined && { enabled: updates.enabled },
1112
+ ...updates.stopOnMatch !== undefined && {
1113
+ stopOnMatch: updates.stopOnMatch
1114
+ },
1115
+ ...updates.tags !== undefined && { tags: updates.tags },
1116
+ ...updates.category !== undefined && { category: updates.category },
1117
+ ...updates.metadata !== undefined && { metadata: updates.metadata },
1118
+ updatedAt: new Date
1119
+ };
1120
+ state.rules.set(ruleId, updated);
1121
+ if (updates.priority !== undefined) {
1122
+ sortRuleOrder(state);
1123
+ }
1124
+ return updated;
1125
+ };
1126
+ var getRule = (state, ruleId) => {
1127
+ return state.rules.get(ruleId);
1128
+ };
1129
+ var getRules = (state, filters) => {
1130
+ const rules = [];
1131
+ for (const id of state.ruleOrder) {
1132
+ const rule2 = state.rules.get(id);
1133
+ if (!rule2)
1134
+ continue;
1135
+ if (filters) {
1136
+ if (filters.enabled !== undefined && rule2.enabled !== filters.enabled) {
1137
+ continue;
1138
+ }
1139
+ if (filters.tags && filters.tags.length > 0) {
1140
+ const hasTag = filters.tags.some((tag) => rule2.tags.includes(tag));
1141
+ if (!hasTag)
1142
+ continue;
1143
+ }
1144
+ if (filters.category !== undefined && rule2.category !== filters.category) {
1145
+ continue;
1146
+ }
1147
+ if (filters.ids && filters.ids.length > 0 && !filters.ids.includes(rule2.id)) {
1148
+ continue;
1149
+ }
1150
+ }
1151
+ rules.push(rule2);
1152
+ }
1153
+ return rules;
1154
+ };
1155
+ var enableRule = (state, ruleId) => {
1156
+ const rule2 = state.rules.get(ruleId);
1157
+ if (!rule2)
1158
+ return false;
1159
+ state.rules.set(ruleId, { ...rule2, enabled: true, updatedAt: new Date });
1160
+ return true;
1161
+ };
1162
+ var disableRule = (state, ruleId) => {
1163
+ const rule2 = state.rules.get(ruleId);
1164
+ if (!rule2)
1165
+ return false;
1166
+ state.rules.set(ruleId, { ...rule2, enabled: false, updatedAt: new Date });
1167
+ return true;
1168
+ };
1169
+ var clearRules = (state) => {
1170
+ state.rules.clear();
1171
+ state.ruleOrder.length = 0;
1172
+ };
1173
+ var addRuleSet = (state, input) => {
1174
+ const ruleSet = {
1175
+ id: input.id ?? generateId(),
1176
+ name: input.name,
1177
+ description: input.description,
1178
+ ruleIds: input.ruleIds,
1179
+ enabled: input.enabled ?? true,
1180
+ metadata: input.metadata
1181
+ };
1182
+ state.ruleSets.set(ruleSet.id, ruleSet);
1183
+ return ruleSet;
1184
+ };
1185
+ var getRuleSet = (state, ruleSetId) => {
1186
+ return state.ruleSets.get(ruleSetId);
1187
+ };
1188
+ var getRuleSets = (state) => {
1189
+ return Array.from(state.ruleSets.values());
1190
+ };
1191
+ var removeRuleSet = (state, ruleSetId) => {
1192
+ return state.ruleSets.delete(ruleSetId);
1193
+ };
1194
+ var getRulesInSet = (state, ruleSetId) => {
1195
+ const ruleSet = state.ruleSets.get(ruleSetId);
1196
+ if (!ruleSet || !ruleSet.enabled)
1197
+ return [];
1198
+ const rules = [];
1199
+ for (const ruleId of ruleSet.ruleIds) {
1200
+ const rule2 = state.rules.get(ruleId);
1201
+ if (rule2) {
1202
+ rules.push(rule2);
1203
+ }
1204
+ }
1205
+ return rules;
1206
+ };
1207
+ var sortRuleOrder = (state) => {
1208
+ state.ruleOrder.sort((a, b) => {
1209
+ const ruleA = state.rules.get(a);
1210
+ const ruleB = state.rules.get(b);
1211
+ if (!ruleA || !ruleB)
1212
+ return 0;
1213
+ return ruleB.priority - ruleA.priority;
1214
+ });
1215
+ };
1216
+ var cloneState = (state) => {
1217
+ const newState = createInitialState();
1218
+ for (const [id, rule2] of state.rules) {
1219
+ newState.rules.set(id, { ...rule2 });
1220
+ }
1221
+ for (const [id, ruleSet] of state.ruleSets) {
1222
+ newState.ruleSets.set(id, { ...ruleSet, ruleIds: [...ruleSet.ruleIds] });
1223
+ }
1224
+ newState.ruleOrder.push(...state.ruleOrder);
1225
+ return newState;
1226
+ };
1227
+
1228
+ // src/engine/engine.ts
1229
+ var resolveConfig = (config) => {
1230
+ if (!config.evaluator && !config.operators) {
1231
+ throw new Error("Engine requires either 'evaluator' or 'operators' config. " + "Pass { evaluator: createEvaluator() } for built-in operators only, " + "or { operators: customOperators } to use custom operators.");
1232
+ }
1233
+ const evaluator = config.evaluator ? config.evaluator : createEvaluator({ operators: config.operators });
1234
+ return {
1235
+ consequences: config.consequences,
1236
+ conflictResolution: config.conflictResolution ?? getDefaultConflictResolution(),
1237
+ cache: {
1238
+ ...getDefaultCacheConfig(),
1239
+ ...config.cache
1240
+ },
1241
+ validation: {
1242
+ ...getDefaultValidationConfig(),
1243
+ ...config.validation
1244
+ },
1245
+ versioning: {
1246
+ ...getDefaultVersioningConfig(),
1247
+ ...config.versioning
1248
+ },
1249
+ hooks: config.hooks ?? {},
1250
+ logLevel: config.logLevel ?? getDefaultLogLevel(),
1251
+ logger: config.logger ?? console,
1252
+ continueOnError: config.continueOnError ?? true,
1253
+ slowRuleThresholdMs: config.slowRuleThresholdMs ?? 10,
1254
+ hookTimeoutMs: config.hookTimeoutMs,
1255
+ evaluator
1256
+ };
1257
+ };
1258
+ var createEngine = (config = {}) => {
1259
+ const resolvedConfig = resolveConfig(config);
1260
+ const state = createInitialState();
1261
+ const cache = resolvedConfig.cache.enabled ? createCache({
1262
+ ttl: resolvedConfig.cache.ttl,
1263
+ maxSize: resolvedConfig.cache.maxSize
1264
+ }) : createNoopCache();
1265
+ let totalEvaluations = 0;
1266
+ let totalMatches = 0;
1267
+ let totalErrors = 0;
1268
+ let cacheHits = 0;
1269
+ let cacheMisses = 0;
1270
+ let totalEvaluationTime = 0;
1271
+ const evaluate = async (contextData, options = {}) => {
1272
+ const context = {
1273
+ data: contextData,
1274
+ timestamp: new Date,
1275
+ correlationId: generateId()
1276
+ };
1277
+ let rulesToEvaluate = getRules(state, {
1278
+ enabled: options.skipDisabled !== false ? true : undefined,
1279
+ tags: options.tags,
1280
+ category: options.category
1281
+ });
1282
+ if (options.ruleSetId) {
1283
+ const ruleSetRules = getRulesInSet(state, options.ruleSetId);
1284
+ const ruleSetIds = new Set(ruleSetRules.map((r) => r.id));
1285
+ rulesToEvaluate = rulesToEvaluate.filter((r) => ruleSetIds.has(r.id));
1286
+ }
1287
+ if (options.maxRules && options.maxRules > 0) {
1288
+ rulesToEvaluate = rulesToEvaluate.slice(0, options.maxRules);
1289
+ }
1290
+ const cacheKey = !options.bypassCache ? `${hashContext(contextData)}:${hashRules(rulesToEvaluate.map((r) => r.id))}` : null;
1291
+ if (cacheKey && cache.has(cacheKey)) {
1292
+ const cached = cache.get(cacheKey);
1293
+ if (cached) {
1294
+ cacheHits++;
1295
+ await executeOnCacheHit(resolvedConfig.hooks, cacheKey, cached, resolvedConfig.hookTimeoutMs);
1296
+ return { ...cached, cacheHit: true };
1297
+ }
1298
+ }
1299
+ if (cacheKey) {
1300
+ cacheMisses++;
1301
+ await executeOnCacheMiss(resolvedConfig.hooks, cacheKey, resolvedConfig.hookTimeoutMs);
1302
+ }
1303
+ await executeBeforeEvaluation(resolvedConfig.hooks, context, rulesToEvaluate, resolvedConfig.hookTimeoutMs);
1304
+ const { result: evaluationResult, durationMs } = measureTime(() => {
1305
+ const results = [];
1306
+ for (const rule2 of rulesToEvaluate) {
1307
+ const result = evaluateRule(rule2, context, resolvedConfig.evaluator, { skipDisabled: true });
1308
+ results.push({ rule: rule2, result });
1309
+ }
1310
+ return results;
1311
+ });
1312
+ const ruleResults = [];
1313
+ const matchedRules = [];
1314
+ const consequences = [];
1315
+ let stoppedEarly = false;
1316
+ let stoppedByRuleId;
1317
+ let rulesErrored = 0;
1318
+ const conflictResolution = options.conflictResolution ?? resolvedConfig.conflictResolution;
1319
+ for (const { rule: rule2, result } of evaluationResult) {
1320
+ await executeBeforeRuleEvaluation(resolvedConfig.hooks, rule2, context, resolvedConfig.hookTimeoutMs);
1321
+ ruleResults.push(result);
1322
+ if (result.error) {
1323
+ rulesErrored++;
1324
+ await executeOnRuleError(resolvedConfig.hooks, rule2, result.error, resolvedConfig.hookTimeoutMs);
1325
+ if (!resolvedConfig.continueOnError) {
1326
+ break;
1327
+ }
1328
+ }
1329
+ if (result.skipped) {
1330
+ await executeOnRuleSkip(resolvedConfig.hooks, rule2, result.skipReason ?? "Unknown", resolvedConfig.hookTimeoutMs);
1331
+ }
1332
+ if (result.evaluationTimeMs > resolvedConfig.slowRuleThresholdMs) {
1333
+ await executeOnSlowRule(resolvedConfig.hooks, rule2, result.evaluationTimeMs, resolvedConfig.slowRuleThresholdMs, resolvedConfig.hookTimeoutMs);
1334
+ }
1335
+ await executeAfterRuleEvaluation(resolvedConfig.hooks, rule2, result, resolvedConfig.hookTimeoutMs);
1336
+ if (result.matched) {
1337
+ matchedRules.push(rule2);
1338
+ for (const consequence of result.consequences) {
1339
+ consequences.push(consequence);
1340
+ await executeOnConsequenceCollected(resolvedConfig.hooks, rule2, consequence, resolvedConfig.hookTimeoutMs);
1341
+ }
1342
+ await executeOnRuleMatch(resolvedConfig.hooks, rule2, context, resolvedConfig.hookTimeoutMs);
1343
+ if (rule2.stopOnMatch) {
1344
+ stoppedEarly = true;
1345
+ stoppedByRuleId = rule2.id;
1346
+ break;
1347
+ }
1348
+ if (conflictResolution === "first-match") {
1349
+ stoppedEarly = true;
1350
+ stoppedByRuleId = rule2.id;
1351
+ break;
1352
+ }
1353
+ }
1354
+ }
1355
+ totalEvaluations++;
1356
+ totalMatches += matchedRules.length;
1357
+ totalErrors += rulesErrored;
1358
+ totalEvaluationTime += durationMs;
1359
+ const executionResult = {
1360
+ context,
1361
+ results: ruleResults,
1362
+ matchedRules,
1363
+ consequences,
1364
+ totalRulesEvaluated: ruleResults.length,
1365
+ totalRulesMatched: matchedRules.length,
1366
+ totalRulesSkipped: ruleResults.filter((r) => r.skipped).length,
1367
+ totalRulesErrored: rulesErrored,
1368
+ executionTimeMs: durationMs,
1369
+ stoppedEarly,
1370
+ stoppedByRuleId,
1371
+ cacheHit: false
1372
+ };
1373
+ if (cacheKey) {
1374
+ cache.set(cacheKey, executionResult);
1375
+ }
1376
+ await executeAfterEvaluation(resolvedConfig.hooks, executionResult, resolvedConfig.hookTimeoutMs);
1377
+ return executionResult;
1378
+ };
1379
+ return {
1380
+ addRule: (input) => addRule(state, input),
1381
+ addRules: (inputs) => addRules(state, inputs),
1382
+ removeRule: (ruleId) => {
1383
+ const result = removeRule(state, ruleId);
1384
+ if (result)
1385
+ cache.clear();
1386
+ return result;
1387
+ },
1388
+ updateRule: (ruleId, updates) => {
1389
+ const result = updateRule(state, ruleId, updates);
1390
+ if (result)
1391
+ cache.clear();
1392
+ return result;
1393
+ },
1394
+ getRule: (ruleId) => getRule(state, ruleId),
1395
+ getRules: (filters) => getRules(state, filters),
1396
+ enableRule: (ruleId) => {
1397
+ const result = enableRule(state, ruleId);
1398
+ if (result)
1399
+ cache.clear();
1400
+ return result;
1401
+ },
1402
+ disableRule: (ruleId) => {
1403
+ const result = disableRule(state, ruleId);
1404
+ if (result)
1405
+ cache.clear();
1406
+ return result;
1407
+ },
1408
+ clearRules: () => {
1409
+ clearRules(state);
1410
+ cache.clear();
1411
+ },
1412
+ addRuleSet: (input) => addRuleSet(state, input),
1413
+ getRuleSet: (ruleSetId) => getRuleSet(state, ruleSetId),
1414
+ getRuleSets: () => getRuleSets(state),
1415
+ removeRuleSet: (ruleSetId) => removeRuleSet(state, ruleSetId),
1416
+ evaluate,
1417
+ clearCache: () => cache.clear(),
1418
+ getCacheStats: () => cache.getStats(),
1419
+ getState: () => ({
1420
+ rules: state.rules,
1421
+ ruleSets: state.ruleSets,
1422
+ ruleOrder: state.ruleOrder
1423
+ }),
1424
+ getStats: () => {
1425
+ const enabledRules = Array.from(state.rules.values()).filter((r) => r.enabled).length;
1426
+ return {
1427
+ totalRules: state.rules.size,
1428
+ enabledRules,
1429
+ disabledRules: state.rules.size - enabledRules,
1430
+ totalRuleSets: state.ruleSets.size,
1431
+ totalEvaluations,
1432
+ totalMatches,
1433
+ totalErrors,
1434
+ avgEvaluationTimeMs: totalEvaluations > 0 ? totalEvaluationTime / totalEvaluations : 0,
1435
+ cacheHits,
1436
+ cacheMisses,
1437
+ cacheHitRate: cacheHits + cacheMisses > 0 ? cacheHits / (cacheHits + cacheMisses) : 0
1438
+ };
1439
+ }
1440
+ };
1441
+ };
1442
+ // src/optimizer/index-builder.ts
1443
+ var DEFAULT_OPTIONS = {
1444
+ indexByField: true,
1445
+ indexByTag: true,
1446
+ indexByCategory: true,
1447
+ indexByPriority: true
1448
+ };
1449
+ var buildIndex = (rules, options = {}) => {
1450
+ const opts = { ...DEFAULT_OPTIONS, ...options };
1451
+ const byId = new Map;
1452
+ const byField = new Map;
1453
+ const byTag = new Map;
1454
+ const byCategory = new Map;
1455
+ const byPriority = new Map;
1456
+ for (const rule2 of rules) {
1457
+ byId.set(rule2.id, rule2);
1458
+ if (opts.indexByField) {
1459
+ const fields = collectConditionFields(rule2.conditions);
1460
+ for (const field of fields) {
1461
+ const existing = byField.get(field) ?? [];
1462
+ existing.push(rule2);
1463
+ byField.set(field, existing);
1464
+ }
1465
+ }
1466
+ if (opts.indexByTag) {
1467
+ for (const tag of rule2.tags) {
1468
+ const existing = byTag.get(tag) ?? [];
1469
+ existing.push(rule2);
1470
+ byTag.set(tag, existing);
1471
+ }
1472
+ }
1473
+ if (opts.indexByCategory && rule2.category) {
1474
+ const existing = byCategory.get(rule2.category) ?? [];
1475
+ existing.push(rule2);
1476
+ byCategory.set(rule2.category, existing);
1477
+ }
1478
+ if (opts.indexByPriority) {
1479
+ const existing = byPriority.get(rule2.priority) ?? [];
1480
+ existing.push(rule2);
1481
+ byPriority.set(rule2.priority, existing);
1482
+ }
1483
+ }
1484
+ const sortedByPriority = [...rules].sort((a, b) => b.priority - a.priority);
1485
+ return {
1486
+ byField,
1487
+ byTag,
1488
+ byCategory,
1489
+ byPriority,
1490
+ byId,
1491
+ sortedByPriority
1492
+ };
1493
+ };
1494
+ var getRulesByField = (index, field) => {
1495
+ return index.byField.get(field) ?? [];
1496
+ };
1497
+ var getRulesByFields = (index, fields, mode = "any") => {
1498
+ if (fields.length === 0)
1499
+ return [];
1500
+ const ruleSets = fields.map((f) => new Set(getRulesByField(index, f)));
1501
+ if (mode === "any") {
1502
+ const combined = new Set;
1503
+ for (const ruleSet of ruleSets) {
1504
+ for (const rule2 of ruleSet) {
1505
+ combined.add(rule2);
1506
+ }
1507
+ }
1508
+ return [...combined];
1509
+ }
1510
+ const [first, ...rest] = ruleSets;
1511
+ if (!first)
1512
+ return [];
1513
+ return [...first].filter((rule2) => rest.every((set) => set.has(rule2)));
1514
+ };
1515
+ var getRulesByTag = (index, tag) => {
1516
+ return index.byTag.get(tag) ?? [];
1517
+ };
1518
+ var getRulesByTags = (index, tags, mode = "any") => {
1519
+ if (tags.length === 0)
1520
+ return [];
1521
+ const ruleSets = tags.map((t) => new Set(getRulesByTag(index, t)));
1522
+ if (mode === "any") {
1523
+ const combined = new Set;
1524
+ for (const ruleSet of ruleSets) {
1525
+ for (const rule2 of ruleSet) {
1526
+ combined.add(rule2);
1527
+ }
1528
+ }
1529
+ return [...combined];
1530
+ }
1531
+ const [first, ...rest] = ruleSets;
1532
+ if (!first)
1533
+ return [];
1534
+ return [...first].filter((rule2) => rest.every((set) => set.has(rule2)));
1535
+ };
1536
+ var getRulesByCategory = (index, category) => {
1537
+ return index.byCategory.get(category) ?? [];
1538
+ };
1539
+ var getRulesByPriority = (index, priority) => {
1540
+ return index.byPriority.get(priority) ?? [];
1541
+ };
1542
+ var getRulesByPriorityRange = (index, minPriority, maxPriority) => {
1543
+ return index.sortedByPriority.filter((rule2) => rule2.priority >= minPriority && rule2.priority <= maxPriority);
1544
+ };
1545
+ var getRuleById = (index, id) => {
1546
+ return index.byId.get(id);
1547
+ };
1548
+ var filterRulesForContext = (index, contextFields) => {
1549
+ const relevantRules = new Set;
1550
+ for (const field of contextFields) {
1551
+ const rules = index.byField.get(field);
1552
+ if (rules) {
1553
+ for (const rule2 of rules) {
1554
+ relevantRules.add(rule2);
1555
+ }
1556
+ }
1557
+ }
1558
+ return [...relevantRules].sort((a, b) => b.priority - a.priority);
1559
+ };
1560
+ var analyzeOptimizations = (rules) => {
1561
+ const suggestions = [];
1562
+ const fieldUsage = new Map;
1563
+ for (const rule2 of rules) {
1564
+ const fields = collectConditionFields(rule2.conditions);
1565
+ for (const field of fields) {
1566
+ fieldUsage.set(field, (fieldUsage.get(field) ?? 0) + 1);
1567
+ }
1568
+ }
1569
+ const frequentFields = [...fieldUsage.entries()].filter(([, count]) => count > rules.length * 0.5).map(([field]) => field);
1570
+ if (frequentFields.length > 0) {
1571
+ suggestions.push({
1572
+ type: "INDEX",
1573
+ severity: "medium",
1574
+ message: `Consider creating indexes for frequently used fields: ${frequentFields.join(", ")}`,
1575
+ details: {
1576
+ fields: frequentFields,
1577
+ usagePercentage: frequentFields.map((f) => ({
1578
+ field: f,
1579
+ percentage: (fieldUsage.get(f) ?? 0) / rules.length * 100
1580
+ }))
1581
+ }
1582
+ });
1583
+ }
1584
+ const priorityGroups = new Map;
1585
+ for (const rule2 of rules) {
1586
+ const existing = priorityGroups.get(rule2.priority) ?? [];
1587
+ existing.push(rule2);
1588
+ priorityGroups.set(rule2.priority, existing);
1589
+ }
1590
+ const largePriorityGroups = [...priorityGroups.entries()].filter(([, group]) => group.length > 5);
1591
+ if (largePriorityGroups.length > 0) {
1592
+ for (const [priority, group] of largePriorityGroups) {
1593
+ suggestions.push({
1594
+ type: "REORDER",
1595
+ severity: "low",
1596
+ message: `${group.length} rules share priority ${priority}. Consider differentiating priorities for more predictable execution order.`,
1597
+ ruleIds: group.map((r) => r.id),
1598
+ details: { priority, count: group.length }
1599
+ });
1600
+ }
1601
+ }
1602
+ const disabledRules = rules.filter((r) => !r.enabled);
1603
+ if (disabledRules.length > rules.length * 0.3) {
1604
+ suggestions.push({
1605
+ type: "SIMPLIFY",
1606
+ severity: "low",
1607
+ message: `${disabledRules.length} rules (${(disabledRules.length / rules.length * 100).toFixed(1)}%) are disabled. Consider removing unused rules.`,
1608
+ ruleIds: disabledRules.map((r) => r.id),
1609
+ details: {
1610
+ disabledCount: disabledRules.length,
1611
+ totalCount: rules.length
1612
+ }
1613
+ });
1614
+ }
1615
+ if (rules.length > 100) {
1616
+ suggestions.push({
1617
+ type: "CACHE",
1618
+ severity: "high",
1619
+ message: `Rule set contains ${rules.length} rules. Enable caching for better performance.`,
1620
+ details: { ruleCount: rules.length }
1621
+ });
1622
+ }
1623
+ return suggestions;
1624
+ };
1625
+ var getIndexStats = (index) => {
1626
+ const totalRules = index.byId.size;
1627
+ const fieldRuleCounts = [...index.byField.values()].map((r) => r.length);
1628
+ const tagRuleCounts = [...index.byTag.values()].map((r) => r.length);
1629
+ return {
1630
+ totalRules,
1631
+ uniqueFields: index.byField.size,
1632
+ uniqueTags: index.byTag.size,
1633
+ uniqueCategories: index.byCategory.size,
1634
+ uniquePriorities: index.byPriority.size,
1635
+ averageRulesPerField: fieldRuleCounts.length > 0 ? fieldRuleCounts.reduce((a, b) => a + b, 0) / fieldRuleCounts.length : 0,
1636
+ averageRulesPerTag: tagRuleCounts.length > 0 ? tagRuleCounts.reduce((a, b) => a + b, 0) / tagRuleCounts.length : 0
1637
+ };
1638
+ };
1639
+ // src/serialization/serializer.ts
1640
+ var EXPORT_VERSION = "1.0.0";
1641
+ var serializeRule = (rule2) => ({
1642
+ id: rule2.id,
1643
+ name: rule2.name,
1644
+ description: rule2.description,
1645
+ conditions: rule2.conditions,
1646
+ consequences: rule2.consequences.map((c) => ({
1647
+ type: c.type,
1648
+ payload: c.payload
1649
+ })),
1650
+ priority: rule2.priority,
1651
+ enabled: rule2.enabled,
1652
+ stopOnMatch: rule2.stopOnMatch,
1653
+ tags: rule2.tags,
1654
+ category: rule2.category,
1655
+ metadata: rule2.metadata,
1656
+ createdAt: rule2.createdAt.toISOString(),
1657
+ updatedAt: rule2.updatedAt.toISOString()
1658
+ });
1659
+ var deserializeRule = (serialized, options = {}) => {
1660
+ const id = options.generateNewIds ? `${options.idPrefix ?? ""}${generateId()}` : serialized.id;
1661
+ const now = new Date;
1662
+ return {
1663
+ id,
1664
+ name: serialized.name,
1665
+ description: serialized.description,
1666
+ conditions: serialized.conditions,
1667
+ consequences: serialized.consequences,
1668
+ priority: serialized.priority,
1669
+ enabled: serialized.enabled,
1670
+ stopOnMatch: serialized.stopOnMatch,
1671
+ tags: serialized.tags,
1672
+ category: serialized.category,
1673
+ metadata: serialized.metadata,
1674
+ createdAt: options.preserveDates ? new Date(serialized.createdAt) : now,
1675
+ updatedAt: options.preserveDates ? new Date(serialized.updatedAt) : now
1676
+ };
1677
+ };
1678
+ var serializeRuleSet = (ruleSet) => ({
1679
+ id: ruleSet.id,
1680
+ name: ruleSet.name,
1681
+ description: ruleSet.description,
1682
+ ruleIds: ruleSet.ruleIds,
1683
+ enabled: ruleSet.enabled,
1684
+ metadata: ruleSet.metadata
1685
+ });
1686
+ var deserializeRuleSet = (serialized, idMapping, options = {}) => {
1687
+ const id = options.generateNewIds ? `${options.idPrefix ?? ""}${generateId()}` : serialized.id;
1688
+ const ruleIds = serialized.ruleIds.map((oldId) => idMapping.get(oldId) ?? oldId);
1689
+ return {
1690
+ id,
1691
+ name: serialized.name,
1692
+ description: serialized.description,
1693
+ ruleIds,
1694
+ enabled: serialized.enabled,
1695
+ metadata: serialized.metadata
1696
+ };
1697
+ };
1698
+ var exportRules = (rules, ruleSets, metadata) => ({
1699
+ version: EXPORT_VERSION,
1700
+ exportedAt: new Date().toISOString(),
1701
+ rules: rules.map(serializeRule),
1702
+ ruleSets: ruleSets?.map(serializeRuleSet),
1703
+ metadata
1704
+ });
1705
+ var exportToJson = (rules, ruleSets, metadata) => {
1706
+ const exportData = exportRules(rules, ruleSets, metadata);
1707
+ return JSON.stringify(exportData, null, 2);
1708
+ };
1709
+ var importRules = (data, options = {}) => {
1710
+ const rules = [];
1711
+ const ruleSets = [];
1712
+ const errors = [];
1713
+ const idMapping = new Map;
1714
+ for (let i = 0;i < data.rules.length; i++) {
1715
+ try {
1716
+ const serialized = data.rules[i];
1717
+ if (!serialized)
1718
+ continue;
1719
+ const rule2 = deserializeRule(serialized, options);
1720
+ idMapping.set(serialized.id, rule2.id);
1721
+ rules.push(rule2);
1722
+ } catch (error) {
1723
+ errors.push({
1724
+ index: i,
1725
+ type: "rule",
1726
+ message: error instanceof Error ? error.message : String(error)
1727
+ });
1728
+ }
1729
+ }
1730
+ if (data.ruleSets) {
1731
+ for (let i = 0;i < data.ruleSets.length; i++) {
1732
+ try {
1733
+ const serialized = data.ruleSets[i];
1734
+ if (!serialized)
1735
+ continue;
1736
+ const ruleSet = deserializeRuleSet(serialized, idMapping, options);
1737
+ ruleSets.push(ruleSet);
1738
+ } catch (error) {
1739
+ errors.push({
1740
+ index: i,
1741
+ type: "ruleSet",
1742
+ message: error instanceof Error ? error.message : String(error)
1743
+ });
1744
+ }
1745
+ }
1746
+ }
1747
+ const importedRuleIds = new Set(rules.map((r) => r.id));
1748
+ const orphanedReferences = [];
1749
+ for (const ruleSet of ruleSets) {
1750
+ const missingRuleIds = ruleSet.ruleIds.filter((id) => !importedRuleIds.has(id));
1751
+ if (missingRuleIds.length > 0) {
1752
+ orphanedReferences.push({
1753
+ ruleSetId: ruleSet.id,
1754
+ ruleSetName: ruleSet.name,
1755
+ missingRuleIds
1756
+ });
1757
+ }
1758
+ }
1759
+ return {
1760
+ success: errors.length === 0,
1761
+ rules,
1762
+ ruleSets,
1763
+ errors,
1764
+ idMapping,
1765
+ orphanedReferences
1766
+ };
1767
+ };
1768
+ var importFromJson = (json, options = {}) => {
1769
+ try {
1770
+ const data = JSON.parse(json);
1771
+ return importRules(data, options);
1772
+ } catch (error) {
1773
+ return {
1774
+ success: false,
1775
+ rules: [],
1776
+ ruleSets: [],
1777
+ errors: [
1778
+ {
1779
+ index: -1,
1780
+ type: "rule",
1781
+ message: `Invalid JSON: ${error instanceof Error ? error.message : String(error)}`
1782
+ }
1783
+ ],
1784
+ idMapping: new Map,
1785
+ orphanedReferences: []
1786
+ };
1787
+ }
1788
+ };
1789
+ var cloneRule = (rule2, newId, newName) => {
1790
+ const now = new Date;
1791
+ return {
1792
+ ...rule2,
1793
+ id: newId ?? generateId(),
1794
+ name: newName ?? `${rule2.name} (Copy)`,
1795
+ createdAt: now,
1796
+ updatedAt: now
1797
+ };
1798
+ };
1799
+ var mergeRuleSets = (baseRules, incomingRules, strategy = "replace") => {
1800
+ const ruleMap = new Map;
1801
+ for (const rule2 of baseRules) {
1802
+ ruleMap.set(rule2.id, rule2);
1803
+ }
1804
+ for (const rule2 of incomingRules) {
1805
+ const existing = ruleMap.get(rule2.id);
1806
+ if (!existing) {
1807
+ ruleMap.set(rule2.id, rule2);
1808
+ } else {
1809
+ switch (strategy) {
1810
+ case "replace":
1811
+ ruleMap.set(rule2.id, rule2);
1812
+ break;
1813
+ case "skip":
1814
+ break;
1815
+ case "merge": {
1816
+ const merged = {
1817
+ ...existing,
1818
+ ...rule2,
1819
+ tags: [...new Set([...existing.tags, ...rule2.tags])],
1820
+ metadata: { ...existing.metadata, ...rule2.metadata },
1821
+ updatedAt: new Date
1822
+ };
1823
+ ruleMap.set(rule2.id, merged);
1824
+ break;
1825
+ }
1826
+ }
1827
+ }
1828
+ }
1829
+ return [...ruleMap.values()];
1830
+ };
1831
+ var diffRuleSets = (oldRules, newRules) => {
1832
+ const oldMap = new Map(oldRules.map((r) => [r.id, r]));
1833
+ const newMap = new Map(newRules.map((r) => [r.id, r]));
1834
+ const added = [];
1835
+ const removed = [];
1836
+ const modified = [];
1837
+ const unchanged = [];
1838
+ for (const [id, newRule] of newMap) {
1839
+ const oldRule = oldMap.get(id);
1840
+ if (!oldRule) {
1841
+ added.push(newRule);
1842
+ } else if (JSON.stringify({ ...oldRule, updatedAt: null, createdAt: null }) !== JSON.stringify({ ...newRule, updatedAt: null, createdAt: null })) {
1843
+ modified.push({ old: oldRule, new: newRule });
1844
+ } else {
1845
+ unchanged.push(newRule);
1846
+ }
1847
+ }
1848
+ for (const [id, oldRule] of oldMap) {
1849
+ if (!newMap.has(id)) {
1850
+ removed.push(oldRule);
1851
+ }
1852
+ }
1853
+ return { added, removed, modified, unchanged };
1854
+ };
1855
+ // src/simulation/simulator.ts
1856
+ import { createEvaluator as createEvaluator2 } from "@f-o-t/condition-evaluator";
1857
+ var toEvaluationContext = (simContext) => ({
1858
+ data: simContext.data,
1859
+ timestamp: new Date,
1860
+ metadata: simContext.metadata
1861
+ });
1862
+ var simulate = (rules, context, evaluator = createEvaluator2()) => {
1863
+ const startTime = performance.now();
1864
+ const enabledRules = rules.filter((r) => r.enabled);
1865
+ const evalContext = toEvaluationContext(context);
1866
+ const results = evaluateRules(enabledRules, evalContext, evaluator);
1867
+ const matchedRules = results.results.filter((r) => r.matched);
1868
+ const unmatchedRules = [];
1869
+ for (const result of results.results) {
1870
+ if (!result.matched) {
1871
+ const reason = result.skipped ? result.skipReason ?? "Skipped" : result.error ? `Error: ${result.error.message}` : "Conditions not met";
1872
+ unmatchedRules.push({
1873
+ ruleId: result.ruleId,
1874
+ ruleName: result.ruleName,
1875
+ reason
1876
+ });
1877
+ }
1878
+ }
1879
+ const consequences = [];
1880
+ for (const match of matchedRules) {
1881
+ for (const consequence of match.consequences) {
1882
+ consequences.push({
1883
+ type: consequence.type,
1884
+ payload: consequence.payload,
1885
+ ruleId: consequence.ruleId,
1886
+ ruleName: consequence.ruleName ?? match.ruleName
1887
+ });
1888
+ }
1889
+ }
1890
+ const executionTimeMs = performance.now() - startTime;
1891
+ return {
1892
+ context,
1893
+ matchedRules,
1894
+ unmatchedRules,
1895
+ consequences,
1896
+ executionTimeMs
1897
+ };
1898
+ };
1899
+ var simulateSingleRule = (rule2, context, evaluator = createEvaluator2()) => {
1900
+ const evalContext = toEvaluationContext(context);
1901
+ const result = evaluateRule(rule2, evalContext, evaluator);
1902
+ return {
1903
+ matched: result.matched,
1904
+ conditionResult: result.conditionResult,
1905
+ consequences: result.matched ? rule2.consequences.map((c) => ({ type: c.type, payload: c.payload })) : []
1906
+ };
1907
+ };
1908
+ var whatIf = (originalRules, modifiedRules, context, evaluator = createEvaluator2()) => {
1909
+ const original = simulate(originalRules, context, evaluator);
1910
+ const modified = simulate(modifiedRules, context, evaluator);
1911
+ const originalMatchIds = new Set(original.matchedRules.map((r) => r.ruleId));
1912
+ const modifiedMatchIds = new Set(modified.matchedRules.map((r) => r.ruleId));
1913
+ const newMatches = [...modifiedMatchIds].filter((id) => !originalMatchIds.has(id));
1914
+ const lostMatches = [...originalMatchIds].filter((id) => !modifiedMatchIds.has(id));
1915
+ const consequenceChanges = [];
1916
+ const originalConsequenceKeys = new Set(original.consequences.map((c) => `${c.ruleId}:${c.type}`));
1917
+ const modifiedConsequenceKeys = new Set(modified.consequences.map((c) => `${c.ruleId}:${c.type}`));
1918
+ for (const key of modifiedConsequenceKeys) {
1919
+ if (!originalConsequenceKeys.has(key)) {
1920
+ const [ruleId, consequenceType] = key.split(":");
1921
+ consequenceChanges.push({
1922
+ type: "added",
1923
+ consequenceType: consequenceType ?? "",
1924
+ ruleId: ruleId ?? ""
1925
+ });
1926
+ }
1927
+ }
1928
+ for (const key of originalConsequenceKeys) {
1929
+ if (!modifiedConsequenceKeys.has(key)) {
1930
+ const [ruleId, consequenceType] = key.split(":");
1931
+ consequenceChanges.push({
1932
+ type: "removed",
1933
+ consequenceType: consequenceType ?? "",
1934
+ ruleId: ruleId ?? ""
1935
+ });
1936
+ }
1937
+ }
1938
+ return {
1939
+ original,
1940
+ modified,
1941
+ differences: {
1942
+ newMatches,
1943
+ lostMatches,
1944
+ consequenceChanges
1945
+ }
1946
+ };
1947
+ };
1948
+ var batchSimulate = (rules, contexts, evaluator = createEvaluator2()) => {
1949
+ const results = contexts.map((context) => simulate(rules, context, evaluator));
1950
+ const ruleMatchFrequency = new Map;
1951
+ const consequenceFrequency = new Map;
1952
+ let totalMatchedRules = 0;
1953
+ let totalExecutionTime = 0;
1954
+ for (const result of results) {
1955
+ totalMatchedRules += result.matchedRules.length;
1956
+ totalExecutionTime += result.executionTimeMs;
1957
+ for (const match of result.matchedRules) {
1958
+ const count = ruleMatchFrequency.get(match.ruleId) ?? 0;
1959
+ ruleMatchFrequency.set(match.ruleId, count + 1);
1960
+ }
1961
+ for (const consequence of result.consequences) {
1962
+ const key = consequence.type;
1963
+ const count = consequenceFrequency.get(key) ?? 0;
1964
+ consequenceFrequency.set(key, count + 1);
1965
+ }
1966
+ }
1967
+ return {
1968
+ results,
1969
+ summary: {
1970
+ totalContexts: contexts.length,
1971
+ averageMatchedRules: contexts.length > 0 ? totalMatchedRules / contexts.length : 0,
1972
+ ruleMatchFrequency,
1973
+ consequenceFrequency,
1974
+ averageExecutionTimeMs: contexts.length > 0 ? totalExecutionTime / contexts.length : 0
1975
+ }
1976
+ };
1977
+ };
1978
+ var findRulesAffectedByContextChange = (rules, originalContext, modifiedContext, evaluator = createEvaluator2()) => {
1979
+ const originalResult = simulate(rules, originalContext, evaluator);
1980
+ const modifiedResult = simulate(rules, modifiedContext, evaluator);
1981
+ const originalMatchIds = new Set(originalResult.matchedRules.map((r) => r.ruleId));
1982
+ const modifiedMatchIds = new Set(modifiedResult.matchedRules.map((r) => r.ruleId));
1983
+ const becameTrue = [];
1984
+ const becameFalse = [];
1985
+ const unchanged = [];
1986
+ for (const rule2 of rules) {
1987
+ const wasMatched = originalMatchIds.has(rule2.id);
1988
+ const isMatched = modifiedMatchIds.has(rule2.id);
1989
+ if (!wasMatched && isMatched) {
1990
+ becameTrue.push(rule2);
1991
+ } else if (wasMatched && !isMatched) {
1992
+ becameFalse.push(rule2);
1993
+ } else {
1994
+ unchanged.push(rule2);
1995
+ }
1996
+ }
1997
+ return { becameTrue, becameFalse, unchanged };
1998
+ };
1999
+ var formatSimulationResult = (result) => {
2000
+ const lines = [
2001
+ "=== Simulation Result ===",
2002
+ "",
2003
+ `Execution Time: ${result.executionTimeMs.toFixed(2)}ms`,
2004
+ "",
2005
+ `Matched Rules (${result.matchedRules.length}):`
2006
+ ];
2007
+ for (const match of result.matchedRules) {
2008
+ lines.push(` - ${match.ruleName} (${match.ruleId})`);
2009
+ }
2010
+ lines.push("");
2011
+ lines.push(`Unmatched Rules (${result.unmatchedRules.length}):`);
2012
+ for (const unmatched of result.unmatchedRules) {
2013
+ lines.push(` - ${unmatched.ruleName}: ${unmatched.reason}`);
2014
+ }
2015
+ lines.push("");
2016
+ lines.push(`Consequences (${result.consequences.length}):`);
2017
+ for (const consequence of result.consequences) {
2018
+ lines.push(` - ${consequence.type} from "${consequence.ruleName}"`);
2019
+ }
2020
+ return lines.join(`
2021
+ `);
2022
+ };
2023
+ // src/types/evaluation.ts
2024
+ import { z as z3 } from "zod";
2025
+ var ConflictResolutionStrategySchema = z3.enum([
2026
+ "priority",
2027
+ "first-match",
2028
+ "all",
2029
+ "most-specific"
2030
+ ]);
2031
+ var EvaluateOptionsSchema = z3.object({
2032
+ conflictResolution: ConflictResolutionStrategySchema.optional(),
2033
+ maxRules: z3.number().int().positive().optional(),
2034
+ timeout: z3.number().int().positive().optional(),
2035
+ skipDisabled: z3.boolean().optional(),
2036
+ tags: z3.array(z3.string()).optional(),
2037
+ category: z3.string().optional(),
2038
+ ruleSetId: z3.string().optional(),
2039
+ bypassCache: z3.boolean().optional()
2040
+ });
2041
+ var EvaluateConfigSchema = z3.object({
2042
+ conflictResolution: ConflictResolutionStrategySchema,
2043
+ continueOnError: z3.boolean(),
2044
+ collectAllConsequences: z3.boolean()
2045
+ });
2046
+ // src/types/rule.ts
2047
+ import { z as z4 } from "zod";
2048
+ var RuleSchema = z4.object({
2049
+ id: z4.string().min(1),
2050
+ name: z4.string().min(1),
2051
+ description: z4.string().optional(),
2052
+ conditions: z4.custom((val) => {
2053
+ return typeof val === "object" && val !== null && "id" in val && "operator" in val;
2054
+ }, "Invalid condition group"),
2055
+ consequences: z4.array(z4.object({
2056
+ type: z4.string(),
2057
+ payload: z4.unknown()
2058
+ })),
2059
+ priority: z4.number().int().default(0),
2060
+ enabled: z4.boolean().default(true),
2061
+ stopOnMatch: z4.boolean().default(false),
2062
+ tags: z4.array(z4.string()).default([]),
2063
+ category: z4.string().optional(),
2064
+ metadata: z4.record(z4.string(), z4.unknown()).optional(),
2065
+ createdAt: z4.date().default(() => new Date),
2066
+ updatedAt: z4.date().default(() => new Date)
2067
+ });
2068
+ var RuleSetSchema = z4.object({
2069
+ id: z4.string().min(1),
2070
+ name: z4.string().min(1),
2071
+ description: z4.string().optional(),
2072
+ ruleIds: z4.array(z4.string()),
2073
+ enabled: z4.boolean().default(true),
2074
+ metadata: z4.record(z4.string(), z4.unknown()).optional()
2075
+ });
2076
+ // src/validation/conflicts.ts
2077
+ import {
2078
+ isConditionGroup as isConditionGroup3
2079
+ } from "@f-o-t/condition-evaluator";
2080
+ var DEFAULT_OPTIONS2 = {
2081
+ checkDuplicateIds: true,
2082
+ checkDuplicateConditions: true,
2083
+ checkOverlappingConditions: true,
2084
+ checkPriorityCollisions: true,
2085
+ checkUnreachableRules: true
2086
+ };
2087
+ var collectConditionFields2 = (condition) => {
2088
+ const fields = new Set;
2089
+ if (isConditionGroup3(condition)) {
2090
+ for (const child of condition.conditions) {
2091
+ const childFields = collectConditionFields2(child);
2092
+ for (const field of childFields) {
2093
+ fields.add(field);
2094
+ }
2095
+ }
2096
+ } else {
2097
+ fields.add(condition.field);
2098
+ }
2099
+ return fields;
2100
+ };
2101
+ var hashConditionGroup = (condition) => {
2102
+ const serialize = (c) => {
2103
+ if (isConditionGroup3(c)) {
2104
+ const sortedConditions = [...c.conditions].map((child) => serialize(child)).sort();
2105
+ return `GROUP:${c.operator}:[${sortedConditions.join(",")}]`;
2106
+ }
2107
+ return `COND:${c.type}:${c.field}:${c.operator}:${JSON.stringify(c.value)}`;
2108
+ };
2109
+ return serialize(condition);
2110
+ };
2111
+ var getConditionOperatorValues = (condition, field) => {
2112
+ const results = [];
2113
+ const traverse = (c) => {
2114
+ if (isConditionGroup3(c)) {
2115
+ for (const child of c.conditions) {
2116
+ traverse(child);
2117
+ }
2118
+ } else if (c.field === field) {
2119
+ results.push({ operator: c.operator, value: c.value });
2120
+ }
2121
+ };
2122
+ traverse(condition);
2123
+ return results;
2124
+ };
2125
+ var areConditionsOverlapping = (conditions1, conditions2) => {
2126
+ const fields1 = collectConditionFields2(conditions1);
2127
+ const fields2 = collectConditionFields2(conditions2);
2128
+ const commonFields = new Set([...fields1].filter((f) => fields2.has(f)));
2129
+ if (commonFields.size === 0)
2130
+ return false;
2131
+ for (const field of commonFields) {
2132
+ const ops1 = getConditionOperatorValues(conditions1, field);
2133
+ const ops2 = getConditionOperatorValues(conditions2, field);
2134
+ for (const op1 of ops1) {
2135
+ for (const op2 of ops2) {
2136
+ if (op1.operator === op2.operator && op1.value === op2.value) {
2137
+ return true;
2138
+ }
2139
+ if (op1.operator === "gt" && op2.operator === "lt" || op1.operator === "lt" && op2.operator === "gt" || op1.operator === "gte" && op2.operator === "lte" || op1.operator === "lte" && op2.operator === "gte") {
2140
+ const val1 = op1.value;
2141
+ const val2 = op2.value;
2142
+ if (typeof val1 === "number" && typeof val2 === "number") {
2143
+ if (op1.operator === "gt" && op2.operator === "lt") {
2144
+ if (val1 < val2)
2145
+ return true;
2146
+ }
2147
+ if (op1.operator === "lt" && op2.operator === "gt") {
2148
+ if (val1 > val2)
2149
+ return true;
2150
+ }
2151
+ }
2152
+ }
2153
+ }
2154
+ }
2155
+ }
2156
+ return false;
2157
+ };
2158
+ var findDuplicateIds = (rules) => {
2159
+ const conflicts = [];
2160
+ const idMap = new Map;
2161
+ for (const rule2 of rules) {
2162
+ const existing = idMap.get(rule2.id) ?? [];
2163
+ existing.push(rule2);
2164
+ idMap.set(rule2.id, existing);
2165
+ }
2166
+ for (const [id, duplicates] of idMap) {
2167
+ if (duplicates.length > 1) {
2168
+ conflicts.push({
2169
+ type: "DUPLICATE_ID",
2170
+ severity: "error",
2171
+ message: `Multiple rules share the same ID: ${id}`,
2172
+ ruleIds: duplicates.map((r) => r.id),
2173
+ rules: duplicates,
2174
+ details: { duplicateId: id, count: duplicates.length }
2175
+ });
2176
+ }
2177
+ }
2178
+ return conflicts;
2179
+ };
2180
+ var findDuplicateConditions = (rules) => {
2181
+ const conflicts = [];
2182
+ const hashMap = new Map;
2183
+ for (const rule2 of rules) {
2184
+ const hash = hashConditionGroup(rule2.conditions);
2185
+ const existing = hashMap.get(hash) ?? [];
2186
+ existing.push(rule2);
2187
+ hashMap.set(hash, existing);
2188
+ }
2189
+ for (const [hash, duplicates] of hashMap) {
2190
+ if (duplicates.length > 1) {
2191
+ conflicts.push({
2192
+ type: "DUPLICATE_CONDITIONS",
2193
+ severity: "warning",
2194
+ message: `Multiple rules have identical conditions: ${duplicates.map((r) => r.name).join(", ")}`,
2195
+ ruleIds: duplicates.map((r) => r.id),
2196
+ rules: duplicates,
2197
+ details: { conditionHash: hash, count: duplicates.length }
2198
+ });
2199
+ }
2200
+ }
2201
+ return conflicts;
2202
+ };
2203
+ var findOverlappingConditions = (rules) => {
2204
+ const conflicts = [];
2205
+ const checked = new Set;
2206
+ for (let i = 0;i < rules.length; i++) {
2207
+ for (let j = i + 1;j < rules.length; j++) {
2208
+ const rule1 = rules[i];
2209
+ const rule2 = rules[j];
2210
+ if (!rule1 || !rule2)
2211
+ continue;
2212
+ const key = [rule1.id, rule2.id].sort().join(":");
2213
+ if (checked.has(key))
2214
+ continue;
2215
+ checked.add(key);
2216
+ if (areConditionsOverlapping(rule1.conditions, rule2.conditions)) {
2217
+ conflicts.push({
2218
+ type: "OVERLAPPING_CONDITIONS",
2219
+ severity: "info",
2220
+ message: `Rules "${rule1.name}" and "${rule2.name}" have overlapping conditions`,
2221
+ ruleIds: [rule1.id, rule2.id],
2222
+ rules: [rule1, rule2],
2223
+ details: {
2224
+ rule1Fields: [...collectConditionFields2(rule1.conditions)],
2225
+ rule2Fields: [...collectConditionFields2(rule2.conditions)]
2226
+ }
2227
+ });
2228
+ }
2229
+ }
2230
+ }
2231
+ return conflicts;
2232
+ };
2233
+ var findPriorityCollisions = (rules) => {
2234
+ const conflicts = [];
2235
+ const priorityMap = new Map;
2236
+ for (const rule2 of rules) {
2237
+ const existing = priorityMap.get(rule2.priority) ?? [];
2238
+ existing.push(rule2);
2239
+ priorityMap.set(rule2.priority, existing);
2240
+ }
2241
+ for (const [priority, rulesWithPriority] of priorityMap) {
2242
+ if (rulesWithPriority.length > 1) {
2243
+ const overlappingPairs = new Set;
2244
+ for (let i = 0;i < rulesWithPriority.length; i++) {
2245
+ for (let j = i + 1;j < rulesWithPriority.length; j++) {
2246
+ const r1 = rulesWithPriority[i];
2247
+ const r2 = rulesWithPriority[j];
2248
+ if (!r1 || !r2)
2249
+ continue;
2250
+ if (areConditionsOverlapping(r1.conditions, r2.conditions)) {
2251
+ overlappingPairs.add(r1.id);
2252
+ overlappingPairs.add(r2.id);
2253
+ }
2254
+ }
2255
+ }
2256
+ if (overlappingPairs.size > 1) {
2257
+ const overlapping = rulesWithPriority.filter((r) => overlappingPairs.has(r.id));
2258
+ conflicts.push({
2259
+ type: "PRIORITY_COLLISION",
2260
+ severity: "warning",
2261
+ message: `Multiple overlapping rules share priority ${priority}: ${overlapping.map((r) => r.name).join(", ")}`,
2262
+ ruleIds: overlapping.map((r) => r.id),
2263
+ rules: overlapping,
2264
+ details: { priority, count: overlapping.length }
2265
+ });
2266
+ }
2267
+ }
2268
+ }
2269
+ return conflicts;
2270
+ };
2271
+ var findUnreachableRules = (rules) => {
2272
+ const conflicts = [];
2273
+ const sortedRules = [...rules].sort((a, b) => b.priority - a.priority);
2274
+ for (let i = 0;i < sortedRules.length; i++) {
2275
+ const rule2 = sortedRules[i];
2276
+ if (!rule2)
2277
+ continue;
2278
+ if (!rule2.enabled)
2279
+ continue;
2280
+ for (let j = 0;j < i; j++) {
2281
+ const higherPriorityRule = sortedRules[j];
2282
+ if (!higherPriorityRule)
2283
+ continue;
2284
+ if (!higherPriorityRule.enabled || !higherPriorityRule.stopOnMatch) {
2285
+ continue;
2286
+ }
2287
+ if (hashConditionGroup(rule2.conditions) === hashConditionGroup(higherPriorityRule.conditions)) {
2288
+ conflicts.push({
2289
+ type: "UNREACHABLE_RULE",
2290
+ severity: "warning",
2291
+ message: `Rule "${rule2.name}" may be unreachable because rule "${higherPriorityRule.name}" has higher priority and stops on match`,
2292
+ ruleIds: [rule2.id, higherPriorityRule.id],
2293
+ rules: [rule2, higherPriorityRule],
2294
+ details: {
2295
+ unreachableRule: rule2.id,
2296
+ blockingRule: higherPriorityRule.id,
2297
+ priorityDifference: higherPriorityRule.priority - rule2.priority
2298
+ }
2299
+ });
2300
+ }
2301
+ }
2302
+ }
2303
+ return conflicts;
2304
+ };
2305
+ var detectConflicts = (rules, options = {}) => {
2306
+ const opts = { ...DEFAULT_OPTIONS2, ...options };
2307
+ const conflicts = [];
2308
+ if (opts.checkDuplicateIds) {
2309
+ conflicts.push(...findDuplicateIds(rules));
2310
+ }
2311
+ if (opts.checkDuplicateConditions) {
2312
+ conflicts.push(...findDuplicateConditions(rules));
2313
+ }
2314
+ if (opts.checkOverlappingConditions) {
2315
+ conflicts.push(...findOverlappingConditions(rules));
2316
+ }
2317
+ if (opts.checkPriorityCollisions) {
2318
+ conflicts.push(...findPriorityCollisions(rules));
2319
+ }
2320
+ if (opts.checkUnreachableRules) {
2321
+ conflicts.push(...findUnreachableRules(rules));
2322
+ }
2323
+ return conflicts;
2324
+ };
2325
+ var hasConflicts = (rules, options = {}) => {
2326
+ return detectConflicts(rules, options).length > 0;
2327
+ };
2328
+ var hasErrors = (rules, options = {}) => {
2329
+ return detectConflicts(rules, options).some((c) => c.severity === "error");
2330
+ };
2331
+ var getConflictsByType = (conflicts, type) => {
2332
+ return conflicts.filter((c) => c.type === type);
2333
+ };
2334
+ var getConflictsBySeverity = (conflicts, severity) => {
2335
+ return conflicts.filter((c) => c.severity === severity);
2336
+ };
2337
+ var formatConflicts = (conflicts) => {
2338
+ if (conflicts.length === 0) {
2339
+ return "No conflicts detected";
2340
+ }
2341
+ const lines = [`Found ${conflicts.length} conflict(s):`];
2342
+ for (const conflict of conflicts) {
2343
+ const severityIcon = conflict.severity === "error" ? "[ERROR]" : conflict.severity === "warning" ? "[WARN]" : "[INFO]";
2344
+ lines.push(` ${severityIcon} ${conflict.type}: ${conflict.message}`);
2345
+ }
2346
+ return lines.join(`
2347
+ `);
2348
+ };
2349
+ // src/validation/integrity.ts
2350
+ import {
2351
+ isConditionGroup as isConditionGroup4
2352
+ } from "@f-o-t/condition-evaluator";
2353
+ var DEFAULT_OPTIONS3 = {
2354
+ checkCircularReferences: true,
2355
+ checkOrphanedRuleSets: true,
2356
+ checkMissingReferences: true,
2357
+ checkFieldConsistency: true
2358
+ };
2359
+ var createIssue = (code, message, severity, details) => ({
2360
+ code,
2361
+ message,
2362
+ severity,
2363
+ path: details?.path,
2364
+ ruleId: details?.ruleId,
2365
+ details: details?.extra
2366
+ });
2367
+ var collectAllFields = (condition) => {
2368
+ const fields = new Set;
2369
+ const traverse = (c) => {
2370
+ if (isConditionGroup4(c)) {
2371
+ for (const child of c.conditions) {
2372
+ traverse(child);
2373
+ }
2374
+ } else {
2375
+ fields.add(c.field);
2376
+ }
2377
+ };
2378
+ traverse(condition);
2379
+ return fields;
2380
+ };
2381
+ var checkDuplicateConditionIds = (condition, ruleId) => {
2382
+ const issues = [];
2383
+ const seenIds = new Map;
2384
+ const traverse = (c, path) => {
2385
+ const id = c.id;
2386
+ const count = seenIds.get(id) ?? 0;
2387
+ seenIds.set(id, count + 1);
2388
+ if (count > 0) {
2389
+ issues.push(createIssue("DUPLICATE_CONDITION_ID", `Duplicate condition ID "${id}" found within rule`, "error", { path, ruleId, extra: { conditionId: id } }));
2390
+ }
2391
+ if (isConditionGroup4(c)) {
2392
+ c.conditions.forEach((child, i) => {
2393
+ traverse(child, `${path}[${i}]`);
2394
+ });
2395
+ }
2396
+ };
2397
+ traverse(condition, "conditions");
2398
+ return issues;
2399
+ };
2400
+ var checkRuleIntegrity = (rule2, options) => {
2401
+ const issues = [];
2402
+ issues.push(...checkDuplicateConditionIds(rule2.conditions, rule2.id));
2403
+ if (rule2.priority < 0) {
2404
+ issues.push(createIssue("NEGATIVE_PRIORITY", `Rule "${rule2.name}" has negative priority: ${rule2.priority}`, "warning", { ruleId: rule2.id, extra: { priority: rule2.priority } }));
2405
+ }
2406
+ if (rule2.priority > 1000) {
2407
+ issues.push(createIssue("EXTREME_PRIORITY", `Rule "${rule2.name}" has very high priority: ${rule2.priority}`, "info", { ruleId: rule2.id, extra: { priority: rule2.priority } }));
2408
+ }
2409
+ if (rule2.consequences.length === 0) {
2410
+ issues.push(createIssue("NO_CONSEQUENCES", `Rule "${rule2.name}" has no consequences defined`, "warning", { ruleId: rule2.id }));
2411
+ }
2412
+ if (options.allowedCategories && rule2.category) {
2413
+ if (!options.allowedCategories.includes(rule2.category)) {
2414
+ issues.push(createIssue("INVALID_CATEGORY", `Rule "${rule2.name}" has invalid category: ${rule2.category}`, "error", {
2415
+ ruleId: rule2.id,
2416
+ extra: {
2417
+ category: rule2.category,
2418
+ allowedCategories: [...options.allowedCategories]
2419
+ }
2420
+ }));
2421
+ }
2422
+ }
2423
+ if (options.allowedTags) {
2424
+ const invalidTags = rule2.tags.filter((t) => !options.allowedTags?.includes(t));
2425
+ if (invalidTags.length > 0) {
2426
+ issues.push(createIssue("INVALID_TAGS", `Rule "${rule2.name}" has invalid tags: ${invalidTags.join(", ")}`, "warning", {
2427
+ ruleId: rule2.id,
2428
+ extra: {
2429
+ invalidTags,
2430
+ allowedTags: [...options.allowedTags]
2431
+ }
2432
+ }));
2433
+ }
2434
+ }
2435
+ if (options.requiredFields) {
2436
+ const ruleFields = collectAllFields(rule2.conditions);
2437
+ const missingFields = options.requiredFields.filter((f) => !ruleFields.has(f));
2438
+ if (missingFields.length > 0) {
2439
+ issues.push(createIssue("MISSING_REQUIRED_FIELDS", `Rule "${rule2.name}" is missing required fields: ${missingFields.join(", ")}`, "warning", { ruleId: rule2.id, extra: { missingFields } }));
2440
+ }
2441
+ }
2442
+ return issues;
2443
+ };
2444
+ var checkRuleSetIntegrity = (ruleSet, rules) => {
2445
+ const issues = [];
2446
+ const ruleIds = new Set(rules.map((r) => r.id));
2447
+ for (const ruleId of ruleSet.ruleIds) {
2448
+ if (!ruleIds.has(ruleId)) {
2449
+ issues.push(createIssue("MISSING_RULE_REFERENCE", `RuleSet "${ruleSet.name}" references non-existent rule: ${ruleId}`, "error", { extra: { ruleSetId: ruleSet.id, missingRuleId: ruleId } }));
2450
+ }
2451
+ }
2452
+ if (ruleSet.ruleIds.length === 0) {
2453
+ issues.push(createIssue("EMPTY_RULESET", `RuleSet "${ruleSet.name}" contains no rules`, "warning", { extra: { ruleSetId: ruleSet.id } }));
2454
+ }
2455
+ const duplicateIds = ruleSet.ruleIds.filter((id, i) => ruleSet.ruleIds.indexOf(id) !== i);
2456
+ if (duplicateIds.length > 0) {
2457
+ issues.push(createIssue("DUPLICATE_RULESET_ENTRIES", `RuleSet "${ruleSet.name}" contains duplicate rule references: ${[...new Set(duplicateIds)].join(", ")}`, "warning", { extra: { ruleSetId: ruleSet.id, duplicateIds } }));
2458
+ }
2459
+ return issues;
2460
+ };
2461
+ var checkFieldConsistency = (rules) => {
2462
+ const issues = [];
2463
+ const fieldTypes = new Map;
2464
+ for (const rule2 of rules) {
2465
+ const traverse = (c) => {
2466
+ if (isConditionGroup4(c)) {
2467
+ for (const child of c.conditions) {
2468
+ traverse(child);
2469
+ }
2470
+ } else {
2471
+ const existing = fieldTypes.get(c.field) ?? [];
2472
+ existing.push({
2473
+ type: c.type,
2474
+ ruleId: rule2.id,
2475
+ ruleName: rule2.name
2476
+ });
2477
+ fieldTypes.set(c.field, existing);
2478
+ }
2479
+ };
2480
+ traverse(rule2.conditions);
2481
+ }
2482
+ for (const [field, types] of fieldTypes) {
2483
+ const uniqueTypes = [...new Set(types.map((t) => t.type))];
2484
+ if (uniqueTypes.length > 1) {
2485
+ issues.push(createIssue("INCONSISTENT_FIELD_TYPE", `Field "${field}" is used with different types: ${uniqueTypes.join(", ")}`, "warning", {
2486
+ extra: {
2487
+ field,
2488
+ types: uniqueTypes,
2489
+ rules: types.map((t) => ({
2490
+ id: t.ruleId,
2491
+ name: t.ruleName,
2492
+ type: t.type
2493
+ }))
2494
+ }
2495
+ }));
2496
+ }
2497
+ }
2498
+ return issues;
2499
+ };
2500
+ var checkIntegrity = (rules, ruleSets = [], options = {}) => {
2501
+ const opts = { ...DEFAULT_OPTIONS3, ...options };
2502
+ const issues = [];
2503
+ for (const rule2 of rules) {
2504
+ issues.push(...checkRuleIntegrity(rule2, opts));
2505
+ }
2506
+ for (const ruleSet of ruleSets) {
2507
+ issues.push(...checkRuleSetIntegrity(ruleSet, rules));
2508
+ }
2509
+ if (opts.checkFieldConsistency) {
2510
+ issues.push(...checkFieldConsistency(rules));
2511
+ }
2512
+ return {
2513
+ valid: issues.filter((i) => i.severity === "error").length === 0,
2514
+ issues
2515
+ };
2516
+ };
2517
+ var checkRuleFieldCoverage = (rules, expectedFields) => {
2518
+ const allFields = new Set;
2519
+ for (const rule2 of rules) {
2520
+ const ruleFields = collectAllFields(rule2.conditions);
2521
+ for (const field of ruleFields) {
2522
+ allFields.add(field);
2523
+ }
2524
+ }
2525
+ const expectedSet = new Set(expectedFields);
2526
+ const coveredFields = [...expectedFields].filter((f) => allFields.has(f));
2527
+ const uncoveredFields = [...expectedFields].filter((f) => !allFields.has(f));
2528
+ const extraFields = [...allFields].filter((f) => !expectedSet.has(f));
2529
+ return {
2530
+ coveredFields,
2531
+ uncoveredFields,
2532
+ extraFields,
2533
+ coveragePercentage: expectedFields.length > 0 ? coveredFields.length / expectedFields.length * 100 : 100
2534
+ };
2535
+ };
2536
+ var getUsedFields = (rules) => {
2537
+ const fields = new Set;
2538
+ for (const rule2 of rules) {
2539
+ const ruleFields = collectAllFields(rule2.conditions);
2540
+ for (const field of ruleFields) {
2541
+ fields.add(field);
2542
+ }
2543
+ }
2544
+ return [...fields].sort();
2545
+ };
2546
+ var getUsedOperators = (rules) => {
2547
+ const operators = [];
2548
+ const seen = new Set;
2549
+ for (const rule2 of rules) {
2550
+ const traverse = (c) => {
2551
+ if (isConditionGroup4(c)) {
2552
+ for (const child of c.conditions) {
2553
+ traverse(child);
2554
+ }
2555
+ } else {
2556
+ const key = `${c.field}:${c.operator}:${c.type}`;
2557
+ if (!seen.has(key)) {
2558
+ seen.add(key);
2559
+ operators.push({
2560
+ field: c.field,
2561
+ operator: c.operator,
2562
+ type: c.type
2563
+ });
2564
+ }
2565
+ }
2566
+ };
2567
+ traverse(rule2.conditions);
2568
+ }
2569
+ return operators;
2570
+ };
2571
+ var formatIntegrityResult = (result) => {
2572
+ if (result.valid && result.issues.length === 0) {
2573
+ return "Integrity check passed - no issues found";
2574
+ }
2575
+ const lines = [
2576
+ result.valid ? `Integrity check passed with ${result.issues.length} warning(s)` : `Integrity check failed with ${result.issues.filter((i) => i.severity === "error").length} error(s)`
2577
+ ];
2578
+ const grouped = {
2579
+ error: result.issues.filter((i) => i.severity === "error"),
2580
+ warning: result.issues.filter((i) => i.severity === "warning"),
2581
+ info: result.issues.filter((i) => i.severity === "info")
2582
+ };
2583
+ for (const [severity, issues] of Object.entries(grouped)) {
2584
+ if (issues.length > 0) {
2585
+ lines.push(`
2586
+ ${severity.toUpperCase()}S (${issues.length}):`);
2587
+ for (const issue of issues) {
2588
+ lines.push(` - [${issue.code}] ${issue.message}`);
2589
+ }
2590
+ }
2591
+ }
2592
+ return lines.join(`
2593
+ `);
2594
+ };
2595
+ // src/validation/schema.ts
2596
+ import {
2597
+ isConditionGroup as isConditionGroup5
2598
+ } from "@f-o-t/condition-evaluator";
2599
+ import { z as z5 } from "zod";
2600
+ var ValidationErrorSchema = z5.object({
2601
+ path: z5.string(),
2602
+ message: z5.string(),
2603
+ code: z5.string()
2604
+ });
2605
+ var ValidationResultSchema = z5.object({
2606
+ valid: z5.boolean(),
2607
+ errors: z5.array(ValidationErrorSchema)
2608
+ });
2609
+ var ValidationOptionsSchema = z5.object({
2610
+ validateConditions: z5.boolean().optional(),
2611
+ validateConsequences: z5.boolean().optional(),
2612
+ strictMode: z5.boolean().optional()
2613
+ });
2614
+ var DEFAULT_OPTIONS4 = ValidationOptionsSchema.parse({});
2615
+ var createError = (path, message, code) => ({
2616
+ path,
2617
+ message,
2618
+ code
2619
+ });
2620
+ var validResult = () => ({
2621
+ valid: true,
2622
+ errors: []
2623
+ });
2624
+ var invalidResult = (errors) => ({
2625
+ valid: false,
2626
+ errors: [...errors]
2627
+ });
2628
+ var validateConditionStructure = (condition, path) => {
2629
+ const errors = [];
2630
+ if (isConditionGroup5(condition)) {
2631
+ if (!condition.id || typeof condition.id !== "string") {
2632
+ errors.push(createError(`${path}.id`, "Condition group must have a string id", "MISSING_GROUP_ID"));
2633
+ }
2634
+ if (!["AND", "OR"].includes(condition.operator)) {
2635
+ errors.push(createError(`${path}.operator`, `Invalid operator: ${condition.operator}. Must be "AND" or "OR"`, "INVALID_GROUP_OPERATOR"));
2636
+ }
2637
+ if (!Array.isArray(condition.conditions)) {
2638
+ errors.push(createError(`${path}.conditions`, "Condition group must have a conditions array", "MISSING_CONDITIONS_ARRAY"));
2639
+ } else if (condition.conditions.length === 0) {
2640
+ errors.push(createError(`${path}.conditions`, "Condition group must have at least one condition", "EMPTY_CONDITIONS_ARRAY"));
2641
+ } else {
2642
+ for (let i = 0;i < condition.conditions.length; i++) {
2643
+ const nestedErrors = validateConditionStructure(condition.conditions[i], `${path}.conditions[${i}]`);
2644
+ errors.push(...nestedErrors);
2645
+ }
2646
+ }
2647
+ } else {
2648
+ if (!condition.id || typeof condition.id !== "string") {
2649
+ errors.push(createError(`${path}.id`, "Condition must have a string id", "MISSING_CONDITION_ID"));
2650
+ }
2651
+ if (!condition.type || typeof condition.type !== "string") {
2652
+ errors.push(createError(`${path}.type`, "Condition must have a type", "MISSING_CONDITION_TYPE"));
2653
+ }
2654
+ if (!condition.field || typeof condition.field !== "string") {
2655
+ errors.push(createError(`${path}.field`, "Condition must have a field", "MISSING_CONDITION_FIELD"));
2656
+ }
2657
+ if (!condition.operator || typeof condition.operator !== "string") {
2658
+ errors.push(createError(`${path}.operator`, "Condition must have an operator", "MISSING_CONDITION_OPERATOR"));
2659
+ }
2660
+ }
2661
+ return errors;
2662
+ };
2663
+ var validateConsequenceStructure = (consequences, consequenceSchemas, strictMode = false) => {
2664
+ const errors = [];
2665
+ for (let i = 0;i < consequences.length; i++) {
2666
+ const consequence = consequences[i];
2667
+ if (!consequence)
2668
+ continue;
2669
+ const path = `consequences[${i}]`;
2670
+ if (!consequence.type || typeof consequence.type !== "string") {
2671
+ errors.push(createError(`${path}.type`, "Consequence must have a type", "MISSING_CONSEQUENCE_TYPE"));
2672
+ continue;
2673
+ }
2674
+ if (strictMode && consequenceSchemas) {
2675
+ const schema = consequenceSchemas[consequence.type];
2676
+ if (!schema) {
2677
+ errors.push(createError(`${path}.type`, `Unknown consequence type: ${consequence.type}`, "UNKNOWN_CONSEQUENCE_TYPE"));
2678
+ } else {
2679
+ const result = schema.safeParse(consequence.payload);
2680
+ if (!result.success) {
2681
+ for (const issue of result.error.issues) {
2682
+ errors.push(createError(`${path}.payload.${issue.path.join(".")}`, issue.message, "INVALID_CONSEQUENCE_PAYLOAD"));
2683
+ }
2684
+ }
2685
+ }
2686
+ }
2687
+ }
2688
+ return errors;
2689
+ };
2690
+ var validateRule = (rule2, options = {}) => {
2691
+ const opts = { ...DEFAULT_OPTIONS4, ...options };
2692
+ const errors = [];
2693
+ const schemaResult = RuleSchema.safeParse(rule2);
2694
+ if (!schemaResult.success) {
2695
+ for (const issue of schemaResult.error.issues) {
2696
+ errors.push(createError(issue.path.join("."), issue.message, "SCHEMA_VALIDATION_FAILED"));
2697
+ }
2698
+ return invalidResult(errors);
2699
+ }
2700
+ const validRule = schemaResult.data;
2701
+ if (opts.validateConditions) {
2702
+ const conditionErrors = validateConditionStructure(validRule.conditions, "conditions");
2703
+ errors.push(...conditionErrors);
2704
+ }
2705
+ if (opts.validateConsequences || opts.strictMode) {
2706
+ const consequenceErrors = validateConsequenceStructure(validRule.consequences, opts.consequenceSchemas, opts.strictMode ?? false);
2707
+ errors.push(...consequenceErrors);
2708
+ }
2709
+ return errors.length > 0 ? invalidResult(errors) : validResult();
2710
+ };
2711
+ var validateRules = (rules, options = {}) => {
2712
+ const errors = [];
2713
+ for (let i = 0;i < rules.length; i++) {
2714
+ const result = validateRule(rules[i], options);
2715
+ if (!result.valid) {
2716
+ for (const error of result.errors) {
2717
+ errors.push(createError(`rules[${i}].${error.path}`, error.message, error.code));
2718
+ }
2719
+ }
2720
+ }
2721
+ return errors.length > 0 ? invalidResult(errors) : validResult();
2722
+ };
2723
+ var validateRuleSet = (ruleSet) => {
2724
+ const errors = [];
2725
+ const schemaResult = RuleSetSchema.safeParse(ruleSet);
2726
+ if (!schemaResult.success) {
2727
+ for (const issue of schemaResult.error.issues) {
2728
+ errors.push(createError(issue.path.join("."), issue.message, "SCHEMA_VALIDATION_FAILED"));
2729
+ }
2730
+ return invalidResult(errors);
2731
+ }
2732
+ return validResult();
2733
+ };
2734
+ var validateConditions = (conditions2) => {
2735
+ const errors = validateConditionStructure(conditions2, "conditions");
2736
+ return errors.length > 0 ? invalidResult(errors) : validResult();
2737
+ };
2738
+ var parseRule = (rule2, options = {}) => {
2739
+ const result = validateRule(rule2, options);
2740
+ if (!result.valid) {
2741
+ throw new Error(`Invalid rule: ${result.errors.map((e) => e.message).join(", ")}`);
2742
+ }
2743
+ return rule2;
2744
+ };
2745
+ var safeParseRule = (rule2, options = {}) => {
2746
+ const result = validateRule(rule2, options);
2747
+ if (!result.valid) {
2748
+ return { success: false, errors: result.errors };
2749
+ }
2750
+ return { success: true, data: rule2 };
2751
+ };
2752
+ var createRuleValidator = (consequenceSchemas, defaultOptions = {}) => {
2753
+ return {
2754
+ validate: (rule2, options = {}) => validateRule(rule2, {
2755
+ ...defaultOptions,
2756
+ ...options,
2757
+ consequenceSchemas
2758
+ }),
2759
+ parse: (rule2, options = {}) => parseRule(rule2, {
2760
+ ...defaultOptions,
2761
+ ...options,
2762
+ consequenceSchemas
2763
+ }),
2764
+ safeParse: (rule2, options = {}) => safeParseRule(rule2, {
2765
+ ...defaultOptions,
2766
+ ...options,
2767
+ consequenceSchemas
2768
+ })
2769
+ };
2770
+ };
2771
+ // src/versioning/version-store.ts
2772
+ var createVersionStore = () => ({
2773
+ histories: new Map
2774
+ });
2775
+ var addVersion = (store, rule2, changeType, options = {}) => {
2776
+ const histories = new Map(store.histories);
2777
+ const existingHistory = histories.get(rule2.id);
2778
+ const currentVersion = existingHistory ? existingHistory.currentVersion + 1 : 1;
2779
+ const version = {
2780
+ versionId: generateId(),
2781
+ ruleId: rule2.id,
2782
+ version: currentVersion,
2783
+ rule: { ...rule2 },
2784
+ createdAt: new Date,
2785
+ createdBy: options.createdBy,
2786
+ comment: options.comment,
2787
+ changeType
2788
+ };
2789
+ const newHistory = {
2790
+ ruleId: rule2.id,
2791
+ currentVersion,
2792
+ versions: existingHistory ? [...existingHistory.versions, version] : [version]
2793
+ };
2794
+ histories.set(rule2.id, newHistory);
2795
+ return { histories };
2796
+ };
2797
+ var getHistory = (store, ruleId) => {
2798
+ return store.histories.get(ruleId);
2799
+ };
2800
+ var getVersion = (store, ruleId, version) => {
2801
+ const history = store.histories.get(ruleId);
2802
+ if (!history)
2803
+ return;
2804
+ return history.versions.find((v) => v.version === version);
2805
+ };
2806
+ var getLatestVersion = (store, ruleId) => {
2807
+ const history = store.histories.get(ruleId);
2808
+ if (!history || history.versions.length === 0)
2809
+ return;
2810
+ return history.versions[history.versions.length - 1];
2811
+ };
2812
+ var getAllVersions = (store, ruleId) => {
2813
+ const history = store.histories.get(ruleId);
2814
+ return history?.versions ?? [];
2815
+ };
2816
+ var rollbackToVersion = (store, ruleId, targetVersion, options = {}) => {
2817
+ const targetVersionRecord = getVersion(store, ruleId, targetVersion);
2818
+ if (!targetVersionRecord) {
2819
+ return { store, rule: undefined };
2820
+ }
2821
+ const rolledBackRule = {
2822
+ ...targetVersionRecord.rule,
2823
+ updatedAt: new Date
2824
+ };
2825
+ const newStore = addVersion(store, rolledBackRule, "update", {
2826
+ createdBy: options.createdBy,
2827
+ comment: options.comment ?? `Rollback to version ${targetVersion}`
2828
+ });
2829
+ return { store: newStore, rule: rolledBackRule };
2830
+ };
2831
+ var compareVersions = (store, ruleId, version1, version2) => {
2832
+ const v1 = getVersion(store, ruleId, version1);
2833
+ const v2 = getVersion(store, ruleId, version2);
2834
+ if (!v1 || !v2) {
2835
+ return null;
2836
+ }
2837
+ const differences = [];
2838
+ const compareFields = [
2839
+ "name",
2840
+ "description",
2841
+ "priority",
2842
+ "enabled",
2843
+ "stopOnMatch",
2844
+ "category"
2845
+ ];
2846
+ for (const field of compareFields) {
2847
+ const oldValue = v1.rule[field];
2848
+ const newValue = v2.rule[field];
2849
+ if (JSON.stringify(oldValue) !== JSON.stringify(newValue)) {
2850
+ differences.push({ field, oldValue, newValue });
2851
+ }
2852
+ }
2853
+ if (JSON.stringify(v1.rule.conditions) !== JSON.stringify(v2.rule.conditions)) {
2854
+ differences.push({
2855
+ field: "conditions",
2856
+ oldValue: v1.rule.conditions,
2857
+ newValue: v2.rule.conditions
2858
+ });
2859
+ }
2860
+ if (JSON.stringify(v1.rule.consequences) !== JSON.stringify(v2.rule.consequences)) {
2861
+ differences.push({
2862
+ field: "consequences",
2863
+ oldValue: v1.rule.consequences,
2864
+ newValue: v2.rule.consequences
2865
+ });
2866
+ }
2867
+ if (JSON.stringify(v1.rule.tags) !== JSON.stringify(v2.rule.tags)) {
2868
+ differences.push({
2869
+ field: "tags",
2870
+ oldValue: v1.rule.tags,
2871
+ newValue: v2.rule.tags
2872
+ });
2873
+ }
2874
+ return { version1: v1, version2: v2, differences };
2875
+ };
2876
+ var getVersionsByDateRange = (store, startDate, endDate) => {
2877
+ const versions = [];
2878
+ for (const history of store.histories.values()) {
2879
+ for (const version of history.versions) {
2880
+ if (version.createdAt >= startDate && version.createdAt <= endDate) {
2881
+ versions.push(version);
2882
+ }
2883
+ }
2884
+ }
2885
+ return versions.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
2886
+ };
2887
+ var getVersionsByChangeType = (store, changeType) => {
2888
+ const versions = [];
2889
+ for (const history of store.histories.values()) {
2890
+ for (const version of history.versions) {
2891
+ if (version.changeType === changeType) {
2892
+ versions.push(version);
2893
+ }
2894
+ }
2895
+ }
2896
+ return versions;
2897
+ };
2898
+ var pruneOldVersions = (store, keepCount) => {
2899
+ const histories = new Map;
2900
+ for (const [ruleId, history] of store.histories) {
2901
+ const prunedVersions = history.versions.length > keepCount ? history.versions.slice(-keepCount) : history.versions;
2902
+ histories.set(ruleId, {
2903
+ ruleId,
2904
+ currentVersion: history.currentVersion,
2905
+ versions: prunedVersions
2906
+ });
2907
+ }
2908
+ return { histories };
2909
+ };
2910
+ var formatVersionHistory = (history) => {
2911
+ const lines = [
2912
+ `=== Version History for Rule: ${history.ruleId} ===`,
2913
+ `Current Version: ${history.currentVersion}`,
2914
+ `Total Versions: ${history.versions.length}`,
2915
+ ""
2916
+ ];
2917
+ for (const version of history.versions) {
2918
+ lines.push(`v${version.version} - ${version.changeType} (${version.createdAt.toISOString()})`);
2919
+ if (version.createdBy) {
2920
+ lines.push(` By: ${version.createdBy}`);
2921
+ }
2922
+ if (version.comment) {
2923
+ lines.push(` Comment: ${version.comment}`);
2924
+ }
2925
+ }
2926
+ return lines.join(`
2927
+ `);
2928
+ };
2929
+ export {
2930
+ withTimeout,
2931
+ whatIf,
2932
+ validateRules,
2933
+ validateRuleSet,
2934
+ validateRule,
2935
+ validateConditions,
2936
+ updateRule,
2937
+ str,
2938
+ sortRules,
2939
+ sortByUpdatedAt,
2940
+ sortByPriority,
2941
+ sortByName,
2942
+ sortByCreatedAt,
2943
+ simulateSingleRule,
2944
+ simulate,
2945
+ serializeRuleSet,
2946
+ serializeRule,
2947
+ safeParseRule,
2948
+ rule,
2949
+ rollbackToVersion,
2950
+ resetBuilderIds,
2951
+ removeRuleSet,
2952
+ removeRule,
2953
+ pruneOldVersions,
2954
+ parseVersioningConfig,
2955
+ parseValidationConfig,
2956
+ parseRule,
2957
+ parseCacheConfig,
2958
+ or,
2959
+ num,
2960
+ mergeRuleSets,
2961
+ measureTimeAsync,
2962
+ measureTime,
2963
+ isConditionGroup6 as isConditionGroup,
2964
+ importRules,
2965
+ importFromJson,
2966
+ hashRules,
2967
+ hashContext,
2968
+ hasErrors,
2969
+ hasConflicts,
2970
+ groupRules,
2971
+ groupByPriority,
2972
+ groupByEnabled,
2973
+ groupByCustom,
2974
+ groupByCategory,
2975
+ getVersionsByDateRange,
2976
+ getVersionsByChangeType,
2977
+ getVersion,
2978
+ getUsedOperators,
2979
+ getUsedFields,
2980
+ getRulesInSet,
2981
+ getRulesByTags,
2982
+ getRulesByTag,
2983
+ getRulesByPriorityRange,
2984
+ getRulesByPriority,
2985
+ getRulesByFields,
2986
+ getRulesByField,
2987
+ getRulesByCategory,
2988
+ getRules,
2989
+ getRuleSets,
2990
+ getRuleSet,
2991
+ getRuleById,
2992
+ getRule,
2993
+ getLatestVersion,
2994
+ getIndexStats,
2995
+ getHistory,
2996
+ getDefaultVersioningConfig,
2997
+ getDefaultValidationConfig,
2998
+ getDefaultLogLevel,
2999
+ getDefaultConflictResolution,
3000
+ getDefaultCacheConfig,
3001
+ getConflictsByType,
3002
+ getConflictsBySeverity,
3003
+ getAllVersions,
3004
+ generateId,
3005
+ formatVersionHistory,
3006
+ formatSimulationResult,
3007
+ formatRuleSetAnalysis,
3008
+ formatIntegrityResult,
3009
+ formatConflicts,
3010
+ findRulesAffectedByContextChange,
3011
+ findMostComplexRules,
3012
+ findLeastUsedFields,
3013
+ filterRulesForContext,
3014
+ filterRules,
3015
+ filterByTags,
3016
+ filterByIds,
3017
+ filterByEnabled,
3018
+ filterByCategory,
3019
+ exportToJson,
3020
+ exportRules,
3021
+ evaluateRules,
3022
+ evaluateRule,
3023
+ enableRule,
3024
+ disableRule,
3025
+ diffRuleSets,
3026
+ detectConflicts,
3027
+ deserializeRuleSet,
3028
+ deserializeRule,
3029
+ date,
3030
+ createVersionStore,
3031
+ createRuleValidator,
3032
+ createRule,
3033
+ createOperator,
3034
+ createNoopCache,
3035
+ createInitialState,
3036
+ createInitialRuleStats,
3037
+ createInitialOptimizerState,
3038
+ createEvaluator3 as createEvaluator,
3039
+ createEngine,
3040
+ createCache,
3041
+ conditions,
3042
+ compareVersions,
3043
+ cloneState,
3044
+ cloneRule,
3045
+ clearRules,
3046
+ checkRuleFieldCoverage,
3047
+ checkIntegrity,
3048
+ buildIndex,
3049
+ bool,
3050
+ batchSimulate,
3051
+ arr,
3052
+ any,
3053
+ and,
3054
+ analyzeRuleSet,
3055
+ analyzeRuleComplexity,
3056
+ analyzeOptimizations,
3057
+ analyzeOperatorUsage,
3058
+ analyzeFieldUsage,
3059
+ analyzeConsequenceUsage,
3060
+ all,
3061
+ addVersion,
3062
+ addRules,
3063
+ addRuleSet,
3064
+ addRule,
3065
+ VersioningConfigSchema,
3066
+ ValidationResultSchema,
3067
+ ValidationOptionsSchema,
3068
+ ValidationErrorSchema,
3069
+ ValidationConfigSchema,
3070
+ RuleStatsSchema,
3071
+ RuleSetSchema,
3072
+ RuleSchema,
3073
+ LogLevelSchema,
3074
+ EvaluateOptionsSchema,
3075
+ EvaluateConfigSchema,
3076
+ EngineStatsSchema,
3077
+ ConflictResolutionStrategySchema,
3078
+ ConditionGroup as ConditionGroupSchema,
3079
+ CacheStatsSchema,
3080
+ CacheConfigSchema
3081
+ };
3082
+
3083
+ //# debugId=FB71FB159972A03264756E2164756E21