Split boundary.sx: separate language contract from app-specific declarations

boundary.sx was mixing three concerns in one file:
- Core SX I/O primitives (the language contract)
- Deployment-specific layout I/O (app architecture)
- Per-service page helpers (fully app-specific)

Now split into three tiers:
1. shared/sx/ref/boundary.sx — core I/O only (frag, query, current-user, etc.)
2. shared/sx/ref/boundary-app.sx — deployment layout contexts (*-header-ctx, *-ctx)
3. {service}/sx/boundary.sx — per-service page helpers

The boundary parser loads all three tiers automatically. Validation error
messages now point to the correct file for each tier.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-06 12:41:38 +00:00
parent a90c8bf3fc
commit 44d5414bc6
8 changed files with 432 additions and 339 deletions

41
blog/sx/boundary.sx Normal file
View File

@@ -0,0 +1,41 @@
;; Blog service — page helper declarations.
(define-page-helper "editor-data"
:params (&key)
:returns "dict"
:service "blog")
(define-page-helper "editor-page-data"
:params (&key)
:returns "dict"
:service "blog")
(define-page-helper "post-admin-data"
:params (&key slug)
:returns "dict"
:service "blog")
(define-page-helper "post-data-data"
:params (&key slug)
:returns "dict"
:service "blog")
(define-page-helper "post-preview-data"
:params (&key slug)
:returns "dict"
:service "blog")
(define-page-helper "post-entries-data"
:params (&key slug)
:returns "dict"
:service "blog")
(define-page-helper "post-settings-data"
:params (&key slug)
:returns "dict"
:service "blog")
(define-page-helper "post-edit-data"
:params (&key slug)
:returns "dict"
:service "blog")

61
events/sx/boundary.sx Normal file
View File

@@ -0,0 +1,61 @@
;; Events service — page helper declarations.
(define-page-helper "calendar-admin-data"
:params (&key calendar-slug)
:returns "dict"
:service "events")
(define-page-helper "day-admin-data"
:params (&key calendar-slug year month day)
:returns "dict"
:service "events")
(define-page-helper "slots-data"
:params (&key calendar-slug)
:returns "dict"
:service "events")
(define-page-helper "slot-data"
:params (&key calendar-slug slot-id)
:returns "dict"
:service "events")
(define-page-helper "entry-data"
:params (&key calendar-slug entry-id)
:returns "dict"
:service "events")
(define-page-helper "entry-admin-data"
:params (&key calendar-slug entry-id year month day)
:returns "dict"
:service "events")
(define-page-helper "ticket-types-data"
:params (&key calendar-slug entry-id year month day)
:returns "dict"
:service "events")
(define-page-helper "ticket-type-data"
:params (&key calendar-slug entry-id ticket-type-id year month day)
:returns "dict"
:service "events")
(define-page-helper "tickets-data"
:params (&key)
:returns "dict"
:service "events")
(define-page-helper "ticket-detail-data"
:params (&key code)
:returns "dict"
:service "events")
(define-page-helper "ticket-admin-data"
:params (&key)
:returns "dict"
:service "events")
(define-page-helper "markets-data"
:params (&key)
:returns "dict"
:service "events")

21
market/sx/boundary.sx Normal file
View File

@@ -0,0 +1,21 @@
;; Market service — page helper declarations.
(define-page-helper "all-markets-data"
:params (&key)
:returns "dict"
:service "market")
(define-page-helper "page-markets-data"
:params (&key slug)
:returns "dict"
:service "market")
(define-page-helper "page-admin-data"
:params (&key slug)
:returns "dict"
:service "market")
(define-page-helper "market-home-data"
:params (&key page-slug market-slug)
:returns "dict"
:service "market")

View File

@@ -69,22 +69,25 @@ def validate_primitive(name: str) -> None:
def validate_io(name: str) -> None: def validate_io(name: str) -> None:
"""Validate that an I/O primitive is declared in boundary.sx.""" """Validate that an I/O primitive is declared in boundary.sx or boundary-app.sx."""
_load_declarations() _load_declarations()
assert _DECLARED_IO is not None assert _DECLARED_IO is not None
if name not in _DECLARED_IO: if name not in _DECLARED_IO:
_report(f"Undeclared I/O primitive: {name!r}. Add to boundary.sx.") _report(
f"Undeclared I/O primitive: {name!r}. "
f"Add to boundary.sx (core) or boundary-app.sx (deployment)."
)
def validate_helper(service: str, name: str) -> None: def validate_helper(service: str, name: str) -> None:
"""Validate that a page helper is declared in boundary.sx.""" """Validate that a page helper is declared in {service}/sx/boundary.sx."""
_load_declarations() _load_declarations()
assert _DECLARED_HELPERS is not None assert _DECLARED_HELPERS is not None
svc_helpers = _DECLARED_HELPERS.get(service, frozenset()) svc_helpers = _DECLARED_HELPERS.get(service, frozenset())
if name not in svc_helpers: if name not in svc_helpers:
_report( _report(
f"Undeclared page helper: {name!r} for service {service!r}. " f"Undeclared page helper: {name!r} for service {service!r}. "
f"Add to boundary.sx." f"Add to {service}/sx/boundary.sx."
) )

View File

@@ -0,0 +1,118 @@
;; ==========================================================================
;; boundary-app.sx — Deployment-specific boundary declarations
;;
;; Layout context I/O primitives for THIS deployment's service architecture.
;; These are NOT part of the SX language contract — a different deployment
;; would declare different layout contexts here.
;;
;; The core SX I/O contract lives in boundary.sx.
;; Per-service page helpers live in {service}/sx/boundary.sx.
;; ==========================================================================
;; --------------------------------------------------------------------------
;; Layout context providers — deployment-specific I/O
;; --------------------------------------------------------------------------
;; Shared across all services (root layout)
(define-io-primitive "root-header-ctx"
:params ()
:returns "dict"
:async true
:doc "Dict with root header values (cart-mini, auth-menu, nav-tree, etc.)."
:context :request)
(define-io-primitive "select-colours"
:params ()
:returns "string"
:async true
:doc "Shared select/hover CSS class string."
:context :request)
(define-io-primitive "account-nav-ctx"
:params ()
:returns "any"
:async true
:doc "Account nav fragments, or nil."
:context :request)
(define-io-primitive "app-rights"
:params ()
:returns "dict"
:async true
:doc "User rights dict from g.rights."
:context :request)
;; Blog service layout
(define-io-primitive "post-header-ctx"
:params ()
:returns "dict"
:async true
:doc "Dict with post-level header values."
:context :request)
;; Cart service layout
(define-io-primitive "cart-page-ctx"
:params ()
:returns "dict"
:async true
:doc "Dict with cart page header values."
:context :request)
;; Events service layouts
(define-io-primitive "events-calendar-ctx"
:params ()
:returns "dict"
:async true
:doc "Dict with events calendar header values."
:context :request)
(define-io-primitive "events-day-ctx"
:params ()
:returns "dict"
:async true
:doc "Dict with events day header values."
:context :request)
(define-io-primitive "events-entry-ctx"
:params ()
:returns "dict"
:async true
:doc "Dict with events entry header values."
:context :request)
(define-io-primitive "events-slot-ctx"
:params ()
:returns "dict"
:async true
:doc "Dict with events slot header values."
:context :request)
(define-io-primitive "events-ticket-type-ctx"
:params ()
:returns "dict"
:async true
:doc "Dict with ticket type header values."
:context :request)
;; Market service layout
(define-io-primitive "market-header-ctx"
:params ()
:returns "dict"
:async true
:doc "Dict with market header data."
:context :request)
;; Federation service layout
(define-io-primitive "federation-actor-ctx"
:params ()
:returns "dict?"
:async true
:doc "Serialized ActivityPub actor dict or nil."
:context :request)

