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

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 (45) hide show
  1. package/package.json +4 -1
  2. package/CHANGELOG.md +0 -168
  3. package/__tests__/builder.test.ts +0 -363
  4. package/__tests__/cache.test.ts +0 -130
  5. package/__tests__/config.test.ts +0 -35
  6. package/__tests__/engine.test.ts +0 -1213
  7. package/__tests__/evaluate.test.ts +0 -339
  8. package/__tests__/exports.test.ts +0 -30
  9. package/__tests__/filter-sort.test.ts +0 -303
  10. package/__tests__/integration.test.ts +0 -419
  11. package/__tests__/money-integration.test.ts +0 -149
  12. package/__tests__/validation.test.ts +0 -862
  13. package/biome.json +0 -39
  14. package/docs/MIGRATION-v3.md +0 -118
  15. package/fot.config.ts +0 -5
  16. package/src/analyzer/analysis.ts +0 -401
  17. package/src/builder/conditions.ts +0 -321
  18. package/src/builder/rule.ts +0 -192
  19. package/src/cache/cache.ts +0 -135
  20. package/src/cache/noop.ts +0 -20
  21. package/src/core/evaluate.ts +0 -185
  22. package/src/core/filter.ts +0 -85
  23. package/src/core/group.ts +0 -103
  24. package/src/core/sort.ts +0 -90
  25. package/src/engine/engine.ts +0 -462
  26. package/src/engine/hooks.ts +0 -235
  27. package/src/engine/state.ts +0 -322
  28. package/src/index.ts +0 -303
  29. package/src/optimizer/index-builder.ts +0 -381
  30. package/src/serialization/serializer.ts +0 -408
  31. package/src/simulation/simulator.ts +0 -359
  32. package/src/types/config.ts +0 -184
  33. package/src/types/consequence.ts +0 -38
  34. package/src/types/evaluation.ts +0 -87
  35. package/src/types/rule.ts +0 -112
  36. package/src/types/state.ts +0 -116
  37. package/src/utils/conditions.ts +0 -108
  38. package/src/utils/hash.ts +0 -30
  39. package/src/utils/id.ts +0 -6
  40. package/src/utils/time.ts +0 -42
  41. package/src/validation/conflicts.ts +0 -440
  42. package/src/validation/integrity.ts +0 -473
  43. package/src/validation/schema.ts +0 -386
  44. package/src/versioning/version-store.ts +0 -337
  45. package/tsconfig.json +0 -29
