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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/dist/analyzer/analysis.d.ts +72 -0
  2. package/dist/analyzer/analysis.d.ts.map +1 -0
  3. package/dist/builder/conditions.d.ts +29 -0
  4. package/dist/builder/conditions.d.ts.map +1 -0
  5. package/dist/builder/rule.d.ts +40 -0
  6. package/dist/builder/rule.d.ts.map +1 -0
  7. package/dist/cache/cache.d.ts +21 -0
  8. package/dist/cache/cache.d.ts.map +1 -0
  9. package/dist/cache/noop.d.ts +3 -0
  10. package/dist/cache/noop.d.ts.map +1 -0
  11. package/dist/core/evaluate.d.ts +21 -0
  12. package/dist/core/evaluate.d.ts.map +1 -0
  13. package/dist/core/filter.d.ts +8 -0
  14. package/dist/core/filter.d.ts.map +1 -0
  15. package/dist/core/group.d.ts +10 -0
  16. package/dist/core/group.d.ts.map +1 -0
  17. package/dist/core/sort.d.ts +14 -0
  18. package/dist/core/sort.d.ts.map +1 -0
  19. package/dist/engine/engine.d.ts +27 -0
  20. package/dist/engine/engine.d.ts.map +1 -0
  21. package/dist/engine/hooks.d.ts +16 -0
  22. package/dist/engine/hooks.d.ts.map +1 -0
  23. package/dist/engine/state.d.ts +19 -0
  24. package/dist/engine/state.d.ts.map +1 -0
  25. package/dist/index.d.ts +33 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +3083 -0
  28. package/dist/index.js.map +37 -0
  29. package/dist/optimizer/index-builder.d.ts +48 -0
  30. package/dist/optimizer/index-builder.d.ts.map +1 -0
  31. package/dist/serialization/serializer.d.ts +79 -0
  32. package/dist/serialization/serializer.d.ts.map +1 -0
  33. package/dist/simulation/simulator.d.ts +65 -0
  34. package/dist/simulation/simulator.d.ts.map +1 -0
  35. package/dist/types/config.d.ts +87 -0
  36. package/dist/types/config.d.ts.map +1 -0
  37. package/dist/types/consequence.d.ts +21 -0
  38. package/dist/types/consequence.d.ts.map +1 -0
  39. package/dist/types/evaluation.d.ts +70 -0
  40. package/dist/types/evaluation.d.ts.map +1 -0
  41. package/dist/types/rule.d.ts +114 -0
  42. package/dist/types/rule.d.ts.map +1 -0
  43. package/dist/types/state.d.ts +65 -0
  44. package/dist/types/state.d.ts.map +1 -0
  45. package/dist/utils/conditions.d.ts +24 -0
  46. package/dist/utils/conditions.d.ts.map +1 -0
  47. package/dist/utils/hash.d.ts +3 -0
  48. package/dist/utils/hash.d.ts.map +1 -0
  49. package/dist/utils/id.d.ts +2 -0
  50. package/dist/utils/id.d.ts.map +1 -0
  51. package/dist/utils/time.d.ts +8 -0
  52. package/dist/utils/time.d.ts.map +1 -0
  53. package/dist/validation/conflicts.d.ts +25 -0
  54. package/dist/validation/conflicts.d.ts.map +1 -0
  55. package/dist/validation/integrity.d.ts +38 -0
  56. package/dist/validation/integrity.d.ts.map +1 -0
  57. package/dist/validation/schema.d.ts +69 -0
  58. package/dist/validation/schema.d.ts.map +1 -0
  59. package/dist/versioning/version-store.d.ts +50 -0
  60. package/dist/versioning/version-store.d.ts.map +1 -0
  61. package/package.json +35 -31
  62. package/CHANGELOG.md +0 -168
  63. package/__tests__/builder.test.ts +0 -363
  64. package/__tests__/cache.test.ts +0 -130
  65. package/__tests__/config.test.ts +0 -35
  66. package/__tests__/engine.test.ts +0 -1213
  67. package/__tests__/evaluate.test.ts +0 -339
  68. package/__tests__/exports.test.ts +0 -30
  69. package/__tests__/filter-sort.test.ts +0 -303
  70. package/__tests__/integration.test.ts +0 -419
  71. package/__tests__/money-integration.test.ts +0 -149
  72. package/__tests__/validation.test.ts +0 -862
  73. package/biome.json +0 -39
  74. package/docs/MIGRATION-v3.md +0 -118
  75. package/fot.config.ts +0 -5
  76. package/src/analyzer/analysis.ts +0 -401
  77. package/src/builder/conditions.ts +0 -321
  78. package/src/builder/rule.ts +0 -192
  79. package/src/cache/cache.ts +0 -135
  80. package/src/cache/noop.ts +0 -20
  81. package/src/core/evaluate.ts +0 -185
  82. package/src/core/filter.ts +0 -85
  83. package/src/core/group.ts +0 -103
  84. package/src/core/sort.ts +0 -90
  85. package/src/engine/engine.ts +0 -462
  86. package/src/engine/hooks.ts +0 -235
  87. package/src/engine/state.ts +0 -322
  88. package/src/index.ts +0 -303
  89. package/src/optimizer/index-builder.ts +0 -381
  90. package/src/serialization/serializer.ts +0 -408
  91. package/src/simulation/simulator.ts +0 -359
  92. package/src/types/config.ts +0 -184
  93. package/src/types/consequence.ts +0 -38
  94. package/src/types/evaluation.ts +0 -87
  95. package/src/types/rule.ts +0 -112
  96. package/src/types/state.ts +0 -116
  97. package/src/utils/conditions.ts +0 -108
  98. package/src/utils/hash.ts +0 -30
  99. package/src/utils/id.ts +0 -6
  100. package/src/utils/time.ts +0 -42
  101. package/src/validation/conflicts.ts +0 -440
  102. package/src/validation/integrity.ts +0 -473
  103. package/src/validation/schema.ts +0 -386
  104. package/src/versioning/version-store.ts +0 -337
  105. package/tsconfig.json +0 -29
