Extend defhandler with :path/:method/:csrf, migrate 12 ref endpoints to SX

defhandler now supports keyword options for public route registration:
  (defhandler name :path "/..." :method :post :csrf false (&key) body)

Infrastructure: forms.sx parses options, HandlerDef stores path/method/csrf,
register_route_handlers() mounts path-based handlers as app routes.

New IO primitives (boundary.sx "Web interop" section): now, sleep,
request-form, request-json, request-header, request-content-type.

First migration: 12 reference API endpoints from Python f-string SX
to declarative .sx handlers in sx/sx/handlers/ref-api.sx.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-11 23:48:05 +00:00
parent 524c99e4ff
commit fba84540e2
10 changed files with 468 additions and 133 deletions

View File

@@ -221,18 +221,29 @@ class Island:
@dataclass
class HandlerDef:
"""A declarative fragment handler defined in an .sx file.
"""A declarative handler defined in an .sx file.
Created by ``(defhandler name (&key param...) body)``.
The body is evaluated in a sandboxed environment with only
s-expression primitives available.
Created by ``(defhandler name :path "/..." :method :get (&key param...) body)``.
When ``path`` is set, the handler is registered as a public route.
When ``path`` is None, it's an internal fragment handler (legacy behaviour).
"""
name: str
params: list[str] # keyword parameter names
body: Any # unevaluated s-expression body
closure: dict[str, Any] = field(default_factory=dict)
path: str | None = None # public route path (None = internal fragment only)
method: str = "get" # HTTP method (get, post, put, patch, delete)
csrf: bool = True # CSRF protection enabled
@property
def is_route(self) -> bool:
"""True if this handler has a public route path."""
return self.path is not None
def __repr__(self):
if self.path:
return f"<handler:{self.name} {self.method.upper()} {self.path}>"
return f"<handler:{self.name}({', '.join(self.params)})>"