JEXL Expressions ${{ }}
Loopstack uses JEXL (JavaScript Expression Language) for evaluating JEXL expressions. Unlike template expressions, JEXL expressions preserve the data type of the result — a number stays a number, an object stays an object, and so on.
A JEXL expression must be the entire value of a YAML field (with optional surrounding whitespace). It cannot be embedded inline within text — use template expressions ({{ }}) for that.
Property Access
Simple property
${{ user.name }}
Nested property
${{ user.profile.address.city }}
Array access by index
${{ items[0] }}
Nested array access
${{ data.users[0].roles[0] }}
Comparison Operators
| Operator | Description | Example |
|---|---|---|
== | Equal | ${{ status == "active" }} |
!= | Not equal | ${{ role != "guest" }} |
< | Less than | ${{ age < 18 }} |
> | Greater than | ${{ score > 80 }} |
<= | Less than or equal | ${{ count <= 10 }} |
>= | Greater than or equal | ${{ price >= 100 }} |
Logical Operators
| Operator | Description | Example |
|---|---|---|
&& | Logical AND | ${{ isActive && isVerified }} |
|| | Logical OR | ${{ isPremium || isTrial }} |
Arithmetic Operators
| Operator | Description | Example |
|---|---|---|
+ | Addition | ${{ price + tax }} |
- | Subtraction | ${{ total - discount }} |
* | Multiplication | ${{ price * quantity }} |
/ | Division | ${{ total / count }} |
% | Modulo | ${{ index % 2 }} |
Arithmetic can be combined with parentheses for complex calculations:
${{ (price - price * discount) * (1 + tax) }}
Ternary Expressions
Use ternary expressions for conditional values:
${{ score > 80 ? "pass" : "fail" }}
Ternary expressions can be nested:
${{ score > 90 ? "A" : score > 80 ? "B" : "C" }}
String Concatenation
${{ "Hello " + name }}
Object Literals
Create objects inline:
${{ {key: name, value: score} }}
Array Literals
Create arrays inline:
${{ [a, b, a + b] }}
Literals
JEXL expressions support string, number, and boolean literals:
- String:
${{ "hello world" }} - Number:
${{ 42 }},${{ 3.14 }} - Boolean:
${{ true }},${{ false }}
Custom Helpers
You can define custom JEXL functions in your workflow class using the @DefineHelper() decorator. A decorated method becomes available as a function in all JEXL expressions of that workflow, using the method name.
Defining a custom helper
import { DefineHelper, Workflow } from '@loopstack/common';
@Workflow({
configFile: __dirname + '/my.workflow.yaml',
})
export class MyWorkflow {
@DefineHelper()
sum(a: number, b: number) {
return a + b;
}
}Using the custom helper in YAML
Once defined, call the helper by its method name using function call syntax:
transitions:
- id: calculate
from: start
to: end
call:
- tool: createChatMessage
args:
role: 'assistant'
content: ${{ sum(10, 20) }}Custom helpers can be combined with other JEXL features:
arguments:
doubled: ${{ multiply(value, 2) }}
greeting: ${{ greet("World") }}
check: ${{ len("hello") > 3 }}
nested: ${{ double(increment(3)) }}Note that JEXL helpers use function call syntax (sum(10, 20)), unlike template expressions which use space-separated syntax ({{ sum 10 20 }}).
Type Preservation
JEXL expressions preserve the data type of the evaluated result:
arguments:
name: ${{ user.name }} # string
age: ${{ user.profile.age }} # number
active: ${{ isActive }} # boolean
tags: ${{ user.tags }} # array
config: ${{ settings }} # object
missing: ${{ user.nonexistent }} # undefined
empty: ${{ nullValue }} # nullThis is the key difference from template expressions ({{ }}), which always return strings.