@@ -1,408 +0,0 @@
1
- import type { ConditionGroup } from "@f-o-t/condition-evaluator";
2
- import type {
3
- ConsequenceDefinitions,
4
- DefaultConsequences,
5
- } from "../types/consequence";
6
- import type { Rule, RuleSet } from "../types/rule";
7
- import { generateId } from "../utils/id";
8
-
9
- export type SerializedRule = {
10
- readonly id: string;
11
- readonly name: string;
12
- readonly description?: string;
13
- readonly conditions: ConditionGroup;
14
- readonly consequences: ReadonlyArray<{
15
- type: string;
16
- payload: unknown;
17
- }>;
18
- readonly priority: number;
19
- readonly enabled: boolean;
20
- readonly stopOnMatch: boolean;
21
- readonly tags: ReadonlyArray<string>;
22
- readonly category?: string;
23
- readonly metadata?: Readonly<Record<string, unknown>>;
24
- readonly createdAt: string;
25
- readonly updatedAt: string;
26
- };
27
-
28
- export type SerializedRuleSet = {
29
- readonly id: string;
30
- readonly name: string;
31
- readonly description?: string;
32
- readonly ruleIds: ReadonlyArray<string>;
33
- readonly enabled: boolean;
34
- readonly metadata?: Readonly<Record<string, unknown>>;
35
- };
36
-
37
- export type ExportFormat = {
38
- readonly version: string;
39
- readonly exportedAt: string;
40
- readonly rules: ReadonlyArray<SerializedRule>;
41
- readonly ruleSets?: ReadonlyArray<SerializedRuleSet>;
42
- readonly metadata?: Readonly<Record<string, unknown>>;
43
- };
44
-
45
- export type ImportOptions = {
46
- readonly generateNewIds?: boolean;
47
- readonly idPrefix?: string;
48
- readonly preserveDates?: boolean;
49
- readonly validateSchema?: boolean;
50
- };
51
-
52
- export type OrphanedReference = {
53
- readonly ruleSetId: string;
54
- readonly ruleSetName: string;
55
- readonly missingRuleIds: ReadonlyArray<string>;
56
- };
57
-
58
- export type ImportResult<
59
- TContext = unknown,
60
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
61
- > = {
62
- readonly success: boolean;
63
- readonly rules: ReadonlyArray<Rule<TContext, TConsequences>>;
64
- readonly ruleSets: ReadonlyArray<RuleSet>;
65
- readonly errors: ReadonlyArray<{
66
- index: number;
67
- type: "rule" | "ruleSet";
68
- message: string;
69
- }>;
70
- readonly idMapping: ReadonlyMap<string, string>;
71
- readonly orphanedReferences: ReadonlyArray<OrphanedReference>;
72
- };
73
-
74
- const EXPORT_VERSION = "1.0.0";
75
-
76
- export const serializeRule = <
77
- TContext = unknown,
78
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
79
- >(
80
- rule: Rule<TContext, TConsequences>,
81
- ): SerializedRule => ({
82
- id: rule.id,
83
- name: rule.name,
84
- description: rule.description,
85
- conditions: rule.conditions,
86
- consequences: rule.consequences.map((c) => ({
87
- type: c.type as string,
88
- payload: c.payload,
89
- })),
90
- priority: rule.priority,
91
- enabled: rule.enabled,
92
- stopOnMatch: rule.stopOnMatch,
93
- tags: rule.tags,
94
- category: rule.category,
95
- metadata: rule.metadata,
96
- createdAt: rule.createdAt.toISOString(),
97
- updatedAt: rule.updatedAt.toISOString(),
98
- });
99
-
100
- export const deserializeRule = <
101
- TContext = unknown,
102
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
103
- >(
104
- serialized: SerializedRule,
105
- options: ImportOptions = {},
106
- ): Rule<TContext, TConsequences> => {
107
- const id = options.generateNewIds
108
- ? `${options.idPrefix ?? ""}${generateId()}`
109
- : serialized.id;
110
-
111
- const now = new Date();
112
-
113
- return {
114
- id,
115
- name: serialized.name,
116
- description: serialized.description,
117
- conditions: serialized.conditions,
118
- consequences: serialized.consequences as Rule<
119
- TContext,
120
- TConsequences
121
- >["consequences"],
122
- priority: serialized.priority,
123
- enabled: serialized.enabled,
124
- stopOnMatch: serialized.stopOnMatch,
125
- tags: serialized.tags,
126
- category: serialized.category,
127
- metadata: serialized.metadata,
128
- createdAt: options.preserveDates ? new Date(serialized.createdAt) : now,
129
- updatedAt: options.preserveDates ? new Date(serialized.updatedAt) : now,
130
- };
131
- };
132
-
133
- export const serializeRuleSet = (ruleSet: RuleSet): SerializedRuleSet => ({
134
- id: ruleSet.id,
135
- name: ruleSet.name,
136
- description: ruleSet.description,
137
- ruleIds: ruleSet.ruleIds,
138
- enabled: ruleSet.enabled,
139
- metadata: ruleSet.metadata,
140
- });
141
-
142
- export const deserializeRuleSet = (
143
- serialized: SerializedRuleSet,
144
- idMapping: Map<string, string>,
145
- options: ImportOptions = {},
146
- ): RuleSet => {
147
- const id = options.generateNewIds
148
- ? `${options.idPrefix ?? ""}${generateId()}`
149
- : serialized.id;
150
-
151
- const ruleIds = serialized.ruleIds.map(
152
- (oldId) => idMapping.get(oldId) ?? oldId,
153
- );
154
-
155
- return {
156
- id,
157
- name: serialized.name,
158
- description: serialized.description,
159
- ruleIds,
160
- enabled: serialized.enabled,
161
- metadata: serialized.metadata,
162
- };
163
- };
164
-
165
- export const exportRules = <
166
- TContext = unknown,
167
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
168
- >(
169
- rules: ReadonlyArray<Rule<TContext, TConsequences>>,
170
- ruleSets?: ReadonlyArray<RuleSet>,
171
- metadata?: Record<string, unknown>,
172
- ): ExportFormat => ({
173
- version: EXPORT_VERSION,
174
- exportedAt: new Date().toISOString(),
175
- rules: rules.map(serializeRule),
176
- ruleSets: ruleSets?.map(serializeRuleSet),
177
- metadata,
178
- });
179
-
180
- export const exportToJson = <
181
- TContext = unknown,
182
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
183
- >(
184
- rules: ReadonlyArray<Rule<TContext, TConsequences>>,
185
- ruleSets?: ReadonlyArray<RuleSet>,
186
- metadata?: Record<string, unknown>,
187
- ): string => {
188
- const exportData = exportRules(rules, ruleSets, metadata);
189
- return JSON.stringify(exportData, null, 2);
190
- };
191
-
192
- export const importRules = <
193
- TContext = unknown,
194
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
195
- >(
196
- data: ExportFormat,
197
- options: ImportOptions = {},
198
- ): ImportResult<TContext, TConsequences> => {
199
- const rules: Rule<TContext, TConsequences>[] = [];
200
- const ruleSets: RuleSet[] = [];
201
- const errors: Array<{
202
- index: number;
203
- type: "rule" | "ruleSet";
204
- message: string;
205
- }> = [];
206
- const idMapping = new Map<string, string>();
207
-
208
- for (let i = 0; i < data.rules.length; i++) {
209
- try {
210
- const serialized = data.rules[i];
211
- if (!serialized) continue;
212
- const rule = deserializeRule<TContext, TConsequences>(
213
- serialized,
214
- options,
215
- );
216
- idMapping.set(serialized.id, rule.id);
217
- rules.push(rule);
218
- } catch (error) {
219
- errors.push({
220
- index: i,
221
- type: "rule",
222
- message: error instanceof Error ? error.message : String(error),
223
- });
224
- }
225
- }
226
-
227
- if (data.ruleSets) {
228
- for (let i = 0; i < data.ruleSets.length; i++) {
229
- try {
230
- const serialized = data.ruleSets[i];
231
- if (!serialized) continue;
232
- const ruleSet = deserializeRuleSet(serialized, idMapping, options);
233
- ruleSets.push(ruleSet);
234
- } catch (error) {
235
- errors.push({
236
- index: i,
237
- type: "ruleSet",
238
- message: error instanceof Error ? error.message : String(error),
239
- });
240
- }
241
- }
242
- }
243
-
244
- // Detect orphaned references (ruleSets referencing non-existent rules)
245
- const importedRuleIds = new Set(rules.map((r) => r.id));
246
- const orphanedReferences: OrphanedReference[] = [];
247
-
248
- for (const ruleSet of ruleSets) {
249
- const missingRuleIds = ruleSet.ruleIds.filter(
250
- (id) => !importedRuleIds.has(id),
251
- );
252
- if (missingRuleIds.length > 0) {
253
- orphanedReferences.push({
254
- ruleSetId: ruleSet.id,
255
- ruleSetName: ruleSet.name,
256
- missingRuleIds,
257
- });
258
- }
259
- }
260
-
261
- return {
262
- success: errors.length === 0,
263
- rules,
264
- ruleSets,
265
- errors,
266
- idMapping,
267
- orphanedReferences,
268
- };
269
- };
270
-
271
- export const importFromJson = <
272
- TContext = unknown,
273
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
274
- >(
275
- json: string,
276
- options: ImportOptions = {},
277
- ): ImportResult<TContext, TConsequences> => {
278
- try {
279
- const data = JSON.parse(json) as ExportFormat;
280
- return importRules<TContext, TConsequences>(data, options);
281
- } catch (error) {
282
- return {
283
- success: false,
284
- rules: [],
285
- ruleSets: [],
286
- errors: [
287
- {
288
- index: -1,
289
- type: "rule",
290
- message: `Invalid JSON: ${error instanceof Error ? error.message : String(error)}`,
291
- },
292
- ],
293
- idMapping: new Map(),
294
- orphanedReferences: [],
295
- };
296
- }
297
- };
298
-
299
- export const cloneRule = <
300
- TContext = unknown,
301
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
302
- >(
303
- rule: Rule<TContext, TConsequences>,
304
- newId?: string,
305
- newName?: string,
306
- ): Rule<TContext, TConsequences> => {
307
- const now = new Date();
308
- return {
309
- ...rule,
310
- id: newId ?? generateId(),
311
- name: newName ?? `${rule.name} (Copy)`,
312
- createdAt: now,
313
- updatedAt: now,
314
- };
315
- };
316
-
317
- export const mergeRuleSets = <
318
- TContext = unknown,
319
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
320
- >(
321
- baseRules: ReadonlyArray<Rule<TContext, TConsequences>>,
322
- incomingRules: ReadonlyArray<Rule<TContext, TConsequences>>,
323
- strategy: "replace" | "skip" | "merge" = "replace",
324
- ): ReadonlyArray<Rule<TContext, TConsequences>> => {
325
- const ruleMap = new Map<string, Rule<TContext, TConsequences>>();
326
-
327
- for (const rule of baseRules) {
328
- ruleMap.set(rule.id, rule);
329
- }
330
-
331
- for (const rule of incomingRules) {
332
- const existing = ruleMap.get(rule.id);
333
-
334
- if (!existing) {
335
- ruleMap.set(rule.id, rule);
336
- } else {
337
- switch (strategy) {
338
- case "replace":
339
- ruleMap.set(rule.id, rule);
340
- break;
341
- case "skip":
342
- break;
343
- case "merge": {
344
- const merged: Rule<TContext, TConsequences> = {
345
- ...existing,
346
- ...rule,
347
- tags: [...new Set([...existing.tags, ...rule.tags])],
348
- metadata: { ...existing.metadata, ...rule.metadata },
349
- updatedAt: new Date(),
350
- };
351
- ruleMap.set(rule.id, merged);
352
- break;
353
- }
354
- }
355
- }
356
- }
357
-
358
- return [...ruleMap.values()];
359
- };
360
-
361
- export const diffRuleSets = <
362
- TContext = unknown,
363
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
364
- >(
365
- oldRules: ReadonlyArray<Rule<TContext, TConsequences>>,
366
- newRules: ReadonlyArray<Rule<TContext, TConsequences>>,
367
- ): {
368
- added: ReadonlyArray<Rule<TContext, TConsequences>>;
369
- removed: ReadonlyArray<Rule<TContext, TConsequences>>;
370
- modified: ReadonlyArray<{
371
- old: Rule<TContext, TConsequences>;
372
- new: Rule<TContext, TConsequences>;
373
- }>;
374
- unchanged: ReadonlyArray<Rule<TContext, TConsequences>>;
375
- } => {
376
- const oldMap = new Map(oldRules.map((r) => [r.id, r]));
377
- const newMap = new Map(newRules.map((r) => [r.id, r]));
378
-
379
- const added: Rule<TContext, TConsequences>[] = [];
380
- const removed: Rule<TContext, TConsequences>[] = [];
381
- const modified: Array<{
382
- old: Rule<TContext, TConsequences>;
383
- new: Rule<TContext, TConsequences>;
384
- }> = [];
385
- const unchanged: Rule<TContext, TConsequences>[] = [];
386
-
387
- for (const [id, newRule] of newMap) {
388
- const oldRule = oldMap.get(id);
389
- if (!oldRule) {
390
- added.push(newRule);
391
- } else if (
392
- JSON.stringify({ ...oldRule, updatedAt: null, createdAt: null }) !==
393
- JSON.stringify({ ...newRule, updatedAt: null, createdAt: null })
394
- ) {
395
- modified.push({ old: oldRule, new: newRule });
396
- } else {
397
- unchanged.push(newRule);
398
- }
399
- }
400
-
401
- for (const [id, oldRule] of oldMap) {
402
- if (!newMap.has(id)) {
403
- removed.push(oldRule);
404
- }
405
- }
406
-
407
- return { added, removed, modified, unchanged };
408
- };