View File

@@ -1,12 +1,12 @@
;; ========================================================================== ;; ==========================================================================
;; boundary.sx — SX boundary contract ;; boundary.sx — SX language boundary contract
;; ;;
;; Declares everything allowed to cross the host-SX boundary: ;; Declares the core I/O primitives that any SX host must provide.
;; I/O primitives (Tier 2) and page helpers (Tier 3). ;; This is the LANGUAGE contract — not deployment-specific.
;; ;;
;; Pure primitives (Tier 1) are declared in primitives.sx. ;; Pure primitives (Tier 1) are declared in primitives.sx.
;; This file declares what primitives.sx does NOT cover: ;; Deployment-specific I/O (layout contexts) lives in boundary-app.sx.
;; async/side-effectful host functions that need request context. ;; Per-service page helpers live in {service}/sx/boundary.sx.
;; ;;
;; Format: ;; Format:
;; (define-io-primitive "name" ;; (define-io-primitive "name"
@@ -16,13 +16,6 @@
;; :doc "description" ;; :doc "description"
;; :context :request) ;; :context :request)
;; ;;
;; (define-page-helper "name"
;; :params (param1 param2)
;; :returns "type"
;; :service "service-name")
;;
;; Bootstrappers read this file and emit frozen sets + validation
;; functions for the target language.
;; ========================================================================== ;; ==========================================================================
@@ -34,9 +27,11 @@
;; -------------------------------------------------------------------------- ;; --------------------------------------------------------------------------
;; Tier 2: I/O primitives — async, side-effectful, need host context ;; Tier 2: Core I/O primitives — async, side-effectful, need host context
;; -------------------------------------------------------------------------- ;; --------------------------------------------------------------------------
;; Cross-service communication
(define-io-primitive "frag" (define-io-primitive "frag"
:params (service frag-type &key) :params (service frag-type &key)
:returns "string" :returns "string"
@@ -58,6 +53,15 @@
:doc "Call an action on another service via internal HTTP." :doc "Call an action on another service via internal HTTP."
:context :request) :context :request)
(define-io-primitive "service"
:params (service-or-method &rest args &key)
:returns "any"
:async true
:doc "Call a domain service method. Two-arg: (service svc method). One-arg: (service method) uses bound handler service."
:context :request)
;; Request context
(define-io-primitive "current-user" (define-io-primitive "current-user"
:params () :params ()
:returns "dict?" :returns "dict?"
@@ -72,13 +76,6 @@
:doc "True if current request has HX-Request header." :doc "True if current request has HX-Request header."
:context :request) :context :request)
(define-io-primitive "service"
:params (service-or-method &rest args &key)
:returns "any"
:async true
:doc "Call a domain service method. Two-arg: (service svc method). One-arg: (service method) uses bound handler service."
:context :request)
(define-io-primitive "request-arg" (define-io-primitive "request-arg"
:params (name &rest default) :params (name &rest default)
:returns "any" :returns "any"
@@ -93,18 +90,11 @@
:doc "Current request path." :doc "Current request path."
:context :request) :context :request)
(define-io-primitive "nav-tree" (define-io-primitive "request-view-args"
:params () :params (key)
:returns "list" :returns "any"
:async true :async true
:doc "Navigation tree as list of node dicts." :doc "Read a URL view argument from the current request."
:context :request)
(define-io-primitive "get-children"
:params (&key parent-type parent-id)
:returns "list"
:async true
:doc "Fetch child entities for a parent."
:context :request) :context :request)
(define-io-primitive "g" (define-io-primitive "g"
@@ -128,6 +118,8 @@
:doc "Raise HTTP error from SX." :doc "Raise HTTP error from SX."
:context :request) :context :request)
;; Routing
(define-io-primitive "url-for" (define-io-primitive "url-for"
:params (endpoint &key) :params (endpoint &key)
:returns "string" :returns "string"
@@ -142,105 +134,23 @@
:doc "Service URL prefix for dev/prod routing." :doc "Service URL prefix for dev/prod routing."
:context :request) :context :request)
(define-io-primitive "root-header-ctx" ;; Navigation and relations
(define-io-primitive "nav-tree"
:params () :params ()
:returns "dict" :returns "list"
:async true :async true
:doc "Dict with root header values (cart-mini, auth-menu, nav-tree, etc.)." :doc "Navigation tree as list of node dicts."
:context :request) :context :request)
(define-io-primitive "post-header-ctx" (define-io-primitive "get-children"
:params () :params (&key parent-type parent-id)
:returns "dict" :returns "list"
:async true :async true
:doc "Dict with post-level header values." :doc "Fetch child entities for a parent."
:context :request) :context :request)
(define-io-primitive "select-colours" ;; Config and host context (sync — no await needed)
:params ()
:returns "string"
:async true
:doc "Shared select/hover CSS class string."
:context :request)
(define-io-primitive "account-nav-ctx"
:params ()
:returns "any"
:async true
:doc "Account nav fragments, or nil."
:context :request)
(define-io-primitive "app-rights"
:params ()
:returns "dict"
:async true
:doc "User rights dict from g.rights."
:context :request)
(define-io-primitive "federation-actor-ctx"
:params ()
:returns "dict?"
:async true
:doc "Serialized ActivityPub actor dict or nil."
:context :request)
(define-io-primitive "request-view-args"
:params (key)
:returns "any"
:async true
:doc "Read a URL view argument from the current request."
:context :request)
(define-io-primitive "cart-page-ctx"
:params ()
:returns "dict"
:async true
:doc "Dict with cart page header values."
:context :request)
(define-io-primitive "events-calendar-ctx"
:params ()
:returns "dict"
:async true
:doc "Dict with events calendar header values."
:context :request)
(define-io-primitive "events-day-ctx"
:params ()
:returns "dict"
:async true
:doc "Dict with events day header values."
:context :request)
(define-io-primitive "events-entry-ctx"
:params ()
:returns "dict"
:async true
:doc "Dict with events entry header values."
:context :request)
(define-io-primitive "events-slot-ctx"
:params ()
:returns "dict"
:async true
:doc "Dict with events slot header values."
:context :request)
(define-io-primitive "events-ticket-type-ctx"
:params ()
:returns "dict"
:async true
:doc "Dict with ticket type header values."
:context :request)
(define-io-primitive "market-header-ctx"
:params ()
:returns "dict"
:async true
:doc "Dict with market header data."
:context :request)
;; Moved from primitives.py — these need host context (infra/config/Quart)
(define-io-primitive "app-url" (define-io-primitive "app-url"
:params (service &rest path) :params (service &rest path)
@@ -278,185 +188,6 @@
:context :config) :context :config)
;; --------------------------------------------------------------------------
;; Tier 3: Page helpers — service-scoped, registered per app
;; --------------------------------------------------------------------------
;; SX docs service
(define-page-helper "highlight"
:params (code lang)
:returns "sx-source"
:service "sx")
(define-page-helper "primitives-data"
:params ()
:returns "dict"
:service "sx")
(define-page-helper "special-forms-data"
:params ()
:returns "dict"
:service "sx")
(define-page-helper "reference-data"
:params (slug)
:returns "dict"
:service "sx")
(define-page-helper "attr-detail-data"
:params (slug)
:returns "dict"
:service "sx")
(define-page-helper "header-detail-data"
:params (slug)
:returns "dict"
:service "sx")
(define-page-helper "event-detail-data"
:params (slug)
:returns "dict"
:service "sx")
(define-page-helper "read-spec-file"
:params (filename)
:returns "string"
:service "sx")
(define-page-helper "bootstrapper-data"
:params (target)
:returns "dict"
:service "sx")
(define-page-helper "bundle-analyzer-data"
:params ()
:returns "dict"
:service "sx")
;; Blog service
(define-page-helper "editor-data"
:params (&key)
:returns "dict"
:service "blog")
(define-page-helper "editor-page-data"
:params (&key)
:returns "dict"
:service "blog")
(define-page-helper "post-admin-data"
:params (&key slug)
:returns "dict"
:service "blog")
(define-page-helper "post-data-data"
:params (&key slug)
:returns "dict"
:service "blog")
(define-page-helper "post-preview-data"
:params (&key slug)
:returns "dict"
:service "blog")
(define-page-helper "post-entries-data"
:params (&key slug)
:returns "dict"
:service "blog")
(define-page-helper "post-settings-data"
:params (&key slug)
:returns "dict"
:service "blog")
(define-page-helper "post-edit-data"
:params (&key slug)
:returns "dict"
:service "blog")
;; Events service
(define-page-helper "calendar-admin-data"
:params (&key calendar-slug)
:returns "dict"
:service "events")
(define-page-helper "day-admin-data"
:params (&key calendar-slug year month day)
:returns "dict"
:service "events")
(define-page-helper "slots-data"
:params (&key calendar-slug)
:returns "dict"
:service "events")
(define-page-helper "slot-data"
:params (&key calendar-slug slot-id)
:returns "dict"
:service "events")
(define-page-helper "entry-data"
:params (&key calendar-slug entry-id)
:returns "dict"
:service "events")
(define-page-helper "entry-admin-data"
:params (&key calendar-slug entry-id year month day)
:returns "dict"
:service "events")
(define-page-helper "ticket-types-data"
:params (&key calendar-slug entry-id year month day)
:returns "dict"
:service "events")
(define-page-helper "ticket-type-data"
:params (&key calendar-slug entry-id ticket-type-id year month day)
:returns "dict"
:service "events")
(define-page-helper "tickets-data"
:params (&key)
:returns "dict"
:service "events")
(define-page-helper "ticket-detail-data"
:params (&key code)
:returns "dict"
:service "events")
(define-page-helper "ticket-admin-data"
:params (&key)
:returns "dict"
:service "events")
(define-page-helper "markets-data"
:params (&key)
:returns "dict"
:service "events")
;; Market service
(define-page-helper "all-markets-data"
:params (&key)
:returns "dict"
:service "market")
(define-page-helper "page-markets-data"
:params (&key slug)
:returns "dict"
:service "market")
(define-page-helper "page-admin-data"
:params (&key slug)
:returns "dict"
:service "market")
(define-page-helper "market-home-data"
:params (&key page-slug market-slug)
:returns "dict"
:service "market")
;; -------------------------------------------------------------------------- ;; --------------------------------------------------------------------------
;; Boundary types — what's allowed to cross the host-SX boundary ;; Boundary types — what's allowed to cross the host-SX boundary
;; -------------------------------------------------------------------------- ;; --------------------------------------------------------------------------

