W7 done done
This commit is contained in:
parent
04745f39a8
commit
1743c51e46
BIN
dist/uce_0.1.1.deb
vendored
BIN
dist/uce_0.1.1.deb
vendored
Binary file not shown.
BIN
dist/uce_0.1.2-1_amd64.deb
vendored
BIN
dist/uce_0.1.2-1_amd64.deb
vendored
Binary file not shown.
BIN
dist/uce_0.1.3-1_amd64.deb
vendored
BIN
dist/uce_0.1.3-1_amd64.deb
vendored
Binary file not shown.
BIN
dist/uce_0.1.4-1_amd64.deb
vendored
BIN
dist/uce_0.1.4-1_amd64.deb
vendored
Binary file not shown.
@ -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.
|
||||
@ -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": ""
|
||||
}
|
||||
]
|
||||
@ -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`.
|
||||
@ -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. |
|
||||
@ -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
|
||||
@ -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:
|
||||
|
||||
|
||||
30
site/doc/pages/base64_decode.txt
Normal file
30
site/doc/pages/base64_decode.txt
Normal 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")`
|
||||
27
site/doc/pages/base64_encode.txt
Normal file
27
site/doc/pages/base64_encode.txt
Normal 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")`
|
||||
@ -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.
|
||||
|
||||
@ -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); });
|
||||
|
||||
@ -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()); });
|
||||
|
||||
@ -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"]);
|
||||
|
||||
@ -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); });
|
||||
|
||||
@ -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"});
|
||||
|
||||
@ -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"});
|
||||
|
||||
@ -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"]);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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 != ""); });
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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"); });
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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"});
|
||||
|
||||
29
site/doc/pages/split_kv.txt
Normal file
29
site/doc/pages/split_kv.txt
Normal 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.
|
||||
@ -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:
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
|
||||
@ -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).
|
||||
|
||||
|
||||
@ -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).
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user