code-docs/packages/operators.md
Framework for parsing and evaluating Lowdefy operators. Provides parsers for build-time, server-side, and client-side operator evaluation.
This package provides:
evaluateOperators - Evaluates operators at build time (in-place tree walker)ServerParser - Evaluates operators on the server (requests)WebParser - Evaluates operators in the browser (UI reactivity)import {
evaluateOperators,
ServerParser,
WebParser,
getFromArray,
getFromObject,
runClass,
runInstance,
} from '@lowdefy/operators';
Operators are functions prefixed with _ that make configuration dynamic:
# Static
title: Welcome
# Dynamic with operator
title:
_if:
test:
_eq:
- _state: user.role
- admin
then: Admin Dashboard
else: User Dashboard
File: packages/operators/src/evaluateOperators.js
Replaces the old BuildParser class. Used by the walker (inline _build.* evaluation) and evaluateStaticOperators (post-walk _ pass). Walks in-place with a recursive function instead of JSON serializer.copy round-trips.
const { output, errors } = evaluateOperators({
input: configObject,
operators: buildOperators,
operatorPrefix: '_build.', // or '_' for static operators
env: process.env,
dynamicIdentifiers, // Set of operators requiring runtime evaluation
typeNames, // Set of registered type names (for type boundaries)
args, // optional arguments (for _function callbacks)
});
Key behaviors:
~dyn marker propagation: bubbles up from children to indicate runtime-dependent contenttype field matching typeNames reset ~dyn propagation_build.* operators always evaluate regardless of ~dyn (they work on YAML structure, not runtime values)parser.parse() callback for recursive evaluation (used by _function/_build.array.map)~r (ref ID) and ~l (line number) from the expression object are transferred to the result (as non-enumerable properties). This ensures addKeys can resolve file + line for operator-produced values (e.g., arrays from _build.array.concat).Build-time operators:
_ref - Include other files (handled by walker, not evaluateOperators)_var - Build variables (handled by walker, not evaluateOperators)_build.env - Environment at build time_dump_yaml / _dump_json - Serialize to stringUsed by @lowdefy/api for server-side evaluation:
const parser = new ServerParser({
operators: serverOperators,
payload: {
secrets, // Application secrets
user, // Current authenticated user
payload, // Request payload from client
urlQuery, // URL query parameters
pageId, // Current page ID
requestId, // Current request ID
global, // Global state
input, // Page input data
lowdefyGlobal, // Lowdefy app configuration
apiResponses, // Previous request responses
},
});
const result = parser.parse({
input: requestProperties,
location: 'request:getUsers',
});
Server-only operators:
_secret - Access secrets (never sent to client)_user - Current user session_payload - Request payload from actionUsed by @lowdefy/engine for client-side evaluation:
const parser = new WebParser({
operators: webOperators,
payload: {
state, // Page state object
urlQuery, // URL query parameters
input, // Navigation input data
global, // Global state (cross-page)
requests, // Request responses cache
event, // Current event object
eventLog, // Array of previous events
user, // Authenticated user
actions, // Actions context for _actions_log
lowdefyGlobal, // Lowdefy app configuration
blockId, // Current block ID
pageId, // Current page ID
},
});
const result = parser.parse({
input: blockProperties,
location: 'block:submitButton',
});
Client operators:
_state - Page state values_url_query - URL parameters_input - Navigation input_global - Global state_request - Request responses_event - Current event data_args - Function arguments_user - Authenticated user info (client-safe fields only)Operators can be written in two forms:
value:
_sum:
- 1
- 2
- 3
value:
_state: fieldName
# Equivalent to:
value:
_state:
key: fieldName
Safely get nested values:
import { getFromObject } from '@lowdefy/operators';
const value = getFromObject(object, 'path.to.value');
Get from array of objects by key:
import { getFromArray } from '@lowdefy/operators';
const item = getFromArray(array, 'id', 'item-123');
Execute operator classes:
// For class-based operators
const result = runClass(OperatorClass, {
params,
location,
...context,
});
// For instance-based operators
const result = runInstance(operatorInstance, {
params,
location,
...context,
});
Operators from plugins follow this pattern:
// _sum operator
function _sum({ params, location }) {
if (!Array.isArray(params)) {
throw new Error(`_sum at ${location} requires array`);
}
return params.reduce((acc, val) => acc + val, 0);
}
// _if operator
function _if({ params, location }) {
const { test, then, else: elseVal } = params;
return test ? then : elseVal;
}
Input Object (with operators)
│
▼
┌─────────────────────┐
│ Recursive Traverse │
│ (find _ prefixes) │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Identify Operator │
│ (lookup in map) │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Parse Nested │
│ (operators in │
│ params first) │
└──────────┬──────────┘
│
▼
┌─────────────────────┐
│ Execute Operator │
│ (with context) │
└──────────┬──────────┘
│
▼
Result Value
Different contexts have different:
_secret only on server)The _ prefix:
Operators can contain operators:
title:
_if:
test:
_gt: # Evaluated first
- _state: count # Evaluated first
- 10
then: Many items
else: Few items
Inner operators evaluate first, then outer.
Operators in YAML provide:
Some operators must never run on the client:
_secret - Would expose secrets_user.password - Sensitive dataThe parsers enforce this by only including safe operators.
Operators cannot:
The _js operator (from @lowdefy/operators-js) is the controlled escape hatch.
evaluateOperators (via walker for _build.*, via evaluateStaticOperators for _ prefix)Operators throw simple, descriptive errors. Parsers format them with received value and location:
// In operator - throw simple error
throw new Error('_sum requires array of numbers.');
// Parser formats to:
// "_sum requires array of numbers. Received: {...} at block:total."
The parsers (evaluateOperators, WebParser, ServerParser) catch operator errors and format them with: