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:
@@ -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