This commit is contained in:
root 2026-06-15 21:42:50 +00:00
parent 34a97e2577
commit 99cd92fb4a
126 changed files with 1615 additions and 1057 deletions

View File

@ -193,6 +193,7 @@ MAX_MEMORY=16777216
SESSION_TIME=2592000
HTTP_PORT=8080
WS_BROKER_OUTBOUND_TIMEOUT_SECONDS=30
```
Important settings:
@ -204,6 +205,7 @@ Important settings:
- `BIN_DIRECTORY` stores generated C++, wasm artifacts, compile output, and runtime caches.
- `TMP_UPLOAD_PATH` and `SESSION_PATH` must be writable by the runtime.
- `HTTP_PORT` is the built-in HTTP/WebSocket listener used for WebSocket upgrade traffic and direct local probes. Bind/firewall it for local access only; nginx/Apache should be the public entry point.
- `WS_BROKER_OUTBOUND_TIMEOUT_SECONDS` controls how long a forwarded WS message can remain queued in the broker before being dropped (default `30`). Set to `0` to disable the timeout.
- `WASM_COMPILE_SCRIPT` must point to `scripts/compile_wasm_unit` unless you provide an equivalent compiler. Relative paths are resolved from the runtime root/`COMPILER_SYS_PATH`. That script calls `scripts/check_unit_wasm.py` after linking each unit and uses the pinned WASI SDK on every deployment host.
- `WASM_CORE_PATH` must point at the built `core.wasm` file.

View File

@ -85,6 +85,14 @@ the boundary).
See [`docs/wasm-phase1-dvalue-abi.md`](wasm-phase1-dvalue-abi.md) for the wire
format details.
Decoder robustness note: UCEB input is untrusted across the wasm membrane. The
UCEB decoder must reject malformed magic/version/varint/length/trailing-data
inputs and excessive nesting with explicit errors, not native or wasm traps. Its
current hard nesting cap is intentionally low (64 levels) to stay well below the
guest stack limit. JSON decoding follows the same rule for malformed strings and
unicode escapes: validate bounds before every read and return an empty/partial
`DValue` rather than reading beyond the input.
---
## 3. Units, handlers, and export naming
@ -144,6 +152,19 @@ suspend the native SIGSEGV/SIGILL recovery handler around the wasm call so that
Wasmtime's own trap signals are not escalated into a native fatal signal (see
`serve_via_wasm` in `handle_complete`).
### Task callbacks and workspace lifetime
`task()` and `task_repeat()` are fork-backed. The `uce_host_task_spawn` hostcall
captures the current `WasmWorkspace*`, but `src/lib/sys.cpp::task()` invokes the
captured callback only in the forked child, before the hostcall stack unwinds in
that child. The parent request may return and destroy its workspace; the child
still has its own copy-on-write stack and its own copy of the per-request wasm
workspace. This means a delayed task callback can run after the spawning request
returns without dereferencing the parent's destroyed workspace. It is still a
callback into the inherited child workspace, not a fresh normal request
workspace; avoid adding host resources to `WasmWorkspace` that are invalid across
`fork()` unless task callback handling is changed to birth a fresh workspace.
---
## 5. Request dispatch (`handle_complete`)
@ -198,12 +219,14 @@ broker loop:
a non-empty STDIN makes the FastCGI transport flush a premature response
before `on_complete` ever runs.
3. Connect to `/run/uce.sock` (non-blocking) and queue the encoded request in
`ws_broker_outbound[fd]`.
`ws_broker_outbound[fd]` with an enqueue timestamp.
`ws_broker_drain_outbound()` runs after every `process(50)` tick: it finishes
writing each queued request, then drains and discards the reply (the unit's
output comes back via the command socket, not this reply), closing the fd when
the worker closes its end.
the worker closes its end. If a forward remains pending beyond
`WS_BROKER_OUTBOUND_TIMEOUT_SECONDS` (default `30`), the broker drops and
closes it so a wedged worker cannot pin broker fds/memory indefinitely.
### 6.2 Outbound: `ws_*` commands flushed back to the broker
@ -211,8 +234,10 @@ Any unit code — not just WebSocket handlers — may call `ws_send` / `ws_send_
/ `ws_close`. In the workspace these **record dispatch commands** rather than
touching a socket (the workspace owns no connections); `wasm-core`'s `ws_*`
(`src/lib/sys.cpp`) append to `websocket_dispatch_commands`, and
`finish_response_meta` (`src/wasm/core.cpp`) emits them as `ws_commands` (plus
`ws_connection_state` if the handler mutated per-connection state).
`finish_response_meta` (`src/wasm/core.cpp`) emits them as `ws_commands`.
If the handler changed per-connection state, the core also emits
`ws_connection_state` even when no commands were emitted, and the native
backend flushes this state-only batch to the broker.
`wasm_backend_serve` (`src/wasm/backend.cpp`) flushes that batch at workspace
teardown — in **any** scenario, not just WS handlers — to the broker's command
@ -239,9 +264,10 @@ serve_http dispatcher uses, so there is no duplicated request-forwarding code.
handlers (it renders nothing, so it accepts every request straight through to
`on_complete`), wires `on_complete=ws_broker_complete` and
`on_websocket_message=ws_broker_ws_message`, listens on `HTTP_PORT` and the
command socket, and loops `process(50)` + `drain_outbound()`. The design is
**non-blocking outbound dispatch + async command-socket flush, all in the
broker's single epoll loop** — the broker never blocks on a worker.
command socket, and loops `process(50)` + `drain_outbound(timeout)`. The
`timeout` comes from `WS_BROKER_OUTBOUND_TIMEOUT_SECONDS` (default `30`). The
design is **non-blocking outbound dispatch + async command-socket flush, all in
the broker's single epoll loop** — the broker never blocks on a worker.
The parent respawns the broker if it dies (`ws_broker_alive` / `ensure_ws_broker`
in `main()`).
@ -288,6 +314,7 @@ header free-functions are `inline`. The wasm backend exposes only declarations
| `CLI_SOCKET_PATH` | `/run/uce/cli.sock` | Worker CLI socket. |
| `HTTP_PORT` | `8080` | Raw HTTP + WebSocket port — owned by the WS broker. |
| `WS_BROKER_SOCKET_PATH` | `/run/uce/ws-broker.sock` | Broker command socket for `ws_*` flushes. |
| `WS_BROKER_OUTBOUND_TIMEOUT_SECONDS` | `30` | Max lifetime in seconds for queued WS broker forwards before drop. |
| `WORKER_COUNT` | `4` | Number of uniform worker processes. |
---

View File

@ -15,6 +15,10 @@ CLI_SOCKET_PATH=/run/uce/cli.sock
# Keep this behind nginx/Apache on localhost or firewall it from public access.
HTTP_PORT=8080
# After this many seconds, an in-flight WS broker render-forward that is still
# not completed (including stalled writes/reads) is dropped.
WS_BROKER_OUTBOUND_TIMEOUT_SECONDS=30
# OPTIONAL PROACTIVE COMPILE ROOT
# Leave empty to scan SITE_DIRECTORY relative to the runtime root.
PRECOMPILE_FILES_IN=

View File

