Use Evaluate.
Evaluates a boolean expression against a context object
The boolean expression to evaluate
Optionalcontext: Record<string, any> = {}The context object containing variables. Its keys are the only free names the expression may use; a key named like an approved global shadows it (see ScreenExpression).
OptionalenableDiagnostics: boolean = falseWhether to include diagnostic information
The evaluation result
Use EvaluateMultiple.
Evaluates multiple expressions and returns all results
Array of expressions to evaluate
The context object
Map of results by name or index
OptionalcontextKeys: Iterable<string, any, any>Use ValidateSyntax.
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 parses and never runs. The expression goes through the same policy screen
evaluate applies (the AST allowlist), then the same function body evaluate compiles is
parsed in strict mode — never compiled, 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. Pass
contextKeys when the evaluation context's keys ARE known, and unknown identifiers are refused
here exactly as Evaluate would refuse them.
Parsing is not evaluation, and the distinction is load-bearing for safety: no code is
constructed from the expression here, so nothing in it can execute. 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.
Because nothing is compiled, the answer does not depend on the host allowing dynamic code (a
strict CSP without unsafe-eval). Undecidable is kept in the result type for callers that
check it, and is no longer returned.
the expression to check
OptionalcontextKeys: Iterable<string, any, any>the names the expression may reference, when known; omitted, free identifiers are left for evaluation time (see ScreenExpression)
Valid: true when it parses; Valid: false with Error when it does not
StaticScreenScreens an expression against the allowlist WITHOUT compiling or running it. Returns the
rejection message, or null when every construct is permitted.
This is the single policy gate for every place MemberJunction compiles a user- or LLM-authored
expression: Evaluate, ValidateSyntax, and the field-rule / integration formula
engine (FieldTransformEngine), which passes ['value', 'fields'].
A context key or arrow parameter named like an approved global (Math, JSON, ...) shadows
it and is screened as data, so Math.abs(x) with a Math context key is refused because
abs is not a safe instance method. Do not pass approved-global names as context keys.
the expression to screen
the names the expression may reference as values — the evaluation
context keys. Pass null only when they are not yet known (authoring-time syntax
checks); free identifiers are then left for evaluation time to decide, while approved
globals used as values, callbacks that are not inline arrows, and every structural rule
are still enforced.
Safe expression evaluator that prevents arbitrary code execution while supporting common boolean expressions and property access patterns.
Supported operations:
?.)items.some(i => i.price > 100), neveritems.some(fn))Math.abs(output.delta) < 5,Number(payload.count) > 3,Object.keys(payload).length)Safety is enforced by parsing the expression to an AST and walking it against an ALLOWLIST before it is compiled. Node types, operators, methods and free identifiers are all allowlisted: a free identifier must be a context key, an arrow parameter in scope, or an approved global used as a call. An unlisted construct (computed member access with a non-literal key,
.constructor/__proto__access, any call outside the safe-method and approved-global lists, any other identifier, 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
Example