Member Junction
    Preparing search index...

    Safe expression evaluator that prevents arbitrary code execution while supporting common boolean expressions and property access patterns.

    Supported operations:

    • Comparison: ==, ===, !=, !==, <, >, <=, >=
    • Logical: &&, ||, !
    • Property access: dot notation (e.g., payload.customer.name), including optional chaining (?.)
    • Array access: bracket notation with a literal index/key (e.g., items[0])
    • Safe methods: .length, .includes(), .startsWith(), .endsWith()
    • Array methods: .some(), .every(), .find(), .filter()
    • Safe globals: the namespaces and bare callables in SAFE_EXPRESSION_GLOBALS (e.g. Math.abs(output.delta) < 5, Number(payload.count) > 3, Object.keys(payload).length)
    • Type checking: typeof

    Safety is enforced by parsing the expression to an AST and walking it against an ALLOWLIST of node types before it is compiled — an unlisted construct (computed member access with a non-literal key, .constructor/__proto__ access, any call outside the safe-method and safe-global lists, host-global identifiers, etc.) is rejected at validation time and never reaches the compiler. A structural allowlist cannot be defeated by string concatenation the way a textual denylist can, and it does not over-reject data that merely mentions a reserved word (e.g. name == 'constructor' is a legal comparison).

    SafeExpressionEvaluator

    const evaluator = new SafeExpressionEvaluator();

    // Simple comparison
    const result1 = evaluator.evaluate(
    "status == 'active'",
    { status: 'active' }
    );

    // Nested property access
    const result2 = evaluator.evaluate(
    "payload.customer.tier == 'premium' && payload.order.total > 1000",
    { payload: { customer: { tier: 'premium' }, order: { total: 1500 } } }
    );

    // Array methods
    const result3 = evaluator.evaluate(
    "items.some(item => item.price > 100)",
    { items: [{ price: 50 }, { price: 150 }] }
    );
    Index

    Constructors

    Methods

    • Evaluates a boolean expression against a context object

      Parameters

      • expression: string

        The boolean expression to evaluate

      • context: Record<string, any>

        The context object containing variables

      • OptionalenableDiagnostics: boolean = false

        Whether to include diagnostic information

      Returns ExpressionEvaluationResult

      The evaluation result

    • Evaluates multiple expressions and returns all results

      Parameters

      • expressions: { expression: string; name?: string }[]

        Array of expressions to evaluate

      • context: Record<string, any>

        The context object

      Returns Record<string, ExpressionEvaluationResult>

      Map of results by name or index

    • Checks whether an expression could be evaluated, without evaluating it.

      The difference matters because evaluate reports two unrelated problems the same way. Given a condition authored against a runtime envelope, an empty context makes payload.x > 1 fail with payload is not defined — indistinguishable, by result shape, from payload.x > failing with Unexpected token. One is a typo the author should be told about at the door; the other is a perfectly good condition that simply has no data yet. A submit-time check built on evaluate therefore refuses every legitimate condition.

      So this compiles and never runs. The expression goes through the same policy screen evaluate applies (the AST allowlist), then the same function body is BUILT and discarded — never invoked. Values are never consulted, which is precisely the property wanted: unknown identifiers, absent properties and undefined chains all PASS, because none of them is a syntax error and whether they resolve is a question about a run that has not happened yet.

      Compilation is not evaluation, and the distinction is load-bearing for safety: Function parses the body and returns; nothing in the expression executes. The policy screen still runs first, so the constructs evaluate refuses are refused here too — those produce a permanent runtime refusal, and an author is better told now.

      Undecidable is the honest third answer. A host that forbids dynamic compilation (a strict CSP without unsafe-eval) cannot answer the question at all, and a validator that read that as "invalid" would refuse every condition in the browser. Callers should treat it as a pass.

      Parameters

      • expression: string

        the expression to check

      Returns { Error?: string; Undecidable?: boolean; Valid: boolean }

      Valid: true when it parses; Valid: false with Error when it definitely does not; Valid: true with Undecidable: true when this environment cannot compile