Fix defhandler to produce sx wire format instead of HTML

execute_handler was using async_render() which renders all the way to
HTML. Fragment providers need to return sx source (s-expression strings)
that consuming apps parse and render client-side.

Added async_eval_to_sx() — a new execution mode that evaluates I/O
primitives and control flow but serializes component/tag calls as sx
source instead of rendering them to HTML. This mirrors how the old
Python handlers used sx_call() to build sx strings.

Also fixed: _ASER_FORMS checked after HTML_TAGS, causing "map" (which
is both an HTML tag and an sx special form) to be serialized as a tag
instead of evaluated. Moved _ASER_FORMS check before HTML_TAGS.

Also fixed: empty? primitive now handles non-len()-able types gracefully.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-03 01:00:00 +00:00
parent 9754b892d6
commit 5578923242
3 changed files with 392 additions and 4 deletions

View File

@@ -122,7 +122,7 @@ async def execute_handler(
4. Return rendered string
"""
from .jinja_bridge import get_component_env, _get_request_context
from .async_eval import async_render
from .async_eval import async_eval_to_sx
from .types import NIL
if args is None:
@@ -139,8 +139,9 @@ async def execute_handler(
# Get request context for I/O primitives
ctx = _get_request_context()
# Async eval+render — I/O primitives are awaited inline
return await async_render(handler_def.body, env, ctx)
# Async eval → sx source — I/O primitives are awaited inline,
# but component/tag calls serialize to sx wire format (not HTML).
return await async_eval_to_sx(handler_def.body, env, ctx)
# ---------------------------------------------------------------------------