Enforce SX boundary contract via boundary.sx spec + runtime validation
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m33s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m33s
Add boundary.sx declaring all 34 I/O primitives, 32 page helpers, and 9 allowed boundary types. Runtime validation in boundary.py checks every registration against the spec — undeclared primitives/helpers crash at startup with SX_BOUNDARY_STRICT=1 (now set in both dev and prod). Key changes: - Move 5 I/O-in-disguise primitives (app-url, asset-url, config, jinja-global, relations-from) from primitives.py to primitives_io.py - Remove duplicate url-for/route-prefix from primitives.py (already in IO) - Fix parse-datetime to return ISO string instead of raw datetime - Add datetime→isoformat conversion in _convert_result at the edge - Wrap page helper return values with boundary type validation - Replace all SxExpr(f"...") patterns with sx_call() or _sx_fragment() - Add assert declaration to primitives.sx Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -238,7 +238,7 @@ def _tokenize_bash(code: str) -> list[tuple[str, str]]:
|
||||
|
||||
def highlight(code: str, language: str = "lisp"):
|
||||
"""Highlight code in the given language. Returns SxExpr for wire format."""
|
||||
from shared.sx.parser import SxExpr
|
||||
from shared.sx.parser import SxExpr, serialize
|
||||
if language in ("lisp", "sx", "sexp"):
|
||||
return SxExpr(highlight_sx(code))
|
||||
elif language in ("python", "py"):
|
||||
@@ -246,5 +246,4 @@ def highlight(code: str, language: str = "lisp"):
|
||||
elif language in ("bash", "sh", "shell"):
|
||||
return SxExpr(highlight_bash(code))
|
||||
# Fallback: no highlighting, just escaped text
|
||||
escaped = code.replace("\\", "\\\\").replace('"', '\\"')
|
||||
return SxExpr(f'(span "{escaped}")')
|
||||
return SxExpr("(span " + serialize(code) + ")")
|
||||
|
||||
@@ -97,7 +97,8 @@
|
||||
|
||||
(define bootstrappers-nav-items (list
|
||||
(dict :label "Overview" :href "/bootstrappers/")
|
||||
(dict :label "JavaScript" :href "/bootstrappers/javascript")))
|
||||
(dict :label "JavaScript" :href "/bootstrappers/javascript")
|
||||
(dict :label "Python" :href "/bootstrappers/python")))
|
||||
|
||||
;; Spec file registry — canonical metadata for spec viewer pages.
|
||||
;; Python only handles file I/O (read-spec-file); all metadata lives here.
|
||||
|
||||
@@ -268,9 +268,11 @@ boot.sx depends on: cssx, orchestration, adapter-dom, render")))
|
||||
(td :class "px-3 py-2 text-green-600" "Live"))
|
||||
(tr :class "border-b border-stone-100"
|
||||
(td :class "px-3 py-2 text-stone-700" "Python")
|
||||
(td :class "px-3 py-2 font-mono text-sm text-stone-400" "bootstrap_py.py")
|
||||
(td :class "px-3 py-2 font-mono text-sm text-stone-400" "shared/sx/")
|
||||
(td :class "px-3 py-2 text-stone-400" "Planned"))
|
||||
(td :class "px-3 py-2 font-mono text-sm text-violet-700"
|
||||
(a :href "/bootstrappers/python" :class "hover:underline"
|
||||
"bootstrap_py.py"))
|
||||
(td :class "px-3 py-2 font-mono text-sm text-stone-500" "sx_ref.py")
|
||||
(td :class "px-3 py-2 text-green-600" "Live"))
|
||||
(tr :class "border-b border-stone-100"
|
||||
(td :class "px-3 py-2 text-stone-700" "Rust")
|
||||
(td :class "px-3 py-2 font-mono text-sm text-stone-400" "bootstrap_rs.py")
|
||||
@@ -320,6 +322,47 @@ boot.sx depends on: cssx, orchestration, adapter-dom, render")))
|
||||
(pre :class "text-xs leading-relaxed whitespace-pre-wrap break-words"
|
||||
(code (highlight bootstrapped-output "javascript"))))))))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Python bootstrapper detail
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(defcomp ~bootstrapper-py-content (&key bootstrapper-source bootstrapped-output)
|
||||
(~doc-page :title "Python Bootstrapper"
|
||||
(div :class "space-y-8"
|
||||
|
||||
(div :class "space-y-3"
|
||||
(p :class "text-stone-600"
|
||||
"This page reads the canonical " (code :class "text-violet-700 text-sm" ".sx")
|
||||
" spec files, runs the Python bootstrapper, and displays both the compiler source and its generated Python output. "
|
||||
"The generated code below is live — it was produced by the bootstrapper at page load time.")
|
||||
(p :class "text-xs text-stone-400 italic"
|
||||
"With SX_USE_REF=1, the server-side SX evaluator running this page IS the bootstrapped output. "
|
||||
"This page re-runs the bootstrapper to display the source and result."))
|
||||
|
||||
(div :class "space-y-3"
|
||||
(div :class "flex items-baseline gap-3"
|
||||
(h2 :class "text-2xl font-semibold text-stone-800" "Bootstrapper")
|
||||
(span :class "text-sm text-stone-400 font-mono" "bootstrap_py.py"))
|
||||
(p :class "text-sm text-stone-500"
|
||||
"The compiler reads " (code :class "text-violet-700 text-sm" ".sx")
|
||||
" spec files (eval, primitives, render, adapter-html) "
|
||||
"and emits a standalone Python module. Platform bridge functions (type constructors, environment ops) "
|
||||
"are emitted as native Python implementations.")
|
||||
(div :class "bg-stone-100 rounded-lg p-5 max-h-96 overflow-y-auto border border-stone-200"
|
||||
(pre :class "text-xs leading-relaxed whitespace-pre-wrap break-words"
|
||||
(code (highlight bootstrapper-source "python")))))
|
||||
|
||||
(div :class "space-y-3"
|
||||
(div :class "flex items-baseline gap-3"
|
||||
(h2 :class "text-2xl font-semibold text-stone-800" "Generated Output")
|
||||
(span :class "text-sm text-stone-400 font-mono" "sx_ref.py"))
|
||||
(p :class "text-sm text-stone-500"
|
||||
"The Python below was generated by running the bootstrapper against the current spec files. "
|
||||
"It is a complete server-side SX evaluator — eval, primitives, and HTML renderer.")
|
||||
(div :class "bg-stone-100 rounded-lg p-5 max-h-96 overflow-y-auto border border-violet-300"
|
||||
(pre :class "text-xs leading-relaxed whitespace-pre-wrap break-words"
|
||||
(code (highlight bootstrapped-output "python"))))))))
|
||||
|
||||
;; ---------------------------------------------------------------------------
|
||||
;; Not found
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
@@ -368,6 +368,10 @@
|
||||
:data (bootstrapper-data slug)
|
||||
:content (if bootstrapper-not-found
|
||||
(~spec-not-found :slug slug)
|
||||
(~bootstrapper-js-content
|
||||
:bootstrapper-source bootstrapper-source
|
||||
:bootstrapped-output bootstrapped-output)))
|
||||
(if (= slug "python")
|
||||
(~bootstrapper-py-content
|
||||
:bootstrapper-source bootstrapper-source
|
||||
:bootstrapped-output bootstrapped-output)
|
||||
(~bootstrapper-js-content
|
||||
:bootstrapper-source bootstrapper-source
|
||||
:bootstrapped-output bootstrapped-output))))
|
||||
|
||||
@@ -135,29 +135,44 @@ def _bootstrapper_data(target: str) -> dict:
|
||||
"""
|
||||
import os
|
||||
|
||||
if target != "javascript":
|
||||
if target not in ("javascript", "python"):
|
||||
return {"bootstrapper-not-found": True}
|
||||
|
||||
ref_dir = os.path.join(os.path.dirname(__file__), "..", "..", "shared", "sx", "ref")
|
||||
if not os.path.isdir(ref_dir):
|
||||
ref_dir = "/app/shared/sx/ref"
|
||||
|
||||
# Read bootstrapper source
|
||||
bs_path = os.path.join(ref_dir, "bootstrap_js.py")
|
||||
try:
|
||||
with open(bs_path, encoding="utf-8") as f:
|
||||
bootstrapper_source = f.read()
|
||||
except FileNotFoundError:
|
||||
bootstrapper_source = "# bootstrapper source not found"
|
||||
if target == "javascript":
|
||||
# Read bootstrapper source
|
||||
bs_path = os.path.join(ref_dir, "bootstrap_js.py")
|
||||
try:
|
||||
with open(bs_path, encoding="utf-8") as f:
|
||||
bootstrapper_source = f.read()
|
||||
except FileNotFoundError:
|
||||
bootstrapper_source = "# bootstrapper source not found"
|
||||
|
||||
# Run the bootstrap to generate JS
|
||||
from shared.sx.ref.bootstrap_js import compile_ref_to_js
|
||||
try:
|
||||
bootstrapped_output = compile_ref_to_js(
|
||||
adapters=["dom", "engine", "orchestration", "boot", "cssx"]
|
||||
)
|
||||
except Exception as e:
|
||||
bootstrapped_output = f"// bootstrap error: {e}"
|
||||
# Run the bootstrap to generate JS
|
||||
from shared.sx.ref.bootstrap_js import compile_ref_to_js
|
||||
try:
|
||||
bootstrapped_output = compile_ref_to_js(
|
||||
adapters=["dom", "engine", "orchestration", "boot", "cssx"]
|
||||
)
|
||||
except Exception as e:
|
||||
bootstrapped_output = f"// bootstrap error: {e}"
|
||||
|
||||
elif target == "python":
|
||||
bs_path = os.path.join(ref_dir, "bootstrap_py.py")
|
||||
try:
|
||||
with open(bs_path, encoding="utf-8") as f:
|
||||
bootstrapper_source = f.read()
|
||||
except FileNotFoundError:
|
||||
bootstrapper_source = "# bootstrapper source not found"
|
||||
|
||||
from shared.sx.ref.bootstrap_py import compile_ref_to_py
|
||||
try:
|
||||
bootstrapped_output = compile_ref_to_py()
|
||||
except Exception as e:
|
||||
bootstrapped_output = f"# bootstrap error: {e}"
|
||||
|
||||
return {
|
||||
"bootstrapper-not-found": None,
|
||||
@@ -176,7 +191,7 @@ def _attr_detail_data(slug: str) -> dict:
|
||||
- attr-not-found (truthy if not found)
|
||||
"""
|
||||
from content.pages import ATTR_DETAILS
|
||||
from shared.sx.helpers import SxExpr
|
||||
from shared.sx.helpers import sx_call
|
||||
|
||||
detail = ATTR_DETAILS.get(slug)
|
||||
if not detail:
|
||||
@@ -193,7 +208,7 @@ def _attr_detail_data(slug: str) -> dict:
|
||||
"attr-description": detail["description"],
|
||||
"attr-example": detail["example"],
|
||||
"attr-handler": detail.get("handler"),
|
||||
"attr-demo": SxExpr(f"(~{demo_name})") if demo_name else None,
|
||||
"attr-demo": sx_call(demo_name) if demo_name else None,
|
||||
"attr-wire-id": wire_id,
|
||||
}
|
||||
|
||||
@@ -201,7 +216,7 @@ def _attr_detail_data(slug: str) -> dict:
|
||||
def _header_detail_data(slug: str) -> dict:
|
||||
"""Return header detail data for a specific header slug."""
|
||||
from content.pages import HEADER_DETAILS
|
||||
from shared.sx.helpers import SxExpr
|
||||
from shared.sx.helpers import sx_call
|
||||
|
||||
detail = HEADER_DETAILS.get(slug)
|
||||
if not detail:
|
||||
@@ -214,14 +229,14 @@ def _header_detail_data(slug: str) -> dict:
|
||||
"header-direction": detail["direction"],
|
||||
"header-description": detail["description"],
|
||||
"header-example": detail.get("example"),
|
||||
"header-demo": SxExpr(f"(~{demo_name})") if demo_name else None,
|
||||
"header-demo": sx_call(demo_name) if demo_name else None,
|
||||
}
|
||||
|
||||
|
||||
def _event_detail_data(slug: str) -> dict:
|
||||
"""Return event detail data for a specific event slug."""
|
||||
from content.pages import EVENT_DETAILS
|
||||
from shared.sx.helpers import SxExpr
|
||||
from shared.sx.helpers import sx_call
|
||||
|
||||
detail = EVENT_DETAILS.get(slug)
|
||||
if not detail:
|
||||
@@ -233,5 +248,5 @@ def _event_detail_data(slug: str) -> dict:
|
||||
"event-title": slug,
|
||||
"event-description": detail["description"],
|
||||
"event-example": detail.get("example"),
|
||||
"event-demo": SxExpr(f"(~{demo_name})") if demo_name else None,
|
||||
"event-demo": sx_call(demo_name) if demo_name else None,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user