import { ConditionInterface , TestedObj , ConditionValue , Operator , OperatorConditionValue , VarType , } from "./types/mongrule" ; const _regexCache = { } ; export function evalCondition ( obj , condition ) { if ( "$or" in condition ) { return evalOr ( obj , condition [ "$or" ] as ConditionInterface [ ] ) ; } if ( "$nor" in condition ) { return ! evalOr ( obj , condition [ "$nor" ] as ConditionInterface [ ] ) ; } if ( "$and" in condition ) { return evalAnd ( obj , condition [ "$and" ] as ConditionInterface [ ] ) ; } if ( "$not" in condition ) { return ! evalCondition ( obj , condition [ "$not" ] as ConditionInterface ) ; } for ( const [ k , v ] of Object . entries ( condition ) ) { if ( ! evalConditionValue ( v , getPath ( obj , k ) ) ) return false ; } return true ; } function getPath ( obj , path ) { const parts = path . split ( "." ) ; let current = obj ; for ( let i = 0 ; i < parts . length ; i ++ ) { if ( parts [ i ] in current ) { current = current [ parts [ i ] ] ; } else { return null ; } } return current ; } function getRegex ( regex ) { if ( ! _regexCache [ regex ] ) { _regexCache [ regex ] = new RegExp ( regex . replace ( /([^\\])\//g , "$1\\/" ) ) ; } return _regexCache [ regex ] ; } function evalConditionValue ( condition , value ) { if ( typeof condition === "string" ) { return value + "" === condition ; } if ( typeof condition === "number" ) { return value * 1 === condition ; } if ( typeof condition === "boolean" ) { return ! ! value === condition ; } if ( Array . isArray ( condition ) || ! isOperatorObject ( condition ) ) { return JSON . stringify ( value ) === JSON . stringify ( condition ) ; } for ( const op in condition ) { if ( ! evalOperatorCondition ( op as Operator , value , condition [ op as keyof OperatorConditionValue ] ) ) { return false ; } } return true ; } function isOperatorObject ( obj ) { const keys = Object . keys ( obj ) ; return ( keys . length > 0 && keys . filter ( ( k ) => k [ 0 ] === "$" ) . length === keys . length ) ; } function getType ( v ) { if ( v === null ) return "null" ; if ( Array . isArray ( v ) ) return "array" ; const t = typeof v ; if ( [ "string" , "number" , "boolean" , "object" , "undefined" ] . includes ( t ) ) { return t as VarType ; } return "unknown" ; } function elemMatch ( actual , expected ) { if ( ! Array . isArray ( actual ) ) return false ; const check = isOperatorObject ( expected ) ? ( v ) => evalConditionValue ( expected , v ) : ( v ) => evalCondition ( v , expected ) ; for ( let i = 0 ; i < actual . length ; i ++ ) { if ( actual [ i ] && check ( actual [ i ] ) ) { return true ; } } return false ; } function evalOperatorCondition ( operator , actual , expected ) { switch ( operator ) { case "$eq" : return actual === expected ; case "$ne" : return actual !== expected ; case "$lt" : return actual < expected ; case "$lte" : return actual <= expected ; case "$gt" : return actual > expected ; case "$gte" : return actual >= expected ; case "$exists" : return expected ? actual !== null : actual === null ; case "$in" : return expected . includes ( actual ) ; case "$nin" : return ! expected . includes ( actual ) ; case "$not" : return ! evalConditionValue ( expected , actual ) ; case "$size" : if ( ! Array . isArray ( actual ) ) return false ; return evalConditionValue ( expected , actual . length ) ; case "$elemMatch" : return elemMatch ( actual , expected ) ; case "$all" : if ( ! Array . isArray ( actual ) ) return false ; for ( let i = 0 ; i < expected . length ; i ++ ) { let passed = false ; for ( let j = 0 ; j < actual . length ; j ++ ) { if ( evalConditionValue ( expected [ i ] , actual [ j ] ) ) { passed = true ; break ; } } if ( ! passed ) return false ; } return true ; case "$regex" : try { return getRegex ( expected ) . test ( actual ) ; } catch ( e ) { return false ; } case "$type" : return getType ( actual ) === expected ; default : console . error ( "Unknown operator: " + operator ) ; return false ; } } function evalOr ( obj , conditions ) { if ( ! conditions . length ) return true ; for ( let i = 0 ; i < conditions . length ; i ++ ) { if ( evalCondition ( obj , conditions [ i ] ) ) { return true ; } } return false ; } function evalAnd ( obj , conditions ) { for ( let i = 0 ; i < conditions . length ; i ++ ) { if ( ! evalCondition ( obj , conditions [ i ] ) ) { return false ; } } return true ; }