@@ -26,13 +26,11 @@ class Engine extends EventEmitter {
2626 this . allowUndefinedConditions = options . allowUndefinedConditions || false
2727 this . replaceFactsInEventParams = options . replaceFactsInEventParams || false
2828 this . pathResolver = options . pathResolver
29- this . operators = new OperatorMap ( )
29+ this . operators = new OperatorMap ( { parent : OperatorMap . shared ( ) } )
3030 this . facts = new Map ( )
3131 this . conditions = new Map ( )
3232 this . status = READY
3333 rules . map ( r => this . addRule ( r ) )
34- defaultOperators . map ( o => this . addOperator ( o ) )
35- defaultDecorators . map ( d => this . addOperatorDecorator ( d ) )
3634 }
3735
3836 /**
@@ -319,6 +317,112 @@ class Engine extends EventEmitter {
319317 } ) . catch ( reject )
320318 } )
321319 }
320+
321+ /**
322+ * Lightweight static evaluation of conditions against facts using the shared operator registry.
323+ * Avoids EventEmitter, Rule, and full Almanac overhead for high-throughput bulk evaluation.
324+ *
325+ * @param {Object } conditions - condition tree with all/any/not structure
326+ * @param {Object } facts - plain object of fact key-value pairs
327+ * @param {Object } [options] - evaluation options
328+ * @param {boolean } [options.allowUndefinedFacts=false] - allow undefined fact references
329+ * @param {Function } [options.pathResolver] - custom path resolver for nested properties
330+ * @param {OperatorMap } [options.operatorMap] - custom operator map (defaults to shared)
331+ * @returns {Promise<{results: Array, failureResults: Array, events: Array, failureEvents: Array}> }
332+ */
333+ static evaluate ( conditions , facts , options = { } ) {
334+ const operatorMap = options . operatorMap || OperatorMap . shared ( )
335+
336+ const almanac = new Almanac ( {
337+ allowUndefinedFacts : options . allowUndefinedFacts || false ,
338+ pathResolver : options . pathResolver
339+ } )
340+
341+ for ( const factId in facts ) {
342+ almanac . addFact ( new Fact ( factId , facts [ factId ] ) )
343+ }
344+
345+ const rootCondition = new Condition ( conditions )
346+
347+ const evaluateCondition = ( condition ) => {
348+ if ( condition . isBooleanOperator ( ) ) {
349+ const subConditions = condition [ condition . operator ]
350+ let comparisonPromise
351+ if ( condition . operator === 'all' ) {
352+ comparisonPromise = all ( subConditions )
353+ } else if ( condition . operator === 'any' ) {
354+ comparisonPromise = any ( subConditions )
355+ } else {
356+ comparisonPromise = notOp ( subConditions )
357+ }
358+ return comparisonPromise . then ( ( comparisonValue ) => {
359+ condition . result = comparisonValue === true
360+ return condition . result
361+ } )
362+ } else {
363+ return condition
364+ . evaluate ( almanac , operatorMap )
365+ . then ( ( evaluationResult ) => {
366+ condition . factResult = evaluationResult . leftHandSideValue
367+ condition . valueResult = evaluationResult . rightHandSideValue
368+ condition . result = evaluationResult . result
369+ return evaluationResult . result
370+ } )
371+ }
372+ }
373+
374+ const evaluateConditions = ( conds , method ) => {
375+ if ( ! Array . isArray ( conds ) ) conds = [ conds ]
376+ return Promise . all (
377+ conds . map ( ( c ) => evaluateCondition ( c ) )
378+ ) . then ( ( results ) => {
379+ return method . call ( results , ( r ) => r === true )
380+ } )
381+ }
382+
383+ const prioritizeAndRun = ( conds , operator ) => {
384+ if ( conds . length === 0 ) return Promise . resolve ( operator === 'all' )
385+ if ( conds . length === 1 ) return evaluateCondition ( conds [ 0 ] )
386+ // No priority sorting in static evaluate — evaluate all at once
387+ return operator === 'any'
388+ ? evaluateConditions ( conds , Array . prototype . some )
389+ : evaluateConditions ( conds , Array . prototype . every )
390+ }
391+
392+ const any = ( conds ) => prioritizeAndRun ( conds , 'any' )
393+ const all = ( conds ) => prioritizeAndRun ( conds , 'all' )
394+ const notOp = ( cond ) => prioritizeAndRun ( [ cond ] , 'not' ) . then ( ( r ) => ! r )
395+
396+ let rootPromise
397+ if ( rootCondition . any ) {
398+ rootPromise = any ( rootCondition . any )
399+ } else if ( rootCondition . all ) {
400+ rootPromise = all ( rootCondition . all )
401+ } else if ( rootCondition . not ) {
402+ rootPromise = notOp ( rootCondition . not )
403+ } else {
404+ rootPromise = evaluateCondition ( rootCondition )
405+ }
406+
407+ return rootPromise . then ( ( result ) => {
408+ const event = { type : 'evaluate' }
409+ if ( result ) {
410+ return {
411+ results : [ { conditions : rootCondition , event, result : true } ] ,
412+ failureResults : [ ] ,
413+ events : [ event ] ,
414+ failureEvents : [ ]
415+ }
416+ } else {
417+ return {
418+ results : [ ] ,
419+ failureResults : [ { conditions : rootCondition , event, result : false } ] ,
420+ events : [ ] ,
421+ failureEvents : [ event ]
422+ }
423+ }
424+ } )
425+ }
322426}
323427
324428export default Engine
0 commit comments