Walks the unresolved tree along path, resolving a Layered loop
base on the way through so the path can continue into it.
The same holds for any other unresolved node the path runs through
-- a reference, a loop's copied block element, a secret, a tagged
value: CASC.md §7.3 resolves against the final tree, in which
origin = %{origins.eu} is the origins.eu block, so
%{origin.code} must find its code. Such a node is resolved
through resolvePath under the path walked so far (walked), so it
is cached once and a reference into its own value is reported as the
cycle it is. Its resolved value is fully resolved, so the rest of the
walk goes through plain objects (fetchResolvedPath); a value
that resolves to a secret map is walked into, and what it finds
stays secret; anything else -- a scalar, a list -- leaves the path
undefined, as in the reference implementation.
the segments of the path already walked
A block written as a list or tuple element ([{ path = "^/admin" }],
CASC.md §6.10): its keys are resolved -- they may interpolate, which
is why it waited until now -- its entries merged into a map the way a
document's are, and that map resolved like any other. While keys are
being resolved there is no tree yet, and a key is a string, so one
here is refused, as the reference refuses it.
Settles each filter's argument to a string. A double-quoted argument interpolates like any double-quoted string (CASC.md §7.2), which is also how a loop binding reaches one. One built from a secret is used for real, and the filtered result is secret too.
An interpolated key segment (CASC.md §4.2): a non-empty string with no
. in it, never built from a secret. An empty one would address the
wrong depth, and a dotted one would silently be read as two segments;
both were once accepted. Loop-built keys reach here too (their
bindings are attached to the references), so the rule is the same
inside a for loop as outside one -- and inside an element block.
A %{...} key segment built by interpolation: unlike a name, any
string will do, except an empty one (wrong depth), a dotted one (it
would silently become two segments), or a secret.
The resolved value at path in the final tree, memoized; a path
reached again while still being resolved is a cycle.
path is in progress from before its fetch: reaching it may resolve
a node along the way (fetchTreePath), and one that leads back here
is part of the cycle -- so a = %{a.b} reports a.b -> a -> a.b.
Joins resolved segments. A segment that resolved to a secret makes the
whole string a Secret whose display text redacts only that segment.
nullwhile resolving keys, before a tree exists