View File

@@ -1,14 +1,23 @@
""" """
Parse boundary.sx and primitives.sx to extract declared names. Parse boundary declarations from multiple sources.
Three tiers of boundary files:
1. shared/sx/ref/boundary.sx — core SX language I/O contract
2. shared/sx/ref/boundary-app.sx — deployment-specific layout I/O
3. {service}/sx/boundary.sx — per-service page helpers
Shared by both bootstrap_py.py and bootstrap_js.py, and used at runtime Shared by both bootstrap_py.py and bootstrap_js.py, and used at runtime
by the validation module. by the validation module.
""" """
from __future__ import annotations from __future__ import annotations
import glob
import logging
import os import os
from typing import Any from typing import Any
logger = logging.getLogger("sx.boundary_parser")
# Allow standalone use (from bootstrappers) or in-project imports # Allow standalone use (from bootstrappers) or in-project imports
try: try:
from shared.sx.parser import parse_all from shared.sx.parser import parse_all
@@ -26,12 +35,28 @@ def _ref_dir() -> str:
return os.path.dirname(os.path.abspath(__file__)) return os.path.dirname(os.path.abspath(__file__))
def _project_root() -> str:
"""Return the project root (3 levels up from shared/sx/ref/)."""
ref = _ref_dir()
# shared/sx/ref -> shared/sx -> shared -> project root
root = os.path.abspath(os.path.join(ref, "..", "..", ".."))
# In Docker the layout is /app/shared/sx/ref -> /app
if not os.path.isdir(root):
root = os.path.abspath(os.path.join(ref, "..", ".."))
return root
def _read_file(filename: str) -> str: def _read_file(filename: str) -> str:
filepath = os.path.join(_ref_dir(), filename) filepath = os.path.join(_ref_dir(), filename)
with open(filepath, encoding="utf-8") as f: with open(filepath, encoding="utf-8") as f:
return f.read() return f.read()
def _read_file_path(filepath: str) -> str:
with open(filepath, encoding="utf-8") as f:
return f.read()
def _extract_keyword_arg(expr: list, key: str) -> Any: def _extract_keyword_arg(expr: list, key: str) -> Any:
"""Extract :key value from a flat keyword-arg list.""" """Extract :key value from a flat keyword-arg list."""
for i, item in enumerate(expr): for i, item in enumerate(expr):
@@ -40,6 +65,51 @@ def _extract_keyword_arg(expr: list, key: str) -> Any:
return None return None
def _extract_declarations(
source: str,
) -> tuple[set[str], dict[str, set[str]]]:
"""Extract I/O primitive names and page helper names from boundary source.
Returns (io_names, {service: helper_names}).
"""
exprs = parse_all(source)
io_names: set[str] = set()
helpers: dict[str, set[str]] = {}
for expr in exprs:
if not isinstance(expr, list) or not expr:
continue
head = expr[0]
if not isinstance(head, Symbol):
continue
if head.name == "define-io-primitive":
name = expr[1]
if isinstance(name, str):
io_names.add(name)
elif head.name == "define-page-helper":
name = expr[1]
service = _extract_keyword_arg(expr, "service")
if isinstance(name, str) and isinstance(service, str):
helpers.setdefault(service, set()).add(name)
return io_names, helpers
def _find_service_boundary_files() -> list[str]:
"""Find all {service}/sx/boundary.sx files in the project."""
root = _project_root()
pattern = os.path.join(root, "*/sx/boundary.sx")
files = glob.glob(pattern)
# Exclude shared/sx/ref/ — that's the core boundary
return [f for f in files if "/shared/" not in f]
# ---------------------------------------------------------------------------
# Public API
# ---------------------------------------------------------------------------
def parse_primitives_sx() -> frozenset[str]: def parse_primitives_sx() -> frozenset[str]:
"""Parse primitives.sx and return frozenset of declared pure primitive names.""" """Parse primitives.sx and return frozenset of declared pure primitive names."""
by_module = parse_primitives_by_module() by_module = parse_primitives_by_module()
@@ -50,12 +120,7 @@ def parse_primitives_sx() -> frozenset[str]:
def parse_primitives_by_module() -> dict[str, frozenset[str]]: def parse_primitives_by_module() -> dict[str, frozenset[str]]:
"""Parse primitives.sx and return primitives grouped by module. """Parse primitives.sx and return primitives grouped by module."""
Returns:
Dict mapping module name (e.g. "core.arithmetic") to frozenset of
primitive names declared under that module.
"""
source = _read_file("primitives.sx") source = _read_file("primitives.sx")
exprs = parse_all(source) exprs = parse_all(source)
modules: dict[str, set[str]] = {} modules: dict[str, set[str]] = {}
@@ -83,37 +148,40 @@ def parse_primitives_by_module() -> dict[str, frozenset[str]]:
def parse_boundary_sx() -> tuple[frozenset[str], dict[str, frozenset[str]]]: def parse_boundary_sx() -> tuple[frozenset[str], dict[str, frozenset[str]]]:
"""Parse boundary.sx and return (io_names, {service: helper_names}). """Parse all boundary sources and return (io_names, {service: helper_names}).
Returns: Loads three tiers:
io_names: frozenset of declared I/O primitive names 1. boundary.sx — core language I/O
helpers: dict mapping service name to frozenset of helper names 2. boundary-app.sx — deployment-specific I/O
3. {service}/sx/boundary.sx — per-service page helpers
""" """
source = _read_file("boundary.sx") all_io: set[str] = set()
exprs = parse_all(source) all_helpers: dict[str, set[str]] = {}
io_names: set[str] = set()
helpers: dict[str, set[str]] = {}
for expr in exprs: def _merge(source: str, label: str) -> None:
if not isinstance(expr, list) or not expr: io_names, helpers = _extract_declarations(source)
continue all_io.update(io_names)
head = expr[0] for svc, names in helpers.items():
if not isinstance(head, Symbol): all_helpers.setdefault(svc, set()).update(names)
continue logger.debug("Boundary %s: %d io, %d helpers", label, len(io_names), sum(len(v) for v in helpers.values()))
if head.name == "define-io-primitive": # 1. Core language contract
name = expr[1] _merge(_read_file("boundary.sx"), "core")
if isinstance(name, str):
io_names.add(name)
elif head.name == "define-page-helper": # 2. Deployment-specific I/O
name = expr[1] app_path = os.path.join(_ref_dir(), "boundary-app.sx")
service = _extract_keyword_arg(expr, "service") if os.path.exists(app_path):
if isinstance(name, str) and isinstance(service, str): _merge(_read_file("boundary-app.sx"), "app")
helpers.setdefault(service, set()).add(name)
frozen_helpers = {svc: frozenset(names) for svc, names in helpers.items()} # 3. Per-service boundary files
return frozenset(io_names), frozen_helpers for filepath in _find_service_boundary_files():
try:
_merge(_read_file_path(filepath), filepath)
except Exception as e:
logger.warning("Failed to parse %s: %s", filepath, e)
frozen_helpers = {svc: frozenset(names) for svc, names in all_helpers.items()}
return frozenset(all_io), frozen_helpers
def parse_boundary_types() -> frozenset[str]: def parse_boundary_types() -> frozenset[str]:
@@ -126,7 +194,6 @@ def parse_boundary_types() -> frozenset[str]:
and expr[0].name == "define-boundary-types"): and expr[0].name == "define-boundary-types"):
type_list = expr[1] type_list = expr[1]
if isinstance(type_list, list): if isinstance(type_list, list):
# (list "number" "string" ...)
return frozenset( return frozenset(
item for item in type_list item for item in type_list
if isinstance(item, str) if isinstance(item, str)

51
sx/sx/boundary.sx Normal file
View File

@@ -0,0 +1,51 @@
;; SX docs service — page helper declarations.
(define-page-helper "highlight"
:params (code lang)
:returns "sx-source"
:service "sx")
(define-page-helper "primitives-data"
:params ()
:returns "dict"
:service "sx")
(define-page-helper "special-forms-data"
:params ()
:returns "dict"
:service "sx")
(define-page-helper "reference-data"
:params (slug)
:returns "dict"
:service "sx")
(define-page-helper "attr-detail-data"
:params (slug)
:returns "dict"
:service "sx")
(define-page-helper "header-detail-data"
:params (slug)
:returns "dict"
:service "sx")
(define-page-helper "event-detail-data"
:params (slug)
:returns "dict"
:service "sx")
(define-page-helper "read-spec-file"
:params (filename)
:returns "string"
:service "sx")
(define-page-helper "bootstrapper-data"
:params (target)
:returns "dict"
:service "sx")
(define-page-helper "bundle-analyzer-data"
:params ()
:returns "dict"
:service "sx")