W7 done done

This commit is contained in:
root 2026-06-15 11:04:16 +00:00
parent 04745f39a8
commit 1743c51e46
33 changed files with 110 additions and 547 deletions

BIN
dist/uce_0.1.1.deb vendored

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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.

View File

@ -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": ""
}
]

View File

@ -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`.

View File

@ -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 | `<? if(allow_server_demos) { render_card("task_repeat.uce", "Task Repeat", "Recurring task scheduling"); } ?>` | 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 | `<input type="text" name="task-name" value="<?= task_name ?>"/>` | 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=<?= uri_encode(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 | `<input type="text" name="task-name" value="<?= task_name ?>"/>` | 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=<?= uri_encode(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 | `<li>nginx serves static files directly from `site/`</li>` | 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. |

View File

@ -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=<entry>
```
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 `<signal.h>`** → 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

View File

@ -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:

View File

@ -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")`

View File

@ -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")`

View File

@ -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.
- `<?= expression ?>` is escaped interpolation; prefer it for user-visible text.
- `<?: expression ?>` is trusted raw markup output, closer to a deliberate `dangerouslySetInnerHTML` decision.
- `<?: expression ?>` 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.

View File

@ -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); });

View File

@ -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()); });

View File

@ -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"]);

View File

@ -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); });

View File

@ -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"});

View File

@ -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"});

View File

@ -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"]);

View File

@ -4,7 +4,7 @@ vector<T> filter(vector<T> items, function<bool (T)> 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

View File

@ -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

View File

@ -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 != ""); });

View File

@ -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");

View File

@ -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"); });

View File

@ -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);

View File

@ -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"});

View File

@ -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.

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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).

View File

@ -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).