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

@@ -176,6 +176,66 @@
:doc "Content-Type of the current request."
:context :request)
(define-io-primitive "request-args-all"
:params ()
:returns "dict"
:async true
:doc "All query string parameters as a dict."
:context :request)
(define-io-primitive "request-form-all"
:params ()
:returns "dict"
:async true
:doc "All form fields as a dict."
:context :request)
(define-io-primitive "request-headers-all"
:params ()
:returns "dict"
:async true
:doc "All request headers as a dict (lowercase keys)."
:context :request)
(define-io-primitive "request-file-name"
:params (field-name)
:returns "string?"
:async true
:doc "Filename of an uploaded file by field name, or nil."
:context :request)
;; Response manipulation
(define-io-primitive "set-response-header"
:params (name value)
:returns "nil"
:async true
:doc "Set a response header. Applied after handler returns."
:context :request)
(define-io-primitive "set-response-status"
:params (status)
:returns "nil"
:async true
:doc "Set the HTTP response status code. Applied after handler returns."
:context :request)
;; Ephemeral state — per-process, resets on restart
(define-io-primitive "state-get"
:params (key &rest default)
:returns "any"
:async true
:doc "Read from ephemeral per-process state dict."
:context :request)
(define-io-primitive "state-set!"
:params (key value)
:returns "nil"
:async true
:doc "Write to ephemeral per-process state dict."
:context :request)
;; --------------------------------------------------------------------------
;; Tier 3: Signal primitives — reactive state for islands

View File

@@ -38,12 +38,13 @@
;; --------------------------------------------------------------------------
;; defhandler — (defhandler name [:path "..." :method :get :csrf false] (&key param...) body)
;; defhandler — (defhandler name [:path "..." :method :get :csrf false :returns "element"] (&key param...) body)
;;
;; Keyword options between name and params list:
;; :path — public route path (string). Without :path, handler is internal-only.
;; :method — HTTP method (keyword: :get :post :put :patch :delete). Default :get.
;; :csrf — CSRF protection (boolean). Default true; set false for POST/PUT etc.
;; :path — public route path (string). Without :path, handler is internal-only.
;; :method — HTTP method (keyword: :get :post :put :patch :delete). Default :get.
;; :csrf — CSRF protection (boolean). Default true; set false for POST/PUT etc.
;; :returns — return type annotation (types.sx vocabulary). Default "element".
;; --------------------------------------------------------------------------
(define parse-handler-args

View File

@@ -226,10 +226,11 @@ def make_handler_def(name, params, body, env, opts=None):
path = opts.get('path') if opts else None
method = str(opts.get('method', 'get')) if opts else 'get'
csrf = opts.get('csrf', True) if opts else True
returns = str(opts.get('returns', 'element')) if opts else 'element'
if isinstance(csrf, str):
csrf = csrf.lower() not in ('false', 'nil', 'no')
return HandlerDef(name=name, params=list(params), body=body, closure=dict(env),
path=path, method=method.lower(), csrf=csrf)
path=path, method=method.lower(), csrf=csrf, returns=returns)
def make_query_def(name, params, doc, body, env):

View File

@@ -185,10 +185,11 @@ def make_handler_def(name, params, body, env, opts=None):
path = opts.get('path') if opts else None
method = str(opts.get('method', 'get')) if opts else 'get'
csrf = opts.get('csrf', True) if opts else True
returns = str(opts.get('returns', 'element')) if opts else 'element'
if isinstance(csrf, str):
csrf = csrf.lower() not in ('false', 'nil', 'no')
return HandlerDef(name=name, params=list(params), body=body, closure=dict(env),
path=path, method=method.lower(), csrf=csrf)
path=path, method=method.lower(), csrf=csrf, returns=returns)
def make_query_def(name, params, doc, body, env):