@@ -1,462 +0,0 @@
1
- import { type Cache, createCache } from "../cache/cache";
2
- import { createNoopCache } from "../cache/noop";
3
- import { evaluateRule } from "../core/evaluate";
4
- import { createEvaluator } from "@f-o-t/condition-evaluator";
5
- import {
6
- type EngineConfig,
7
- getDefaultCacheConfig,
8
- getDefaultConflictResolution,
9
- getDefaultLogLevel,
10
- getDefaultValidationConfig,
11
- getDefaultVersioningConfig,
12
- type ResolvedEngineConfig,
13
- } from "../types/config";
14
- import type {
15
- AggregatedConsequence,
16
- ConsequenceDefinitions,
17
- DefaultConsequences,
18
- } from "../types/consequence";
19
- import type {
20
- EngineExecutionResult,
21
- EvaluateOptions,
22
- EvaluationContext,
23
- } from "../types/evaluation";
24
- import type {
25
- Rule,
26
- RuleFilters,
27
- RuleInput,
28
- RuleSet,
29
- RuleSetInput,
30
- } from "../types/rule";
31
- import type {
32
- CacheStats,
33
- EngineState,
34
- EngineStats,
35
- MutableEngineState,
36
- } from "../types/state";
37
- import { createInitialState } from "../types/state";
38
- import { hashContext, hashRules } from "../utils/hash";
39
- import { generateId } from "../utils/id";
40
- import { measureTime } from "../utils/time";
41
- import {
42
- executeAfterEvaluation,
43
- executeAfterRuleEvaluation,
44
- executeBeforeEvaluation,
45
- executeBeforeRuleEvaluation,
46
- executeOnCacheHit,
47
- executeOnCacheMiss,
48
- executeOnConsequenceCollected,
49
- executeOnRuleError,
50
- executeOnRuleMatch,
51
- executeOnRuleSkip,
52
- executeOnSlowRule,
53
- } from "./hooks";
54
- import {
55
- addRule,
56
- addRuleSet,
57
- addRules,
58
- clearRules,
59
- disableRule,
60
- enableRule,
61
- getRule,
62
- getRuleSet,
63
- getRuleSets,
64
- getRules,
65
- getRulesInSet,
66
- removeRule,
67
- removeRuleSet,
68
- updateRule,
69
- } from "./state";
70
-
71
- export type Engine<
72
- TContext = unknown,
73
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
74
- > = {
75
- readonly addRule: (
76
- input: RuleInput<TContext, TConsequences>,
77
- ) => Rule<TContext, TConsequences>;
78
- readonly addRules: (
79
- inputs: RuleInput<TContext, TConsequences>[],
80
- ) => Rule<TContext, TConsequences>[];
81
- readonly removeRule: (ruleId: string) => boolean;
82
- readonly updateRule: (
83
- ruleId: string,
84
- updates: Partial<RuleInput<TContext, TConsequences>>,
85
- ) => Rule<TContext, TConsequences> | undefined;
86
- readonly getRule: (
87
- ruleId: string,
88
- ) => Rule<TContext, TConsequences> | undefined;
89
- readonly getRules: (
90
- filters?: RuleFilters,
91
- ) => ReadonlyArray<Rule<TContext, TConsequences>>;
92
- readonly enableRule: (ruleId: string) => boolean;
93
- readonly disableRule: (ruleId: string) => boolean;
94
- readonly clearRules: () => void;
95
-
96
- readonly addRuleSet: (input: RuleSetInput) => RuleSet;
97
- readonly getRuleSet: (ruleSetId: string) => RuleSet | undefined;
98
- readonly getRuleSets: () => ReadonlyArray<RuleSet>;
99
- readonly removeRuleSet: (ruleSetId: string) => boolean;
100
-
101
- readonly evaluate: (
102
- context: TContext,
103
- options?: EvaluateOptions,
104
- ) => Promise<EngineExecutionResult<TContext, TConsequences>>;
105
-
106
- readonly clearCache: () => void;
107
- readonly getCacheStats: () => CacheStats;
108
-
109
- readonly getState: () => Readonly<EngineState<TContext, TConsequences>>;
110
- readonly getStats: () => EngineStats;
111
- };
112
-
113
- const resolveConfig = <
114
- TContext = unknown,
115
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
116
- >(
117
- config: EngineConfig<TContext, TConsequences>,
118
- ): ResolvedEngineConfig<TContext, TConsequences> => {
119
- // Validate evaluator config
120
- if (!config.evaluator && !config.operators) {
121
- throw new Error(
122
- "Engine requires either 'evaluator' or 'operators' config. " +
123
- "Pass { evaluator: createEvaluator() } for built-in operators only, " +
124
- "or { operators: customOperators } to use custom operators."
125
- );
126
- }
127
-
128
- // Create evaluator from config
129
- const evaluator = config.evaluator
130
- ? config.evaluator
131
- : createEvaluator({ operators: config.operators });
132
-
133
- return {
134
- consequences: config.consequences,
135
- conflictResolution:
136
- config.conflictResolution ?? getDefaultConflictResolution(),
137
- cache: {
138
- ...getDefaultCacheConfig(),
139
- ...config.cache,
140
- },
141
- validation: {
142
- ...getDefaultValidationConfig(),
143
- ...config.validation,
144
- },
145
- versioning: {
146
- ...getDefaultVersioningConfig(),
147
- ...config.versioning,
148
- },
149
- hooks: config.hooks ?? {},
150
- logLevel: config.logLevel ?? getDefaultLogLevel(),
151
- logger: config.logger ?? console,
152
- continueOnError: config.continueOnError ?? true,
153
- slowRuleThresholdMs: config.slowRuleThresholdMs ?? 10,
154
- hookTimeoutMs: config.hookTimeoutMs,
155
- evaluator, // Add to resolved config
156
- };
157
- };
158
-
159
- export const createEngine = <
160
- TContext = unknown,
161
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
162
- >(
163
- config: EngineConfig<TContext, TConsequences> = {},
164
- ): Engine<TContext, TConsequences> => {
165
- const resolvedConfig = resolveConfig(config);
166
- const state: MutableEngineState<TContext, TConsequences> =
167
- createInitialState();
168
-
169
- const cache: Cache<EngineExecutionResult<TContext, TConsequences>> =
170
- resolvedConfig.cache.enabled
171
- ? createCache({
172
- ttl: resolvedConfig.cache.ttl,
173
- maxSize: resolvedConfig.cache.maxSize,
174
- })
175
- : createNoopCache();
176
-
177
- let totalEvaluations = 0;
178
- let totalMatches = 0;
179
- let totalErrors = 0;
180
- let cacheHits = 0;
181
- let cacheMisses = 0;
182
- let totalEvaluationTime = 0;
183
-
184
- const evaluate = async (
185
- contextData: TContext,
186
- options: EvaluateOptions = {},
187
- ): Promise<EngineExecutionResult<TContext, TConsequences>> => {
188
- const context: EvaluationContext<TContext> = {
189
- data: contextData,
190
- timestamp: new Date(),
191
- correlationId: generateId(),
192
- };
193
-
194
- let rulesToEvaluate = getRules(state, {
195
- enabled: options.skipDisabled !== false ? true : undefined,
196
- tags: options.tags,
197
- category: options.category,
198
- });
199
-
200
- if (options.ruleSetId) {
201
- const ruleSetRules = getRulesInSet(state, options.ruleSetId);
202
- const ruleSetIds = new Set(ruleSetRules.map((r) => r.id));
203
- rulesToEvaluate = rulesToEvaluate.filter((r) => ruleSetIds.has(r.id));
204
- }
205
-
206
- // Rules are already sorted by priority via state.ruleOrder (sorted on add/update)
207
-
208
- if (options.maxRules && options.maxRules > 0) {
209
- rulesToEvaluate = rulesToEvaluate.slice(0, options.maxRules);
210
- }
211
-
212
- const cacheKey = !options.bypassCache
213
- ? `${hashContext(contextData)}:${hashRules(rulesToEvaluate.map((r) => r.id))}`
214
- : null;
215
-
216
- if (cacheKey && cache.has(cacheKey)) {
217
- const cached = cache.get(cacheKey);
218
- if (cached) {
219
- cacheHits++;
220
- await executeOnCacheHit(
221
- resolvedConfig.hooks,
222
- cacheKey,
223
- cached,
224
- resolvedConfig.hookTimeoutMs,
225
- );
226
- return { ...cached, cacheHit: true };
227
- }
228
- }
229
-
230
- if (cacheKey) {
231
- cacheMisses++;
232
- await executeOnCacheMiss(
233
- resolvedConfig.hooks,
234
- cacheKey,
235
- resolvedConfig.hookTimeoutMs,
236
- );
237
- }
238
-
239
- await executeBeforeEvaluation(
240
- resolvedConfig.hooks,
241
- context,
242
- rulesToEvaluate,
243
- resolvedConfig.hookTimeoutMs,
244
- );
245
-
246
- const { result: evaluationResult, durationMs } = measureTime(() => {
247
- const results: Array<{
248
- rule: Rule<TContext, TConsequences>;
249
- result: ReturnType<typeof evaluateRule<TContext, TConsequences>>;
250
- }> = [];
251
-
252
- for (const rule of rulesToEvaluate) {
253
- const result = evaluateRule(rule, context, resolvedConfig.evaluator, { skipDisabled: true });
254
- results.push({ rule, result });
255
- }
256
-
257
- return results;
258
- });
259
-
260
- const ruleResults: ReturnType<
261
- typeof evaluateRule<TContext, TConsequences>
262
- >[] = [];
263
- const matchedRules: Rule<TContext, TConsequences>[] = [];
264
- const consequences: AggregatedConsequence<TConsequences>[] = [];
265
- let stoppedEarly = false;
266
- let stoppedByRuleId: string | undefined;
267
- let rulesErrored = 0;
268
-
269
- const conflictResolution =
270
- options.conflictResolution ?? resolvedConfig.conflictResolution;
271
-
272
- for (const { rule, result } of evaluationResult) {
273
- await executeBeforeRuleEvaluation(
274
- resolvedConfig.hooks,
275
- rule,
276
- context,
277
- resolvedConfig.hookTimeoutMs,
278
- );
279
-
280
- ruleResults.push(result);
281
-
282
- if (result.error) {
283
- rulesErrored++;
284
- await executeOnRuleError(
285
- resolvedConfig.hooks,
286
- rule,
287
- result.error,
288
- resolvedConfig.hookTimeoutMs,
289
- );
290
- if (!resolvedConfig.continueOnError) {
291
- break;
292
- }
293
- }
294
-
295
- if (result.skipped) {
296
- await executeOnRuleSkip(
297
- resolvedConfig.hooks,
298
- rule,
299
- result.skipReason ?? "Unknown",
300
- resolvedConfig.hookTimeoutMs,
301
- );
302
- }
303
-
304
- if (result.evaluationTimeMs > resolvedConfig.slowRuleThresholdMs) {
305
- await executeOnSlowRule(
306
- resolvedConfig.hooks,
307
- rule,
308
- result.evaluationTimeMs,
309
- resolvedConfig.slowRuleThresholdMs,
310
- resolvedConfig.hookTimeoutMs,
311
- );
312
- }
313
-
314
- await executeAfterRuleEvaluation(
315
- resolvedConfig.hooks,
316
- rule,
317
- result,
318
- resolvedConfig.hookTimeoutMs,
319
- );
320
-
321
- if (result.matched) {
322
- matchedRules.push(rule);
323
-
324
- for (const consequence of result.consequences) {
325
- consequences.push(consequence);
326
- await executeOnConsequenceCollected(
327
- resolvedConfig.hooks,
328
- rule,
329
- consequence,
330
- resolvedConfig.hookTimeoutMs,
331
- );
332
- }
333
-
334
- await executeOnRuleMatch(
335
- resolvedConfig.hooks,
336
- rule,
337
- context,
338
- resolvedConfig.hookTimeoutMs,
339
- );
340
-
341
- if (rule.stopOnMatch) {
342
- stoppedEarly = true;
343
- stoppedByRuleId = rule.id;
344
- break;
345
- }
346
-
347
- if (conflictResolution === "first-match") {
348
- stoppedEarly = true;
349
- stoppedByRuleId = rule.id;
350
- break;
351
- }
352
- }
353
- }
354
-
355
- totalEvaluations++;
356
- totalMatches += matchedRules.length;
357
- totalErrors += rulesErrored;
358
- totalEvaluationTime += durationMs;
359
-
360
- const executionResult: EngineExecutionResult<TContext, TConsequences> = {
361
- context,
362
- results: ruleResults,
363
- matchedRules,
364
- consequences,
365
- totalRulesEvaluated: ruleResults.length,
366
- totalRulesMatched: matchedRules.length,
367
- totalRulesSkipped: ruleResults.filter((r) => r.skipped).length,
368
- totalRulesErrored: rulesErrored,
369
- executionTimeMs: durationMs,
370
- stoppedEarly,
371
- stoppedByRuleId,
372
- cacheHit: false,
373
- };
374
-
375
- if (cacheKey) {
376
- cache.set(cacheKey, executionResult);
377
- }
378
-
379
- await executeAfterEvaluation(
380
- resolvedConfig.hooks,
381
- executionResult,
382
- resolvedConfig.hookTimeoutMs,
383
- );
384
-
385
- return executionResult;
386
- };
387
-
388
- return {
389
- addRule: (input) => addRule(state, input),
390
- addRules: (inputs) => addRules(state, inputs),
391
- removeRule: (ruleId) => {
392
- const result = removeRule(state, ruleId);
393
- if (result) cache.clear();
394
- return result;
395
- },
396
- updateRule: (ruleId, updates) => {
397
- const result = updateRule(state, ruleId, updates);
398
- if (result) cache.clear();
399
- return result;
400
- },
401
- getRule: (ruleId) => getRule(state, ruleId),
402
- getRules: (filters) => getRules(state, filters),
403
- enableRule: (ruleId) => {
404
- const result = enableRule(state, ruleId);
405
- if (result) cache.clear();
406
- return result;
407
- },
408
- disableRule: (ruleId) => {
409
- const result = disableRule(state, ruleId);
410
- if (result) cache.clear();
411
- return result;
412
- },
413
- clearRules: () => {
414
- clearRules(state);
415
- cache.clear();
416
- },
417
-
418
- addRuleSet: (input) => addRuleSet(state, input),
419
- getRuleSet: (ruleSetId) => getRuleSet(state, ruleSetId),
420
- getRuleSets: () => getRuleSets(state),
421
- removeRuleSet: (ruleSetId) => removeRuleSet(state, ruleSetId),
422
-
423
- evaluate,
424
-
425
- clearCache: () => cache.clear(),
426
- getCacheStats: () => cache.getStats(),
427
-
428
- getState: () => ({
429
- rules: state.rules as ReadonlyMap<
430
- string,
431
- Rule<TContext, TConsequences>
432
- >,
433
- ruleSets: state.ruleSets as ReadonlyMap<string, RuleSet>,
434
- ruleOrder: state.ruleOrder,
435
- }),
436
-
437
- getStats: () => {
438
- const enabledRules = Array.from(state.rules.values()).filter(
439
- (r) => r.enabled,
440
- ).length;
441
- return {
442
- totalRules: state.rules.size,
443
- enabledRules,
444
- disabledRules: state.rules.size - enabledRules,
445
- totalRuleSets: state.ruleSets.size,
446
- totalEvaluations,
447
- totalMatches,
448
- totalErrors,
449
- avgEvaluationTimeMs:
450
- totalEvaluations > 0
451
- ? totalEvaluationTime / totalEvaluations
452
- : 0,
453
- cacheHits,
454
- cacheMisses,
455
- cacheHitRate:
456
- cacheHits + cacheMisses > 0
457
- ? cacheHits / (cacheHits + cacheMisses)
458
- : 0,
459
- };
460
- },
461
- };
462
- };
@@ -1,235 +0,0 @@
1
- import type { EngineHooks } from "../types/config";
2
- import type {
3
- AggregatedConsequence,
4
- ConsequenceDefinitions,
5
- DefaultConsequences,
6
- } from "../types/consequence";
7
- import type {
8
- EngineExecutionResult,
9
- EvaluationContext,
10
- RuleEvaluationResult,
11
- } from "../types/evaluation";
12
- import type { Rule } from "../types/rule";
13
- import { withTimeout } from "../utils/time";
14
-
15
- const toError = (error: unknown): Error =>
16
- error instanceof Error ? error : new Error(String(error));
17
-
18
- const executeWithTimeout = async (
19
- hookName: string,
20
- hookFn: () => void | Promise<void>,
21
- hooks: EngineHooks<unknown, ConsequenceDefinitions>,
22
- timeoutMs?: number,
23
- ): Promise<void> => {
24
- try {
25
- const promise = Promise.resolve(hookFn());
26
- if (timeoutMs !== undefined && timeoutMs > 0) {
27
- await withTimeout(
28
- promise,
29
- timeoutMs,
30
- `Hook '${hookName}' timed out after ${timeoutMs}ms`,
31
- );
32
- } else {
33
- await promise;
34
- }
35
- } catch (error) {
36
- hooks.onHookError?.(hookName, toError(error));
37
- }
38
- };
39
-
40
- export const executeBeforeEvaluation = async <
41
- TContext = unknown,
42
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
43
- >(
44
- hooks: EngineHooks<TContext, TConsequences>,
45
- context: EvaluationContext<TContext>,
46
- rules: ReadonlyArray<Rule<TContext, TConsequences>>,
47
- timeoutMs?: number,
48
- ): Promise<void> => {
49
- if (!hooks.beforeEvaluation) return;
50
- await executeWithTimeout(
51
- "beforeEvaluation",
52
- () => hooks.beforeEvaluation?.(context, rules),
53
- hooks as EngineHooks<unknown, ConsequenceDefinitions>,
54
- timeoutMs,
55
- );
56
- };
57
-
58
- export const executeAfterEvaluation = async <
59
- TContext = unknown,
60
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
61
- >(
62
- hooks: EngineHooks<TContext, TConsequences>,
63
- result: EngineExecutionResult<TContext, TConsequences>,
64
- timeoutMs?: number,
65
- ): Promise<void> => {
66
- if (!hooks.afterEvaluation) return;
67
- await executeWithTimeout(
68
- "afterEvaluation",
69
- () => hooks.afterEvaluation?.(result),
70
- hooks as EngineHooks<unknown, ConsequenceDefinitions>,
71
- timeoutMs,
72
- );
73
- };
74
-
75
- export const executeBeforeRuleEvaluation = async <
76
- TContext = unknown,
77
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
78
- >(
79
- hooks: EngineHooks<TContext, TConsequences>,
80
- rule: Rule<TContext, TConsequences>,
81
- context: EvaluationContext<TContext>,
82
- timeoutMs?: number,
83
- ): Promise<void> => {
84
- if (!hooks.beforeRuleEvaluation) return;
85
- await executeWithTimeout(
86
- "beforeRuleEvaluation",
87
- () => hooks.beforeRuleEvaluation?.(rule, context),
88
- hooks as EngineHooks<unknown, ConsequenceDefinitions>,
89
- timeoutMs,
90
- );
91
- };
92
-
93
- export const executeAfterRuleEvaluation = async <
94
- TContext = unknown,
95
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
96
- >(
97
- hooks: EngineHooks<TContext, TConsequences>,
98
- rule: Rule<TContext, TConsequences>,
99
- result: RuleEvaluationResult<TContext, TConsequences>,
100
- timeoutMs?: number,
101
- ): Promise<void> => {
102
- if (!hooks.afterRuleEvaluation) return;
103
- await executeWithTimeout(
104
- "afterRuleEvaluation",
105
- () => hooks.afterRuleEvaluation?.(rule, result),
106
- hooks as EngineHooks<unknown, ConsequenceDefinitions>,
107
- timeoutMs,
108
- );
109
- };
110
-
111
- export const executeOnRuleMatch = async <
112
- TContext = unknown,
113
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
114
- >(
115
- hooks: EngineHooks<TContext, TConsequences>,
116
- rule: Rule<TContext, TConsequences>,
117
- context: EvaluationContext<TContext>,
118
- timeoutMs?: number,
119
- ): Promise<void> => {
120
- if (!hooks.onRuleMatch) return;
121
- await executeWithTimeout(
122
- "onRuleMatch",
123
- () => hooks.onRuleMatch?.(rule, context),
124
- hooks as EngineHooks<unknown, ConsequenceDefinitions>,
125
- timeoutMs,
126
- );
127
- };
128
-
129
- export const executeOnRuleSkip = async <
130
- TContext = unknown,
131
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
132
- >(
133
- hooks: EngineHooks<TContext, TConsequences>,
134
- rule: Rule<TContext, TConsequences>,
135
- reason: string,
136
- timeoutMs?: number,
137
- ): Promise<void> => {
138
- if (!hooks.onRuleSkip) return;
139
- await executeWithTimeout(
140
- "onRuleSkip",
141
- () => hooks.onRuleSkip?.(rule, reason),
142
- hooks as EngineHooks<unknown, ConsequenceDefinitions>,
143
- timeoutMs,
144
- );
145
- };
146
-
147
- export const executeOnRuleError = async <
148
- TContext = unknown,
149
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
150
- >(
151
- hooks: EngineHooks<TContext, TConsequences>,
152
- rule: Rule<TContext, TConsequences>,
153
- ruleError: Error,
154
- timeoutMs?: number,
155
- ): Promise<void> => {
156
- if (!hooks.onRuleError) return;
157
- await executeWithTimeout(
158
- "onRuleError",
159
- () => hooks.onRuleError?.(rule, ruleError),
160
- hooks as EngineHooks<unknown, ConsequenceDefinitions>,
161
- timeoutMs,
162
- );
163
- };
164
-
165
- export const executeOnConsequenceCollected = async <
166
- TContext = unknown,
167
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
168
- >(
169
- hooks: EngineHooks<TContext, TConsequences>,
170
- rule: Rule<TContext, TConsequences>,
171
- consequence: AggregatedConsequence<TConsequences>,
172
- timeoutMs?: number,
173
- ): Promise<void> => {
174
- if (!hooks.onConsequenceCollected) return;
175
- await executeWithTimeout(
176
- "onConsequenceCollected",
177
- () => hooks.onConsequenceCollected?.(rule, consequence),
178
- hooks as EngineHooks<unknown, ConsequenceDefinitions>,
179
- timeoutMs,
180
- );
181
- };
182
-
183
- export const executeOnCacheHit = async <
184
- TContext = unknown,
185
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
186
- >(
187
- hooks: EngineHooks<TContext, TConsequences>,
188
- key: string,
189
- result: EngineExecutionResult<TContext, TConsequences>,
190
- timeoutMs?: number,
191
- ): Promise<void> => {
192
- if (!hooks.onCacheHit) return;
193
- await executeWithTimeout(
194
- "onCacheHit",
195
- () => hooks.onCacheHit?.(key, result),
196
- hooks as EngineHooks<unknown, ConsequenceDefinitions>,
197
- timeoutMs,
198
- );
199
- };
200
-
201
- export const executeOnCacheMiss = async <
202
- TContext = unknown,
203
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
204
- >(
205
- hooks: EngineHooks<TContext, TConsequences>,
206
- key: string,
207
- timeoutMs?: number,
208
- ): Promise<void> => {
209
- if (!hooks.onCacheMiss) return;
210
- await executeWithTimeout(
211
- "onCacheMiss",
212
- () => hooks.onCacheMiss?.(key),
213
- hooks as EngineHooks<unknown, ConsequenceDefinitions>,
214
- timeoutMs,
215
- );
216
- };
217
-
218
- export const executeOnSlowRule = async <
219
- TContext = unknown,
220
- TConsequences extends ConsequenceDefinitions = DefaultConsequences,
221
- >(
222
- hooks: EngineHooks<TContext, TConsequences>,
223
- rule: Rule<TContext, TConsequences>,
224
- timeMs: number,
225
- threshold: number,
226
- timeoutMs?: number,
227
- ): Promise<void> => {
228
- if (!hooks.onSlowRule) return;
229
- await executeWithTimeout(
230
- "onSlowRule",
231
- () => hooks.onSlowRule?.(rule, timeMs, threshold),
232
- hooks as EngineHooks<unknown, ConsequenceDefinitions>,
233
- timeoutMs,
234
- );
235
- };