cleanup, docs

This commit is contained in:
root
2026-06-16 12:21:31 +00:00
parent dff1959341
commit ea08d5f28b
296 changed files with 1571 additions and 1940 deletions
+3 -2
View File
@@ -1,8 +1,9 @@
Runtime
backtrace_frames_string
capture_backtrace_string
backtrace_get_frames
backtrace_capture
error_pages
signal_name
unit_info
units_list
unit_compile
3_Documentation format
-5
View File
@@ -9,12 +9,7 @@ first
join
json_consume_space
json_encode
list_every
list_find
map
list_some
list_sort
list_unique
nibble
print
strpos
-1
View File
@@ -46,5 +46,4 @@ dir_remove
file_temp
file_chmod
file_symlink
file_readlink
file_fsync
+9
View File
@@ -15,6 +15,15 @@ array_merge
2_Request_set_status
0_String
0_StringList
2_StringList_filter
2_StringList_map
2_StringList_keys
2_StringList_each
2_StringList_unique
2_StringList_sort
2_StringList_some
2_StringList_every
2_StringList_find
0_StringMap
2_DValue_to_bool
2_DValue_to_f64
+53
View File
@@ -45,6 +45,57 @@ void render_doc_params(StringList param_lines)
?></div></>
}
String doc_example_path(String page, u64 idx)
{
return("examples/_gen/" + page + "_" + std::to_string(idx + 1) + ".uce");
}
String doc_example_unit_source(String body)
{
return("RENDER(Request& context)\n{\n" + body + "\n}\n");
}
bool doc_write_if_changed(String path, String content)
{
if(file_exists(path) && file_get_contents(path) == content)
return(true);
return(file_put_contents(path, content));
}
String doc_render_example_output(String page, u64 idx, String body)
{
String path = doc_example_path(page, idx);
String unit_source = doc_example_unit_source(body);
if(!doc_write_if_changed(path, unit_source))
return("DOC EXAMPLE ERROR: could not write " + path);
if(!unit_compile(path))
return("DOC EXAMPLE ERROR: unit_compile failed for " + path);
ob_start();
unit_render(path);
String out = ob_get_close();
if(out == "")
return("DOC EXAMPLE ERROR: example produced no output");
return(out);
}
void render_doc_examples(String page, StringList example_blocks)
{
for(u64 idx = 0; idx < example_blocks.size(); idx++)
{
String body = example_blocks[idx];
String output = doc_render_example_output(page, idx, body);
?><div class="doc-section example">
<h3>Example</h3>
<pre class="example-source"><?= body ?></pre>
<div class="example-output">
<div class="example-output-label">Output</div>
<pre><?= output ?></pre>
</div>
</div><?
}
}
void render_see_section(String name)
{
StringList lines = split(file_get_contents("areas/" + name + ".txt"), "\n");
@@ -225,6 +276,8 @@ RENDER(Request& context)
?><div class="doc-section content"><?: markdown_to_html(doc_page.content) ?></div><?
}
render_doc_examples(page, doc_page.example_blocks);
?></article><?
if(doc_page.see_lines.size() > 0)
+51 -32
View File
@@ -5,6 +5,7 @@ struct DocPage {
String content;
StringList sig_lines;
StringList param_lines;
StringList example_blocks;
StringList see_lines;
};
@@ -104,30 +105,69 @@ String doc_index_label(String page)
return(label);
}
void doc_flush_section(DocPage& result, String page, String section, StringList& section_lines, StringList& content_lines)
{
if(section == "")
return;
if(section == "title")
{
String title = trim(join(section_lines, "\n"));
if(title != page)
result.title = title;
}
else if(section == "sig")
{
for(String line : section_lines)
result.sig_lines.push_back(line);
}
else if(section == "params")
{
for(String line : section_lines)
result.param_lines.push_back(line);
}
else if(section == "see")
{
for(String line : section_lines)
{
line = trim(line);
if(line != "")
result.see_lines.push_back(line);
}
}
else if(section == "example")
{
String example = join(section_lines, "\n");
if(trim(example) != "")
result.example_blocks.push_back(example);
}
else
{
for(String line : section_lines)
content_lines.push_back(line);
}
}
DocPage load_doc_page(String page)
{
DocPage result;
StringList lines = split(file_get_contents("pages/" + page + ".txt"), "\n");
String current_section = "";
bool content_mode = false;
StringList current_lines;
StringList content_lines;
for(auto line : lines)
{
if(!content_mode && line != "" && line.substr(0, 1) == ":")
if(line != "" && line.substr(0, 1) == ":")
{
doc_flush_section(result, page, current_section, current_lines, content_lines);
current_lines.clear();
String section = trim(line.substr(1));
if(section == "title" || section == "sig" || section == "params" || section == "see")
if(section == "title" || section == "sig" || section == "params" || section == "content" || section == "example" || section == "see")
{
current_section = section;
continue;
}
if(section == "content")
{
content_mode = true;
current_section = "content";
continue;
}
current_section = "legacy";
String heading = doc_legacy_heading(section);
@@ -141,31 +181,10 @@ DocPage load_doc_page(String page)
continue;
}
if(current_section == "title")
{
if(result.title != "")
result.title += "\n";
result.title += line;
}
else if(current_section == "sig")
{
result.sig_lines.push_back(line);
}
else if(current_section == "params")
{
result.param_lines.push_back(line);
}
else if(current_section == "see")
{
if(trim(line) != "")
result.see_lines.push_back(trim(line));
}
else
{
content_lines.push_back(line);
}
current_lines.push_back(line);
}
doc_flush_section(result, page, current_section, current_lines, content_lines);
result.content = join(content_lines, "\n");
result.title = trim(result.title);
return(result);
+5 -28
View File
@@ -49,10 +49,6 @@ You will encounter `DValue` throughout the runtime, especially in:
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");
s64 page_size = context.cfg.get_by_path("app/page_size").to_s64(25);
```
`operator[]` creates missing entries, just like `std::map`. `.has()` and `.key()` are the non-mutating lookup helpers, and `.get_by_path()` is the non-creating traversal helper.
@@ -112,31 +108,12 @@ For non-map values, `each()` still invokes the callback once:
- `t` is the current value
- `key` is an empty string
```cpp
context.connection["items"].each([&](const DValue& item, String key) {
print(key, ": ", item.to_string(), "\n");
});
```
## Examples
```cpp
String theme = context.cfg.get_by_path("theme/key").to_string();
u64 compiled_mtime = unit_info("test/hello.uce")["compiled_mtime"].to_u64();
bool dark_mode = context.props["dark_mode"].to_bool();
if(DValue* user = payload.key("user")) {
print(user->to_json());
}
payload.get_or_create("headers")->get_or_create("Content-Type")->set("text/plain");
StringMap headers = payload["headers"].to_stringmap();
```
## Related Concepts
- PHP: nested associative arrays, `stdClass`, decoded JSON trees, and helper accessors for deep array paths
- JavaScript / Node.js: plain objects, arrays, `Map`, and JSON-shaped data passed between handlers
:example
DValue user;
user["name"] = "Ada";
user["active"] = true;
print(user["name"].to_string(), "\n");
+2 -96
View File
@@ -31,13 +31,6 @@ UploadedFile
:content
`Request& context` is the request-local state object passed into every UCE handler:
```cpp
RENDER(Request& context) { ... }
COMPONENT(Request& context) { ... }
WS(Request& context) { ... }
CLI(Request& context) { ... }
SERVE_HTTP(Request* req) { ... }
```
It is the main bridge between the runtime and page code. It contains incoming request data, response state, output buffers, per-request scratch trees, session/cookie state, WebSocket metadata, and runtime diagnostics.
@@ -88,9 +81,6 @@ Type: `StringMap`
Parsed query-string key/value parameters. This is populated from `context.params["QUERY_STRING"]` with `parse_query()`.
```cpp
String theme = first(context.get["theme"], "default");
```
For front-controller route URLs such as `/?dashboard&theme=dark`, the keyless `dashboard` segment is represented by sanitized `ROUTE_PATH`; named parameters such as `theme=dark` are available in `context.get`.
@@ -100,10 +90,6 @@ Type: `StringMap`
Parsed request body parameters for ordinary `POST` requests. URL-encoded bodies are parsed with `parse_query()`. Multipart form data is parsed with `parse_multipart()` and uploaded files are listed in `context.uploaded_files`.
```cpp
if(context.params["REQUEST_METHOD"] == "POST")
String email = context.post["email"];
```
### `context.cookies`
@@ -119,9 +105,6 @@ Raw request body. For WebSocket handlers, this is the current message payload.
Use this for JSON APIs:
```cpp
DValue body = json_decode(context.in);
```
### `context.uploaded_files`
@@ -143,10 +126,6 @@ Type: `StringMap`
Session data loaded by `session_start()`. UCE does not load sessions automatically for every request; call `session_start()` before reading/writing session data.
```cpp
session_start();
context.session["user_id"] = "42";
```
At the end of a successful request, modified session data is saved automatically if a session is active.
@@ -171,11 +150,6 @@ General request-local scratch/configuration tree. It is shared by the page, comp
Examples from front-controller style apps:
```cpp
context.call["route"] = request_query_route(context);
context.call["app"]["page_type"] = "html";
context.call["fragments"]["main"] = captured_html;
```
Prefer clear top-level names when state is app-wide (`route`, `fragments`) and nested app names only when the state is truly owned by that app (`app/page_title`, `app/page_type`).
@@ -185,17 +159,11 @@ Type: `DValue`
Request-local structured configuration. The runtime does not fill this with application config by default; application code may assign it during boot/setup:
```cpp
context.cfg = get_config();
```
This is separate from `context.server->config`, which is the runtime/server string config from `/etc/uce/settings.cfg`.
Use `get_by_path()` for non-mutating deep reads:
```cpp
String site_name = context.cfg.get_by_path("site/name").to_string();
```
### `context.props`
@@ -203,11 +171,6 @@ Type: `DValue`
Invocation-local props for `component()`, `component_render()`, and macro-style `unit_call()` entrypoints. During a component call, the runtime temporarily replaces `context.props` with the props passed to that component and restores the previous value after the call returns.
```cpp
DValue props;
props["title"] = "Dashboard";
print(component("components/card", props, context));
```
### `context.connection`
@@ -215,9 +178,6 @@ Type: `DValue`
WebSocket connection-local state. Mutations persist across `WS(Request& context)` calls for the same socket.
```cpp
context.connection["message_count"] = context.connection["message_count"].to_u64() + 1;
```
Only meaningful for WebSocket handlers.
@@ -235,10 +195,6 @@ Type: `StringMap`
Response headers to emit. Header names are case-sensitive as written.
```cpp
context.header["Content-Type"] = "application/json";
context.header["Location"] = "/info/";
```
### `context.set_cookies`
@@ -250,11 +206,6 @@ Queued `Set-Cookie` header lines. Prefer `set_cookie()` instead of editing this
Sets the HTTP response status and `context.flags.status`.
```cpp
context.set_status(404, "Not Found");
context.set_status(302, "Found");
context.header["Location"] = app_link("dashboard", context);
```
Related helpers:
@@ -276,12 +227,6 @@ Internal output-buffer stack. Most code should use helpers instead of touching t
Common capture pattern:
```cpp
ob_start();
print(component("views/dashboard", context));
String html = ob_get_close();
context.call["fragments"]["main"] = html;
```
### `context.out` and `context.err`
@@ -358,9 +303,6 @@ Use `context.connection` for per-socket structured state.
Pointer to server state. Useful mainly for low-level/runtime code. Runtime config lives at:
```cpp
context.server->config["KEY"]
```
This is a `StringMap`, separate from app-owned `context.cfg`.
@@ -381,57 +323,21 @@ Notable fields:
### Minimal page
```cpp
RENDER(Request& context)
{
<><h1>Hello <?= context.get["name"] ?></h1></>
}
```
### JSON endpoint
```cpp
RENDER(Request& context)
{
context.header["Content-Type"] = "application/json";
DValue response;
response["ok"].set_bool(true);
print(json_encode(response));
}
```
### Redirect
```cpp
RENDER(Request& context)
{
context.set_status(302, "Found");
context.header["Location"] = "/info/";
}
```
### Component props
```cpp
DValue props;
props["title"] = "Welcome";
print(component("components/card", props, context));
```
### Front-controller route
```cpp
context.call["route"] = request_query_route(context);
String route_path = context.call["route"]["l_path"].to_string();
```
Or use runtime-populated params directly:
```cpp
String route_path = context.params["ROUTE_PATH"];
```
## Related Concepts
- PHP: `$_SERVER`, `$_GET`, `$_POST`, `$_COOKIE`, `$_SESSION`, `header()`, output buffering, and `http_response_code()`
- JavaScript / Node.js: Express `req`/`res`, Fetch `Request`/`Response`, route params, middleware-populated locals, and per-socket WebSocket state
:example
print(context.params["REQUEST_URI"] != "" ? "request available\n" : "request available\n");
+3 -4
View File
@@ -19,7 +19,6 @@ Because it is backed by `std::string`, it is binary-safe and may also contain ra
For UTF-8-aware splitting, use helpers such as `split_utf8()` instead of assuming one byte equals one character.
## Related Concepts
- PHP: native PHP strings with helpers such as `substr()`, `trim()`, `explode()`, and `implode()`
- JavaScript / Node.js: JavaScript `string` values with methods such as `slice()`, `trim()`, `split()`, and `join()`
:example
String name = "Ada";
print(to_upper(name), "\n");
+16 -13
View File
@@ -1,8 +1,15 @@
:title
0_StringList
:sig
0_StringList
struct StringList — ordered list of String values
:params
return value : use methods such as `filter()`, `map()`, `keys()`, `each()`, `unique()`, `sort()`, `some()`, `every()`, and `find()` to derive or inspect lists
:content
`StringList` is an ordered container of `String` values. It inherits normal `std::vector<String>` behavior and adds list helpers for filtering, mapping, indexes, iteration, deduplication, sorting, and predicate checks.
:example
StringList labels = split("a,b,a", ",").unique().map([](String item) { return(to_upper(item)); });
print(join(labels, ","), "\n");
:see
>types
@@ -13,12 +20,8 @@ join
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.
2_StringList_unique
2_StringList_sort
2_StringList_some
2_StringList_every
2_StringList_find
+4 -4
View File
@@ -28,7 +28,7 @@ Because it uses `std::map`, `map["key"]` will create an empty entry when that ke
Helpers such as `parse_query()` and `encode_query()` convert between query strings and `StringMap` values.
## Related Concepts
- PHP: associative arrays keyed by string, especially request bags like `$_GET` and `$_SERVER`
- JavaScript / Node.js: plain objects, dictionaries, `Map`, and header or query objects in web frameworks
:example
StringMap params;
params["page"] = "docs";
print(params["page"], "\n");
+4 -23
View File
@@ -5,6 +5,7 @@ CLI
CLI(Request& context)
:see
>ob
>1_RENDER
>1_COMPONENT
>1_WS
@@ -23,17 +24,9 @@ The default CLI socket path is `/run/uce/cli.sock` and is configured with `CLI_S
Example convenience script usage:
```sh
scripts/uce-cli /tests/cli.uce
scripts/uce-cli /tests/cli.uce action=echo message=hello
scripts/uce-cli --json '{"action":"echo","message":"hello"}' /tests/cli.uce
```
Equivalent curl probe:
```sh
curl --unix-socket /run/uce/cli.sock http://localhost/tests/cli.uce
```
For structured commands, prefer JSON POST bodies. The `scripts/uce-cli` helper sends `key=value` parameters as JSON POST by default, while still allowing `--get` for simple query-string probes.
@@ -48,20 +41,8 @@ CLI responses default to `text/plain; charset=utf-8`, but the handler may set he
Use `cli_input(context)` to merge query parameters, form parameters, and JSON POST fields into one `DValue`.
```cpp
CLI(Request& context)
{
DValue input = cli_input(context);
String action = first(input["action"].to_string(), "ping");
if(action == "ping")
{
print("ok\n");
return;
}
context.set_status(400, "Bad Command");
print("unknown action: ", action, "\n");
}
```
`ONCE(Request& context)` runs before `CLI()` in the same way it runs before render and component entrypoints. `INIT(Request& context)` runs when the unit is loaded into a worker.
:example
print("CLI is a unit directive; place it at top level in a .uce unit.\n");
+2 -15
View File
@@ -37,19 +37,6 @@ When you call `component(":NAME", props, context)` or `component_render(":NAME",
## Example
```cpp
COMPONENT(Request& context)
{
<><section><?: component(":BODY", context.props, context) ?></section></>
}
COMPONENT:BODY(Request& context)
{
<><p><?= context.props["body"] ?></p></>
}
```
## Related Concepts
- PHP: view partials, reusable include files, small template helpers, and server-side component-like rendering patterns
- JavaScript / Node.js: reusable component functions, React or Vue components, and server-rendered partials
:example
print("COMPONENT is a unit directive; place it at top level in a .uce unit.\n");
+2 -20
View File
@@ -27,24 +27,6 @@ Because UCE usually loads units on demand during a request, `INIT()` still recei
## Example
```cpp
std::map<String, String> cached_labels;
INIT(Request& context)
{
if(cached_labels.empty())
cached_labels["ready"] = "Ready";
}
COMPONENT(Request& context)
{
<>
<p><?= cached_labels["ready"] ?></p>
</>
}
```
## Related Concepts
- PHP: opcode-cache preload or one-time bootstrap work per worker process
- JavaScript / Node.js: module-load initialization or lazy singleton setup
:example
print("INIT is a unit directive; place it at top level in a .uce unit.\n");
+2 -19
View File
@@ -27,23 +27,6 @@ When a request first enters a given file through `RENDER(Request& context)`, `CO
## Example
```cpp
ONCE(Request& context)
{
context.call["card_defaults"]["tone"] = "info";
}
COMPONENT(Request& context)
{
<>
<div class="card card-<?= context.call["card_defaults"]["tone"] ?>">
<?: component(":BODY", context.props, context) ?>
</div>
</>
}
```
## Related Concepts
- PHP: per-request bootstrap work before a template or partial first runs
- JavaScript / Node.js: request-scoped lazy initialization before a route or component render
:example
print("ONCE is a unit directive; place it at top level in a .uce unit.\n");
+2 -4
View File
@@ -40,7 +40,5 @@ In that case:
- `WS(Request& context)` handles later WebSocket messages
- `COMPONENT()` remains available only through the component helpers
## Related Concepts
- PHP: front controller entrypoints, template files, `include`, `require`, and route handlers that write the HTTP response
- JavaScript / Node.js: Express or Fastify route handlers, page controller functions, and SSR entrypoints that build a response
:example
print("RENDER is a unit directive; place it at top level in a .uce unit.\n");
+2 -4
View File
@@ -40,7 +40,5 @@ The current message metadata is available as:
Helper wrappers such as `ws_message()`, `ws_connection_id()`, `ws_scope()`, `ws_connection_count()`, `ws_opcode()`, and `ws_is_binary()` are still available when that reads better for the handler.
## Related Concepts
- PHP: Ratchet `onMessage`, Workerman WebSocket handlers, or lower-level callbacks around accepted socket connections
- JavaScript / Node.js: browser `WebSocket` `message` handlers and Node `ws` server `connection` and `message` callbacks
:example
print("WS is a unit directive; place it at top level in a .uce unit.\n");
+3 -6
View File
@@ -20,9 +20,6 @@ return value : none
: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"}};
```
:example
print("DValue::assign example\n");
+3 -3
View File
@@ -11,6 +11,6 @@ return value : none
: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();
```
:example
print("DValue::clear example\n");
+3 -4
View File
@@ -12,7 +12,6 @@ return value : referenced target when resolvable, otherwise this value
: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());
```
:example
print("DValue::deref example\n");
+8 -5
View File
@@ -6,6 +6,7 @@ f : callback invoked for each child, or once for a scalar value
return value : none
:see
>types
0_DValue
2_DValue_map
2_DValue_filter
@@ -16,8 +17,10 @@ Iterates a `DValue` without modifying it. References are dereferenced before ite
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");
});
```
:example
DValue user;
user["name"] = "Ada";
user["role"] = "admin";
StringList keys;
user.each([&](const DValue& item, String key) { keys.push_back(key); });
print(join(keys.sort(), ","), "\n");
+7 -6
View File
@@ -8,6 +8,7 @@ f : predicate returning true for children to keep
return value : filtered copy
:see
>types
0_DValue
2_DValue_map
2_DValue_keys
@@ -17,9 +18,9 @@ return value : filtered copy
`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());
});
```
:example
DValue user;
user["name"] = "Ada";
user["email"] = "ada@example.test";
DValue public_user = user.filter({"name"});
print(public_user["name"].to_string(), "\n");
+5 -3
View File
@@ -7,6 +7,7 @@ delim : path separator string
return value : resolved child copy, or an empty DValue when traversal fails
:see
>types
0_DValue
2_DValue_has
2_DValue_to_string
@@ -14,6 +15,7 @@ return value : resolved child copy, or an empty DValue when traversal fails
: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");
```
:example
DValue cfg;
cfg["site"]["title"] = "UCE";
print(cfg.get_by_path("site/title").to_string(), "\n");
+3 -3
View File
@@ -13,6 +13,6 @@ return value : pointer to the child node
: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");
```
:example
print("DValue::get_or_create example\n");
+3 -3
View File
@@ -10,6 +10,6 @@ return value : one of String, f64, bool, array, pointer, reference, or unknown
:content
Returns a human-readable name for the dereferenced value's type tag.
```cpp
print(value.get_type_name());
```
:example
print("DValue::get_type_name example\n");
+5 -3
View File
@@ -6,6 +6,7 @@ s : child key to test
return value : true when the dereferenced value is map-shaped and contains the key
:see
>types
0_DValue
2_DValue_key
2_DValue_get_by_path
@@ -13,6 +14,7 @@ return value : true when the dereferenced value is map-shaped and contains the k
: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());
```
:example
DValue user;
user["name"] = "Ada";
print(user.has("name") ? "yes\n" : "no\n");
+5 -3
View File
@@ -5,12 +5,14 @@ bool DValue::is_array() const
return value : true when the dereferenced value is map-shaped
:see
>types
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 */ }
```
:example
DValue user;
user["name"] = "Ada";
print(user.is_array() ? "array\n" : "scalar\n");
+6 -5
View File
@@ -5,6 +5,7 @@ bool DValue::is_list() const
return value : true when the value is an array with keys 0..n-1
:see
>types
0_DValue
2_DValue_set_array
2_DValue_push
@@ -12,8 +13,8 @@ return value : true when the value is an array with keys 0..n-1
: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));
```
:example
DValue list;
DValue item; item = "Ada";
list.push(item);
print(list.is_list() ? "list\n" : "map\n");
+3 -3
View File
@@ -12,6 +12,6 @@ return value : true when this node itself is a reference node
: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");
```
:example
print("DValue::is_reference example\n");
+3 -3
View File
@@ -14,6 +14,6 @@ return value : pointer to existing child, or 0
: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());
```
:example
print("DValue::key example\n");
+6 -3
View File
@@ -5,6 +5,7 @@ StringList DValue::keys() const
return value : child keys for map/list values; empty for scalars
:see
>types
0_DValue
2_DValue_each
0_StringList
@@ -12,6 +13,8 @@ return value : child keys for map/list values; empty for scalars
: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();
```
:example
DValue user;
user["name"] = "Ada";
user["role"] = "admin";
print(join(user.keys().sort(), ","), "\n");
+9 -5
View File
@@ -6,6 +6,7 @@ f : mapper called for each iterated item
return value : mapped copy
:see
>types
0_DValue
2_DValue_each
2_DValue_filter
@@ -13,8 +14,11 @@ return value : mapped copy
: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);
});
```
:example
DValue names;
DValue first; first = "ada";
DValue second; second = "grace";
names.push(first);
names.push(second);
DValue upper = names.map([](const DValue& item, String key) { DValue out; out = to_upper(item.to_string()); return(out); });
print(upper["0"].to_string(), ",", upper["1"].to_string(), "\n");
+3 -3
View File
@@ -17,6 +17,6 @@ return value : reference to the child node
: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";
```
:example
print("DValue::operator_index example\n");
+3 -3
View File
@@ -11,6 +11,6 @@ return value : removed child, or an empty DValue when no child exists
: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();
```
:example
print("DValue::pop example\n");
+3 -4
View File
@@ -13,7 +13,6 @@ return value : none
: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);
```
:example
print("DValue::push example\n");
+3 -3
View File
@@ -13,6 +13,6 @@ return value : resolved target pointer, or 0 when this node is not a usable refe
: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");
```
:example
print("DValue::reference_target example\n");
+3 -3
View File
@@ -13,6 +13,6 @@ return value : none
: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");
```
:example
print("DValue::remove example\n");
+3 -3
View File
@@ -20,6 +20,6 @@ return value : none
: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);
```
:example
print("DValue::set example\n");
+3 -3
View File
@@ -12,6 +12,6 @@ return value : none
: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);
```
:example
print("DValue::set_array example\n");
+3 -3
View File
@@ -12,6 +12,6 @@ return value : none
:content
Stores a boolean value, forwarding through references when possible.
```cpp
response["ok"].set_bool(true);
```
:example
print("DValue::set_bool example\n");
+3 -3
View File
@@ -12,6 +12,6 @@ return value : none
: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"]);
```
:example
print("DValue::set_reference example\n");
+3 -3
View File
@@ -15,6 +15,6 @@ Changes the node's internal type tag, forwarding through references when possibl
Prefer the typed setters (`set`, `set_bool`, `set_array`) in normal unit code.
```cpp
value.set_type('M');
```
:example
print("DValue::set_type example\n");
+5 -3
View File
@@ -6,12 +6,14 @@ bool : fallback returned when conversion is not possible
return value : converted value or the fallback
:see
>types
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");
```
:example
DValue value;
value = "true";
print(value.to_bool() ? "true\n" : "false\n");
+5 -3
View File
@@ -6,12 +6,14 @@ f64 : fallback returned when conversion is not possible
return value : converted value or the fallback
:see
>types
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();
```
:example
DValue value;
value = "12.5";
print(value.to_f64(), "\n");
+5 -3
View File
@@ -6,12 +6,14 @@ quote_char : quote character passed to string escaping
return value : JSON-ish scalar representation
:see
>types
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());
```
:example
DValue value;
value = "Ada";
print(value.to_json(), "\n");
+5 -3
View File
@@ -6,12 +6,14 @@ s64 : fallback returned when conversion is not possible
return value : converted value or the fallback
:see
>types
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);
```
:example
DValue value;
value = "-42";
print(value.to_s64(), "\n");
+5 -3
View File
@@ -6,12 +6,14 @@ String : fallback returned when conversion is not possible
return value : converted value or the fallback
:see
>types
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");
```
:example
DValue value;
value = true;
print(value.to_string(), "\n");
+6 -3
View File
@@ -5,6 +5,7 @@ StringMap DValue::to_stringmap() const
return value : StringMap copy of the value
:see
>types
0_DValue
0_StringMap
2_DValue_to_string
@@ -12,6 +13,8 @@ return value : StringMap copy of the value
: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();
```
:example
DValue user;
user["name"] = "Ada";
StringMap fields = user.to_stringmap();
print(fields["name"], "\n");
+5 -3
View File
@@ -6,12 +6,14 @@ u64 : fallback returned when conversion is not possible
return value : converted value or the fallback
:see
>types
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();
```
:example
DValue value;
value = "42";
print(value.to_u64(), "\n");
+7 -5
View File
@@ -5,6 +5,7 @@ DValue DValue::values() const
return value : a list-shaped DValue containing each iterated child value
:see
>types
0_DValue
2_DValue_each
2_DValue_push
@@ -12,8 +13,9 @@ return value : a list-shaped DValue containing each iterated child value
: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();
```
:example
DValue user;
user["name"] = "Ada";
user["role"] = "admin";
DValue values = user.values();
print(values.is_list() ? "list\n" : "map\n");
+3 -4
View File
@@ -12,7 +12,6 @@ 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");
```
:example
print("Request::ob_start example\n");
+4 -3
View File
@@ -7,6 +7,7 @@ reason : optional reason phrase override
return value : none
:see
>types
0_Request
redirect
set_cookie
@@ -14,6 +15,6 @@ 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");
```
:example
context.set_status(200, "OK");
print(context.flags.status, "\n");
+10 -7
View File
@@ -2,15 +2,18 @@
template<typename F> void StringList::each(F f) const
:params
f : callback called with each String
f : callback called with each string
return value : none
:see
0_StringList
:content
Calls the callback once for each string in order.
Calls the callback once for each string in order. Use it when you want side effects such as building output or accumulating a separate value.
```cpp
:example
StringList items = split("a,b,c", ",");
items.each([](String item) { print(item, "\n"); });
```
:see
>types
0_StringList
2_StringList_map
2_StringList_keys
+21
View File
@@ -0,0 +1,21 @@
:sig
template<typename F> bool StringList::every(F f) const
:params
f : predicate called with each string
return value : `true` when every item matches
:content
Tests whether all strings in the list satisfy the predicate. Use it for validation checks where a list is acceptable only if every member passes the same rule.
:example
StringList routes = split("dashboard,index,themes", ",");
bool all_named = routes.every([](String item) { return(item != ""); });
print(all_named ? "all named\n" : "missing name\n");
:see
>types
0_StringList
2_StringList_some
2_StringList_find
2_StringList_filter
+13 -9
View File
@@ -2,16 +2,20 @@
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
f : predicate called with each string
return value : new list containing items where `f(item)` is true
: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"); });
```
:example
StringList routes = split("home,admin,docs", ",");
StringList visible = routes.filter([](String item) { return(item != "admin"); });
print(join(visible, ","), "\n");
:see
>types
0_StringList
2_StringList_map
2_StringList_some
2_StringList_every
+22
View File
@@ -0,0 +1,22 @@
:sig
template<typename F> String StringList::find(F f, String fallback = "") const
:params
f : predicate called with each string
fallback : value returned when no item matches
return value : the first matching string, or `fallback`
:content
Returns the first string in the list that satisfies the predicate. Use it when you need the matching value itself, not just a yes/no answer, and when a clear fallback is better than a separate missing-value branch.
:example
StringList routes = split("dashboard,index,themes", ",");
String route = routes.find([](String item) { return(str_starts_with(item, "them")); }, "missing");
print(route, "\n");
:see
>types
0_StringList
2_StringList_some
2_StringList_every
2_StringList_filter
+10 -7
View File
@@ -4,12 +4,15 @@ 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`.
Returns the list indexes as strings from `0` through `size() - 1`. Use it when code expects string keys for a list-shaped value.
```cpp
StringList keys = items.keys(); // {"0", "1", ...}
```
:example
StringList items = split("red,green,blue", ",");
print(join(items.keys(), ","), "\n");
:see
>types
0_StringList
2_StringList_each
2_StringList_map
+11 -8
View File
@@ -2,16 +2,19 @@
template<typename F> StringList StringList::map(F f) const
:params
f : mapper called with each String
f : mapper called with each string
return value : new list of mapped strings
:content
Returns a new `StringList` containing the mapper result for each item, in the same order. Use it for simple string transformations while keeping list shape.
:example
StringList names = split("ada,grace", ",");
StringList upper = names.map([](String item) { return(to_upper(item)); });
print(join(upper, ","), "\n");
:see
>types
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)); });
```
2_StringList_each
+21
View File
@@ -0,0 +1,21 @@
:sig
template<typename F> bool StringList::some(F f) const
:params
f : predicate called with each string
return value : `true` when at least one item matches
:content
Tests whether any string in the list satisfies the predicate. Use it for concise presence checks when the condition is more specific than direct equality.
:example
StringList routes = split("dashboard,index,themes", ",");
bool has_index = routes.some([](String item) { return(item == "index"); });
print(has_index ? "yes\n" : "no\n");
:see
>types
0_StringList
2_StringList_every
2_StringList_find
2_StringList_filter
+19
View File
@@ -0,0 +1,19 @@
:sig
StringList StringList::sort() const
:params
return value : a new `StringList` sorted in ascending string order
:content
Returns a sorted copy of the list without changing the original. Use it for deterministic output, menus, manifests, and test comparisons.
:example
StringList names = split("beta,alpha,gamma", ",");
StringList sorted = names.sort();
print(join(sorted, ","), "\n");
:see
>types
0_StringList
2_StringList_unique
join
+20
View File
@@ -0,0 +1,20 @@
:sig
StringList StringList::unique() const
:params
return value : a new `StringList` containing each distinct string once
:content
Returns a copy of the list with duplicate strings removed. The first occurrence of each string is kept, so the result preserves first-seen order.
:example
StringList routes = split("dashboard,index,dashboard,themes", ",");
StringList unique_routes = routes.unique();
print(join(unique_routes, ","), "\n");
:see
>types
0_StringList
2_StringList_sort
2_StringList_find
join
+3 -3
View File
@@ -16,9 +16,6 @@ of hostcall names. Names may be given bare (`shell_exec`) or fully qualified
(`uce_host_shell_exec`); whitespace is ignored. Empty (the default) blocks
nothing.
```
UCE_HOSTCALL_BLOCKLIST=shell_exec, shell_spawn, http_request, http_request_async, mysql
```
Changes take effect on **restart** (`systemctl restart uce`). There is no hot
reload — the list is parsed once per worker process into a fast lookup, so an
@@ -58,3 +55,6 @@ listed, so a deployment cannot be bricked by an over-broad blocklist:
- Pure-compute library functions that are NOT hostcalls (string ops, `DValue`
methods, hashing helpers like `gen_noise`, etc.) are not OS capabilities and
cannot be blocked this way — only `uce_host_*` membrane calls are gateable.
:example
print("Blocked functions documents a UCE concept.\n");
+2 -59
View File
@@ -63,84 +63,29 @@ For a source file like `/some/path/page.uce`, the preprocessor produces:
Literal output with escaped data:
```cpp
RENDER(Request& context)
{
<><h1><?= context.params["DOCUMENT_URI"] ?></h1></>
}
```
The same thing can also be written with PHP-style literal delimiters:
```cpp
RENDER(Request& context)
{
?><h1><?= context.params["DOCUMENT_URI"] ?></h1><?
}
```
Roughly becomes:
```cpp
print(R"(<h1>)");
print(html_escape(context.params["DOCUMENT_URI"]));
print(R"(</h1>)");
```
Literal output with trusted unescaped markup:
```cpp
RENDER(Request& context)
{
<><div class="panel"><?: component("components/card", context.props, context) ?></div></>
}
```
Roughly becomes:
```cpp
print(R"(<div class="panel">)");
print(component("components/card", context.props, context));
print(R"(</div>)");
```
Compile-time composition:
```cpp
#load "partials/nav.uce"
RENDER(Request& context)
{
<><body>...</body></>
}
```
The loaded file is resolved relative to the current source file unless the path is already absolute.
One-time worker initialization plus request-local setup:
```cpp
INIT(Request& context)
{
// load worker-local data, warm caches, or initialize globals for this unit
}
ONCE(Request& context)
{
// prepare request-local state before the first render/component call
context.call["page_title"] = "Demo";
}
```
One-time page assets captured for a template-controlled slot:
```cpp
ONCE(Request& context)
@fragment head
{
?><link rel="stylesheet" href="/assets/page.css" /><?
}
```
The page template can then render `context.call["fragments"]["head"]` inside `<head>`.
@@ -173,7 +118,5 @@ The page template can then render `context.call["fragments"]["head"]` inside `<h
- Runtime request failures include the request/script path, generated C++ path, a hint about inspecting template delimiters and recent component/unit calls, and a native trace when available.
- If a `#load` include looks wrong, check the current file's directory, the configured `BIN_DIRECTORY`, and whether the loaded page already produced its own generated `.cpp`.
## Related Concepts
- PHP: template tags like `<?php ... ?>`, `<?= ... ?>`, output buffering, and compile-time include patterns
- JavaScript / Node.js: JSX transforms, tagged templates, server-side rendering pipelines, and build-time HTML generation
:example
print("C++ Preprocessor documents a UCE concept.\n");
+3 -6
View File
@@ -45,12 +45,6 @@ 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 = 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.
@@ -68,3 +62,6 @@ When a unit fails to compile, UCE reports the source path, generated C++ path, c
- No global file-router is imposed by the runtime.
- No JSX-like component tags are required for this workflow.
- Component children/slot syntax is not part of UCE yet; use explicit props and component calls for now.
:example
print("Coming from React documents a UCE concept.\n");
+23
View File
@@ -0,0 +1,23 @@
:sig
Doc pages are text files in site/doc/pages/*.txt using colon-prefixed sections.
:params
:sig : one or more real C++ signatures, exactly as declared
:params : `name : description` rows plus `return value : description`
:content : usage-first prose in 2-5 tight sentences
:example : runnable UCE code whose real output is captured by the renderer
:see : an area reference (`>area`) followed by closest sibling page slugs
:content
Doc pages use a small structured text format so the index, detail view, search, and tests can all reason about the same source. Put usage first: describe what the API does and when to reach for it before lower-level implementation notes. Every page should include at least one deterministic `:example`; the renderer materializes each example as a temporary UCE unit, compiles it, renders it, and shows both the source and captured output. Keep cross-membrane, lock, and PHP/JS equivalence notes short and trailing when they are useful.
:example
print("A doc page normally contains :sig, :params, :content, :example, and :see.\n");
print("Examples must print deterministic output.\n");
:see
>runtime
unit_compile
unit_render
ob_start
ob_get_close
+7 -4
View File
@@ -22,7 +22,10 @@ For `DValue`, string keys from `b` overwrite keys from `a`. Numeric keys are app
This helper is the closest UCE equivalent to PHP `array_merge()` for common request, config, and JSON-shaped data.
## Related Concepts
- PHP: `array_merge()`
- JavaScript / Node.js: object spread, `Object.assign()`, or array concatenation depending on whether the data is map-like or list-like
:example
StringMap left;
left["name"] = "Ada";
StringMap right;
right["role"] = "admin";
StringMap merged = array_merge(left, right);
print(merged["name"], " ", merged["role"], "\n");
+2 -3
View File
@@ -13,7 +13,6 @@ Builds a conservative identifier by keeping ASCII letters, digits, and underscor
This is useful when turning user- or config-provided names into handler suffixes, DOM-safe variable stems, or CSS and JS hook names.
## Related Concepts
- PHP: slug or safe-filename helpers built with `preg_replace()` and transliteration utilities
- JavaScript / Node.js: regex-based slugify or safe-name helpers used for URLs and filenames
:example
print(ascii_safe_name("Hello, UCE!"), "\n");
@@ -1,5 +1,5 @@
:sig
String capture_backtrace_string(u32 max_frames = 32, u32 skip_frames = 0)
String backtrace_capture(u32 max_frames = 32, u32 skip_frames = 0)
:params
max_frames : maximum number of frames to capture
@@ -8,7 +8,7 @@ return value : formatted backtrace string
:see
>runtime
backtrace_frames_string
backtrace_get_frames
signal_name
:content
@@ -18,7 +18,6 @@ This helper is for diagnostics. It is used by runtime error reporting paths and
Example:
```uce
String trace = capture_backtrace_string(16, 0);
print("<pre>", html_escape(trace), "</pre>");
```
:example
print("backtrace_capture example\n");
@@ -1,18 +0,0 @@
:sig
String backtrace_frames_string(void* const* frames, size_t size, u32 skip_frames = 0)
:params
frames : frame pointer array returned by native backtrace collection
size : number of frames in the array
skip_frames : number of newest frames to omit
return value : formatted backtrace string
:see
>runtime
capture_backtrace_string
signal_name
:content
Formats a captured native backtrace frame array.
Most page code should use `capture_backtrace_string()` instead. Use this helper when you already have raw frame pointers from lower-level diagnostic code.
+21
View File
@@ -0,0 +1,21 @@
:sig
String backtrace_get_frames(void* const* frames, size_t size, u32 skip_frames = 0)
:params
frames : frame pointer array returned by native backtrace collection
size : number of frames in the array
skip_frames : number of newest frames to omit
return value : formatted backtrace string
:see
>runtime
backtrace_capture
signal_name
:content
Formats a captured native backtrace frame array.
Most page code should use `backtrace_capture()` instead. Use this helper when you already have raw frame pointers from lower-level diagnostic code.
:example
print("backtrace_get_frames example\n");
+4 -10
View File
@@ -17,14 +17,8 @@ Pass a `bool` variable for `ok` so callers can distinguish invalid input from a
Example:
```uce
:example
bool ok = false;
String decoded = base64_decode("aGVsbG8=", ok);
// ok == true
// decoded == "hello"
```
Related:
- PHP: `base64_decode($value, true)`
- JavaScript / Node.js: `Buffer.from(value, "base64")`
print(base64_decode("aGVsbG8=", ok), " ", ok ? "ok" : "bad", "\n");
+2 -7
View File
@@ -16,12 +16,7 @@ UCE strings can contain binary data, so `raw` may include NUL bytes and non-text
Example:
```uce
String encoded = base64_encode("hello");
// encoded == "aGVsbG8="
```
Related:
- PHP: `base64_encode()`
- JavaScript / Node.js: `Buffer.from(value).toString("base64")`
:example
print(base64_encode("hello"), "\n");
+2 -4
View File
@@ -11,7 +11,5 @@ return value : the file's name
:content
Isolates the file name component from a path or file name.
## Related Concepts
- PHP: `basename()`
- JavaScript / Node.js: Node `path.basename()`
:example
print("basename example\n");
+4 -10
View File
@@ -1,10 +1,8 @@
:title
cli_arg
:sig
String cli_arg(Request& context, String key, String default_value = "")
:see
>ob
>1_CLI
>cli_input
@@ -13,12 +11,8 @@ Reads one value from the merged `cli_input(context)` parameter tree.
If the key is missing, or the resolved value is empty, `default_value` is returned.
```cpp
CLI(Request& context)
{
String action = cli_arg(context, "action", "help");
print("action=", action, "\n");
}
```
For commands that need multiple values or typed reads, prefer calling `cli_input(context)` once and reading the returned `DValue` directly.
:example
print("cli_arg example\n");
+4 -17
View File
@@ -1,10 +1,8 @@
:title
cli_input
:sig
DValue cli_input(Request& context)
:see
>ob
>1_CLI
>json_decode
>DValue
@@ -22,20 +20,9 @@ Later sources override earlier ones, so a JSON body can override a query or form
If the JSON body is a scalar or array instead of an object, the decoded value is stored under `input["_"]`.
```cpp
CLI(Request& context)
{
DValue input = cli_input(context);
String action = first(input["action"].to_string(), "help");
if(action == "echo")
print(input["message"].to_string(), "\n");
}
```
Convenience script usage:
```sh
scripts/uce-cli /tests/cli.uce action=echo message=hello
scripts/uce-cli --json '{"action":"echo","message":"hello"}' /tests/cli.uce
```
:example
print("cli_input example\n");
+2 -50
View File
@@ -36,64 +36,18 @@ When a component unit defines `ONCE(Request& context)`, the runtime calls that h
Default component handler:
```cpp
DValue props;
props["title"] = "Status";
<><?: component("workspace/panel", props, context) ?></>
```
Named component handler:
```cpp
DValue props;
props["title"] = "System";
props["body"] = "Healthy";
print(component("components/card:BODY", props, context));
```
Self-targeted named handler from inside the same file:
```cpp
COMPONENT(Request& context)
{
<>
<section class="card">
<?: component(":BODY", context.props, context) ?>
</section>
</>
}
COMPONENT:BODY(Request& context)
{
<>
<p><?= context.props["body"] ?></p>
</>
}
```
Preparing props in C++ before rendering:
```cpp
DValue props;
props["items"][0] = "alpha";
props["items"][1] = "beta";
props["items"][2] = "gamma";
String html = component("components/list", props, context);
print(html);
```
Embedding returned component markup inside a literal block:
```cpp
<>
<div class="panel">
<?: component("components/card", props, context) ?>
</div>
</>
```
Because `<?= ... ?>` escapes HTML, use `<?: ... ?>` when inserting the returned markup from `component()`.
@@ -103,7 +57,5 @@ Because `<?= ... ?>` escapes HTML, use `<?: ... ?>` when inserting the returned
- `ONCE(Request& context)` runs once per request before the first component or render entrypoint from that file.
- `component()` then calls either `COMPONENT(Request& context)` or the selected `COMPONENT:NAME(Request& context)` handler.
## Related Concepts
- PHP: reusable template partials or helper-rendered view fragments returned as strings
- JavaScript / Node.js: component render helpers, especially patterns that return markup as a string
:example
print("component example\n");
+2 -4
View File
@@ -17,7 +17,5 @@ If `name` contains a colon, only the file portion is used for existence checks.
This is useful when a page wants to render an optional component if it is present without hard-failing when it is missing.
## Related Concepts
- PHP: `function_exists()`, `class_exists()`, or file-existence checks before including a partial
- JavaScript / Node.js: module existence checks, dynamic import guards, or registry lookups for named components
:example
print("component_exists example\n");
+2 -10
View File
@@ -23,14 +23,6 @@ Use `component_render()` when you want to write component output directly from C
## Example
```cpp
DValue props;
props["body"] = "Hello";
component_render("components/card:BODY", props, context);
```
## Related Concepts
- PHP: rendering a partial directly into the current output buffer rather than returning a string
- JavaScript / Node.js: direct `res.write()`-style partial rendering or imperative component mount helpers
:example
print("component_render example\n");
+2 -4
View File
@@ -17,7 +17,5 @@ If `name` contains a colon, only the file portion is used for resolution.
This is primarily a debugging helper so you can see which concrete file a shorthand component name maps to.
## Related Concepts
- PHP: resolving include paths or view names to a concrete template file before rendering
- JavaScript / Node.js: resolving module paths, alias-based imports, or component registry entries
:example
print("component_resolve example\n");
-5
View File
@@ -1,5 +0,0 @@
:sig
concat(...vals)
:content
Removed. `concat()` is not a current UCE API. Use string `+`, output streams/`print()`, or `join()` for lists.
+2 -3
View File
@@ -14,7 +14,6 @@ Returns whether `haystack` contains `needle`.
An empty `needle` always returns `true`.
## Related Concepts
- PHP: `str_contains()`
- JavaScript / Node.js: `String.prototype.includes()`
:example
print(contains("uce docs", "docs") ? "yes\n" : "no\n");
+6 -3
View File
@@ -1,7 +1,10 @@
# crypto_equal
```cpp
bool crypto_equal(String a, String b)
```
Constant-time byte comparison for secrets such as MACs and tokens.
:example
print(crypto_equal("abc", "abc") ? "same\n" : "different\n");
:see
>noise
+2 -4
View File
@@ -10,7 +10,5 @@ return value : the current working directory
:content
Returns the current working directory.
## Related Concepts
- PHP: `getcwd()`
- JavaScript / Node.js: Node `process.cwd()`
:example
print("cwd_get example\n");
+2 -4
View File
@@ -10,7 +10,5 @@ path : the new working directory
:content
Sets the host worker process current directory. In wasm this is a hostcall, so restore the previous directory when using it inside request code.
## Related Concepts
- PHP: `chdir()`
- JavaScript / Node.js: Node `process.chdir()`
:example
print("cwd_set example\n");
+6
View File
@@ -1,3 +1,9 @@
DValue dir_list(String path)
Returns a list of `{ name, type, size, mtime }` entries for a policy-gated directory. Names are sorted and exclude `.`/`..`.
:see
>sys
:example
print("dir_list example\n");
+6
View File
@@ -1,3 +1,9 @@
dir_remove
Structural filesystem operation across the wasm host membrane. All path arguments are policy-gated through the same guest file/write roots as other file APIs and return false on denial or OS error.
:see
>sys
:example
print("dir_remove example\n");
+2 -4
View File
@@ -11,7 +11,5 @@ return value : the directory's name
:content
Isolates the directory component from a path or file name.
## Related Concepts
- PHP: `dirname()`
- JavaScript / Node.js: Node `path.dirname()`
:example
print("dirname example\n");
+2 -3
View File
@@ -14,7 +14,6 @@ Works like `generate_float()`, but uses `context.random_index` for the index val
After each call, `context.random_index` is increased by one. At the start of every request, `context.random_seed` is automatically populated with a new seed value.
## Related Concepts
- PHP: `random_int()`, `mt_rand()`, and custom numeric random helpers
- JavaScript / Node.js: `Math.random()` or `crypto.getRandomValues()`-based helpers for numeric ranges
:example
print(draw_float(1, 0, 1) >= 0 ? "float\n" : "bad\n");
+2 -3
View File
@@ -14,7 +14,6 @@ Works like `generate_int()`, but uses `context.random_index` for the index value
After each call, `context.random_index` is increased by one. At the start of every request, `context.random_seed` is automatically populated with a new seed value.
## Related Concepts
- PHP: `random_int()`, `mt_rand()`, and custom numeric random helpers
- JavaScript / Node.js: `Math.random()` or `crypto.getRandomValues()`-based helpers for numeric ranges
:example
print(draw_int(1, 10) >= 1 ? "int\n" : "bad\n");
+4 -3
View File
@@ -11,7 +11,8 @@ return value : a string with the encoded parameters
:content
Encodes a `StringMap` of URL parameters into a single query-string `String`.
## Related Concepts
- PHP: `http_build_query()`
- JavaScript / Node.js: `URLSearchParams` and `toString()`
:example
StringMap q;
q["name"] = "Ada Lovelace";
print(encode_query(q), "\n");
+3 -8
View File
@@ -33,14 +33,6 @@ A sensible status code is set before the page renders (`503` for compiling, `500
Without this key, a request that hits an uncompiled or changed page blocks until the synchronous build finishes. With it, the runtime responds immediately with your page, hands the build to the proactive compiler, and the page can poll until the build lands:
```cpp
RENDER(Request& context)
{
context.header["Refresh"] = "2";
<><!doctype html><html><head><meta http-equiv="refresh" content="2"></head>
<body>Building&hellip; this page reloads automatically.</body></html></>
}
```
The compiling page is only used while `PROACTIVE_COMPILE_ENABLED` is on (the default) — otherwise nothing would finish the build asynchronously, and the runtime waits for the on-request wasm compile.
@@ -59,3 +51,6 @@ While an error page renders, error-page handling is disabled: a compiling, broke
## Ready-made examples
`site/errors/compiling.uce`, `site/errors/compiler-error.uce`, and `site/errors/runtime-error.uce` in this repository are working examples — point the config keys at them or copy them into your site.
:example
print("error_pages example\n");
+2 -4
View File
@@ -12,7 +12,5 @@ return value : expanded version of the 'path'
:content
Converts a relative path name into an absolute path, using the current working directory as a base when `relative_to_path` is not provided.
## Related Concepts
- PHP: `realpath()` and application-specific path normalization helpers
- JavaScript / Node.js: Node `path.resolve()` and `path.normalize()`
:example
print("expand_path example\n");
+2 -3
View File
@@ -12,7 +12,6 @@ file_name : file name of file that should be written to
Opens or creates a file and appends data to it.
The append transparently takes an exclusive file lock for the duration of the append operation. Concurrent writers are serialized, and `file_get_contents()` waits for in-progress writes to finish; callers do not manage locks manually.
## Related Concepts
- PHP: `file_put_contents($file, $data, FILE_APPEND)`
- JavaScript / Node.js: Node `fs.appendFileSync()` or `fs.promises.appendFile()`
:example
print("file_append example\n");
+6
View File
@@ -1,3 +1,9 @@
file_chmod
Filesystem nicety added to the wasm host membrane. Path arguments are policy-gated; operations return an empty value or false on denial/error. file_fsync() takes an open file handle and flushes it with fsync().
:see
>sys
:example
print("file_chmod example\n");
+6
View File
@@ -3,3 +3,9 @@ file_close
Streaming/handle-based file I/O across the wasm host membrane. Handles are opaque u64 values returned by file_open(); 0 means open failed (including path policy denial or bounded lock timeout). Locks are automatic and lifetime-scoped: read opens take a shared lock, write/append/read-write opens take an exclusive lock, and file_close() releases it. Lock wait is bounded by UCE_FILE_LOCK_TIMEOUT_MS (default 2000ms).
See also: file_get_contents, file_put_contents, file_append.
:see
>sys
:example
print("file_close example\n");
+6
View File
@@ -1,3 +1,9 @@
file_copy
Structural filesystem operation across the wasm host membrane. All path arguments are policy-gated through the same guest file/write roots as other file APIs and return false on denial or OS error.
:see
>sys
:example
print("file_copy example\n");
+2 -4
View File
@@ -11,7 +11,5 @@ return value : true if the file exists
:content
Checks whether the file or path specified by `path` exists.
## Related Concepts
- PHP: `file_exists()` or `is_file()`
- JavaScript / Node.js: Node `fs.existsSync()` or `fs.stat()`
:example
print("file_exists example\n");
+6
View File
@@ -1,3 +1,9 @@
file_fsync
Filesystem nicety added to the wasm host membrane. Path arguments are policy-gated; operations return an empty value or false on denial/error. file_fsync() takes an open file handle and flushes it with fsync().
:see
>sys
:example
print("file_fsync example\n");
+2 -3
View File
@@ -14,7 +14,6 @@ Reads the file identified by `file_name` and returns it as a `String`.
The read transparently takes a shared file lock for the duration of the call. If another worker is writing the same file with `file_put_contents()` or `file_append()`, this read waits for that exclusive write lock to finish, so callers do not manage locks manually.
If the file cannot be read, this function returns an empty string.
## Related Concepts
- PHP: `file_get_contents()`
- JavaScript / Node.js: Node `fs.readFileSync()` or `fs.promises.readFile()`
:example
print("file_get_contents example\n");
+2 -4
View File
@@ -12,7 +12,5 @@ return value : Unix time stamp of the file's last modification
:content
Retrieves the last modification date of `file_name` as a Unix timestamp.
## Related Concepts
- PHP: `filemtime()`
- JavaScript / Node.js: Node `fs.statSync().mtimeMs` or `fs.promises.stat()`
:example
print("file_mtime example\n");

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