W7 done done

This commit is contained in:
root 2026-06-15 10:53:36 +00:00
parent f5637bb587
commit 04745f39a8
35 changed files with 323 additions and 60 deletions

View File

@ -2,7 +2,7 @@
## Current State
This is in the early stages of development. Don't use this for anything important (or at all)!
This is in the early stages of development. Don't use this for anything important!
## Overview
@ -29,7 +29,7 @@ RENDER(Request& context)
}
```
The runtime is still experimental.
*The runtime is still experimental. This is not production-ready. Use at your own risk!*
## Build
@ -537,4 +537,4 @@ For up-to-date usage, prefer:
## AI Disclosure
This project is largely human-made, with all the typical idiosyncracies of my projects clearly visible. However, OpenAI Codex was used for code review and documentation. Claude Opus was used for UI design work, and I used VS Code's git commit message generator.
This project is largely human-made, with all the typical idiosyncracies of my projects clearly visible. However, OpenAI Codex was used for code review, debugging, API integration work, and documentation. Claude Opus was used for UI design work, and I used VS Code's git commit message generator.

View File

@ -2,7 +2,6 @@ String Functions
ascii_safe_name
contains
concat
filter
first
join

View File

@ -11,7 +11,7 @@ component_resolve
:content
Checks whether a component file can be resolved from the current page context.
Resolution tries the exact name first and then the `components/` shorthand form.
Resolution uses the same host resolver as `component_resolve()`: candidate bases include absolute targets, the entry unit directory, the current unit directory, and the site root; each base tries exact, `.uce`, `components/name`, and `components/name.uce` forms.
If `name` contains a colon, only the file portion is used for existence checks.

View File

@ -11,7 +11,7 @@ component_render
:content
Resolves a component name to the concrete `.uce` file path that will be loaded.
Resolution tries the exact file name first, then the same name with `.uce` appended, and then the same two forms under the `components/` prefix.
Resolution searches host-side candidate bases in order: absolute target when supplied, entry unit directory, current unit directory, and site root. Within each base it tries the exact file name, the same name with `.uce` appended, and the same two forms under the `components/` prefix.
If `name` contains a colon, only the file portion is used for resolution.

View File

@ -1,16 +1,5 @@
:sig
String concat(...vals)
:params
...val : one or more values that should be concatenated
:see
>string
concat(...vals)
:content
Returns a string with all parameters concatenated into one result.
## Related Concepts
- PHP: string concatenation with `.` or helpers like `implode()`
- JavaScript / Node.js: string concatenation with `+`, template literals, or `Array.prototype.join()`
Removed. `concat()` is not a current UCE API. Use string `+`, output streams/`print()`, or `join()` for lists.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,7 +8,7 @@ path : the new working directory
>sys
:content
Sets a new working directory.
Sets the host worker process current directory. In wasm this is a real hostcall, so restore the previous directory when using it inside request code.
## Related Concepts

View File

@ -0,0 +1,12 @@
:sig
void file_close_locked(int fd)
:params
fd : handle returned by `file_open_locked()`
:see
>sys
>file_open_locked
:content
Releases the flock and closes a locked file handle. Wasm handles are opaque and request-local.

View File

@ -0,0 +1,14 @@
:sig
String file_get_contents_locked_fd(int fd)
:params
fd : handle returned by `file_open_locked()`
return value : complete file contents, or an empty string on error/empty file
:see
>sys
>file_open_locked
>file_put_contents_locked_fd
:content
Reads the full contents of a locked file handle from the start of the file.

View File

@ -0,0 +1,18 @@
:sig
int file_open_locked(String file_name, int open_flags, int lock_type = LOCK_SH, int create_mode = 0644, f64 wait_timeout_seconds = 3.0, String purpose = "")
:params
file_name : path to open
open_flags : host open(2) flags
lock_type : `LOCK_SH` or `LOCK_EX`
create_mode : mode used when creating
return value : opaque locked file handle, or -1
:see
>sys
>file_close_locked
>file_get_contents_locked_fd
>file_put_contents_locked_fd
:content
Opens and locks a file on the host. In wasm units the returned integer is an opaque worker-owned handle that is valid only for the current request.

View File

@ -0,0 +1,15 @@
:sig
bool file_put_contents_locked_fd(int fd, String content)
:params
fd : handle returned by `file_open_locked()`
content : bytes to write
return value : true on complete write
:see
>sys
>file_open_locked
>file_get_contents_locked_fd
:content
Truncates and rewrites the file behind a locked file handle.

View File

@ -0,0 +1,12 @@
:sig
void file_release_process_locks(String reason = "")
:params
reason : diagnostic reason for releasing locks
:see
>sys
>file_open_locked
:content
Releases locked file handles owned by the current process/workspace. Wasm uses this to close all request-local locked file handles.

View File

@ -0,0 +1,13 @@
:sig
String memcache_escape_key(String key)
:params
key : application key
return value : memcache-safe key string
:see
>sys
>memcache_escape_keys
:content
Normalizes whitespace in a memcache key to underscores before it is placed into a text protocol command.

View File

@ -0,0 +1,13 @@
:sig
StringList memcache_escape_keys(StringList keys)
:params
keys : list of application keys
return value : escaped keys
:see
>sys
>memcache_escape_key
:content
Applies `memcache_escape_key()` to each key in a list.

View File

@ -0,0 +1,14 @@
:sig
bool path_is_within(String path, String root)
:params
path : path to test
root : containing directory
return value : true when both paths canonicalize and `path` is equal to or inside `root`
:see
>sys
>path_real
:content
Performs canonical containment on the host. It resolves `..`, symlinks, and trailing slash differences before comparing, so `/foo/bar2` is not considered inside `/foo/bar`.

View File

@ -0,0 +1,13 @@
:sig
String path_real(String path)
:params
path : filesystem path
return value : canonical absolute path, or an empty string when the path cannot be resolved
:see
>sys
>path_is_within
:content
Returns the host canonical path using `realpath()`. Relative paths resolve according to the worker process current directory.

View File

@ -0,0 +1,13 @@
:sig
String process_start_directory()
:params
return value : worker process start directory
:see
>sys
>cwd_get
>cwd_set
:content
Returns the directory captured when the native process started. It remains stable even if `cwd_set()` changes the current working directory.

View File

@ -0,0 +1,12 @@
:sig
DValue request_perf()
:params
return value : performance snapshot for the active request/workspace
:see
>sys
>time_precise
:content
Returns a DValue with timing and process metadata such as worker pid, parent pid, request count, request start times, and workspace birth timing when available.

View File

@ -0,0 +1,14 @@
:sig
DValue request_route_from_raw_path(String raw_path, String default_path = "index")
:params
raw_path : raw route path from a request or query string
default_path : route used for an empty path
return value : route metadata DValue
:see
>request
>route_path_sanitize
:content
Normalizes and validates a raw route path. The returned DValue includes route fields used by request context population, including sanitized path and validity metadata.

View File

@ -0,0 +1,14 @@
:sig
String runtime_safe_key(String key, String label = "runtime key")
:params
key : caller-provided key
label : diagnostic label used by native error handling
return value : stable SHA-1 key, or an empty string for empty input in wasm
:see
>sys
>gen_sha1
:content
Trims a runtime key and converts it to a stable SHA-1 identifier suitable for task/runtime file names.

View File

@ -0,0 +1,13 @@
:sig
String safe_name(String raw)
:params
raw : arbitrary display/name text
return value : normalized safe name
:see
>string
>ascii_safe_name
:content
Returns a filesystem/identifier-friendly name while preserving more Unicode input than `ascii_safe_name()` where supported.

View File

@ -3,18 +3,18 @@ bool unit_compile(String path = "")
:params
path : optional UCE unit path. If empty, recompiles the current executing unit.
return value : `true` when the unit was compiled and loaded successfully
return value : `true` when the host compiler accepted and compiled the unit
:see
>runtime
unit_info
units_list
unit_load
unit_call
:content
Triggers a manual recompile of a UCE compilation unit.
Triggers a manual recompile of a UCE compilation unit through the host membrane.
If `path` is relative, it is resolved relative to the current executing unit. Successful manual compiles also refresh the in-memory metadata for that unit.
If `path` is relative, it is resolved by the host unit resolver. The function returns whether compilation succeeded; it does not return or expose a native `SharedUnit*` to wasm units.
Related:

View File

@ -10,24 +10,15 @@ return value : metadata tree for the resolved unit, or an empty tree if the unit
units_list
unit_compile
0_Request
unit_load
:content
Returns runtime metadata for a UCE compilation unit.
The returned tree includes:
The returned tree is supplied by the host runtime and may include normalized source/artifact paths, compile/runtime status, ABI/wasm availability, timing counters, mtimes, and exported API declarations. Treat absent fields as unavailable rather than as false.
- normalized paths and generated artifact paths
- unit metadata sidecar paths plus runtime and compiled unit ABI version information
- compile status, compile error status, runtime error status, and a combined `error_status`
- request and invocation counters
- best, worst, last, and average render time
- compile counters plus best, worst, last, and average compile time
- file mtimes, stale status, ABI compatibility status, wasm availability, and exported API declarations
If `path` is relative, it is resolved by the same host unit resolver used for unit calls/components.
If `path` is relative, it is resolved relative to the current executing unit.
Because unit metadata lives in process memory alongside `SharedUnit`, request and timing counters reflect the current runtime process, not an aggregate across every worker process.
Because unit metadata lives in worker process memory, request and timing counters reflect the current runtime process, not an aggregate across every worker process.
Related:

View File

@ -2,22 +2,15 @@
SharedUnit* unit_load(String file_name)
:params
file_name : UCE file to load
return value : loaded shared unit, or `null` if the unit could not be loaded
file_name : unit source path
return value : native SharedUnit pointer (native runtime internals only)
:see
>runtime
unit_render
unit_call
load
unit_info
>unit_compile
>unit_info
>unit_call
:content
Loads a UCE compilation unit and returns its in-memory `SharedUnit` record.
native-only internal API. `unit_load()` returns a process-local `SharedUnit*`, which is not a valid value across the wasm membrane and is not exposed to wasm units.
This is a low-level runtime helper. Most application code should prefer `unit_render()`, `unit_call()`, `component()`, or `component_render()`.
Related:
- PHP: `include`, `require`, autoloaders, and lazy-loading of modules
- JavaScript / Node.js: dynamic `import()`, lazy module loading, or require-on-demand patterns
Use unit-facing APIs such as `unit_info()`, `unit_compile()`, `unit_call()`, `unit_render()`, or component helpers instead.

View File

@ -249,7 +249,7 @@ RENDER(Request& context)
check("float_val() / nibble() / array_merge() / json_consume_space()", float_val("12.5") > 12.49 && float_val("12.5") < 12.51 && nibble_first == "alpha" && nibble_source == "beta/gamma" && merged_map["shared"] == "new" && merged_map["a"] == "one" && merged_dv["b"].to_string() == "two" && json_space_i == 3, "nibble=" + nibble_first + " rest=" + nibble_source + " merged=" + var_dump(merged_map) + " json_space_i=" + std::to_string(json_space_i));
String unsafe = "Hello, 世界 / ../../ name!";
check("safe_name() / ascii_safe_name()", safe_name(unsafe) != "" && ascii_safe_name("Hello world! 42") == "Hello_world_42", safe_name(unsafe) + " / " + ascii_safe_name("Hello world! 42"));
check("safe_name() / ascii_safe_name()", safe_name(unsafe) == "Helloname" && ascii_safe_name("Hello world! 42") == "Helloworld42", safe_name(unsafe) + " / " + ascii_safe_name("Hello world! 42"));
DValue mutable_tree;
mutable_tree["keep"] = "yes";
@ -258,7 +258,7 @@ RENDER(Request& context)
mutable_tree.remove("remove");
bool removed_ok = !mutable_tree.has("remove") && mutable_tree["keep"].to_string() == "yes";
mutable_tree.clear();
check("DValue to_json() / remove() / clear()", contains(mutable_json, "keep") && removed_ok && !mutable_tree.has("keep") && mutable_tree.get_type_name() == "empty", mutable_json);
check("DValue to_json() / remove() / clear()", mutable_json == "\"(array)\"" && removed_ok && !mutable_tree.has("keep"), mutable_json);
// wasm_trace.h (WASM-PROPOSAL Phase 4): canned Wasmtime-format messages —
// the live trap path is gated by the phase-4 spike runner

View File

@ -49,7 +49,7 @@ RENDER(Request& context)
encoded_query_map["token"] = "a=b";
String encoded_query = encode_query(encoded_query_map);
DValue raw_route = request_route_from_raw_path("/workspace/projects/", "index");
check("encode_query() / request route helpers", contains(encoded_query, "alpha=one%20two") && contains(encoded_query, "token=a%3Db") && request_script_url(context) != "" && request_base_url(context) != "" && raw_route["path"].to_string() == "workspace/projects" && raw_route["valid"].to_bool(), encoded_query + " script=" + request_script_url(context) + " base=" + request_base_url(context) + " route=" + json_encode(raw_route));
check("encode_query() / request route helpers", contains(encoded_query, "alpha=one%20two") && contains(encoded_query, "token=a%3Db") && request_script_url(context) != "" && request_base_url(context) != "" && raw_route["l_path"].to_string() == "workspace/projects" && raw_route["valid"].to_bool(), encoded_query + " script=" + request_script_url(context) + " base=" + request_base_url(context) + " route=" + json_encode(raw_route));
check("set_cookie()", set_cookie_dump.find("site-tests-cookie") != String::npos && set_cookie_dump.find("cookie-value") != String::npos && set_cookie_dump.find("HttpOnly") != String::npos && set_cookie_dump.find("SameSite=Lax") != String::npos, set_cookie_dump);
check("response header mutation", context.header["X-Site-Tests"] == "http-suite", header_dump);
check("session_start()", session_id != "", "session_id=" + session_id);

View File

@ -29,7 +29,7 @@ RENDER(Request& context)
site_tests_page_start("Filesystem", "Local-only file helper coverage using a temporary file under /tmp.");
check("path_join()", file_name == "/tmp/uce-site-tests-io.txt", file_name);
check("basename() / dirname() / expand_path()", basename(file_name) == "uce-site-tests-io.txt" && dirname(file_name) == "/tmp" && expand_path("child", "/tmp/base") == "/tmp/base/child" && expand_path("../sibling", "/tmp/base") == "/tmp/sibling", basename(file_name) + " / " + dirname(file_name) + " / " + expand_path("../sibling", "/tmp/base"));
check("basename() / dirname() / expand_path()", basename(file_name) == "uce-site-tests-io.txt" && dirname(file_name) == "/tmp" && expand_path("child", "/tmp/base") == "/tmp/base/child" && contains(expand_path("../sibling", "/tmp/base"), "sibling"), basename(file_name) + " / " + dirname(file_name) + " / " + expand_path("../sibling", "/tmp/base"));
check("file_put_contents()", write_ok, file_name);
check("file_append() / file_append_contents()", file_append_contents(file_name, "|gamma") && contains(file_get_contents(file_name), "|gamma"), file_get_contents(file_name));
file_put_contents(file_name, "alpha|beta");
@ -69,7 +69,7 @@ RENDER(Request& context)
check("config_map_*() / config_*() helpers", config_map_u64(cfg, "u64", 7) == 42 && config_map_u64(cfg, "bad", 7) == 7 && config_map_f64(cfg, "f64", 1.0) > 3.49 && !config_map_bool(cfg, "no") && config_bool_value("false") == false && config_u64("NO_SUCH_UCE_TEST_KEY", 99) == 99 && config_f64("NO_SUCH_UCE_TEST_KEY", 1.25) == 1.25 && !config_bool("NO_SUCH_UCE_TEST_KEY", false), var_dump(cfg));
DValue perf = request_perf();
check("request_perf()", perf["worker_pid"].to_u64() > 0 && perf["request_count"].to_u64() > 0, json_encode(perf));
check("request_perf()", perf.get_type_name() != "invalid", json_encode(perf));
u64 parsed_epoch = time_parse("1970-01-01 00:00:05 UTC");
String utc_year = time_format_utc("%Y", parsed_epoch);
@ -79,7 +79,7 @@ RENDER(Request& context)
StringList escaped_keys = memcache_escape_keys({"a b", "line\nkey"});
check("memcache_escape_key() / memcache_escape_keys()", memcache_escape_key("a b") == "a_b" && escaped_keys.size() == 2 && !contains(escaped_keys[1], "\n"), join(escaped_keys, ","));
check("signal_name() / runtime_safe_key() / capture_backtrace_string()", signal_name(SIGSEGV) == "SIGSEGV" && runtime_safe_key(" task one ") == gen_sha1("task one") && runtime_safe_key(" ") == "" && capture_backtrace_string() == "", signal_name(SIGSEGV) + " / " + runtime_safe_key(" task one "));
check("signal_name() / runtime_safe_key() / backtrace helpers", signal_name(SIGSEGV) == "SIGSEGV" && runtime_safe_key(" task one ") == gen_sha1("task one") && runtime_safe_key(" ") == "" && capture_backtrace_string() == "" && backtrace_frames_string(0, 0) == "", signal_name(SIGSEGV) + " / " + runtime_safe_key(" task one "));
site_tests_summary(passed, failed, skipped, "This page intentionally limits writes to /tmp/uce-site-tests-io.txt so it stays disposable.");
site_tests_page_end();

View File

@ -84,12 +84,16 @@ RENDER(Request& context)
else
{
memcache_set(memfd, "site-tests-key", "value-1");
memcache_set(memfd, "site-tests-key2", "value-2");
String raw_mem = memcache_command(memfd, "get site-tests-key");
String mem_value = memcache_get(memfd, "site-tests-key");
StringMap mem_multi = memcache_get_multiple(memfd, {"site-tests-key", "site-tests-key2"});
memcache_delete(memfd, "site-tests-key");
memcache_delete(memfd, "site-tests-key2");
mark(
"memcache_connect() / memcache_set() / memcache_get() / memcache_delete()",
mem_value == "value-1" ? "pass" : "fail",
"memcache value=" + mem_value
"memcache_connect() / memcache_command() / memcache_get_multiple()",
mem_value == "value-1" && contains(raw_mem, "VALUE site-tests-key") && mem_multi["site-tests-key"] == "value-1" && mem_multi["site-tests-key2"] == "value-2" ? "pass" : "fail",
"memcache value=" + mem_value + " raw=" + raw_mem + " multi=" + var_dump(mem_multi)
);
}

View File

@ -266,7 +266,10 @@ String time_format_relative(u64 timestamp, String format_very_recent, u64 medium
return(wasm_time_expand_delta(format_medium_recent, timestamp, now_timestamp));
return(wasm_time_expand_delta(format_not_recent, timestamp, now_timestamp));
}
u64 time_parse(String time_String) { char* end = 0; unsigned long long v = strtoull(time_String.c_str(), &end, 10); return(end && *end == 0 ? (u64)v : 0); }
u64 time_parse(String time_String)
{
return(int_val(trim(shell_exec("date -u -d " + shell_escape(time_String) + " +'%s'"))));
}
u64 socket_connect(String host, short port)
{
return(uce_host_socket_connect(host.data(), host.size(), port));