SxExpr aser wire format fix + Playwright test infrastructure + blob protocol
Aser serialization: aser-call/fragment now return SxExpr instead of String. serialize/inspect passes SxExpr through unquoted, preventing the double- escaping (\" → \\\" ) that broke client-side parsing when aser wire format was output via raw! into <script> tags. Added make-sx-expr + sx-expr-source primitives to OCaml and JS hosts. Binary blob protocol: eval, aser, aser-slot, and sx-page-full now send SX source as length-prefixed blobs instead of escaped strings. Eliminates pipe desync from concurrent requests and removes all string-escape round-trips between Python and OCaml. Bridge safety: re-entrancy guard (_in_io_handler) raises immediately if an IO handler tries to call the bridge, preventing silent deadlocks. Fetch error logging: orchestration.sx error callback now logs method + URL via log-warn. Platform catches (fetchAndRestore, fetchPreload, bindBoostForm) also log errors instead of silently swallowing them. Transpiler fixes: makeEnv, scopePeek, scopeEmit, makeSxExpr added as platform function definitions + transpiler mappings — were referenced in transpiled code but never defined as JS functions. Playwright test infrastructure: - nav() captures JS errors and fails fast with the actual error message - Checks for [object Object] rendering artifacts - New tests: delete-row interaction, full page refresh, back button, direct load with fresh context, code block content verification - Default base URL changed to localhost:8013 (standalone dev server) - docker-compose.dev-sx.yml: port 8013 exposed for local testing - test-sx-build.sh: build + unit tests + Playwright smoke tests Geography content: index page component written (sx/sx/geography/index.sx) describing OCaml evaluator, wire formats, rendering pipeline, and topic links. Wiring blocked by aser-expand-component children passing issue. Tests: 1080/1080 JS, 952/952 OCaml, 66/66 Playwright Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -21,10 +21,6 @@ async def execute_query(query_def: QueryDef, params: dict[str, str]) -> Any:
|
||||
"""
|
||||
from .jinja_bridge import get_component_env, _get_request_context
|
||||
import os
|
||||
if os.environ.get("SX_USE_REF") == "1":
|
||||
from .ref.async_eval_ref import async_eval
|
||||
else:
|
||||
from .async_eval import async_eval
|
||||
|
||||
env = dict(get_component_env())
|
||||
env.update(query_def.closure)
|
||||
@@ -38,6 +34,26 @@ async def execute_query(query_def: QueryDef, params: dict[str, str]) -> Any:
|
||||
val = int(val)
|
||||
env[param] = val
|
||||
|
||||
if os.environ.get("SX_USE_OCAML") == "1":
|
||||
from .ocaml_bridge import get_bridge
|
||||
from .parser import serialize, parse_all
|
||||
from .pages import _wrap_with_env
|
||||
bridge = await get_bridge()
|
||||
sx_text = _wrap_with_env(query_def.body, env)
|
||||
ctx = {"_helper_service": ""}
|
||||
raw = await bridge.eval(sx_text, ctx=ctx)
|
||||
if raw:
|
||||
parsed = parse_all(raw)
|
||||
result = parsed[0] if parsed else None
|
||||
else:
|
||||
result = None
|
||||
return _normalize(result)
|
||||
|
||||
if os.environ.get("SX_USE_REF") == "1":
|
||||
from .ref.async_eval_ref import async_eval
|
||||
else:
|
||||
from .async_eval import async_eval
|
||||
|
||||
ctx = _get_request_context()
|
||||
result = await async_eval(query_def.body, env, ctx)
|
||||
return _normalize(result)
|
||||
@@ -50,10 +66,6 @@ async def execute_action(action_def: ActionDef, payload: dict[str, Any]) -> Any:
|
||||
"""
|
||||
from .jinja_bridge import get_component_env, _get_request_context
|
||||
import os
|
||||
if os.environ.get("SX_USE_REF") == "1":
|
||||
from .ref.async_eval_ref import async_eval
|
||||
else:
|
||||
from .async_eval import async_eval
|
||||
|
||||
env = dict(get_component_env())
|
||||
env.update(action_def.closure)
|
||||
@@ -64,6 +76,26 @@ async def execute_action(action_def: ActionDef, payload: dict[str, Any]) -> Any:
|
||||
val = payload.get(param, payload.get(snake, NIL))
|
||||
env[param] = val
|
||||
|
||||
if os.environ.get("SX_USE_OCAML") == "1":
|
||||
from .ocaml_bridge import get_bridge
|
||||
from .parser import serialize, parse_all
|
||||
from .pages import _wrap_with_env
|
||||
bridge = await get_bridge()
|
||||
sx_text = _wrap_with_env(action_def.body, env)
|
||||
ctx = {"_helper_service": ""}
|
||||
raw = await bridge.eval(sx_text, ctx=ctx)
|
||||
if raw:
|
||||
parsed = parse_all(raw)
|
||||
result = parsed[0] if parsed else None
|
||||
else:
|
||||
result = None
|
||||
return _normalize(result)
|
||||
|
||||
if os.environ.get("SX_USE_REF") == "1":
|
||||
from .ref.async_eval_ref import async_eval
|
||||
else:
|
||||
from .async_eval import async_eval
|
||||
|
||||
ctx = _get_request_context()
|
||||
result = await async_eval(action_def.body, env, ctx)
|
||||
return _normalize(result)
|
||||
|
||||
Reference in New Issue
Block a user