@umpire/dsl
When you write Umpire rules entirely in TypeScript â no JSON schemas, no round-tripping across runtimes â @umpire/dsl is the only expression package you need. It owns the Expr type, the expr builder, and the compiler that turns expressions into runtime predicates.
If your rules need to survive a JSON boundary, @umpire/json re-exports everything here as a superset â same expr, compileExpr, and getExprFieldRefs, plus expr.check() and the portable builders. This page is the reference for all of it; just import from @umpire/json instead.
Install
Section titled âInstallâyarn add @umpire/dslexpr is the expression builder. Each method returns an Expr object you can pass to rule factories or compose with and, or, and not.
import { expr } from '@umpire/dsl'
const when = expr.and( expr.present('country'), expr.gt('total', 100),)Array and object payloads are cloned on construction, so mutating a values array after passing it to expr.in() does not affect the expression.
Field comparison
Section titled âField comparisonâ| Method | Signature | True when |
|---|---|---|
eq | (field, value: JsonPrimitive) => Expr | field strictly equals value |
neq | (field, value: JsonPrimitive) => Expr | field does not strictly equal value |
gt | (field, value: number) => Expr | field is a number greater than value |
gte | (field, value: number) => Expr | field is a number greater than or equal to value |
lt | (field, value: number) => Expr | field is a number less than value |
lte | (field, value: number) => Expr | field is a number less than or equal to value |
present | (field) => Expr | field is not null or undefined |
absent | (field) => Expr | field is null or undefined |
truthy | (field) => Expr | field is truthy |
falsy | (field) => Expr | field is falsy |
in | (field, values: JsonPrimitive[]) => Expr | field value is in the list |
notIn | (field, values: JsonPrimitive[]) => Expr | field value is not in the list |
Logical combinators
Section titled âLogical combinatorsâ| Method | Signature | Meaning |
|---|---|---|
and | (...exprs: Expr[]) => Expr | all sub-expressions must be true |
or | (...exprs: Expr[]) => Expr | at least one sub-expression must be true |
not | (expr: Expr) => Expr | inverts the sub-expression |
Condition ops
Section titled âCondition opsâConditions are external values provided by the host runtime â account tier, feature flags, server-supplied sets. They are declared on the schema, not on individual fields.
| Method | Signature | True when |
|---|---|---|
cond | (condition) => Expr | condition is truthy |
condEq | (condition, value: JsonPrimitive) => Expr | condition strictly equals value |
condIn | (condition, values: JsonPrimitive[]) => Expr | condition value is in the list |
fieldInCond | (field, condition) => Expr | field value is contained in the condition array |
fieldInCond requires an array condition at compile time â the type must be 'string[]' or 'number[]'. At runtime it throws if the condition value is not an array.
import { expr, compileExpr } from '@umpire/dsl'
// A plan-gated feature: the selected plan must be in the set the server says are eligibleconst when = expr.fieldInCond('plan', 'eligiblePlans')
// Compiles assuming eligiblePlans was declared as { type: 'string[]' }const predicate = compileExpr(when, { fieldNames: new Set(['plan']), conditions: { eligiblePlans: { type: 'string[]' } },})
predicate({ plan: 'pro' }, { eligiblePlans: ['pro', 'enterprise'] }) // truepredicate({ plan: 'free' }, { eligiblePlans: ['pro', 'enterprise'] }) // falsecompileExpr(expression, options)
Section titled âcompileExpr(expression, options)âCompiles an Expr into a predicate function.
function compileExpr<F, C>( expression: Expr, options: { fieldNames: Set<string> conditions?: Record<string, { type: 'boolean' | 'string' | 'number' | 'string[]' | 'number[]' }> allowUndeclaredConditions?: boolean },): (values: FieldValues<F>, conditions: C) => booleancompileExpr validates the expression at compile time:
- Every field referenced must appear in
fieldNames. Unknown fields throw. - Every condition referenced must appear in
conditions, unlessallowUndeclaredConditionsistrue. fieldInCondadditionally requires the condition type to be'string[]'or'number[]'.
The returned predicate accepts current field values and runtime conditions.
import { compileExpr, expr } from '@umpire/dsl'
const when = expr.and( expr.present('country'), expr.gt('total', 100),)
const predicate = compileExpr(when, { fieldNames: new Set(['country', 'total']),})
predicate({ country: 'US', total: 150 }, {}) // truepredicate({ country: null, total: 150 }, {}) // falseWhen the expression references exactly one field, the compiled predicate carries a _checkField property naming that field. Rule factories use this to attach structural dependency information without re-parsing the expression.
getExprFieldRefs(expression)
Section titled âgetExprFieldRefs(expression)âReturns the unique set of field names referenced by an expression.
function getExprFieldRefs(expression: Expr): string[]import { getExprFieldRefs, expr } from '@umpire/dsl'
const when = expr.and( expr.present('country'), expr.gt('total', 100), expr.gt('total', 50), // duplicate reference)
getExprFieldRefs(when) // ['country', 'total']Condition-only ops (cond, condEq, condIn) contribute no field refs. fieldInCond contributes its field operand but not its condition operand.
Expr is the union type of all expression AST nodes. You typically donât construct these by hand â use expr.* â but the type is useful when youâre storing or passing expressions around:
import type { Expr } from '@umpire/dsl'
function buildAvailabilityExpr(role: string): Expr { if (role === 'admin') return expr.truthy('adminEnabled') return expr.present('orgId')}ExprBuilder<F, C>
Section titled âExprBuilder<F, C>âExprBuilder<F, C> is the typed shape of expr, parameterized over field names F and condition keys C. At module scope, expr is typed as ExprBuilder<Record<string, FieldDef>, Record<string, unknown>> â it accepts any string. To get autocomplete on your specific field and condition names, narrow the type:
import type { ExprBuilder } from '@umpire/dsl'import type { SignupFields, SignupConditions } from './schema'
declare const e: ExprBuilder<SignupFields, SignupConditions>// e.present('email') â valid// e.present('typo') â TypeScript errorThe typed builder is most useful when authoring rules in a factory function where you want the compiler to catch field name mistakes.
What this package does not include
Section titled âWhat this package does not includeâexpr.check() is not part of @umpire/dsl. Checks that depend on named validators â namedValidators.email(), namedValidators.minLength(n), and so on â live in @umpire/json. If you need to express âthis field is available only when another field passes an email validator,â use @umpire/json directly. Its expr is a drop-in superset.
See also
Section titled âSee alsoâ@umpire/jsonbuilders & checks âexpr.check(),namedValidators, and portable rule builders@umpire/jsonoverview â JSON schema contract,fromJson,toJson