cleanup, docs
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -46,5 +46,4 @@ dir_remove
|
||||
file_temp
|
||||
file_chmod
|
||||
file_symlink
|
||||
file_readlink
|
||||
file_fsync
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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,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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
@@ -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");
|
||||
|
||||
@@ -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.
|
||||
@@ -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");
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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.
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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… 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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user