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

@@ -713,84 +713,9 @@ def register(url_prefix: str = "/") -> Blueprint:
# Reference API endpoints — remaining Python-only
#
# Most reference endpoints migrated to sx/sx/handlers/ref-api.sx.
# These remain because they need Python-specific features:
# - File upload access (request.files)
# - Dynamic all-params iteration
# - Stateful counters with non-200 responses
# - SSE streaming
# - Custom response headers
# SSE stays in Python — fundamentally different paradigm (async generator).
# ------------------------------------------------------------------
def _ref_wire(wire_id: str, sx_src: str) -> str:
"""Build OOB swap showing the wire response text."""
from sxc.pages.renders import _oob_code
return _oob_code(f"ref-wire-{wire_id}", sx_src)
@csrf_exempt
@bp.post("/geography/hypermedia/reference/api/upload-name")
async def ref_upload_name():
from shared.sx.helpers import sx_response
files = await request.files
f = files.get("file")
name = f.filename if f else "(no file)"
sx_src = f'(span :class "text-stone-800 text-sm" "Received: " (strong "{name}"))'
oob = _ref_wire("sx-encoding", sx_src)
return sx_response(f'(<> {sx_src} {oob})')
@bp.get("/geography/hypermedia/reference/api/echo-headers")
async def ref_echo_headers():
from shared.sx.helpers import sx_response
custom = [(k, v) for k, v in request.headers if k.lower().startswith("x-")]
if not custom:
sx_src = '(span :class "text-stone-400 text-sm" "No custom headers received.")'
else:
items = " ".join(
f'(li (strong "{k}") ": " "{v}")' for k, v in custom)
sx_src = f'(ul :class "text-sm text-stone-700 space-y-1" {items})'
oob = _ref_wire("sx-headers", sx_src)
return sx_response(f'(<> {sx_src} {oob})')
@bp.get("/geography/hypermedia/reference/api/echo-vals")
async def ref_echo_vals_get():
from shared.sx.helpers import sx_response
vals = list(request.args.items())
if not vals:
sx_src = '(span :class "text-stone-400 text-sm" "No values received.")'
else:
items = " ".join(
f'(li (strong "{k}") ": " "{v}")' for k, v in vals)
sx_src = f'(ul :class "text-sm text-stone-700 space-y-1" {items})'
oob_include = _ref_wire("sx-include", sx_src)
return sx_response(f'(<> {sx_src} {oob_include})')
@csrf_exempt
@bp.post("/geography/hypermedia/reference/api/echo-vals")
async def ref_echo_vals_post():
from shared.sx.helpers import sx_response
form = await request.form
vals = list(form.items())
if not vals:
sx_src = '(span :class "text-stone-400 text-sm" "No values received.")'
else:
items = " ".join(
f'(li (strong "{k}") ": " "{v}")' for k, v in vals)
sx_src = f'(ul :class "text-sm text-stone-700 space-y-1" {items})'
oob = _ref_wire("sx-vals", sx_src)
return sx_response(f'(<> {sx_src} {oob})')
_ref_flaky = {"n": 0}
@bp.get("/geography/hypermedia/reference/api/flaky")
async def ref_flaky():
from shared.sx.helpers import sx_response
_ref_flaky["n"] += 1
n = _ref_flaky["n"]
if n % 3 != 0:
return Response("", status=503, content_type="text/plain")
sx_src = f'(span :class "text-emerald-700 text-sm" "Success on attempt " "{n}" "!")'
oob = _ref_wire("sx-retry", sx_src)
return sx_response(f'(<> {sx_src} {oob})')
@bp.get("/geography/hypermedia/reference/api/sse-time")
async def ref_sse_time():
async def generate():
@@ -930,24 +855,4 @@ def register(url_prefix: str = "/") -> Blueprint:
)
return sx_response(sx_src)
# --- Header demos ---
@bp.get("/geography/hypermedia/reference/api/trigger-event")
async def ref_trigger_event():
from shared.sx.helpers import sx_response
now = datetime.now().strftime("%H:%M:%S")
sx_src = f'(span :class "text-stone-800 text-sm" "Loaded at " (strong "{now}") " — check the border!")'
resp = sx_response(sx_src)
resp.headers["SX-Trigger"] = "showNotice"
return resp
@bp.get("/geography/hypermedia/reference/api/retarget")
async def ref_retarget():
from shared.sx.helpers import sx_response
now = datetime.now().strftime("%H:%M:%S")
sx_src = f'(span :class "text-violet-700 text-sm" "Retargeted at " (strong "{now}"))'
resp = sx_response(sx_src)
resp.headers["SX-Retarget"] = "#ref-hdr-retarget-alt"
return resp
return bp

