Flow Studio
Resolution Pipeline
Expression resolution is a pipeline step that runs inside the node execution pipeline, between configuration loading and executor invocation. It transforms a raw configuration dictionary (with expression strings) into a resolved dictionary (with concrete values).
Where Resolution Sits in the Pipeline
| Stage | Action |
|---|---|
| Entry (100) | Load node definition from the process template |
| EntryValidate (200) | Parse raw config JSON → build EvaluationContext → resolve expressions → populate settings |
| PreGuardRails (250) | Run rate limit and quota guard rails |
| PreProcess (300) | HIL suspension check |
| Process (400) | Your ExecuteInternalAsync (already has resolved values) |
| PostProcess (500) | Post-execution hooks |
| Exit (600) | Persist result, emit SignalR event |
Resolution Steps in EntryValidate
- Raw config JSON is read from
ProcessElement.Configuration - Config is parsed into a
Dictionary<string, object>byINodeConfigurationParser ExpressionContextFactory.Create(elementContext)builds theEvaluationContextIConfigExpressionResolver.ResolveAsync(config, context)iterates every field:- 4a. If field value is a literal string without
@{— kept as-is - 4b. If field value contains
@{directive:...}— handled by the directive resolver (no Jint) - 4c. If field value is
@{js:...}— passed toIExpressionEvaluator.EvaluateAsync() - Resolved config stored in
elementContext.ResolvedConfig BaseNodeExecutor.LoadConfigAsync()reads fromResolvedConfig.ToMergedJson()and populatessettings
IConfigExpressionResolver Contract
// IConfigExpressionResolver.cs
public interface IConfigExpressionResolver
{
Task<Dictionary<string, object>> ResolveAsync(
Dictionary<string, object> config,
EvaluationContext context,
CancellationToken cancellationToken = default);
}
Resolution Failure Handling
If an expression fails to resolve (Jint timeout, syntax error, referenced node not found), the resolver has two strategies depending on the field configuration:
| Strategy | Behaviour |
|---|---|
| Fail-fast (default for required fields) | Resolution exception propagates → node is marked Failed before executor runs |
| Null-coerce (optional fields) | Failed expression resolves to null; executor receives null value and must handle it |
Partial Resolution (Mixed Fields)
Not all fields in a config dictionary must contain expressions. The resolver skips literal fields. A single node can have a mix: some fields are literals, others are expressions, others are nested objects with expressions inside them. The resolver handles nested JSON recursively.
// Input config (before resolution):
{
"to": "@{output:form-1.email}", // expression
"subject": "Your order is ready", // literal (no expression)
"body": "Hello @{output:form-1.firstName}, your order @{var:orderId} is ready."
}
// Output config (after resolution):
{
"to": "alice@example.com", // resolved
"subject": "Your order is ready", // unchanged
"body": "Hello Alice, your order ORD-4821 is ready." // resolved
}