@ -24,9 +24,8 @@ PUBLIC_APIS = [
("file_put_contents", True, "public"), ("file_append_contents", False, "public"),
("cwd_get", True, "public"), ("cwd_set", True, "public"), ("process_start_directory", True, "public"),
("file_mtime", True, "public"), ("file_unlink", True, "public"), ("expand_path", True, "public"),
("ls", True, "public"), ("config_map_u64", True, "public"), ("config_map_f64", True, "public"),
("config_bool_value", True, "public"), ("config_map_bool", True, "public"),
("config_u64", True, "public"), ("config_f64", True, "public"), ("config_bool", True, "public"),
("ls", True, "public"), ("to_u64", True, "public"), ("to_s64", True, "public"),
("to_f64", True, "public"), ("to_bool", True, "public"),
("request_perf", True, "public"), ("time_format_local", True, "public"),
("time_format_relative", True, "public"), ("time_parse", True, "public"),
("backtrace_frames_string", False, "public"), ("capture_backtrace_string", False, "public"),

View File

@ -8,7 +8,7 @@ RENDER(Request& context)
StringList routes = {"index", "dashboard", "themes", "dashboard", "workspace/projects"};
StringList unique_routes = list_unique(routes);
StringList sorted_routes = list_sort(unique_routes);
StringList labels = map(sorted_routes, [](String route) { return(to_upper(replace(route, "/", " / "))); });
StringList labels = sorted_routes.map([](String route) { return(to_upper(replace(route, "/", " / "))); });
DValue cards;
DValue card;
@ -16,9 +16,8 @@ RENDER(Request& context)
card["title"] = "Themes"; card["section"] = "app"; card["href"] = "?themes"; cards.push(card); card.clear();
card["title"] = "Docs"; card["section"] = "reference"; card["href"] = "../doc/index.uce"; cards.push(card);
DValue app_cards = dv_filter(cards, [](DValue item, String key) { return(item["section"].to_string() == "app"); });
DValue titles = dv_map(app_cards, [](DValue item, String key) { DValue out; out = item["title"].to_string(); return(out); });
DValue grouped = dv_group_by(cards, [](DValue item, String key) { return(item["section"].to_string()); });
DValue app_cards = cards.filter([](DValue item, String key) { return(item["section"].to_string() == "app"); });
DValue titles = app_cards.map([](DValue item, String key) { DValue out; out = item["title"].to_string(); return(out); });
<><html>
<head>
@ -31,7 +30,7 @@ RENDER(Request& context)
<p>Small data-shaping helpers are useful for route lists, nav records, cards, and other render-adjacent structures.</p>
<div class="system-info"><h3>StringList route labels</h3><pre><?= join(labels, "\n") ?></pre></div>
<div class="system-info"><h3>DValue app card titles</h3><pre><?= json_encode(titles) ?></pre></div>
<div class="system-info"><h3>Grouped cards</h3><pre><?= json_encode(grouped) ?></pre></div>
<div class="system-info"><h3>All card values</h3><pre><?= json_encode(cards.values()) ?></pre></div>
<details>
<summary>Request Parameters</summary>
<pre><?= var_dump(p) ?></pre>

View File

@ -3,26 +3,27 @@ Types
0_Request
array_merge
0_DValue
dv_filter
dv_group_by
dv_keys
dv_map
dv_omit
dv_pick
dv_values
each
get_by_path
has
is_array
is_list
set_status
String
StringList
StringMap
2_DValue_filter
2_DValue_keys
2_DValue_map
2_DValue_values
2_DValue_each
2_DValue_get_by_path
2_DValue_has
2_DValue_is_array
2_DValue_is_list
2_Request_set_status
0_String
0_StringList
0_StringMap
2_DValue_to_bool
2_DValue_to_f64
2_DValue_to_json
2_DValue_to_s64
2_DValue_to_string
2_DValue_to_stringmap
2_DValue_to_u64
to_bool
to_f64
to_json
to_s64
to_string
to_stringmap
to_u64

View File

@ -13,12 +13,25 @@ enum class DocPageKind
function,
struct_page,
directive,
method,
info
};
String doc_method_label(String page)
{
String label = page;
nibble(label, "_");
String class_name = nibble(label, "_");
if(label == "")
return(class_name);
return(class_name + "::" + label);
}
String doc_default_title(String page)
{
String page_title = page;
if(page.substr(0, 2) == "2_")
return(doc_method_label(page));
if(page_title.length() > 1 && page_title[1] == '_')
nibble(page_title, "_");
return(page_title);
@ -60,6 +73,8 @@ DocPageKind doc_page_kind(String page)
return(DocPageKind::struct_page);
if(page.substr(0, 2) == "1_")
return(DocPageKind::directive);
if(page.substr(0, 2) == "2_")
return(DocPageKind::method);
if(page.substr(0, 2) == "3_")
return(DocPageKind::info);
return(DocPageKind::function);
@ -71,6 +86,8 @@ String doc_page_kind_badge(DocPageKind kind)
return("struct");
if(kind == DocPageKind::directive)
return("directive");
if(kind == DocPageKind::method)
return("method");
if(kind == DocPageKind::info)
return("info");
return("");
@ -80,6 +97,8 @@ String doc_index_label(String page)
{
String label = page;
auto kind = doc_page_kind(page);
if(kind == DocPageKind::method)
return(doc_method_label(page));
if(kind == DocPageKind::struct_page || kind == DocPageKind::directive || kind == DocPageKind::info)
nibble(label, "_");
return(label);

View File

@ -6,7 +6,7 @@ DValue
:see
>types
get_by_path
2_DValue_get_by_path
json_decode
:content
@ -47,7 +47,7 @@ You will encounter `DValue` throughout the runtime, especially in:
- `.to_bool(default)` performs best-effort boolean conversion.
- `.to_stringmap()` converts a map-shaped tree into `StringMap`.
All read accessors are `const` and never modify the tree; they work directly on `const DValue&` values such as `each()` callback parameters. Every `to_*` conversion takes an optional default that is returned when the value is missing or cannot be converted — see the individual pages (`to_string`, `to_s64`, `to_u64`, `to_f64`, `to_bool`) for the exact rules:
All read accessors are `const` and never modify the tree; they work directly on `const DValue&` values such as `each()` callback parameters. Every `to_*` conversion takes an optional default that is returned when the value is missing or cannot be converted — see the individual pages (`2_DValue_to_string`, `2_DValue_to_s64`, `2_DValue_to_u64`, `2_DValue_to_f64`, `2_DValue_to_bool`) for the exact rules:
```cpp
String title = context.props["title"].to_string("Untitled");
@ -100,7 +100,7 @@ Useful inspection helpers include:
## each()
`each(std::function<void (const DValue& t, String key)> f)` iterates over the current tree value (see the `each` page for details).
`each(std::function<void (const DValue& t, String key)> f)` iterates over the current tree value (see `2_DValue_each` for details).
For map-shaped `DValue` values, the callback runs once per child entry and receives:

View File

@ -11,7 +11,7 @@ request_script_url
request_base_url
request_query_path
request_query_route
set_status
2_Request_set_status
component
component_render
unit_render
@ -25,7 +25,7 @@ ws_connection_id
ws_connections
ws_send
0_DValue
StringMap
0_StringMap
UploadedFile
:content

View File

@ -0,0 +1,28 @@
:title
SQLite
:sig
SQLite
:see
>types
0_DValue
0_StringMap
sqlite_connect
sqlite_query
2_SQLite_connect
2_SQLite_query
2_SQLite_error
2_SQLite_disconnect
:content
SQLite connection wrapper for unit code. It opens database files, applies the runtime default pragmas, supports named parameters, and returns query rows as list-shaped `DValue` values.
```cpp
SQLite db;
if(db.connect("tmp/app.db")) {
DValue rows = db.query("select * from notes");
}
```
Use exactly one SQL statement per `query()` call and named `:name` parameters.

View File

@ -1,5 +1,5 @@
:sig
String
0_String
:see
>types

View File

@ -0,0 +1,24 @@
:title
0_StringList
:sig
0_StringList
:see
>types
0_String
split
join
2_StringList_filter
2_StringList_map
2_StringList_keys
2_StringList_each
:content
Sequential container of `String` values. It inherits normal `std::vector<String>` behavior and adds collection helpers for filtering, mapping, indexes, and iteration.
```cpp
StringList labels = split("a,b,c", ",").map([](String s) { return(to_upper(s)); });
```
See the method pages for details.

View File

@ -1,5 +1,5 @@
:sig
StringMap
0_StringMap
:see
>types

View File

@ -0,0 +1,28 @@
:title
DValue::operator=
:sig
void DValue::operator=(String v)
void DValue::operator=(f64 v)
void DValue::operator=(void* v)
void DValue::operator=(DValue v)
void DValue::operator=(StringMap v)
:params
v : assigned value
return value : none
:see
0_DValue
2_DValue_set
2_DValue_set_bool
:content
Assignment overloads forward to the matching `set(...)` overloads. They are the normal concise way to write string, number, pointer, `DValue`, and `StringMap` values. Boolean values should use `set_bool()` to avoid overload ambiguity.
```cpp
DValue value;
value = "ok";
value = 42.0;
context.call["user"] = StringMap{{"name", "Ada"}};
```

View File

@ -0,0 +1,16 @@
:sig
void DValue::clear()
:params
return value : none
:see
0_DValue
2_DValue_set_array
:content
Clears the value into an empty map-shaped value and resets its array index. Unlike `set_array()`, this does not set list mode.
```cpp
context.call["flash"].clear();
```

View File

@ -0,0 +1,18 @@
:sig
DValue& DValue::deref()
const DValue& DValue::deref() const
:params
return value : referenced target when resolvable, otherwise this value
:see
0_DValue
2_DValue_is_reference
:content
Returns the value after following internal reference links. Most public accessors call this for you, so direct use is rare in unit code.
```cpp
const DValue& actual = value.deref();
print(actual.get_type_name());
```

View File

@ -0,0 +1,23 @@
:sig
void DValue::each(std::function<void (const DValue& item, String key)> f) const
:params
f : callback invoked for each child, or once for a scalar value
return value : none
:see
0_DValue
2_DValue_map
2_DValue_filter
2_DValue_is_list
:content
Iterates a `DValue` without modifying it. References are dereferenced before iteration.
For map-shaped values, the callback receives each child and its key. List-shaped maps iterate in numeric index order (`0`, `1`, `2`, ...); ordinary maps iterate in `std::map` string-key order. Scalar values invoke the callback once with the current value and an empty key.
```cpp
rows.each([&](const DValue& row, String key) {
print(key, ": ", row.get_by_path("title").to_string("Untitled"), "\n");
});
```

View File

@ -0,0 +1,25 @@
:sig
DValue DValue::filter(StringList keys) const
DValue DValue::filter(std::function<bool (const DValue& item, String key)> f) const
:params
keys : child keys to copy when using the key-list overload
f : predicate returning true for children to keep
return value : filtered copy
:see
0_DValue
2_DValue_map
2_DValue_keys
:content
`filter(keys)` copies existing children with the named keys into a new map-shaped `DValue`.
`filter(f)` calls the predicate for each item from `each()`. Kept children from list-shaped input are pushed into a new list and re-indexed from zero; kept children from map-shaped input keep their original keys. Scalar input is passed to the predicate once and, if accepted, is pushed into a list.
```cpp
DValue public_user = user.filter({"name", "avatar"});
DValue visible = items.filter([](const DValue& item, String) {
return(!item["hidden"].to_bool());
});
```

View File

@ -0,0 +1,19 @@
:sig
DValue DValue::get_by_path(String path, String delim = "/") const
:params
path : delimited child path to traverse
delim : path separator string
return value : resolved child copy, or an empty DValue when traversal fails
:see
0_DValue
2_DValue_has
2_DValue_to_string
:content
Traverses nested map/list children without creating missing nodes. Empty path segments are ignored. If any segment is missing or an intermediate value is not map-shaped, the method returns an empty `DValue`.
```cpp
String label = context.cfg.get_by_path("theme/options/label").to_string("Default");
```

View File

@ -0,0 +1,18 @@
:sig
DValue* DValue::get_or_create(String s)
:params
s : child key to create or return
return value : pointer to the child node
:see
0_DValue
2_DValue_key
2_DValue_has
:content
Ensures the value is map-shaped and returns the named child. If the key does not exist, an empty child is created. Creating a non-numeric key on a list-shaped value clears list mode.
```cpp
payload.get_or_create("headers")->get_or_create("Content-Type")->set("text/plain");
```

View File

@ -0,0 +1,15 @@
:sig
String DValue::get_type_name() const
:params
return value : one of String, f64, bool, array, pointer, reference, or unknown
:see
0_DValue
:content
Returns a human-readable name for the dereferenced value's type tag.
```cpp
print(value.get_type_name());
```

View File

@ -0,0 +1,18 @@
:sig
bool DValue::has(String s) const
:params
s : child key to test
return value : true when the dereferenced value is map-shaped and contains the key
:see
0_DValue
2_DValue_key
2_DValue_get_by_path
:content
Checks for a child key without creating it. Returns false for scalars and missing keys.
```cpp
if(context.props.has("title")) print(context.props["title"].to_string());
```

View File

@ -0,0 +1,16 @@
:sig
bool DValue::is_array() const
:params
return value : true when the dereferenced value is map-shaped
:see
0_DValue
2_DValue_is_list
:content
Returns true when the value's current type is `M`, the map/list container type. It does not require contiguous numeric keys; use `is_list()` for that.
```cpp
if(payload.get_by_path("items").is_array()) { /* has children */ }
```

View File

@ -0,0 +1,19 @@
:sig
bool DValue::is_list() const
:params
return value : true when the value is an array with keys 0..n-1
:see
0_DValue
2_DValue_set_array
2_DValue_push
:content
Returns true for map-shaped values that represent a list. An empty map is a list only if it was explicitly put in list mode with `set_array()` or by list operations. Non-empty lists must have canonical numeric string keys from `0` through `size - 1`.
```cpp
DValue first; first = "first";
DValue items; items.set_array(); items.push(first);
if(items.is_list()) print(json_encode(items));
```

View File

@ -0,0 +1,17 @@
:sig
bool DValue::is_reference() const
:params
return value : true when this node itself is a reference node
:see
0_DValue
2_DValue_reference_target
2_DValue_deref
:content
Reports whether the current node's direct type tag is the internal reference tag. Most read methods dereference automatically; this helper is mainly useful when handling reference-aware runtime state.
```cpp
if(value.is_reference()) print("reference");
```

View File

@ -0,0 +1,19 @@
:sig
DValue* DValue::key(String s)
const DValue* DValue::key(String s) const
:params
s : child key to find
return value : pointer to existing child, or 0
:see
0_DValue
2_DValue_has
2_DValue_get_or_create
:content
Looks up one child without creating it. The non-const overload forwards through references; both overloads return `0` for scalars or missing keys.
```cpp
if(const DValue* user = payload.key("user")) print(user->to_json());
```

View File

@ -0,0 +1,17 @@
:sig
StringList DValue::keys() const
:params
return value : child keys for map/list values; empty for scalars
:see
0_DValue
2_DValue_each
0_StringList
:content
Returns the keys produced by `each()`. Scalar values have no child key, so the result is empty. List-shaped values return their numeric keys as strings.
```cpp
StringList names = context.props["user"].keys();
```

View File

@ -0,0 +1,20 @@
:sig
DValue DValue::map(std::function<DValue (const DValue& item, String key)> f) const
:params
f : mapper called for each iterated item
return value : mapped copy
:see
0_DValue
2_DValue_each
2_DValue_filter
:content
Builds a new `DValue` by replacing each iterated child with the mapper's returned value. List-shaped input stays list-shaped and is re-indexed by `push()`. Map-shaped input preserves keys. Scalar input is mapped once and pushed into a list.
```cpp
DValue labels = rows.map([](const DValue& row, String) {
DValue out; out = row.get_by_path("label").to_string(); return(out);
});
```

View File

@ -0,0 +1,22 @@
:title
DValue::operator[]
:sig
DValue& DValue::operator[](String s)
:params
s : child key to return or create
return value : reference to the child node
:see
0_DValue
2_DValue_get_or_create
2_DValue_key
2_DValue_has
:content
Returns a mutable reference to a child, creating the child when it does not already exist. If this value is an internal reference, the operation is forwarded to the target. This has the same creation behavior as `get_or_create()`, so use `has()`, `key()`, or `get_by_path()` for non-mutating reads.
```cpp
context.call["user"]["name"] = "Ada";
```

View File

@ -0,0 +1,16 @@
:sig
DValue DValue::pop()
:params
return value : removed child, or an empty DValue when no child exists
:see
0_DValue
2_DValue_push
:content
Ensures the value is map-shaped, removes the last entry according to `std::map` reverse key order, and returns it. For list-mode values, the next array index is reset to the new size.
```cpp
DValue last = items.pop();
```

View File

@ -0,0 +1,19 @@
:sig
void DValue::push(const DValue& child)
:params
child : value to append/copy
return value : none
:see
0_DValue
2_DValue_pop
2_DValue_set_array
:content
Appends a child under the next numeric key. Empty values become list-shaped. Existing contiguous lists continue at their size; non-list maps use the next unused numeric key and remain non-list.
```cpp
DValue first; first = "first";
DValue list; list.set_array(); list.push(first);
```

View File

@ -0,0 +1,18 @@
:sig
DValue* DValue::reference_target()
const DValue* DValue::reference_target() const
:params
return value : resolved target pointer, or 0 when this node is not a usable reference
:see
0_DValue
2_DValue_is_reference
2_DValue_deref
:content
If this node is an internal reference, follows reference links up to the runtime safety limit and returns the final non-reference target. Returns `0` for non-references, null/self references, or chains that still resolve to a reference.
```cpp
if(DValue* target = value.reference_target()) target->set("updated");
```

View File

@ -0,0 +1,18 @@
:sig
void DValue::remove(String s)
:params
s : child key to erase
return value : none
:see
0_DValue
2_DValue_clear
2_DValue_has
:content
Ensures the value is map-shaped and erases the named child. Removing the last child resets the next array index to zero.
```cpp
context.call["user"].remove("password");
```

View File

@ -0,0 +1,25 @@
:sig
void DValue::set(String s)
void DValue::set(void* p)
void DValue::set(f64 f)
void DValue::set(DValue source)
void DValue::set(StringMap source)
:params
s : string value for the String overload
p : pointer value for the pointer overload
f : floating point value for the f64 overload
source : DValue or StringMap to copy
return value : none
:see
0_DValue
2_DValue_set_bool
2_DValue_set_array
:content
Assigns a new value, forwarding through references when possible. String, pointer, and f64 overloads set scalar nodes. `set(DValue)` copies the source's current type and payload. `set(StringMap)` creates a map-shaped value with each string entry as a child.
```cpp
DValue user; user["name"].set("Ada"); user["score"].set(9.5);
```

View File

@ -0,0 +1,17 @@
:sig
void DValue::set_array()
:params
return value : none
:see
0_DValue
2_DValue_push
2_DValue_is_list
:content
Clears the value and makes it an empty list-shaped map. Subsequent `push()` calls append numeric keys starting at `0`.
```cpp
DValue rows; rows.set_array(); rows.push(row);
```

View File

@ -0,0 +1,17 @@
:sig
void DValue::set_bool(bool b)
:params
b : boolean value to store
return value : none
:see
0_DValue
2_DValue_to_bool
:content
Stores a boolean value, forwarding through references when possible.
```cpp
response["ok"].set_bool(true);
```

View File

@ -0,0 +1,17 @@
:sig
void DValue::set_reference(DValue* target)
:params
target : target node pointer
return value : none
:see
0_DValue
2_DValue_reference_target
:content
Stores an internal reference to another `DValue`. Most unit code should not need to create references directly; normal reads and writes generally follow existing references automatically.
```cpp
alias.set_reference(&context.call["state"]);
```

View File

@ -0,0 +1,20 @@
:sig
void DValue::set_type(char t)
:params
t : internal type tag such as S, F, B, M, P, or R
return value : none
:see
0_DValue
2_DValue_set
2_DValue_set_array
:content
Changes the node's internal type tag, forwarding through references when possible. Switching to map type clears children, resets list state, and resets the array index.
Prefer the typed setters (`set`, `set_bool`, `set_array`) in normal unit code.
```cpp
value.set_type('M');
```

View File

@ -0,0 +1,17 @@
:sig
bool DValue::to_bool(bool default_value = false) const
:params
bool : fallback returned when conversion is not possible
return value : converted value or the fallback
:see
0_DValue
2_DValue_get_by_path
:content
Converts strings, numbers, bools, pointers, and maps to bool. Text such as `true`, `yes`, `on`, `1`, `false`, `no`, `off`, `0`, and `null` is recognized; non-empty unparseable strings are truthy; empty strings use the default. Multi-child maps are true when non-empty.
```cpp
if(context.props["enabled"].to_bool(true)) print("enabled");
```

View File

@ -0,0 +1,17 @@
:sig
f64 DValue::to_f64(f64 default_value = 0) const
:params
f64 : fallback returned when conversion is not possible
return value : converted value or the fallback
:see
0_DValue
2_DValue_get_by_path
:content
Converts strings, f64, bool, pointer, and single-value maps to a finite double. Invalid or empty strings return the default.
```cpp
f64 price = row["price"].to_f64();
```

View File

@ -0,0 +1,17 @@
:sig
String DValue::to_json(char quote_char = '"') const
:params
quote_char : quote character passed to string escaping
return value : JSON-ish scalar representation
:see
0_DValue
json_encode
:content
Returns the JSON scalar representation used by this low-level method: strings are escaped and quoted, numbers are emitted with `std::to_string`, bools as `true`/`false`, maps as `"(array)"`, pointers as `"(pointer)"`, and references as `"(reference)"`. For full structured encoding, use the runtime JSON helpers.
```cpp
print(context.props["title"].to_json());
```

View File

@ -0,0 +1,17 @@
:sig
s64 DValue::to_s64(s64 default_value = 0) const
:params
s64 : fallback returned when conversion is not possible
return value : converted value or the fallback
:see
0_DValue
2_DValue_get_by_path
:content
Converts strings, f64, bool, pointer, and single-value maps to a signed integer. Invalid or empty strings return the default. Values outside the signed range are clamped.
```cpp
s64 count = cfg.get_by_path("limits/count").to_s64(10);
```

View File

@ -0,0 +1,17 @@
:sig
String DValue::to_string(String default_value = "") const
:params
String : fallback returned when conversion is not possible
return value : converted value or the fallback
:see
0_DValue
2_DValue_get_by_path
:content
Returns the stored string, converts f64/bool/pointer values to text, and returns the default for empty strings, maps, unresolved references, or unknown types. Bool text is `(true)` or `(false)`.
```cpp
String title = row["title"].to_string("Untitled");
```

View File

@ -0,0 +1,17 @@
:sig
StringMap DValue::to_stringmap() const
:params
return value : StringMap copy of the value
:see
0_DValue
0_StringMap
2_DValue_to_string
:content
Converts a `DValue` to `StringMap`. Map children become entries converted with `to_string()`. Non-empty strings become `value=<string>`. f64, bool, and pointer values become `value=<to_string()>`. References that cannot be resolved produce an empty map.
```cpp
StringMap headers = context.call["headers"].to_stringmap();
```

View File

@ -0,0 +1,17 @@
:sig
u64 DValue::to_u64(u64 default_value = 0) const
:params
u64 : fallback returned when conversion is not possible
return value : converted value or the fallback
:see
0_DValue
2_DValue_get_by_path
:content
Converts strings, f64, bool, pointer, and single-value maps to an unsigned integer. Invalid or empty strings return the default. Negative values clamp to zero and oversized values clamp to `u64` max.
```cpp
u64 id = row["id"].to_u64();
```

View File

@ -0,0 +1,19 @@
:sig
DValue DValue::values() const
:params
return value : a list-shaped DValue containing each iterated child value
:see
0_DValue
2_DValue_each
2_DValue_push
:content
Copies the values produced by `each()` into a new list-shaped `DValue`.
```cpp
DValue titles = articles.map([](const DValue& row, String) {
DValue v; v = row.get_by_path("title").to_string(); return(v);
}).values();
```

View File

@ -0,0 +1,18 @@
:sig
void Request::ob_start()
:params
return value : none
:see
0_Request
ob_start
ob_get_close
:content
Starts a new output buffer for the request and makes it the active `context.ob`. Most unit code should prefer the higher-level output-buffer helpers (`ob_start()`, `ob_get()`, `ob_get_close()`, `ob_close()`) rather than calling the method directly.
```cpp
context.ob_start();
print("captured");
```

View File

@ -0,0 +1,19 @@
:sig
void Request::set_status(s32 code, String reason = "")
:params
code : HTTP status code
reason : optional reason phrase override
return value : none
:see
0_Request
redirect
set_cookie
:content
Sets the request status line and mirrors the numeric status into `context.flags.status`. When `reason` is omitted, common HTTP status codes get their standard phrase. FastCGI-style requests use a `Status:` prefix; other requests use `HTTP/1.1`.
```cpp
context.set_status(404, "Not Found");
```

View File

@ -0,0 +1,18 @@
:sig
bool SQLite::connect(String path)
:params
path : database file path
return value : true when the database opens and default pragmas apply
:see
0_SQLite
2_SQLite_error
2_SQLite_disconnect
:content
Opens or creates a SQLite database, registers the wrapper for request cleanup, sets a busy timeout, and applies default pragmas (`foreign_keys=ON`, WAL journal mode, synchronous NORMAL). On failure, details are available through `error()`.
```cpp
SQLite db; if(!db.connect("tmp/app.db")) print(db.error());
```

View File

@ -0,0 +1,15 @@
:sig
void SQLite::disconnect()
:params
return value : none
:see
0_SQLite
:content
Forgets any worker-cache entry for this wrapper, closes the SQLite connection if open, clears the connection pointer, and unregisters it from request cleanup.
```cpp
db.disconnect();
```

View File

@ -0,0 +1,16 @@
:sig
String SQLite::error()
:params
return value : current statement/connection error text, or an empty string
:see
0_SQLite
:content
Returns stored statement information when present; otherwise returns SQLite's current connection error message when connected.
```cpp
DValue rows = db.query("select * from missing");
print(db.error());
```

View File

@ -0,0 +1,19 @@
:sig
DValue SQLite::query(String q)
DValue SQLite::query(String q, const StringMap& params)
:params
q : exactly one SQL statement
params : named :name parameter values; missing keys bind NULL
return value : result rows as a list-shaped DValue, or empty on error/non-row statements
:see
0_SQLite
2_SQLite_error
:content
Prepares and runs exactly one SQL statement. Extra trailing SQL statements are rejected. Parameters must be named `:name` placeholders; positional `?` placeholders are rejected. Result rows are returned as a list of maps keyed by column name. Integer, float, text, blob, and null column types map to matching `DValue` representations where null leaves the field empty. After completion, `affected_rows`, `insert_id`, and `statement_info` are updated.
```cpp
DValue rows = db.query("select * from users where id=:id", {{"id", "42"}});
```

View File

@ -0,0 +1,16 @@
:sig
template<typename F> void StringList::each(F f) const
:params
f : callback called with each String
return value : none
:see
0_StringList
:content
Calls the callback once for each string in order.
```cpp
items.each([](String item) { print(item, "\n"); });
```

View File

@ -0,0 +1,17 @@
:sig
template<typename F> StringList StringList::filter(F f) const
:params
f : predicate called with each String
return value : new list containing items where f(item) is true
:see
0_StringList
2_StringList_map
:content
Returns a new `StringList` containing the original items for which the predicate returns true. Order is preserved.
```cpp
StringList routes = split("home admin", " ").filter([](String s) { return(s != "admin"); });
```

View File

@ -0,0 +1,15 @@
:sig
StringList StringList::keys() const
:params
return value : index keys as strings
:see
0_StringList
:content
Returns the list indexes as strings from `0` through `size() - 1`.
```cpp
StringList keys = items.keys(); // {"0", "1", ...}
```

View File

@ -0,0 +1,17 @@
:sig
template<typename F> StringList StringList::map(F f) const
:params
f : mapper called with each String
return value : new list of mapped strings
:see
0_StringList
2_StringList_filter
:content
Returns a new `StringList` containing the mapper result for each item, in the same order.
```cpp
StringList upper = names.map([](String s) { return(to_upper(s)); });
```

View File

@ -1,25 +0,0 @@
:sig
StringList
:see
>types
String
split
split_space
split_utf8
join
regex_split
:content
Sequential container of `String` values.
`StringList` is an alias for `std::vector<String>`.
It is returned by split-style helpers such as `split()`, `split_space()`, `split_utf8()`, and `regex_split()`.
Use `join()` when you want to turn a `StringList` back into a single `String`.
Related:
- PHP: indexed arrays of strings
- JavaScript / Node.js: arrays of strings

View File

@ -10,7 +10,7 @@ return value : merged result
:see
>types
0_DValue
StringMap
0_StringMap
json_decode
:content

View File

@ -12,7 +12,8 @@ unit_render
3_C++ Preprocessor
map
filter
dv_filter
2_DValue_filter
2_DValue_map
:content
UCE is server-first C++ with a small template preprocessor. It does not try to be React, but several concepts map cleanly.
@ -45,10 +46,10 @@ That keeps file-based and hierarchical routing in normal UCE code instead of hid
The function library includes small collection helpers for common route/menu/card transformations:
```cpp
auto visible = filter(routes, [](String route) { return(route != "admin"); });
auto labels = map(visible, [](String route) { return(to_upper(route)); });
DValue app_items = dv_filter(menu, [](DValue item, String key) { return(item["section"].to_string() == "app"); });
DValue by_section = dv_group_by(menu, [](DValue item, String key) { return(item["section"].to_string()); });
auto visible = routes.filter([](String route) { return(route != "admin"); });
auto labels = visible.map([](String route) { return(to_upper(route)); });
DValue app_items = menu.filter([](DValue item, String key) { return(item["section"].to_string() == "app"); });
DValue labels_by_key = menu.map([](DValue item, String key) { DValue out; out = item["label"].to_string(); return(out); });
```
Use these when a short transformation is clearer than a loop. Prefer explicit loops for side effects or multi-step validation.

View File

@ -1,13 +0,0 @@
:sig
bool config_bool(String key, bool fallback = true)
:params
key : server configuration key
fallback : value returned when missing
:see
>sys
>config_bool_value
:content
Reads a boolean value from the active server configuration.

View File

@ -1,13 +0,0 @@
:sig
bool config_bool_value(String raw, bool fallback = true)
:params
raw : raw string value
fallback : value returned for an empty string
:see
>sys
>config_bool
:content
Parses common configuration booleans. Empty uses the fallback; `0`, `false`, `no`, and `off` are false; other non-empty values are true.

View File

@ -1,13 +0,0 @@
:sig
f64 config_f64(String key, f64 fallback)
:params
key : server configuration key
fallback : value returned when missing or invalid
:see
>sys
>config_map_f64
:content
Reads a floating-point value from the active server configuration.

View File

@ -1,14 +0,0 @@
:sig
bool config_map_bool(StringMap& cfg, String key, bool fallback = true)
:params
cfg : configuration map
key : entry to parse
fallback : value returned when missing
:see
>sys
>config_bool_value
:content
Reads a boolean from a string map using `config_bool_value()` semantics.

View File

@ -1,14 +0,0 @@
:sig
f64 config_map_f64(StringMap& cfg, String key, f64 fallback)
:params
cfg : configuration map
key : entry to parse
fallback : value returned when missing or invalid
:see
>sys
>config_f64
:content
Reads a floating-point value from a string map with fallback handling.

View File

@ -1,14 +0,0 @@
:sig
u64 config_map_u64(StringMap& cfg, String key, u64 fallback)
:params
cfg : configuration map
key : entry to parse
fallback : value returned when missing or invalid
:see
>sys
>config_u64
:content
Reads an unsigned integer from a string map with fallback handling.

View File

@ -1,13 +0,0 @@
:sig
u64 config_u64(String key, u64 fallback)
:params
key : server configuration key
fallback : value returned when missing or invalid
:see
>sys
>config_map_u64
:content
Reads an unsigned integer from the active server configuration.

View File

@ -1,26 +0,0 @@
:title
dv_filter
:sig
DValue dv_filter(DValue tree, function<bool (DValue, String)> f)
:see
StringList
0_DValue
filter
:content
Keeps children for which f returns true. List-like input stays list-like.
These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering.
```cpp
DValue visible = dv_filter(items, [](DValue item, String key) { return(item["hidden"].to_bool() == false); });
```
Prefer these helpers over open-coded loops when the transformation itself is the important part of the code. Use an explicit loop when mutation, error handling, or side effects are the main concern.
## Related Concepts
- JavaScript / React: `Array.map`, `Array.filter`, `Array.find`, object `pick`/`omit`, and grouping route or navigation records before rendering.
- PHP: `array_map`, `array_filter`, `array_unique`, and associative array projection.

View File

@ -1,26 +0,0 @@
:title
dv_group_by
:sig
DValue dv_group_by(DValue tree, function<String (DValue, String)> f)
:see
StringList
0_DValue
filter
:content
Groups children into list-like buckets by the string returned from f.
These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering.
```cpp
DValue by_section = dv_group_by(menu, [](DValue item, String key) { return(item["section"].to_string()); });
```
Prefer these helpers over open-coded loops when the transformation itself is the important part of the code. Use an explicit loop when mutation, error handling, or side effects are the main concern.
## Related Concepts
- JavaScript / React: `Array.map`, `Array.filter`, `Array.find`, object `pick`/`omit`, and grouping route or navigation records before rendering.
- PHP: `array_map`, `array_filter`, `array_unique`, and associative array projection.

View File

@ -1,26 +0,0 @@
:title
dv_keys
:sig
StringList dv_keys(DValue tree)
:see
StringList
0_DValue
filter
:content
Returns map keys from a DValue. Scalar values produce an empty list.
These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering.
```cpp
StringList keys = dv_keys(context.cfg["menu"]);
```
Prefer these helpers over open-coded loops when the transformation itself is the important part of the code. Use an explicit loop when mutation, error handling, or side effects are the main concern.
## Related Concepts
- JavaScript / React: `Array.map`, `Array.filter`, `Array.find`, object `pick`/`omit`, and grouping route or navigation records before rendering.
- PHP: `array_map`, `array_filter`, `array_unique`, and associative array projection.

View File

@ -1,26 +0,0 @@
:title
dv_map
:sig
DValue dv_map(DValue tree, function<DValue (DValue, String)> f)
:see
StringList
0_DValue
filter
:content
Transforms each child. List-like input stays list-like; map input keeps keys.
These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering.
```cpp
DValue titles = dv_map(items, [](DValue item, String key) { DValue out; out = item["title"].to_string(); return(out); });
```
Prefer these helpers over open-coded loops when the transformation itself is the important part of the code. Use an explicit loop when mutation, error handling, or side effects are the main concern.
## Related Concepts
- JavaScript / React: `Array.map`, `Array.filter`, `Array.find`, object `pick`/`omit`, and grouping route or navigation records before rendering.
- PHP: `array_map`, `array_filter`, `array_unique`, and associative array projection.

View File

@ -1,26 +0,0 @@
:title
dv_omit
:sig
DValue dv_omit(DValue tree, StringList keys)
:see
StringList
0_DValue
filter
:content
Copies a DValue map except for selected keys.
These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering.
```cpp
DValue safe_user = dv_omit(user, {"password_hash"});
```
Prefer these helpers over open-coded loops when the transformation itself is the important part of the code. Use an explicit loop when mutation, error handling, or side effects are the main concern.
## Related Concepts
- JavaScript / React: `Array.map`, `Array.filter`, `Array.find`, object `pick`/`omit`, and grouping route or navigation records before rendering.
- PHP: `array_map`, `array_filter`, `array_unique`, and associative array projection.

View File

@ -1,26 +0,0 @@
:title
dv_pick
:sig
DValue dv_pick(DValue tree, StringList keys)
:see
StringList
0_DValue
filter
:content
Copies only selected keys from a DValue map.
These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering.
```cpp
DValue public_user = dv_pick(user, {"name", "avatar"});
```
Prefer these helpers over open-coded loops when the transformation itself is the important part of the code. Use an explicit loop when mutation, error handling, or side effects are the main concern.
## Related Concepts
- JavaScript / React: `Array.map`, `Array.filter`, `Array.find`, object `pick`/`omit`, and grouping route or navigation records before rendering.
- PHP: `array_map`, `array_filter`, `array_unique`, and associative array projection.

View File

@ -1,26 +0,0 @@
:title
dv_values
:sig
DValue dv_values(DValue tree)
:see
StringList
0_DValue
filter
:content
Returns child values as a list-like DValue.
These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering.
```cpp
DValue menu_items = dv_values(context.cfg["menu"]);
```
Prefer these helpers over open-coded loops when the transformation itself is the important part of the code. Use an explicit loop when mutation, error handling, or side effects are the main concern.
## Related Concepts
- JavaScript / React: `Array.map`, `Array.filter`, `Array.find`, object `pick`/`omit`, and grouping route or navigation records before rendering.
- PHP: `array_map`, `array_filter`, `array_unique`, and associative array projection.

View File

@ -1,36 +0,0 @@
:sig
void DValue::each(function<void (const DValue& item, String key)> f) const
:params
f : callback invoked per child with the child node and its key
return value : none
:see
>types
0_DValue
dv_map
dv_filter
dv_keys
is_list
:content
Iterates a `DValue`. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
For map-shaped nodes the callback runs once per child, in key order, receiving the child and its key. For scalar nodes it runs exactly once with the node itself and an empty key.
The callback receives the child as `const DValue&` — no copy is made, and all read accessors (`to_string()`, `to_s64()`, `get_by_path()`, ...) work on it directly:
```cpp
rows.each([&](const DValue& row, String key) {
out("<li>", html_escape(row.get_by_path("title").to_string("Untitled")), "</li>");
});
```
Declaring the callback parameter as plain `DValue` also works but deep-copies every child; prefer `const DValue&`. To transform or filter into a new tree, use `dv_map()` / `dv_filter()` instead of mutating inside the callback.
List-shaped trees (see `is_list`) iterate in numeric index order, matching `json_encode()` and the other serializers. Keyed maps iterate in string key order.
## Related Concepts
- PHP: `foreach ($tree as $key => $item)`
- JavaScript: `Object.entries(obj).forEach(([key, item]) => ...)`

View File

@ -15,7 +15,7 @@ page_runtime_error : UCE page rendered (status 500) after a recovered fault or u
>runtime
0_Request
0_DValue
set_status
2_Request_set_status
:content
Three optional keys in `/etc/uce/settings.cfg` route error conditions to developer-defined UCE pages. Each key names a `.uce` file, either absolute or relative to the server's working directory. When a key is unset, or the named file does not exist, or the error page itself fails, the runtime's built-in plain-text behavior stands.

View File

@ -8,7 +8,7 @@ return value : a f64 containing the number (0 if no number could be identified).
:see
>string
int_val
String
0_String
:content
Extracts a floating point number from a `String`.

View File

@ -1,34 +0,0 @@
:sig
DValue DValue::get_by_path(String path, String delim = "/") const
:params
path : slash-delimited path to traverse
delim : optional path separator
return value : the resolved child node, or an empty `DValue` when the path cannot be followed
:see
0_DValue
0_Request
>types
json_decode
has
to_string
:content
Traverses a nested `DValue` without creating missing keys. This is a read accessor: it is `const` and never modifies the tree, unlike `operator[]`, which creates missing entries.
Empty path segments are ignored, so leading and trailing `/` characters are harmless. If any intermediate node is not a map or a segment is missing, `get_by_path()` returns an empty `DValue`.
A missing path therefore reads like an empty value — combine it with the `to_*` default arguments to express a fallback in one call:
## Example
```cpp
String label = context.cfg.get_by_path("theme/options/portal-dark/label").to_string("Portal Dark");
s64 page_size = context.cfg.get_by_path("app/page_size").to_s64(25);
```
## Related Concepts
- PHP: deep array access helpers for associative arrays, decoded JSON, or configuration trees
- JavaScript / Node.js: optional chaining like `obj?.a?.b`, lodash `get`, or small path-walking helpers

View File

@ -1,36 +0,0 @@
:sig
bool DValue::has(String s) const
:params
s : child key to test
return value : true when the node is map-shaped and contains the key
:see
>types
0_DValue
get_by_path
each
is_array
:content
Tests whether a map-shaped `DValue` contains a child key, without creating it. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
Returns `false` for scalar nodes and for missing keys.
This matters because `operator[]` creates missing entries, exactly like `std::map`. Use `has()` (or `get_by_path()`) when you only want to look:
```cpp
if(context.props.has("avatar"))
print(component("components/basic/avatar", context.props["avatar"], context));
```
`has()` only checks one level. For nested checks, combine with `get_by_path()`:
```cpp
bool configured = context.cfg.get_by_path("mail/smtp").is_array();
```
## Related Concepts
- PHP: `array_key_exists()` / `isset()`
- JavaScript: `Object.hasOwn(obj, key)` / `key in obj`

View File

@ -9,7 +9,7 @@ return value : a u64 containing the number (0 if no number could be identified).
:see
>string
float_val
String
0_String
:content
Extracts an integer value from `s`.

View File

@ -1,34 +0,0 @@
:sig
bool DValue::is_array() const
:params
return value : true when the node is map-shaped
:see
>types
0_DValue
is_list
has
each
:content
Tests whether a `DValue` node is map-shaped, i.e. holds nested child values rather than a scalar. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
`is_array()` is true for both list-like and keyed containers; use `is_list()` to distinguish the two:
```cpp
DValue rows = sqlite_query(db, "select * from notes");
if(rows.is_array())
{
rows.each([&](const DValue& row, String key) {
// ...
});
}
```
Scalar values (strings, numbers, booleans, pointers) return `false`, as do unresolvable references.
## Related Concepts
- PHP: `is_array()`
- JavaScript: `typeof value === "object"`

View File

@ -1,35 +0,0 @@
:sig
bool DValue::is_list() const
:params
return value : true when the node is a sequential, numerically indexed container
:see
>types
0_DValue
is_array
push
each
dv_values
:content
Tests whether a map-shaped `DValue` represents a list: its keys are the numeric strings `"0"`, `"1"`, `"2"`, ... in unbroken sequence. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
Containers built with `push()` are lists. An empty container counts as a list when it was created with `set_array()` or `push()`; a keyed map (or a map with gaps in its numeric keys) is `is_array()` but not `is_list()`.
```cpp
DValue items;
items.push(first_item);
items.push(second_item);
// items.is_list() == true
items["custom"] = "x";
// items.is_list() == false, items.is_array() == true
```
`dv_map()` and `dv_filter()` use this distinction: list inputs re-index from zero, while map inputs keep their original keys.
## Related Concepts
- PHP: `array_is_list()`
- JavaScript: `Array.isArray()`

View File

@ -8,9 +8,9 @@ return value : a DValue object containing the deserialized JSON data
:see
0_DValue
json_encode
to_bool
to_f64
to_u64
2_DValue_to_bool
2_DValue_to_f64
2_DValue_to_u64
:content
Deserializes `s` into a `DValue` structure.

View File

@ -11,7 +11,7 @@ return value : string containing the JSON result
>types
json_decode
0_DValue
String
0_String
html_escape
:content

View File

@ -5,7 +5,7 @@ list_every
bool list_every(StringList items, function<bool (String)> f)
:see
StringList
0_StringList
0_DValue
filter

View File

@ -6,7 +6,7 @@ Use filter(StringList items, function<bool (String)> f)
:see
filter
StringList
0_StringList
:content
`list_filter()` was removed because `StringList` is `std::vector<String>` and the generic `filter()` helper covers the same behavior without a second implementation to maintain.

View File

@ -5,7 +5,7 @@ list_find
String list_find(StringList items, function<bool (String)> f, String fallback = "")
:see
StringList
0_StringList
0_DValue
filter

View File

@ -7,7 +7,7 @@ Use map(StringList items, function<String (String)> f)
:see
map
filter
StringList
0_StringList
:content
`list_map()` was removed because `StringList` is `std::vector<String>` and the generic `map()` helper covers the same behavior without a second implementation to maintain.

View File

@ -5,7 +5,7 @@ list_some
bool list_some(StringList items, function<bool (String)> f)
:see
StringList
0_StringList
0_DValue
filter

View File

@ -5,7 +5,7 @@ list_sort
StringList list_sort(StringList items)
:see
StringList
0_StringList
0_DValue
filter

View File

@ -5,7 +5,7 @@ list_unique
StringList list_unique(StringList items)
:see
StringList
0_StringList
0_DValue
filter

View File

@ -13,7 +13,7 @@ return value : a new list containing the transformed values
:see
>string
filter
StringList
0_StringList
0_DValue
:content

View File

@ -12,7 +12,7 @@ markdown_to_ast
component
component_render
json_decode
String
0_String
:content
Renders Markdown source into HTML and returns the generated markup as a `String`.

View File

@ -6,7 +6,7 @@ url : target URL for the redirect
code : optional HTTP redirect status, defaults to `302`
:see
set_status
2_Request_set_status
0_Request
:content

View File

@ -14,7 +14,7 @@ regex_search
regex_search_all
regex_replace
regex_split
String
0_String
:content
Tests whether `subject` matches `pattern` from start to end.

View File

@ -16,7 +16,7 @@ regex_search_all
regex_replace
split
join
StringList
0_StringList
:content
Splits `subject` wherever `pattern` matches.

View File

@ -1,20 +0,0 @@
:sig
void context.set_status(s32 code, String reason = "")
:params
code : HTTP status code
reason : optional reason phrase override
:see
0_Request
>types
:content
Sets the current request status line and mirrors the numeric status into `context.flags.status`.
When `reason` is omitted, UCE fills in a standard reason phrase for common HTTP status codes such as `200`, `302`, `400`, `404`, and `500`.
Related:
- PHP: `http_response_code()` and explicit status-line header control
- JavaScript / Node.js: `res.status(...)`, `Response` init status, or low-level status assignment on an HTTP response

View File

@ -1,37 +1,22 @@
:sig
bool DValue::to_bool(bool default_value = false) const
bool to_bool(String s, bool fallback = false)
:params
default_value : returned when the value is missing or empty
return value : the value as a boolean, or `default_value`
s : string to parse
fallback : value returned when `s` is empty or not a recognized boolean token
return value : parsed bool, or `fallback`.
:see
>types
0_DValue
json_decode
to_f64
to_u64
to_string
to_s64
to_f64
:content
Reads a `DValue` value as a boolean. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
Parses a trimmed, case-insensitive boolean string.
String values such as `true`, `yes`, `on`, and `1` read as true. Values such as `false`, `no`, `off`, `0`, and `null` read as false. Numeric values read as true when non-zero.
Truthy tokens are `"1"`, `"true"`, `"yes"`, and `"on"`. Falsey tokens are `"0"`, `"false"`, `"no"`, and `"off"`. Empty or unrecognized strings return the fallback.
`default_value` is returned when the node is unset, holds an empty string, or is an unresolvable reference. Note the truthiness rule: a non-empty string that parses as neither a boolean word nor a number still reads as `true` — only missing/empty values fall back to the default.
A map-shaped node with exactly one entry unwraps to that entry's value; other maps read as true when non-empty.
## Example
```cpp
bool log_requests = context.cfg.get_by_path("app/log_requests").to_bool(true);
bool wants_compact = context.props["compact"].to_bool();
```uce
bool enabled = to_bool(context.server->config["FEATURE_ENABLED"], true);
```
Use this when consuming request data, JSON-decoded values, config trees, or component props where the original input may be string-shaped.
## Related Concepts
- PHP: `filter_var($v, FILTER_VALIDATE_BOOLEAN)`
- JavaScript: `Boolean(value)` plus string handling

View File

@ -1,42 +1,23 @@
:sig
f64 DValue::to_f64(f64 default_value = 0) const
f64 to_f64(String s, f64 fallback = 0)
:params
default_value : returned when the value is missing or cannot be parsed as a number
return value : the value as a floating-point number, or `default_value`
s : string to parse
fallback : value returned when `s` is empty or not a complete floating-point number
return value : parsed f64, or `fallback`.
:see
>types
0_DValue
json_decode
float_val
to_bool
to_s64
to_u64
to_s64
to_bool
float_val
:content
Reads a `DValue` value as a floating-point number. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
Parses a trimmed string as a floating-point number.
String values are trimmed and parsed permissively: numeric forms and the boolean words understood by `to_bool()` both convert. Boolean values become `1.0` or `0.0`.
Unlike `float_val()`, the whole trimmed string must be consumed by the parser. Empty strings and partially parsed values such as `"3.5ms"` return the fallback.
`default_value` is returned when:
- the node is unset or holds an empty string
- the string does not parse as a finite number
- the node is map-shaped with more than one entry, or an unresolvable reference
A map-shaped node with exactly one entry unwraps to that entry's value before converting.
## Example
```cpp
f64 ratio = context.props["ratio"].to_f64(1.0);
f64 threshold = context.cfg.get_by_path("alerts/threshold").to_f64(0.75);
```uce
f64 timeout = to_f64(context.server->config["REQUEST_TIMEOUT"], 15.0);
```
Use this for numeric config values, JSON-decoded fields, component props, and request data that should be treated as a number.
## Related Concepts
- PHP: `floatval()` with a fallback
- JavaScript: `parseFloat(x) || fallback`

View File

@ -1,34 +0,0 @@
:sig
String DValue::to_json(char quote_char = '"') const
:params
quote_char : quote character used around string output
return value : the scalar as a single JSON token
:see
>types
0_DValue
json_encode
json_decode
to_string
:content
Renders a single scalar `DValue` value as a JSON token. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
Strings are escaped and quoted, numbers render as numeric literals, and booleans render as `true` / `false`.
This is **not** a serializer for nested data: map-shaped nodes render as the placeholder string `"(array)"`. Use `json_encode()` to serialize a whole tree.
## Example
```cpp
String token = context.props["label"].to_json(); // "\"Don't panic\""
String body = json_encode(context.props); // full tree serialization
```
The `quote_char` parameter exists for embedding output inside single-quoted contexts; for HTML attributes prefer double quotes plus `html_escape()`.
## Related Concepts
- JavaScript: `JSON.stringify(value)` for a single scalar
- PHP: `json_encode($scalar)`

View File

@ -1,41 +1,23 @@
:sig
s64 DValue::to_s64(s64 default_value = 0) const
s64 to_s64(String s, s64 fallback = 0)
:params
default_value : returned when the value is missing or cannot be parsed as a number
return value : the value as a signed 64-bit integer, or `default_value`
s : string to parse
fallback : value returned when `s` is empty or not a complete signed integer
return value : parsed s64, or `fallback`.
:see
>types
0_DValue
to_u64
to_f64
to_bool
to_string
int_val
:content
Reads a `DValue` value as a signed integer. This is a read accessor: it is `const`, never creates or modifies nodes, and dereferences internal references automatically.
Parses a trimmed string as a base-10 signed integer.
String values are trimmed and parsed permissively: plain integers, floating-point forms (truncated toward zero), and the boolean words understood by `to_bool()` (`yes` reads as `1`) all convert. Results outside the `s64` range clamp to the range boundaries. Boolean values become `1` or `0`.
The whole trimmed string must be consumed by the parser. Empty strings and partially parsed values such as `"-12px"` return the fallback.
`default_value` is returned when:
- the node is unset or holds an empty string
- the string does not parse as a number (`"not-a-number"`)
- the node is map-shaped with more than one entry, or an unresolvable reference
A map-shaped node with exactly one entry unwraps to that entry's value before converting — this matches how single-value rows from query results read.
## Example
```cpp
s64 page = context.get["page"].to_s64(1);
s64 limit = context.cfg.get_by_path("app/page_size").to_s64(25);
```uce
s64 offset = to_s64(context.params["offset"], 0);
```
`json_decode()` stores JSON numbers as string-valued nodes, so this is the normal way to consume decoded numeric fields.
## Related Concepts
- PHP: `intval()` with a fallback
- JavaScript: `parseInt(x) || fallback`

Some files were not shown because too many files have changed in this diff Show More