diff --git a/dist/uce_0.1.1.deb b/dist/uce_0.1.1.deb deleted file mode 100644 index 09f08a4..0000000 Binary files a/dist/uce_0.1.1.deb and /dev/null differ diff --git a/dist/uce_0.1.2-1_amd64.deb b/dist/uce_0.1.2-1_amd64.deb deleted file mode 100644 index df54d7d..0000000 Binary files a/dist/uce_0.1.2-1_amd64.deb and /dev/null differ diff --git a/dist/uce_0.1.3-1_amd64.deb b/dist/uce_0.1.3-1_amd64.deb deleted file mode 100644 index c02e12e..0000000 Binary files a/dist/uce_0.1.3-1_amd64.deb and /dev/null differ diff --git a/dist/uce_0.1.4-1_amd64.deb b/dist/uce_0.1.4-1_amd64.deb deleted file mode 100644 index 6886b42..0000000 Binary files a/dist/uce_0.1.4-1_amd64.deb and /dev/null differ diff --git a/docs/react-developer-affordances-todo.md b/docs/react-developer-affordances-todo.md deleted file mode 100644 index 81d8b04..0000000 --- a/docs/react-developer-affordances-todo.md +++ /dev/null @@ -1,147 +0,0 @@ -# React Developer Affordances Todo - -## Objective - -Add practical value for developers coming from React frameworks while preserving UCE's server-first C++ model. Defer component syntax/children work, avoid global head/assets/islands in the runtime, and focus on function-library data helpers, diagnostics, docs, examples, demos, and a starter-local router with starter-local asset/island components. - -## Success Criteria - -- [x] Function library has useful collection/data-shaping helpers with docs and tests. -- [x] Compile/runtime diagnostics are more helpful, especially for generated-code and common preprocessor mistakes. -- [x] Docs include a concise React/Next/Remix orientation guide. -- [x] Starter example uses a centralized hierarchical/file-based router in `index.uce` efficiently. -- [x] Starter-local asset/island affordances live as component handlers in the starter, not global runtime APIs. -- [x] Network tests and relevant build checks pass on `k-uce`. - -## Current State - -- Status: complete -- Last updated: 2026-05-28 -- Source of truth: `/root/mount_ssh/k-uce-root-htdocs-uce` -- Runtime/live target: `k-uce:/Code/uce.openfu.com/uce`; rebuilt and restarted `uce.service` on `k-uce`. - -## Goal Tree - -Legend: `[ ]` not started, `[~]` in progress, `[x]` done, `[!]` blocked, `[-]` superseded - -- [x] G1: Add collection/data helpers to function library - - Why: React-framework developers routinely shape arrays/objects near render code. - - Done when: helpers are declared, implemented, documented, and covered by tests. - - Verify: build plus focused site/network tests. - - [x] G1.1: Identify current `StringList`/`DValue` idioms and choose helper surface. - - [x] G1.2: Implement minimal high-value helpers without broad template complexity. - - [x] G1.3: Add docs and examples for helpers. - - [x] G1.4: Add/extend tests. -- [x] G2: Improve developer diagnostics - - Why: React frameworks win by making failures easy to act on. - - Done when: compile/runtime error output includes actionable context and docs mention debugging flow. - - Verify: intentional broken page surfaces improved message. - - [x] G2.1: Inspect current compiler/runtime error rendering path. - - [x] G2.2: Add source excerpt / generated path / common-hint text where appropriate. - - [x] G2.3: Document diagnostics. -- [x] G3: Add React/Next/Remix orientation docs - - Why: mapping familiar concepts reduces onboarding cost without adding syntax. - - Done when: docs page exists and is linked from docs/README/demo surfaces. - - Verify: docs page renders live. -- [x] G4: Starter-local router and starter affordances - - Why: User specifically wants hierarchical/file-based routing beautifully in starter `index.uce`. - - Done when: starter routes go through a central router in `index.uce`, and starter-local asset/island component handlers exist and are used where sensible. - - Verify: key starter routes render 200. - - [x] G4.1: Inspect current starter routing. - - [x] G4.2: Refactor to clear route table / hierarchical file resolution in `index.uce`. - - [x] G4.3: Add starter-local `COMPONENT:asset` / `COMPONENT:island` style handlers in one unit. - - [x] G4.4: Use them efficiently in starter pages/layout. -- [x] G5: Demos and examples - - Why: Affordances must be visible to developers, not hidden in APIs. - - Done when: docs/demo/tests expose examples. - - Verify: demo URLs return 200 and tests pass. -- [x] G6: Verification and project docs - - Done when: build/test commands are run on `k-uce`, project notes updated, and adversarial review completed. - -## Execution Queue - -Complete. - -## Decisions - -- 2026-05-28: Defer component children/slots and JSX-like preprocessor syntax. -- 2026-05-28: Do not add global runtime head/assets/islands APIs; implement asset/island as starter-local components. -- 2026-05-28: Do not add a generic runtime file-based-routing system; demonstrate hierarchical/file routing inside starter `index.uce`. -- 2026-05-28: Keep collection helpers explicit (`list_*`, `dv_*`) instead of overloading generic names such as `map`/`sort`. - -## Assumptions - -- Current source-of-truth mount is live-editable; runtime validation requires SSH to `k-uce`. -- Existing site tests are the right place for function-library coverage. - -## Blockers and Risks - -- No current blockers. -- Future risk: if UCE grows a full parser or component-tag syntax, keep this pass's explicit component/router APIs as a stable lower-level fallback. - -## Evidence and Verification Log - -- 2026-05-28: Created plan after reviewing README, preprocessor docs, and function library headers. -- 2026-05-28: `ssh k-uce 'cd /Code/uce.openfu.com/uce && bash scripts/build_linux.sh'` succeeded. -- 2026-05-28: Restarted `uce.service` on `k-uce`. -- 2026-05-28: `tests/run_network_tests.py --match core` passed. -- 2026-05-28: Manual checks returned `200` for `/examples/uce-starter/index.uce`, `?dashboard`, `?workspace/projects`, `?themes`, `/demo/collections.uce`, `/doc/index.uce?p=coming_from_react`, `/doc/index.uce?p=list_map`, and `/doc/index.uce?p=dv_group_by`. -- 2026-05-28: Full internal network suite passed, 25/25. -- 2026-05-28: Temporary broken `/tests/diagnostic-probe.uce` returned `500` with formatted `UCE compile error` diagnostics; source and cache artifacts were removed afterward. - -## Change Log - -- 2026-05-28: Created initial goal tree. -- 2026-05-28: Implemented helpers, diagnostics, docs, demo, starter router, starter-local web affordance components, tests, and validation. - -## Follow-up Cleanup 2026-05-29 - -- Removed duplicate route cleanup from `starter_router_candidates()` because `app_make_route()` is the single normalization point for `l_path`. -- Weeded out nearby duplicate/obsolete starter code: - - `starter_router_add_candidate(...)` now owns repeated candidate tree construction. - - Removed unused `app_resolve_view()` / `starter_resolve_view()` after moving routing into starter `index.uce`. - - Removed unused legacy registered asset rendering functions from `lib/app.uce`; registered assets now render through `components/theme/web_affordances.uce`. - - `app_init()` now reuses `app_base_url(context)` instead of repeating base URL derivation. - - `web_affordances.uce` now uses one `starter_render_asset_group(...)` loop for CSS and JS. -- Verification: rebuilt on `k-uce`, restarted `uce.service`, checked key starter routes, and ran `tests/run_network_tests.py --match core` successfully. - -## Follow-up Routed Views 2026-05-29 - -- Changed starter route dispatch from `unit_render(...)` to `component(...)`. -- Converted all routed `site/examples/uce-starter/views/*.uce` files to `COMPONENT(Request& context)` rather than `RENDER(Request& context)` because they are central-router-only views. -- Updated the starter README and verified key starter routes. No service restart was required because only `.uce`/docs changed. - -## Follow-up Canonical Starter URLs 2026-05-29 - -- Canonicalized starter self-links from `/examples/uce-starter/index.uce?...` to `/examples/uce-starter/?...` with `app_canonical_script_url(...)`. -- Updated the starter README to show canonical directory URLs. -- Touched the starter front controller to force the `#load`ed helper change into the cached generated unit. -- Verified canonical/direct starter routes and checked generated self-links. No service restart was required. - -## Follow-up Not Found Component 2026-05-29 - -- Moved `starter_router_render_not_found` markup into `components/basic/notfound.uce`. -- Router now delegates 404 body rendering through `component("components/basic/notfound", props, context)`. -- Verified missing routes return `404` and normal dashboard route returns `200`. No service restart required. - -## Follow-up Page Shell Component 2026-05-29 - -- Moved app page rendering into `themes/page.uce` as a component. -- Removed `app_render_page`, `app_theme_page_component`, and `starter_render_page` from `lib/app.uce`. -- Page template resolution now follows `context.call["app"]["page_type"]`: current theme first, common fallback second. -- Verified representative HTML routes and the JSON page-type fallback. No service restart required. - -## Follow-up Deep Starter Context Cleanup 2026-05-29 - -- Removed repeated `starter_boot(context)` calls; root `index.uce` is the boot point. -- Removed `context.call["starter"]` duplicated state and JSON side-channel state. -- JSON routes now use only `context.call["app"]["page_type"]` plus normal captured output. -- Simplified `themes/page.uce` and `themes/common/page.json.uce` accordingly. -- Replaced `starter_*` alias helper usage with direct `app_*` helpers and removed alias wrappers except `StarterUser`. -- Verified key routes and core tests. No service restart required. - -## Follow-up Route Context Flattening 2026-05-29 - -- Flattened `context.call["app"]["route"]` to `context.call["route"]`. -- Moved former `context.call["app"]["router"]` metadata into `context.call["route"]`. -- Verified representative starter HTML, 404, and JSON routes. No service restart required. diff --git a/docs/wasm-baselines/native-baseline-2026-06-12.json b/docs/wasm-baselines/native-baseline-2026-06-12.json deleted file mode 100644 index 28ad94c..0000000 --- a/docs/wasm-baselines/native-baseline-2026-06-12.json +++ /dev/null @@ -1,104 +0,0 @@ -[ - { - "backend": "native", - "target": "template-heavy-doc", - "url": "/doc/singlepage.uce", - "ok": true, - "status": 200, - "samples_ms": [ - 318.72209906578064, - 340.9293442964554, - 319.705568253994, - 324.0259513258934, - 350.93583166599274, - 313.20811808109283, - 317.0944079756737, - 310.312956571579, - 307.89367109537125, - 308.1864267587662, - 311.681292951107, - 313.39504569768906, - 314.3857270479202, - 312.5988021492958, - 313.25943768024445, - 312.9996135830879, - 334.4864323735237, - 314.2518773674965, - 309.7490146756172, - 317.4726217985153 - ], - "median_ms": 313.8234615325928, - "mean_ms": 318.2647120207548, - "min_ms": 307.89367109537125, - "max_ms": 350.93583166599274, - "note": "" - }, - { - "backend": "native", - "target": "sqlite-page", - "url": "/demo/sqlite.uce", - "ok": true, - "status": 200, - "samples_ms": [ - 3.4144148230552673, - 6.088584661483765, - 3.463640809059143, - 3.498159348964691, - 3.5023540258407593, - 6.214611232280731, - 3.563329577445984, - 3.3720433712005615, - 3.330707550048828, - 3.3808723092079163, - 3.322914242744446, - 3.3655911684036255, - 3.374151885509491, - 3.358304500579834, - 3.3307820558547974, - 3.268897533416748, - 3.360658884048462, - 3.3061057329177856, - 6.233863532543182, - 3.432638943195343 - ], - "median_ms": 3.3775120973587036, - "mean_ms": 3.809131309390068, - "min_ms": 3.268897533416748, - "max_ms": 6.233863532543182, - "note": "" - }, - { - "backend": "native", - "target": "component-heavy-starter", - "url": "/examples/uce-starter/?dashboard", - "ok": true, - "status": 200, - "samples_ms": [ - 74.01428371667862, - 40.9795418381691, - 40.995217859745026, - 41.04568809270859, - 40.724173188209534, - 41.10313206911087, - 40.59913754463196, - 41.28593951463699, - 41.10439121723175, - 40.649913251399994, - 42.06441342830658, - 42.074643075466156, - 43.475523591041565, - 41.8255478143692, - 41.068583726882935, - 41.116394102573395, - 41.09777510166168, - 42.58237034082413, - 41.312023997306824, - 70.98117470741272 - ], - "median_ms": 41.11039265990257, - "mean_ms": 44.50499340891838, - "min_ms": 40.59913754463196, - "max_ms": 74.01428371667862, - "note": "" - } -] diff --git a/docs/wasm-baselines/native-baseline-2026-06-12.md b/docs/wasm-baselines/native-baseline-2026-06-12.md deleted file mode 100644 index 413facc..0000000 --- a/docs/wasm-baselines/native-baseline-2026-06-12.md +++ /dev/null @@ -1,19 +0,0 @@ -# Phase 5 native baseline — 2026-06-12 - -Host: `k-uce` / `uce.openfu.com` via localhost with Host header. - -This is a durable informational snapshot. The Phase 5 gate recomputes native medians during paired native/WASM runs; these numbers are not hard-coded budgets. - -- Warmup suite: 82/82 passed; excludes `site tests tasks` to avoid perturbing task lifecycle state. -- Measured full network suite: 83/83 passed. -- Measured starter subset: 14/14 passed. -- Static audit default scan: 50 code findings, documentation prose excluded by default. - -| target | median ms | mean ms | samples | -|---|---:|---:|---:| -| template-heavy-doc | 313.8 | 318.3 | 20 | -| sqlite-page | 3.4 | 3.8 | 20 | -| component-heavy-starter | 41.1 | 44.5 | 20 | - -Raw benchmark JSON: `native-baseline-2026-06-12.json`. -Static audit snapshot: `site-static-audit-2026-06-12.md`. diff --git a/docs/wasm-baselines/site-static-audit-2026-06-12.md b/docs/wasm-baselines/site-static-audit-2026-06-12.md deleted file mode 100644 index a199d6f..0000000 --- a/docs/wasm-baselines/site-static-audit-2026-06-12.md +++ /dev/null @@ -1,56 +0,0 @@ -# Phase 5 site static-state audit - -Findings: 50 code, 0 documentation prose. - -| file | line | severity | kind | code | note | -|---|---:|---|---|---|---| -| site/demo/index.uce | 76 | code | background task | `` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | -| site/demo/once-init.uce | 1 | code | static local/global | `static s64 demo_worker_init_count = 0;` | Check whether state is request-local, immutable, or intentionally persistent; unit statics reset per wasm workspace. | -| site/demo/once-init.uce | 2 | code | static local/global | `static s64 demo_component_hits = 0;` | Check whether state is request-local, immutable, or intentionally persistent; unit statics reset per wasm workspace. | -| site/demo/once-init.uce | 4 | code | init hook | `INIT(Request& context)` | Audit behavior under per-request wasm workspaces; ONCE/INIT may need host-side cache semantics if used for cross-request state. | -| site/demo/once-init.uce | 10 | code | once hook | `ONCE(Request& context)` | Audit behavior under per-request wasm workspaces; ONCE/INIT may need host-side cache semantics if used for cross-request state. | -| site/demo/once-init.uce | 44 | code | once hook | `ONCE() and INIT()` | Audit behavior under per-request wasm workspaces; ONCE/INIT may need host-side cache semantics if used for cross-request state. | -| site/demo/once-init.uce | 44 | code | init hook | `ONCE() and INIT()` | Audit behavior under per-request wasm workspaces; ONCE/INIT may need host-side cache semantics if used for cross-request state. | -| site/demo/once-init.uce | 47 | code | once hook | `This page calls the same named component twice. `ONCE()` should only run once for the request, while `INIT()` should stay stable for the currently loaded worker copy.` | Audit behavior under per-request wasm workspaces; ONCE/INIT may need host-side cache semantics if used for cross-request state. | -| site/demo/once-init.uce | 47 | code | init hook | `This page calls the same named component twice. `ONCE()` should only run once for the request, while `INIT()` should stay stable for the currently loaded worker copy.` | Audit behavior under per-request wasm workspaces; ONCE/INIT may need host-side cache semantics if used for cross-request state. | -| site/demo/task-status.uce | 11 | code | background task | `String task_name = first(context.get["task-name"], "example-task");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | -| site/demo/task-status.uce | 13 | code | background task | `print("Task Name: ", task_name, "\n");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | -| site/demo/task-status.uce | 14 | code | background task | `print("Task ID: ", task_pid(task_name), "\n");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | -| site/demo/task-status.uce | 15 | code | background task | `print("Task Running: ", task_pid(task_name) == 0 ? "no" : "yes", "\n");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | -| site/demo/task.uce | 13 | code | background task | `String task_name = first(context.get["task-name"], "example-task");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | -| site/demo/task.uce | 37 | code | background task | `` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | -| site/demo/task.uce | 46 | code | background task | `load(document.getElementById('task-status'), 'task-status.uce?task-name=');` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | -| site/demo/task.uce | 56 | code | background task | `print("Task Name: ", task_name, "\n");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | -| site/demo/task.uce | 57 | code | background task | `print("Task ID: ", task_pid(task_name), "\n");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | -| site/demo/task.uce | 58 | code | background task | `print("Task Running: ", task_pid(task_name) == 0 ? "no" : "yes", "\n");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | -| site/demo/task.uce | 71 | code | background task | `print("New Task ID: ", task(task_name, []() {` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | -| site/demo/task_repeat.uce | 13 | code | background task | `String task_name = first(context.get["task-name"], "example-task");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | -| site/demo/task_repeat.uce | 37 | code | background task | `` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | -| site/demo/task_repeat.uce | 46 | code | background task | `load(document.getElementById('task-status'), 'task-status.uce?task-name=');` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | -| site/demo/task_repeat.uce | 56 | code | background task | `print("Task Name: ", task_name, "\n");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | -| site/demo/task_repeat.uce | 57 | code | background task | `print("Task ID: ", task_pid(task_name), "\n");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | -| site/demo/task_repeat.uce | 58 | code | background task | `print("Task Running: ", task_pid(task_name) == 0 ? "no" : "yes", "\n");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | -| site/demo/task_repeat.uce | 71 | code | background task | `print("New Task ID: ", task_repeat(task_name, 5, []() {` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | -| site/examples/uce-starter/components/data/widgets.uce | 3 | code | once hook | `ONCE(Request& context)` | Audit behavior under per-request wasm workspaces; ONCE/INIT may need host-side cache semantics if used for cross-request state. | -| site/examples/uce-starter/components/workspace/primitives.uce | 3 | code | once hook | `ONCE(Request& context)` | Audit behavior under per-request wasm workspaces; ONCE/INIT may need host-side cache semantics if used for cross-request state. | -| site/examples/uce-starter/lib/user.class.h | 13 | code | static local/global | `static String session_key()` | Check whether state is request-local, immutable, or intentionally persistent; unit statics reset per wasm workspace. | -| site/examples/uce-starter/lib/user.class.h | 18 | code | static local/global | `static String normalize_email(String email)` | Check whether state is request-local, immutable, or intentionally persistent; unit statics reset per wasm workspace. | -| site/examples/uce-starter/lib/user.class.h | 23 | code | static local/global | `static String hash_id(String raw)` | Check whether state is request-local, immutable, or intentionally persistent; unit statics reset per wasm workspace. | -| site/examples/uce-starter/lib/user.class.h | 45 | code | static local/global | `static String password_hash(String password, String salt)` | Check whether state is request-local, immutable, or intentionally persistent; unit statics reset per wasm workspace. | -| site/examples/uce-starter/lib/user.class.h | 53 | code | static local/global | `static DValue read_json_file(String file_name)` | Check whether state is request-local, immutable, or intentionally persistent; unit statics reset per wasm workspace. | -| site/examples/uce-starter/lib/user.class.h | 64 | code | static local/global | `static bool write_json_file(String file_name, DValue data)` | Check whether state is request-local, immutable, or intentionally persistent; unit statics reset per wasm workspace. | -| site/examples/uce-starter/views/dashboard.uce | 3 | code | once hook | `ONCE(Request& context)` | Audit behavior under per-request wasm workspaces; ONCE/INIT may need host-side cache semantics if used for cross-request state. | -| site/info/index.uce | 269 | code | static local/global | `
  • nginx serves static files directly from `site/`
  • ` | Check whether state is request-local, immutable, or intentionally persistent; unit statics reset per wasm workspace. | -| site/tests/preprocessor.uce | 3 | code | once hook | `ONCE(Request& context)` | Audit behavior under per-request wasm workspaces; ONCE/INIT may need host-side cache semantics if used for cross-request state. | -| site/tests/tasks.uce | 25 | code | background task | `pid_t repeat_existing = task_pid("site-tests-repeat");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | -| site/tests/tasks.uce | 27 | code | background task | `task_kill(repeat_existing, 15);` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | -| site/tests/tasks.uce | 37 | code | background task | `repeat_pid = task_repeat("site-tests-repeat", 1.0, []() {` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | -| site/tests/tasks.uce | 55 | code | background task | `pid_t seen_short_pid = task_pid("site-tests-short");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | -| site/tests/tasks.uce | 56 | code | background task | `pid_t seen_repeat_pid = task_pid("site-tests-repeat");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | -| site/tests/tasks.uce | 57 | code | background task | `pid_t seen_timeout_pid = task_pid("site-tests-timeout");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | -| site/tests/tasks.uce | 58 | code | background task | `pid_t seen_unsafe_key_pid = task_pid("site-tests/../unsafe key");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | -| site/tests/tasks.uce | 59 | code | background task | `int short_alive = seen_short_pid == 0 ? -1 : task_kill(seen_short_pid, 0);` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | -| site/tests/tasks.uce | 60 | code | background task | `int repeat_alive = seen_repeat_pid == 0 ? -1 : task_kill(seen_repeat_pid, 0);` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | -| site/tests/tasks.uce | 72 | code | background task | `check("task_pid() + task_kill(pid, 0)", short_alive == 0, "kill(0) result=" + std::to_string(short_alive));` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | -| site/tests/tasks.uce | 73 | code | background task | `check("task_repeat()", repeat_pid != 0 && seen_repeat_pid != 0, "started=" + std::to_string(repeat_pid) + " seen=" + std::to_string(seen_repeat_pid));` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | -| site/tests/tasks.uce | 77 | code | background task | `check("task_kill() rejects negative pid", task_kill(-1, 0) == -1, "kill(-1, 0) rejected");` | Task APIs cross request lifetimes; verify they are host handles, not guest statics. | diff --git a/docs/wasm-toolchain-findings.md b/docs/wasm-toolchain-findings.md deleted file mode 100644 index 6d2eb94..0000000 --- a/docs/wasm-toolchain-findings.md +++ /dev/null @@ -1,197 +0,0 @@ -# Phase 0 findings — toolchain & runtime spike - -- **Status: EXIT CRITERION PASSED** (2026-06-12, on k-uce) -- **Runtime selected: Wasmtime** (v45.0.1, C API). WAMR rejected — evidence below. -- Exceptions decision (§11.1, error codes / `-fno-exceptions`) **confirmed viable**: - both the stubs and two real generated units compile with `-fno-exceptions`, - no try/catch blocker anywhere. - -The exit criterion ran end-to-end: a core stub (libc/libc++ statically linked, -owns memory/allocator) and a unit stub (PIC, `dylink.0`) were linked **at -runtime** by `loader.cpp` and produced: - -``` -hello from unit; unit-data-segment-ok; counter=7; mapsum=3; core-string+unit[cb:42][got-func-ok][fn:42] -core_counter (in linear memory): before=7 after=8 -PHASE0 EXIT CRITERION: PASS -``` - -which validates, in one render call: unit data-segment relocation -(`__memory_base`), GOT.mem read **and write** of a core global, `std::string` -/`std::map` in unit code on the core's heap, a heap C++ object created in core -and mutated/read by the unit, function pointers crossing unit→core→unit -through the shared table, GOT.func resolution, and a `std::function` lambda -allocated in the unit and invoked by core. That is the §3.4 contract -("DValue inside the workspace: no serialization, ever") demonstrated at the -ABI level. - -## Toolchain pins - -| What | Version | Where on k-uce | -|---|---|---| -| wasi-sdk | 33 (clang 22.1.0-wasi-sdk) | `/opt/wasi-sdk` | -| target triple | `wasm32-wasip1` (`wasm32-wasi` is deprecated) | — | -| Wasmtime C API | v45.0.1 (prebuilt x86_64-linux release) | `/opt/wasmtime` | -| WAMR (rejected) | WAMR-2.4.4, built from source | `/opt/wamr` | -| cmake / ninja | 3.31.6 / 1.12.1 (apt) | — | - -## Runtime selection: why not WAMR - -WAMR was tried first per §9 ("preferred ... use Wasmtime only if blocked"). -We are blocked, on the load-bearing requirement itself: - -1. **WAMR's wasm-c-api ignores imported memories and tables.** At unit - instantiation it logs `"doesn't support import memories and tables for - now, ignore them"` (`wasm_c_api.c`) and gives the instance its own - memory/table — which silently destroys the shared-workspace model. -2. **Host-side `wasm_table_grow` / `wasm_memory_grow` are explicitly - unsupported** ("Only allow growing a table via the opcode table.grow"). -3. Its build banner lists *Import/Export of Mutable Globals* as unsupported — - the dylink ABI imports `__stack_pointer` and every `GOT.*` entry as a - mutable global. - -Its multi-module feature is name-based auto-resolution, not host-orchestrated -dylink (no host-computed `__memory_base`/`__table_base`, no GOT). Making WAMR -fit means implementing import binding through the c-api layer and runtime -internals — a runtime-development project, not a patch. - -**Wasmtime v45.0.1 passed everything on the first run** through the standard -`wasm.h` C API: host-created funcref table shared by both instances, exported -memory imported by the unit, host-created (mutable) globals, cross-instance -export→import wiring. The remaining §9 criteria also favor it: AOT artifacts -(`.cwasm` precompilation) for the unit cache, epoch interruption for CPU -limits, and built-in copy-on-write memory-image instantiation for the §6 core -snapshot (machinery we'd have had to build ourselves on WAMR). - -Trade-off accepted (was already in §10): Rust codebase, heavier to -vendor/patch. Pin the release artifact (lib + headers, checksummed) the way -sqlite is vendored; building from source stays possible but is not the -default path. The C API .so is ~27 MB. - -## Module build recipe (what `build_modules.sh` settled on) - -Core (non-PIC reactor, owns libc/libc++/allocator): -``` -clang++ --target=wasm32-wasip1 -mexec-model=reactor -O1 -fno-exceptions \ - core.cpp -o core.wasm \ - -Wl,--export-all -Wl,--import-table \ - -Wl,--export=__stack_pointer -Wl,--export=__heap_base \ - -Wl,--undefined=_ZTVN10__cxxabiv117__class_type_infoE -``` - -Unit (PIC side module): -``` -clang++ --target=wasm32-wasip1 -fPIC -fvisibility=default \ - -fvisibility-inlines-hidden -O1 -fno-exceptions -c unit.cpp -wasm-ld -shared --experimental-pic --unresolved-symbols=import-dynamic \ - --Bsymbolic unit.o -o unit.wasm --export= -``` - -Hard-won flag findings: - -1. **`--unresolved-symbols=import-dynamic`** is required for the side-module - link; undefined symbols then become `env.*` function imports and `GOT.*` - globals exactly per the Emscripten dylink ABI. -2. **`-fvisibility-inlines-hidden` is mandatory.** Without it one libc++ - vague-linkage lambda (`std::map` tree-emplace internals, missing libc++'s - usual hide-from-ABI attribute) is emitted as *both* an export and an - import of the unit — a self-import the loader cannot satisfy at - instantiation time without lazy-binding trampolines. `--Bsymbolic` alone - did **not** bind it. -3. **Core symbol closure**: `--export-all` only exports what got *linked*. - The unit needed `__cxxabiv1::__class_type_info`'s vtable (RTTI machinery - behind `std::function`), which the core never references — forced in with - `--undefined=`. The production core needs a closure strategy: - `--whole-archive` for libc/libc++/libc++abi, or a curated keep-list. The - loader also implements the complementary fallback (resolve `GOT.mem` of - weak data from the unit's *own* exports post-instantiation, patching the - provisional GOT global). -4. **`--import-table` on the core** + a **host-created table** is the right - shape (see loader notes); `--export-table`/`--growable-table` was the - first attempt and died on WAMR's host-grow limitation, but host-created - stays the better design under Wasmtime too: the loader picks table size - (core's declared minimum + headroom) before any instantiation. -5. `wasm32-wasi` triple is deprecated in wasi-sdk 33 → use `wasm32-wasip1`. - -## Loader notes (`loader.cpp`, ~450 lines, standard wasm-c-api) - -Sequence proven: instantiate core (45 WASI imports satisfied with named trap -stubs — none was ever called) → parse `dylink.0` (`mem_info`: memsize/align, -tablesize) → `__memory_base` = call core's exported `malloc` → `__table_base` -= bump pointer starting at core's table-import minimum → build the unit's -import vector (memory/table/`__stack_pointer` shared from core; `env.*` -functions from core exports; `GOT.mem.*` as host mutable i32 globals holding -addresses read from core's exported data-symbol globals) → instantiate → -patch deferred GOT entries → `__wasm_apply_data_relocs` → `__wasm_call_ctors` -→ call the entry export. - -- **Erratum (found in Phase 3): self-resolved `GOT.mem` values must add - `__memory_base`.** A PIC module's exported data symbols are i32 globals - holding offsets *relative to its `__memory_base`*, not absolute addresses — - the linker adds the base when patching deferred GOT entries (there is no - `__wasm_apply_global_relocs` export to do it). Copying the export verbatim - reads/writes core memory at low addresses and renders silently wrong values; - the Phase 3 fixture's `self-got`/`callback` markers exist to catch exactly - this. `GOT.mem` entries resolved from the *core's* exports are absolute - already (the core is non-PIC) and need no adjustment. -- **GOT.func is resolved guest-side**: the core exports a helper returning - `(intptr_t)&func` — taking the address forces a link-time elem entry and a - wasm function pointer *is* its table index. No host-side funcref injection - is needed at all (it was WAMR-unsupported; under Wasmtime it would work but - the guest-side registry is simpler and runtime-agnostic). The production - core should carry a name→funcptr registry (dlsym-shaped) for its API - surface. -- **wasi-libc gotcha**: `_initialize` has a double-init guard ending in - `__builtin_trap()`. WAMR runs `_initialize` automatically at instantiation - (so calling it again traps "unreachable"); Wasmtime does not (so you must - call it). Cost one debugging round; recorded here so it never costs another. -- Export-name `wasm_name_t` may include the trailing NUL in `size` (WAMR - did); trim when indexing exports by name. - -## Real generated units (delegated grind — full log in `realunit-report.md`) - -`site/demo/collections.uce.cpp` and `hello.uce.cpp` (taken verbatim from the -live unit cache at `/tmp/uce/work/...`) both compile and link as PIC side -modules with `dylink.0`, **no allocator definitions**, with only shim-level -intervention. `collections.wasm`: 42 KB, 52 imports — including exactly the -predicted `GOT.mem.context` for the global `Request*`. Friction points that -become Phase 2 work items: - -1. **`types.h` defines global `operator new/delete` in every unit** — must be - gated (`#ifdef`) out of side-module builds; allocator ownership belongs to - the core (§3.2: "the one fatal misconfiguration"). -2. **`sys.h` includes ``** → wasi needs `-D_WASI_EMULATED_SIGNAL` - (+ `-lwasi-emulated-signal` in the core) or an `#ifdef __wasm__` carve-out; - signals/fork/exec/sockets in `sys.h` have no wasi equivalent and move - behind hostcalls anyway (§5.1). -3. **Generated units include `uce_lib.h` by absolute path** — the - preprocessor should emit a logical include so the wasm build can supply - its own include order. -4. **Header-inline connector wrappers (MySQL etc.) get pulled into every - unit** regardless of use; the §3.3 membrane split (thin wasm-side shim, - host-side implementation) resolves this and shrinks unit import lists. -5. The real-unit compile used the pre-`-fvisibility-inlines-hidden` flag set - (parallel work); the final unit flag set above should be used from - Phase 2 on. - -## Implications for the next phases - -- **Phase 1 (DValue C ABI, native)**: unaffected by any of this; proceed as - written. -- **Phase 2 (core module + membrane)**: add the closure strategy - (whole-archive), the GOT.func name→funcptr registry, the `types.h` - allocator gate, the signal emulation define, and the preprocessor include - change. Compile `uce_lib` with the core recipe above. -- **Phase 3 (loader)**: `loader.cpp` here is the skeleton — dylink parsing, - base allocation, GOT resolution, and init sequencing are all proven; what - remains is the registry/dispatch layer, ABI stamping, and multi-unit - placement. -- **Phase 4**: use Wasmtime's epoch interruption for CPU limits and its - memory-image/CoW instantiation for the core snapshot rather than building - either by hand. - -## Artifacts (on k-uce, not in git) - -- `/tmp/uce/wasm-phase0/{core.wasm,unit.wasm,loader}` — exit-criterion run -- `/tmp/uce/wasm-phase0/realunit/` — real-unit compiles + shim tree + inspector dumps -- `/opt/wasi-sdk`, `/opt/wasmtime`, `/opt/wamr` — toolchains/runtimes diff --git a/site/doc/pages/1_RENDER.txt b/site/doc/pages/1_RENDER.txt index 61ebc83..da71a96 100644 --- a/site/doc/pages/1_RENDER.txt +++ b/site/doc/pages/1_RENDER.txt @@ -32,7 +32,7 @@ For a normal direct page request, `context.props` starts empty. If the page is invoked from another UCE file via `unit_render(file_name, context)`, the callee receives that same `context`. -Pages intended to serve WebSocket traffic may expose both `RENDER(Request& context)` and `WS(Request& context)`. Files may also define `COMPONENT()` handlers when they intentionally need both page and component behavior in one unit. +Pages that serve WebSocket traffic may expose both `RENDER(Request& context)` and `WS(Request& context)`. Files may also define `COMPONENT()` handlers when one unit needs both page and component behavior. In that case: diff --git a/site/doc/pages/base64_decode.txt b/site/doc/pages/base64_decode.txt new file mode 100644 index 0000000..3fdc599 --- /dev/null +++ b/site/doc/pages/base64_decode.txt @@ -0,0 +1,30 @@ +:sig +String base64_decode(String raw, bool& ok) + +:params +raw : Base64 encoded string +ok : set to `true` when decoding succeeds; set to `false` for invalid input +return value : decoded binary-safe string, or an empty string when decoding fails + +:see +>string +base64_encode + +:content +Decodes a Base64 string. + +Pass a `bool` variable for `ok` so callers can distinguish invalid input from a valid empty decoded value. + +Example: + +```uce +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")` diff --git a/site/doc/pages/base64_encode.txt b/site/doc/pages/base64_encode.txt new file mode 100644 index 0000000..a825959 --- /dev/null +++ b/site/doc/pages/base64_encode.txt @@ -0,0 +1,27 @@ +:sig +String base64_encode(String raw) + +:params +raw : binary-safe source string +return value : Base64 encoded string + +:see +>string +base64_decode + +:content +Encodes a string with Base64. + +UCE strings can contain binary data, so `raw` may include NUL bytes and non-text bytes. + +Example: + +```uce +String encoded = base64_encode("hello"); +// encoded == "aGVsbG8=" +``` + +Related: + +- PHP: `base64_encode()` +- JavaScript / Node.js: `Buffer.from(value).toString("base64")` diff --git a/site/doc/pages/coming_from_react.txt b/site/doc/pages/coming_from_react.txt index 8f6ae29..e99884c 100644 --- a/site/doc/pages/coming_from_react.txt +++ b/site/doc/pages/coming_from_react.txt @@ -27,7 +27,7 @@ UCE is server-first C++ with a small template preprocessor. It does not try to b - `ONCE(Request& context)` is per-request setup for a unit before its first render/component entry. - `INIT(Request& context)` is worker-local setup when a unit is loaded. - `` is escaped interpolation; prefer it for user-visible text. -- `` is trusted raw markup output, closer to a deliberate `dangerouslySetInnerHTML` decision. +- `` writes trusted raw markup, similar to `dangerouslySetInnerHTML` in React. - `unit_render()` renders another page unit; `component()` returns component HTML as a string. ## Routes and Layouts @@ -51,11 +51,11 @@ DValue app_items = dv_filter(menu, [](DValue item, String key) { return(item["se DValue by_section = dv_group_by(menu, [](DValue item, String key) { return(item["section"].to_string()); }); ``` -Use these when the transformation communicates intent. Prefer explicit loops when side effects or multi-step validation are the main concern. +Use these when a short transformation is clearer than a loop. Prefer explicit loops for side effects or multi-step validation. ## Assets and Islands -Global runtime APIs for assets and islands are intentionally not part of UCE core. The starter emits CSS and JavaScript from the owning unit's `ONCE(Request& context)` hook, with a few shared sibling asset components when multiple components need the same files. The only starter web-affordance helper left is `COMPONENT:island` in `components/theme/web_affordances.uce` for small progressive-enhancement modules. This keeps app policy in the app without an asset registry layer. +UCE core does not provide a global asset or island registry. The starter emits CSS and JavaScript from the owning unit's `ONCE(Request& context)` hook, with a few shared sibling asset components when multiple components need the same files. The starter's `COMPONENT:island` helper in `components/theme/web_affordances.uce` covers small progressive-enhancement modules while keeping app policy in the app. ## Debugging @@ -66,4 +66,4 @@ When a unit fails to compile, UCE reports the source path, generated C++ path, c - No client-side virtual DOM is built into UCE. - No global file-router is imposed by the runtime. - No JSX-like component tags are required for this workflow. -- Component children/slot syntax is intentionally deferred; use explicit props and component calls for now. +- Component children/slot syntax is not part of UCE yet; use explicit props and component calls for now. diff --git a/site/doc/pages/dv_filter.txt b/site/doc/pages/dv_filter.txt index 97f41e5..f74de2c 100644 --- a/site/doc/pages/dv_filter.txt +++ b/site/doc/pages/dv_filter.txt @@ -12,7 +12,7 @@ filter :content Keeps children for which f returns true. List-like input stays list-like. -These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary. +These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering. ```cpp DValue visible = dv_filter(items, [](DValue item, String key) { return(item["hidden"].to_bool() == false); }); diff --git a/site/doc/pages/dv_group_by.txt b/site/doc/pages/dv_group_by.txt index 4d99686..c1b8e9b 100644 --- a/site/doc/pages/dv_group_by.txt +++ b/site/doc/pages/dv_group_by.txt @@ -12,7 +12,7 @@ filter :content Groups children into list-like buckets by the string returned from f. -These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary. +These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering. ```cpp DValue by_section = dv_group_by(menu, [](DValue item, String key) { return(item["section"].to_string()); }); diff --git a/site/doc/pages/dv_keys.txt b/site/doc/pages/dv_keys.txt index 643f60e..8b23c0f 100644 --- a/site/doc/pages/dv_keys.txt +++ b/site/doc/pages/dv_keys.txt @@ -12,7 +12,7 @@ filter :content Returns map keys from a DValue. Scalar values produce an empty list. -These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary. +These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering. ```cpp StringList keys = dv_keys(context.cfg["menu"]); diff --git a/site/doc/pages/dv_map.txt b/site/doc/pages/dv_map.txt index 7f74aee..20caf99 100644 --- a/site/doc/pages/dv_map.txt +++ b/site/doc/pages/dv_map.txt @@ -12,7 +12,7 @@ filter :content Transforms each child. List-like input stays list-like; map input keeps keys. -These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary. +These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering. ```cpp DValue titles = dv_map(items, [](DValue item, String key) { DValue out; out = item["title"].to_string(); return(out); }); diff --git a/site/doc/pages/dv_omit.txt b/site/doc/pages/dv_omit.txt index 9c64ae3..b278b50 100644 --- a/site/doc/pages/dv_omit.txt +++ b/site/doc/pages/dv_omit.txt @@ -12,7 +12,7 @@ filter :content Copies a DValue map except for selected keys. -These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary. +These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering. ```cpp DValue safe_user = dv_omit(user, {"password_hash"}); diff --git a/site/doc/pages/dv_pick.txt b/site/doc/pages/dv_pick.txt index 1dea1c1..4524f4a 100644 --- a/site/doc/pages/dv_pick.txt +++ b/site/doc/pages/dv_pick.txt @@ -12,7 +12,7 @@ filter :content Copies only selected keys from a DValue map. -These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary. +These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering. ```cpp DValue public_user = dv_pick(user, {"name", "avatar"}); diff --git a/site/doc/pages/dv_values.txt b/site/doc/pages/dv_values.txt index 838157e..ae1da6b 100644 --- a/site/doc/pages/dv_values.txt +++ b/site/doc/pages/dv_values.txt @@ -12,7 +12,7 @@ filter :content Returns child values as a list-like DValue. -These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary. +These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering. ```cpp DValue menu_items = dv_values(context.cfg["menu"]); diff --git a/site/doc/pages/filter.txt b/site/doc/pages/filter.txt index 873fc8e..86a81e0 100644 --- a/site/doc/pages/filter.txt +++ b/site/doc/pages/filter.txt @@ -4,7 +4,7 @@ vector filter(vector items, function f) :params items : list of items to be filtered -f : a function that decides which items should be in the new list +f : predicate function; items are kept when this returns `true` return value : a new list :see diff --git a/site/doc/pages/is_list.txt b/site/doc/pages/is_list.txt index ff9af51..d504a35 100644 --- a/site/doc/pages/is_list.txt +++ b/site/doc/pages/is_list.txt @@ -27,7 +27,7 @@ items["custom"] = "x"; // items.is_list() == false, items.is_array() == true ``` -`dv_map()` and `dv_filter()` use this distinction to decide whether results re-index from zero or keep their original keys. +`dv_map()` and `dv_filter()` use this distinction: list inputs re-index from zero, while map inputs keep their original keys. ## Related Concepts diff --git a/site/doc/pages/list_every.txt b/site/doc/pages/list_every.txt index 2ea8864..c37e269 100644 --- a/site/doc/pages/list_every.txt +++ b/site/doc/pages/list_every.txt @@ -12,7 +12,7 @@ filter :content Returns true when every item matches. -These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary. +These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering. ```cpp bool all_named = list_every(routes, [](String s) { return(s != ""); }); diff --git a/site/doc/pages/list_find.txt b/site/doc/pages/list_find.txt index 9912f8a..9b84fff 100644 --- a/site/doc/pages/list_find.txt +++ b/site/doc/pages/list_find.txt @@ -12,7 +12,7 @@ filter :content Returns the first matching item or fallback. -These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary. +These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering. ```cpp String route = list_find(routes, [](String s) { return(str_starts_with(s, "dashboard")); }, "index"); diff --git a/site/doc/pages/list_some.txt b/site/doc/pages/list_some.txt index f1ea7a0..3398548 100644 --- a/site/doc/pages/list_some.txt +++ b/site/doc/pages/list_some.txt @@ -12,7 +12,7 @@ filter :content Returns true when any item matches. -These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary. +These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering. ```cpp bool has_dashboard = list_some(routes, [](String s) { return(s == "dashboard"); }); diff --git a/site/doc/pages/list_sort.txt b/site/doc/pages/list_sort.txt index 1d9813f..d18d9f7 100644 --- a/site/doc/pages/list_sort.txt +++ b/site/doc/pages/list_sort.txt @@ -12,7 +12,7 @@ filter :content Returns a sorted copy of the list. -These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary. +These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering. ```cpp auto sorted = list_sort(tags); diff --git a/site/doc/pages/list_unique.txt b/site/doc/pages/list_unique.txt index 5d59cdf..999f7ee 100644 --- a/site/doc/pages/list_unique.txt +++ b/site/doc/pages/list_unique.txt @@ -12,7 +12,7 @@ filter :content Returns the first occurrence of each string, preserving input order. -These helpers are intentionally small data-shaping conveniences for render code, routers, and configuration trees. They are useful when porting habits from React/Next/Remix code where lists of routes, navigation items, cards, or records are transformed close to the rendering boundary. +These helpers keep common data-shaping code close to render code, routers, and configuration trees. They are useful for route lists, navigation items, cards, and records that need simple transformations before rendering. ```cpp auto tags = list_unique({"uce", "docs", "uce"}); diff --git a/site/doc/pages/split_kv.txt b/site/doc/pages/split_kv.txt new file mode 100644 index 0000000..0f6739d --- /dev/null +++ b/site/doc/pages/split_kv.txt @@ -0,0 +1,29 @@ +:sig +StringMap split_kv(String s, char separator = '=', bool trim_whitespace = true, bool uppercase_keys = false) + +:params +s : input containing one key/value pair per line +separator : character separating each key from its value +trim_whitespace : trim keys and values when true +uppercase_keys : uppercase keys when true +return value : map of parsed keys and values + +:see +>string +split +split_http_headers + +:content +Parses simple line-based key/value text into a `StringMap`. + +Each non-empty line is split on the first `separator`. Lines without the separator are kept with an empty value. + +Example: + +```uce +StringMap cfg = split_kv("host = localhost\nport = 8080"); +// cfg["host"] == "localhost" +// cfg["port"] == "8080" +``` + +This is useful for small config files, metadata blocks, and tests that need predictable key/value parsing. diff --git a/site/doc/pages/task.txt b/site/doc/pages/task.txt index 997dc31..b31ec68 100644 --- a/site/doc/pages/task.txt +++ b/site/doc/pages/task.txt @@ -20,7 +20,7 @@ If a process with the same `key` is already running anywhere in the runtime inst Task keys may contain ordinary user-facing text. UCE hashes the key before using it as an internal lock/status filename so slashes and other path-like characters cannot escape the task state directory. -`timeout` is enforced in the child process with an alarm. The default is ten minutes. Pass `0` only for tasks that are intentionally unbounded and have their own shutdown path. +`timeout` is enforced in the child process with an alarm. The default is ten minutes. Pass `0` only for tasks that have their own shutdown path. Related: diff --git a/site/doc/pages/task_repeat.txt b/site/doc/pages/task_repeat.txt index ed73080..7bce267 100644 --- a/site/doc/pages/task_repeat.txt +++ b/site/doc/pages/task_repeat.txt @@ -21,7 +21,7 @@ Starts a repeating background worker process. If a process with the same `key` is already running anywhere in the runtime instance, `task_repeat()` does not start a second worker and instead returns the PID of the existing one. Coordination is through the same shared task state used by `task()`. -`timeout` bounds the lifetime of the repeating worker. The default is ten minutes. Pass `0` only for intentionally unbounded workers with another shutdown path. +`timeout` bounds the lifetime of the repeating worker. The default is ten minutes. Pass `0` only for workers that have another shutdown path. Related: diff --git a/site/doc/pages/ws_message.txt b/site/doc/pages/ws_message.txt index c81a542..13abe36 100644 --- a/site/doc/pages/ws_message.txt +++ b/site/doc/pages/ws_message.txt @@ -12,7 +12,7 @@ Returns the payload of the current WebSocket message being handled by `WS(Reques For text frames this is the decoded text payload. For binary frames this `String` contains the raw message bytes. -Use `ws_is_binary()` or `ws_opcode()` to decide how the payload should be interpreted. +Use `ws_is_binary()` or `ws_opcode()` to choose how to parse the payload. Related: diff --git a/site/doc/pages/xml_decode.txt b/site/doc/pages/xml_decode.txt index f4a1dae..00a6aa6 100644 --- a/site/doc/pages/xml_decode.txt +++ b/site/doc/pages/xml_decode.txt @@ -15,7 +15,7 @@ String :content Parses a simple XML document into a structured `DValue`. -`xml_decode()` is intentionally small. It does not validate schemas, DTDs, namespaces, or document types. It parses the first root element and returns the same structural element shape accepted by `xml_encode()`. +`xml_decode()` parses a compact XML subset. It does not validate schemas, DTDs, namespaces, or document types. It parses the first root element and returns the same structural element shape accepted by `xml_encode()`. Try the live example in the [XML demo](../demo/xml.uce). diff --git a/site/doc/pages/yaml_decode.txt b/site/doc/pages/yaml_decode.txt index 259c4c4..148063d 100644 --- a/site/doc/pages/yaml_decode.txt +++ b/site/doc/pages/yaml_decode.txt @@ -15,7 +15,7 @@ xml_decode :content Parses a practical YAML subset into a `DValue`. -`yaml_decode()` is designed for concise UCE config files. It intentionally avoids full YAML schema behavior and does not support anchors, aliases, tags, directives, or complex inline collection syntax. +`yaml_decode()` is designed for concise UCE config files. It supports a practical YAML subset and does not implement anchors, aliases, tags, directives, or complex inline collection syntax. Try the live example in the [YAML demo](../demo/yaml.uce).