View File

@@ -8,6 +8,7 @@
(defhandler ref-time
:path "/geography/hypermedia/reference/api/time"
:method :get
:returns "element"
(&key)
(let ((now (now "%H:%M:%S")))
(<>
@@ -21,6 +22,7 @@
:path "/geography/hypermedia/reference/api/greet"
:method :post
:csrf false
:returns "element"
(&key)
(let ((name (request-form "name" "stranger")))
(<>
@@ -34,6 +36,7 @@
:path "/geography/hypermedia/reference/api/status"
:method :put
:csrf false
:returns "element"
(&key)
(let ((status (request-form "status" "unknown")))
(<>
@@ -47,6 +50,7 @@
:path "/geography/hypermedia/reference/api/theme"
:method :patch
:csrf false
:returns "element"
(&key)
(let ((theme (request-form "theme" "unknown")))
(<>
@@ -60,6 +64,7 @@
:path "/geography/hypermedia/reference/api/item/<item_id>"
:method :delete
:csrf false
:returns "element"
(&key)
(<>
(~doc-oob-code :target-id "ref-wire-sx-delete" :text "\"\"")))
@@ -69,6 +74,7 @@
(defhandler ref-trigger-search
:path "/geography/hypermedia/reference/api/trigger-search"
:method :get
:returns "element"
(&key)
(let ((q (request-arg "q" "")))
(let ((sx-text (if (= q "")
@@ -85,6 +91,7 @@
(defhandler ref-swap-item
:path "/geography/hypermedia/reference/api/swap-item"
:method :get
:returns "element"
(&key)
(let ((now (now "%H:%M:%S")))
(<>
@@ -97,6 +104,7 @@
(defhandler ref-oob
:path "/geography/hypermedia/reference/api/oob"
:method :get
:returns "element"
(&key)
(let ((now (now "%H:%M:%S")))
(<>
@@ -111,6 +119,7 @@
(defhandler ref-select-page
:path "/geography/hypermedia/reference/api/select-page"
:method :get
:returns "element"
(&key)
(let ((now (now "%H:%M:%S")))
(<>
@@ -127,6 +136,7 @@
(defhandler ref-slow-echo
:path "/geography/hypermedia/reference/api/slow-echo"
:method :get
:returns "element"
(&key)
(let ((q (request-arg "q" "")))
(sleep 800)
@@ -140,6 +150,7 @@
(defhandler ref-prompt-echo
:path "/geography/hypermedia/reference/api/prompt-echo"
:method :get
:returns "element"
(&key)
(let ((name (request-header "SX-Prompt" "anonymous")))
(<>
@@ -152,5 +163,135 @@
(defhandler ref-error-500
:path "/geography/hypermedia/reference/api/error-500"
:method :get
:returns "nil"
(&key)
(abort 500 "Server error"))
;; ==========================================================================
;; Remaining reference endpoints — migrated from Python
;; ==========================================================================
;; --- sx-encoding demo: file upload name ---
(defhandler ref-upload-name
:path "/geography/hypermedia/reference/api/upload-name"
:method :post
:csrf false
:returns "element"
(&key)
(let ((name (request-file-name "file")))
(let ((display (if (nil? name) "(no file)" name)))
(let ((sx-text (str "(span :class \"text-stone-800 text-sm\" \"Received: \" (strong \"" display "\"))")))
(<>
(span :class "text-stone-800 text-sm" "Received: " (strong display))
(~doc-oob-code :target-id "ref-wire-sx-encoding" :text sx-text))))))
;; --- sx-headers demo: echo custom headers ---
(defhandler ref-echo-headers
:path "/geography/hypermedia/reference/api/echo-headers"
:method :get
:returns "element"
(&key)
(let ((all-headers (into (list) (request-headers-all))))
(let ((custom (filter
(fn (pair) (starts-with? (first pair) "x-"))
all-headers)))
(let ((sx-text
(if (empty? custom)
"(span :class \"text-stone-400 text-sm\" \"No custom headers received.\")"
(str "(ul :class \"text-sm text-stone-700 space-y-1\" "
(join " " (map (fn (pair) (str "(li (strong \"" (first pair) "\") \": \" \"" (nth pair 1) "\")")) custom))
")"))))
(<>
(if (empty? custom)
(span :class "text-stone-400 text-sm" "No custom headers received.")
(ul :class "text-sm text-stone-700 space-y-1"
(map (fn (pair) (li (strong (first pair)) ": " (nth pair 1))) custom)))
(~doc-oob-code :target-id "ref-wire-sx-headers" :text sx-text))))))
;; --- sx-include demo: echo GET query params ---
(defhandler ref-echo-vals-get
:path "/geography/hypermedia/reference/api/echo-vals"
:method :get
:returns "element"
(&key)
(let ((vals (into (list) (request-args-all))))
(let ((sx-text
(if (empty? vals)
"(span :class \"text-stone-400 text-sm\" \"No values received.\")"
(str "(ul :class \"text-sm text-stone-700 space-y-1\" "
(join " " (map (fn (pair) (str "(li (strong \"" (first pair) "\") \": \" \"" (nth pair 1) "\")")) vals))
")"))))
(<>
(if (empty? vals)
(span :class "text-stone-400 text-sm" "No values received.")
(ul :class "text-sm text-stone-700 space-y-1"
(map (fn (pair) (li (strong (first pair)) ": " (nth pair 1))) vals)))
(~doc-oob-code :target-id "ref-wire-sx-include" :text sx-text)))))
;; --- sx-vals demo: echo POST form values ---
(defhandler ref-echo-vals-post
:path "/geography/hypermedia/reference/api/echo-vals"
:method :post
:csrf false
:returns "element"
(&key)
(let ((vals (into (list) (request-form-all))))
(let ((sx-text
(if (empty? vals)
"(span :class \"text-stone-400 text-sm\" \"No values received.\")"
(str "(ul :class \"text-sm text-stone-700 space-y-1\" "
(join " " (map (fn (pair) (str "(li (strong \"" (first pair) "\") \": \" \"" (nth pair 1) "\")")) vals))
")"))))
(<>
(if (empty? vals)
(span :class "text-stone-400 text-sm" "No values received.")
(ul :class "text-sm text-stone-700 space-y-1"
(map (fn (pair) (li (strong (first pair)) ": " (nth pair 1))) vals)))
(~doc-oob-code :target-id "ref-wire-sx-vals" :text sx-text)))))
;; --- sx-retry demo: flaky endpoint (fails 2/3 times) ---
(defhandler ref-flaky
:path "/geography/hypermedia/reference/api/flaky"
:method :get
:returns "element"
(&key)
(let ((n (+ (state-get "ref-flaky-n" 0) 1)))
(state-set! "ref-flaky-n" n)
(if (not (= (mod n 3) 0))
(do
(set-response-status 503)
"")
(let ((sx-text (str "(span :class \"text-emerald-700 text-sm\" \"Success on attempt \" \"" n "\" \"!\")")))
(<>
(span :class "text-emerald-700 text-sm" "Success on attempt " (str n) "!")
(~doc-oob-code :target-id "ref-wire-sx-retry" :text sx-text))))))
;; --- sx-trigger-event demo: response header triggers ---
(defhandler ref-trigger-event
:path "/geography/hypermedia/reference/api/trigger-event"
:method :get
:returns "element"
(&key)
(let ((now (now "%H:%M:%S")))
(set-response-header "SX-Trigger" "showNotice")
(<>
(span :class "text-stone-800 text-sm" "Loaded at " (strong now) " — check the border!"))))
;; --- sx-retarget demo: response header retargets ---
(defhandler ref-retarget
:path "/geography/hypermedia/reference/api/retarget"
:method :get
:returns "element"
(&key)
(let ((now (now "%H:%M:%S")))
(set-response-header "SX-Retarget" "#ref-hdr-retarget-alt")
(<>
(span :class "text-violet-700 text-sm" "Retargeted at " (strong now)))))