Phase 5: async IO rendering — components call IO primitives client-side

Wire async rendering into client-side routing: pages whose component
trees reference IO primitives (highlight, current-user, etc.) now
render client-side via Promise-aware asyncRenderToDom. IO calls proxy
through /sx/io/<name> endpoint, which falls back to page helpers.

- Add has-io flag to page registry entries (helpers.py)
- Remove IO purity filter — include IO-dependent components in bundles
- Extend try-client-route with 4 paths: pure, data, IO, data+IO
- Convert tryAsyncEvalContent to callback style, add platform mapping
- IO proxy falls back to page helpers (highlight works via proxy)
- Demo page: /isomorphism/async-io with inline highlight calls

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-07 08:12:42 +00:00
parent 04ff03f5d4
commit 79fa1411dc
10 changed files with 1502 additions and 264 deletions

View File

@@ -691,6 +691,15 @@ def _build_pages_sx(service: str) -> str:
deps = components_needed(content_src, _COMPONENT_ENV)
deps_sx = "(" + " ".join(_sx_literal(d) for d in sorted(deps)) + ")"
# Check if any dep component has IO refs (needs async rendering)
from .types import Component as _Comp
has_io = "false"
for dep_name in deps:
comp = _COMPONENT_ENV.get(dep_name)
if isinstance(comp, _Comp) and comp.io_refs:
has_io = "true"
break
# Build closure as SX dict
closure_parts: list[str] = []
for k, v in page_def.closure.items():
@@ -703,6 +712,7 @@ def _build_pages_sx(service: str) -> str:
+ " :path " + _sx_literal(page_def.path)
+ " :auth " + _sx_literal(auth)
+ " :has-data " + has_data
+ " :has-io " + has_io
+ " :content " + _sx_literal(content_src)
+ " :deps " + deps_sx
+ " :closure " + closure_sx + "}"