Migrate remaining 7 ref endpoints to SX, add :returns type annotations

Add 14 new IO primitives to boundary.sx: web interop (request-form,
request-json, request-header, request-content-type, request-args-all,
request-form-all, request-headers-all, request-file-name), response
manipulation (set-response-header, set-response-status), ephemeral
state (state-get, state-set!), and timing (now, sleep).

All 19 reference handlers now have :returns type annotations using
types.sx vocabulary. Response meta (headers/status) flows through
context vars, applied by register_route_handlers after execution.

Only SSE endpoint remains in Python (async generator paradigm).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-12 00:14:40 +00:00
parent fba84540e2
commit 575d100f67
9 changed files with 338 additions and 103 deletions

View File

@@ -46,6 +46,13 @@ _handler_service: contextvars.ContextVar[Any] = contextvars.ContextVar(
"_handler_service", default=None
)
_response_meta: contextvars.ContextVar[dict | None] = contextvars.ContextVar(
"_response_meta", default=None
)
# Ephemeral per-process state — resets on restart. For demos/testing only.
_ephemeral_state: dict[str, Any] = {}
def set_handler_service(service_obj: Any) -> None:
"""Bind the local domain service for ``(service ...)`` primitive calls."""
@@ -57,6 +64,16 @@ def get_handler_service() -> Any:
return _handler_service.get(None)
def reset_response_meta() -> None:
"""Reset response meta for a new request."""
_response_meta.set(None)
def get_response_meta() -> dict | None:
"""Get response meta (headers/status) set by handler IO primitives."""
return _response_meta.get(None)
class RequestContext:
"""Per-request context provided to I/O primitives."""
__slots__ = ("user", "is_htmx", "extras")
@@ -372,6 +389,105 @@ async def _io_request_content_type(
return request.content_type or NIL
@register_io_handler("request-args-all")
async def _io_request_args_all(
args: list[Any], kwargs: dict[str, Any], ctx: RequestContext
) -> dict:
"""``(request-args-all)`` → all query params as dict."""
from quart import request
return dict(request.args)
@register_io_handler("request-form-all")
async def _io_request_form_all(
args: list[Any], kwargs: dict[str, Any], ctx: RequestContext
) -> dict:
"""``(request-form-all)`` → all form fields as dict."""
from quart import request
form = await request.form
return dict(form)
@register_io_handler("request-headers-all")
async def _io_request_headers_all(
args: list[Any], kwargs: dict[str, Any], ctx: RequestContext
) -> dict:
"""``(request-headers-all)`` → all headers as dict (lowercase keys)."""
from quart import request
return {k.lower(): v for k, v in request.headers}
@register_io_handler("request-file-name")
async def _io_request_file_name(
args: list[Any], kwargs: dict[str, Any], ctx: RequestContext
) -> Any:
"""``(request-file-name "field")`` → filename or nil."""
if not args:
raise ValueError("request-file-name requires a field name")
from quart import request
from .types import NIL
files = await request.files
f = files.get(str(args[0]))
return f.filename if f else NIL
@register_io_handler("set-response-header")
async def _io_set_response_header(
args: list[Any], kwargs: dict[str, Any], ctx: RequestContext
) -> Any:
"""``(set-response-header "Name" "value")`` → set on response after handler."""
if len(args) < 2:
raise ValueError("set-response-header requires name and value")
from .types import NIL
meta = _response_meta.get(None)
if meta is None:
meta = {"headers": {}, "status": None}
_response_meta.set(meta)
meta["headers"][str(args[0])] = str(args[1])
return NIL
@register_io_handler("set-response-status")
async def _io_set_response_status(
args: list[Any], kwargs: dict[str, Any], ctx: RequestContext
) -> Any:
"""``(set-response-status 503)`` → set status code on response."""
if not args:
raise ValueError("set-response-status requires a status code")
from .types import NIL
meta = _response_meta.get(None)
if meta is None:
meta = {"headers": {}, "status": None}
_response_meta.set(meta)
meta["status"] = int(args[0])
return NIL
@register_io_handler("state-get")
async def _io_state_get(
args: list[Any], kwargs: dict[str, Any], ctx: RequestContext
) -> Any:
"""``(state-get "key" default?)`` → read from ephemeral state."""
if not args:
raise ValueError("state-get requires a key")
from .types import NIL
key = str(args[0])
default = args[1] if len(args) > 1 else NIL
return _ephemeral_state.get(key, default)
@register_io_handler("state-set!")
async def _io_state_set(
args: list[Any], kwargs: dict[str, Any], ctx: RequestContext
) -> Any:
"""``(state-set! "key" value)`` → write to ephemeral state."""
if len(args) < 2:
raise ValueError("state-set! requires key and value")
from .types import NIL
_ephemeral_state[str(args[0])] = args[1]
return NIL
@register_io_handler("csrf-token")
async def _io_csrf_token(
args: list[Any], kwargs: dict[str, Any], ctx: RequestContext