The templjs query engine resolves values from structured input data using:
flowchart LR
A[Template Expression] --> B{Path Type?}
B -->|dot notation| C[Property Lookup]
B -->|bracket index| D[Array / Key Access]
B -->|variable index| E[Resolve Index Var First]
C --> F[Resolved Value]
D --> F
E --> D
F --> G{Filters?}
G -->|yes| H[Apply Filter Chain]
G -->|no| I[Output]
H --> H2{More Filters?}
H2 -->|yes| H
H2 -->|no| I
flowchart LR
V[Value] --> F1["filter1()"]
F1 --> F2["filter2(arg)"]
F2 --> F3["filter3(a, b)"]
F3 --> Out[Final Output]
style V fill:#ddf,stroke:#99b
style Out fill:#dfd,stroke:#9b9
Syntax in templates:
{{ value | upper | truncate(50) | escape }}
Source implementation: Source implementation: src/packages/core/src/query-engine/query-engine.ts
Primary behavior tests: Primary behavior tests: src/packages/core/test/query-engine/query-engine.test.ts
Use . to walk nested object properties.
{{ user.profile.name }}
With input:
{
"user": {
"profile": {
"name": "Alice"
}
}
}
Result:
Alice
Use bracket notation for numeric indexes.
{{ items[1] }}
{
"items": ["zero", "one", "two"]
}
Result:
one
Bracket indexes can reference another root variable.
{{ items[idx] }}
{
"items": ["zero", "one", "two"],
"idx": 2
}
Result:
two
Chained bracket access also works:
{{ matrix[row][col] }}
Use quoted brackets for object keys that are not valid identifiers.
{{ user["display-name"] }}
By default, unresolved paths return undefined.
Programmatic callers can provide defaultValue or enable strict mode through the query engine API.
Implementation reference: Implementation reference: src/packages/core/src/query-engine/types.ts
Apply filters with pipe syntax.
{{ user.name | upper }}
{{ total | round(2) }}
{{ tags | join(", ") }}
Built-in function catalogs:
templjs supports delimiter trim markers to remove formatting-only whitespace around expressions and statements.
Syntax contract:
{{- expr }}: trim whitespace immediately to the left of the expression{{ expr -}}: trim whitespace immediately to the right of the expression{{- expr -}}: trim both sides{%- statement %} / {% statement -%} / {%- statement -%}: same trim behavior for statementsTrim markers are optional. Templates without trim markers keep existing whitespace behavior.
JSON:
{
"items": [
{%- for item in items -%}
"{{- item -}}"{%- if !loop.last -%},{%- endif -%}
{%- endfor -%}
]
}
Markdown:
# Release Notes
{%- for entry in entries -%}
- {{- entry.title -}}
{%- endfor -%}
HTML:
<ul>
{%- for item in items -%}
<li>{{- item.name -}}</li>
{%- endfor -%}
</ul>
{%- ... -%} to the outer loop/condition boundaries before trimming every nested expression.{{- value -}} when values are punctuation-sensitive (CSV/JSON lists) to avoid accidental spaces around separators.Some filter names are overloaded by runtime category. For example, reverse supports both strings and arrays. The query engine selects the appropriate overload by inspecting the input value and argument shape.
Metadata and overload tests:
Array helpers such as filter and find support simple expression strings.
Examples:
{{ scores | filter("> 90") }}
{{ users | find("active == true") }}
{{ items | where("published") }}
Supported comparison forms include:
> 1, <= 10, == "ok"age >= 18, status == "active"published100 in query execution options.reduce is registered but currently returns the input array unchanged.