Dynamic IO proxy: derive proxied primitives from component io_refs

Replace hardcoded IO primitive lists on both client and server with
data-driven registration. Page registry entries carry :io-deps (list
of IO primitive names) instead of :has-io boolean. Client registers
proxied IO on demand per page via registerIoDeps(). Server builds
allowlist from component analysis.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-07 09:13:53 +00:00
parent c2a85ed026
commit cb0990feb3
7 changed files with 61 additions and 38 deletions

View File

@@ -555,11 +555,13 @@ def mount_io_endpoint(app: Any, service_name: str) -> None:
from .jinja_bridge import _get_request_context
from .parser import serialize
# Allowlist of IO primitives + page helpers the client may call
_ALLOWED_IO = {
"highlight", "current-user", "request-arg", "request-path",
"htmx-request?", "app-url", "asset-url", "config",
}
# Build allowlist from all component IO refs across this service
from .jinja_bridge import _COMPONENT_ENV
from .types import Component as _Comp
_ALLOWED_IO: set[str] = set()
for _val in _COMPONENT_ENV.values():
if isinstance(_val, _Comp) and _val.io_refs:
_ALLOWED_IO.update(_val.io_refs)
async def io_proxy(name: str) -> Any:
if name not in _ALLOWED_IO:
@@ -607,4 +609,4 @@ def mount_io_endpoint(app: Any, service_name: str) -> None:
view_func=io_proxy,
methods=["GET", "POST"],
)
logger.info("Mounted IO proxy endpoint for %s at /sx/io/<name>", service_name)
logger.info("Mounted IO proxy for %s: %s", service_name, sorted(_ALLOWED_IO))