The data layer¶
The data layer (sources and pipelines) is the heart of tui-builder: the
TUI is one consumer of it and wrangl is another. This page covers how
it is put together. For every field, see the
reference: sources,
pipelines and
templating.
One map, two kinds of entry¶
Everything under data.sources: is one map. Each entry's type: picks
either a leaf kind, which fetches from outside the program (http,
exec, file, websocket, static, merge), or an operator,
which reads other entries and transforms them (passthrough, filter,
project, derive, sort, union, compose, join, cache).
Components and wrangl address either kind by name, so swapping an
http source for an exec one, or putting a filter in front of it,
changes nothing downstream.
The split in the YAML mirrors the code: data: is read by both binaries,
tui: only by tui-builder.
The model¶
Every entry, leaf or operator, satisfies one contract:
type Source interface {
Fetch(ctx context.Context) (any, error)
Refresh() time.Duration
}
type StreamingSource interface {
Source
Subscribe(ctx context.Context) (<-chan Event, error)
}
Fetch returns the value with root: already applied. Refresh
reports the polling interval. Streaming sources also implement
Subscribe, pushing one Event per frame until cancelled.
Two more interfaces cover sets too big to hold whole:
type WindowedSource interface {
Source
FetchWindow(ctx context.Context, q WindowQuery) (WindowPage, error)
}
type AnchoredSource interface {
Source
FetchEdge(ctx context.Context, q EdgeQuery) (EdgePage, error)
}
A source with window: is windowed: the request carries which rows,
matching what filter, in what sort order, and the answer is one page.
With window.cursor: it is Anchored instead: it's walked from an
edge (older or newer than a cursor) rather than paged by offset. Both
still implement Fetch, which returns the first page, so wrangl sees
representative data. The TUI and wrangl consume sources through these
interfaces and nothing else.
Lifecycle¶
Two independent properties, both inferred from the config (there is no
lifecycle: field), and reported by wrangl --list and --describe:
| Cadence | When |
|---|---|
streamed |
websocket, or exec / http with follow: true |
polled |
refresh: is set |
one-shot |
neither |
| Binding | When |
|---|---|
| self-contained | no parameters, or every required one has a default |
needs params |
at least one required parameter without a default |
They combine: polled (refresh: 5s) · needs params,
streamed (follow) · needs params.
A windowed source is fetched a page at a time as the bound component
scrolls, filters and sorts, rather than on a cadence. One with
window.growing: also polls its newest end every window.follow_every
(default 2s) while its while: expression holds; refresh: can't be
combined with it.
wrangl¶
wrangl runs this layer on its own and writes the result to stdout,
which makes every source and pipeline inspectable and scriptable without
a terminal UI. See wrangl for its flags and output
format.
The boundary¶
The data layer never depends on the TUI:
internal/config,internal/datasource,internal/pipeline,internal/expr,internal/action,internal/outputandcmd/wranglmust not importinternal/screen,internal/buildor any tuilib package.scripts/check-data-layer-boundary.shwalksgo list -depsin CI and fails the build on a violation.
Data wrangling is the product and the TUI is one sink. wrangl stays
cheap to build and test without Bubble Tea. See AGENTS.md
for the rule.