59 std::optional<std::chrono::milliseconds>
age;
75 [[nodiscard]]
static std::optional<Inspector>
over(
Screen& screen MORPH_LIFETIMEBOUND) {
76 if (!screen._core->recording) {
79 return Inspector{screen};
84 [[nodiscard]] std::string
const&
screenId() const noexcept {
return _screen->id(); }
93 [[nodiscard]] std::vector<InspectedNode>
nodes()
const {
95 Quiet
const quiet{core};
96 std::vector<InspectedNode> out;
99 std::shared_ptr<detail::RtScope>
const scope = entry.
scope.lock();
100 std::shared_ptr<detail::Bindings const>
const bindings = entry.
bindings.lock();
101 if (!scope || !bindings) {
107 .testId = std::string{node.
name(
"testId")},
110 for (
auto const& [key, expression] : node.
props) {
113 out.push_back(std::move(inspected));
128 [[nodiscard]] std::vector<InspectedQuery>
queries()
const {
130 std::vector<InspectedQuery> out;
133 collect(*scope, scope == _screen->_scope.get() ?
"screen" :
"nested", out);
136 collect(*app,
"app", out);
144 [[nodiscard]] std::vector<CommandLogEntry>
commandLog()
const {
145 auto const& log = _screen->_core->commandLog;
146 return {log.begin(), log.end()};
160 auto const found = scope->
names.find(query);
167 .bindings = std::make_shared<detail::Bindings>(),
168 .cancelClose = std::make_shared<bool>(
false),
169 .boundary =
nullptr};
170 core.session.asUser([&] { core.runtime().batch([&] { core.runList(commands, ctx, {}); }); });
180 explicit Quiet(
detail::ScreenCore& core) : _core{&core}, _was{core.inspecting} { core.inspecting =
true; }
182 Quiet(Quiet
const&) =
delete;
183 Quiet& operator=(Quiet
const&) =
delete;
184 Quiet(Quiet&&) =
delete;
185 Quiet& operator=(Quiet&&) =
delete;
188 detail::ScreenCore* _core;
192 [[nodiscard]]
static std::string text(Value
const& value) {
193 std::string
const*
const string = value.asString();
194 return string ==
nullptr ? std::string{} : *string;
197 void collect(detail::RtScope
const& scope, std::string
const& where, std::vector<InspectedQuery>& out)
const {
198 detail::ScreenCore
const& core = *_screen->_core;
199 std::chrono::milliseconds
const now = core.session.now();
200 for (
auto const& query : scope.queries) {
201 InspectedQuery inspected{.name = query->decl().name,
204 .model = query->typeId(),
205 .action = query->decl().action,
210 if (std::shared_ptr<detail::CacheEntry>
const& entry = query->entry()) {
211 inspected.key = entry->key;
212 inspected.lastError = entry->error.peek();
213 if (entry->fetchedAt) {
214 inspected.age = now - *entry->fetchedAt;
216 bool const old = inspected.age && *inspected.age >= std::chrono::milliseconds{query->decl().staleTime};
217 if (entry->pending.peek()) {
218 inspected.state =
"pending";
219 }
else if (!inspected.lastError.empty()) {
220 inspected.state =
"error";
221 }
else if (!entry->hasValue.peek()) {
222 inspected.state =
"idle";
224 inspected.state = entry->stale || old ?
"stale" :
"fresh";
227 out.push_back(std::move(inspected));
229 for (
auto const& mutation : scope.mutations) {
230 InspectedQuery inspected{.name = mutation->decl().name,
233 .model = mutation->typeId(),
234 .action = mutation->decl().action,
238 .lastError = text(mutation->error())};
239 if (mutation->pending()) {
240 inspected.state =
"pending";
241 }
else if (!inspected.lastError.empty()) {
242 inspected.state =
"error";
243 }
else if (!mutation->result().isNull()) {
244 inspected.state =
"fresh";
246 out.push_back(std::move(inspected));
250 explicit Inspector(Screen& screen) : _screen{&
screen} {}
Screen screen(Screen::Builder builder)
A screen with its id as a template argument, as spec 5 §11 writes it.
decltype(auto) untracked(F &&fn) const
Runs fn with tracking off: reads inside it subscribe nothing.
std::vector< InspectedNode > nodes() const
Every node of the screen whose scope is alive, in the order it was lowered (a parent before its child...
static std::optional< Inspector > over(Screen &screen)
An inspector over screen, when its session records what an inspector reads.
bool refetch(std::string_view query)
Refetches a query by running the document command {"refetch": "<query>"} in the scope that declares i...
std::string const & screenId() const noexcept
The screen's document id.
std::vector< CommandLogEntry > commandLog() const
The latest command lists the screen ran, oldest first, at most detail::kCommandLogSize.
std::vector< InspectedQuery > queries() const
Every query and mutation of the screen's live scopes, the screen's own first, then those of the app d...
reactive::RuntimeStats counters() const
The reactive runtime's counters: flushes, Effects per flush, batches and misuses.
A mounted screen: its scopes, its view mounted through Mounted, and what its host asks of it.
One document scope at run time: the screen, a dialog's or drawer's content, a switch case or a row.
std::map< std::string, NameRef, std::less<> > names
Its names.
One mounted screen's interpreter: its document, diagnostics, slots and preferences,...
reactive::Runtime & runtime() const noexcept
The runtime.
bool inspecting
Set while the inspector evaluates, so that an expression error it meets is not reported.
The UI document (spec 5 §3): its model — declarations, commands, view nodes, components and customiza...
std::vector< Command > Commands
A command list, run as one batch.
@ Refetch
{"refetch": "q"}
The UI document interpreter: Interpreter::mount turns a loaded Document into reactive state and a vie...
Value evalIn(RtScope const &scope, Bindings const *bindings, ExprPtr const &expr)
Evaluates an expression in a scope.
morph::reactive::Runtime: the owner, batching and flush scheduling of a signal graph.
What a Runtime has counted since it was made: its flushes, the Effects they ran, its batches and the ...
One command. Which members a kind uses is listed with each member.
A view node of the document (spec 5 §9), or a structured part of one (a table column,...
std::string path
The node's JSON Pointer.
std::string_view name(std::string_view key) const
A plain string property.
std::string kind
The node kind (text, table, …), or for a part its part kind (column, tab, case, …).
std::string id
The element id; empty when none is given.
std::map< std::string, ExprPtr, std::less<> > props
Expression properties, by key.
A mounted node as the inspector lists it.
std::string kind
The node kind: text, button, column, …
std::string id
The element id; empty when the node has none.
std::string testId
The testId; empty when the node has none.
ValueRecord properties
Each expression property, evaluated in the scope the node was lowered in, by its document key.
std::string path
The node's JSON Pointer in the document.
A query or mutation as the inspector lists it.
std::string scope
Where it is declared: screen, nested (a dialog's, drawer's, switch case's or row's scope) or app.
std::string lastError
The last failure's text; empty when none.
bool mutation
Whether it is a mutation.
std::optional< std::chrono::milliseconds > age
How long ago the shown value arrived, on the session's clock; empty before any reply and for a mutati...
std::string state
idle, pending, fresh, stale or error.
std::string model
The model type.
std::string name
The declared name.
std::string action
The action it runs.
std::string key
The cache key: model type, instance, action and canonical body; empty for an idle query and a mutatio...
Where a command list runs: its scope, the names bound there, and what it may answer.
A node as it was lowered, which the inspector lists while its scope lives.
std::weak_ptr< Bindings const > bindings
The names bound where it was lowered.
DocNode const * node
The node.
std::weak_ptr< RtScope > scope
The scope it was lowered in.
The UI document's typed values (ui::Value), its types (ui::Type), their JSON form,...
std::map< std::string, Value, std::less<> > ValueRecord
A record value: field name to value, ordered by name.