sx-host plan steps 1-2: defhelper + SX config + SXTP spec + nav tools
Step 1 — defhelper: SX-defined page data helpers replace Python helpers. (defhelper name (params) body) in .sx files, using existing IO primitives (query, action, service). Loaded into OCaml kernel as pure SX defines. Step 2 — SX config: app-config.sx replaces app-config.yaml with (defconfig) form. (env-get "VAR") resolves secrets from environment. Kebab-to-underscore aliasing ensures backward compatibility with all 174 config consumers. Also: SXTP protocol spec (applications/sxtp/spec.sx), docs article, sx_nav move/delete modes, reactive-runtime moved to geography. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -51,6 +51,7 @@ def _eval_error_sx(e: EvalError, context: str) -> str:
|
||||
|
||||
_PAGE_REGISTRY: dict[str, dict[str, PageDef]] = {}
|
||||
_PAGE_HELPERS: dict[str, dict[str, Any]] = {} # service → name → callable
|
||||
_SX_HELPERS: dict[str, dict[str, str]] = {} # service → name → SX source
|
||||
|
||||
|
||||
def register_page(service: str, page_def: PageDef) -> None:
|
||||
@@ -137,6 +138,19 @@ def get_page_helpers(service: str) -> dict[str, Any]:
|
||||
return dict(_PAGE_HELPERS.get(service, {}))
|
||||
|
||||
|
||||
def register_sx_helper(service: str, name: str, source: str) -> None:
|
||||
"""Register an SX-defined helper (from defhelper) for a service."""
|
||||
if service not in _SX_HELPERS:
|
||||
_SX_HELPERS[service] = {}
|
||||
_SX_HELPERS[service][name] = source
|
||||
logger.debug("Registered SX helper %s:%s", service, name)
|
||||
|
||||
|
||||
def get_sx_helpers(service: str) -> dict[str, str]:
|
||||
"""Return SX-defined helpers for a service (name → SX source)."""
|
||||
return dict(_SX_HELPERS.get(service, {}))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Loading — parse .sx files and collect PageDef instances
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -179,8 +193,8 @@ def _parse_defpage(expr: list) -> PageDef | None:
|
||||
|
||||
|
||||
def load_page_file(filepath: str, service_name: str) -> list[PageDef]:
|
||||
"""Parse an .sx file and register any defpage definitions."""
|
||||
from .parser import parse_all
|
||||
"""Parse an .sx file and register any defpage/defhelper definitions."""
|
||||
from .parser import parse_all, serialize
|
||||
|
||||
with open(filepath, encoding="utf-8") as f:
|
||||
source = f.read()
|
||||
@@ -189,16 +203,42 @@ def load_page_file(filepath: str, service_name: str) -> list[PageDef]:
|
||||
pages: list[PageDef] = []
|
||||
|
||||
for expr in exprs:
|
||||
if (isinstance(expr, list) and expr
|
||||
and hasattr(expr[0], 'name') and expr[0].name == "defpage"):
|
||||
if not isinstance(expr, list) or not expr:
|
||||
continue
|
||||
head = getattr(expr[0], 'name', None)
|
||||
if head == "defpage":
|
||||
pd = _parse_defpage(expr)
|
||||
if pd:
|
||||
register_page(service_name, pd)
|
||||
pages.append(pd)
|
||||
elif head == "defhelper":
|
||||
_parse_defhelper(expr, service_name, serialize)
|
||||
|
||||
return pages
|
||||
|
||||
|
||||
def _parse_defhelper(expr: list, service_name: str, serialize) -> None:
|
||||
"""Parse (defhelper name (params...) body...) and register as SX helper.
|
||||
|
||||
Translates to a (define name (fn (params...) body...)) SX source string
|
||||
that will be loaded into the OCaml kernel at render time.
|
||||
"""
|
||||
if len(expr) < 4:
|
||||
logger.warning("defhelper: too few forms: %s", expr[:2])
|
||||
return
|
||||
name = expr[1].name if hasattr(expr[1], 'name') else str(expr[1])
|
||||
params = expr[2]
|
||||
body = expr[3:]
|
||||
|
||||
# Build the equivalent define/fn form
|
||||
body_sx = " ".join(serialize(b) for b in body)
|
||||
if len(body) > 1:
|
||||
body_sx = f"(do {body_sx})"
|
||||
params_sx = serialize(params)
|
||||
sx_source = f'(define {name} (fn {params_sx} {body_sx}))'
|
||||
register_sx_helper(service_name, name, sx_source)
|
||||
|
||||
|
||||
def load_page_dir(directory: str, service_name: str) -> list[PageDef]:
|
||||
"""Load all .sx files from a directory and register pages."""
|
||||
import glob as glob_mod
|
||||
|
||||
Reference in New Issue
Block a user