Fix pipe desync: send_ok_raw escapes newlines, expand-components? in env
- send_ok_raw: when SX wire format contains newlines (string literals), fall back to (ok "...escaped...") instead of (ok-raw ...) to keep the pipe single-line. Prevents multi-line responses from desyncing subsequent requests. - expand-components? flag set in kernel env (not just VM adapter globals) so aser-list's env-has? check finds it during component expansion. - SX_STANDALONE: restore no_oauth but generate CSRF via session cookie so mutation handlers (DELETE etc.) still work without account service. - Shell statics injection: only inject small values (hashes, URLs) as kernel vars. Large blobs (CSS, component_defs) use placeholder tokens. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -170,23 +170,20 @@ class OcamlBridge:
|
||||
static = _get_shell_static()
|
||||
except Exception:
|
||||
return # not ready yet (no app context)
|
||||
# Define small values as kernel variables.
|
||||
# Large blobs (component_defs, pages_sx, init_sx) use placeholders
|
||||
# at render time — NOT injected here.
|
||||
for key in ("sx_css", "component_hash", "sx_css_classes", "asset_url",
|
||||
# Only inject small, safe values as kernel variables.
|
||||
# Large/complex blobs use placeholder tokens at render time.
|
||||
for key in ("component_hash", "sx_css_classes", "asset_url",
|
||||
"sx_js_hash", "body_js_hash"):
|
||||
val = static.get(key, "")
|
||||
if val is None:
|
||||
val = ""
|
||||
val = static.get(key) or ""
|
||||
var = f"__shell-{key.replace('_', '-')}"
|
||||
defn = f'(define {var} "{_escape(str(val))}")'
|
||||
try:
|
||||
await self._send(f'(load-source "{_escape(defn)}")')
|
||||
await self._read_until_ok(ctx=None)
|
||||
except OcamlBridgeError:
|
||||
pass
|
||||
# Also inject list/nil values
|
||||
for key in ("head_scripts", "inline_css", "inline_head_js", "body_scripts"):
|
||||
except OcamlBridgeError as e:
|
||||
_logger.warning("Shell static inject failed for %s: %s", key, e)
|
||||
# List/nil values
|
||||
for key in ("head_scripts", "body_scripts"):
|
||||
val = static.get(key)
|
||||
var = f"__shell-{key.replace('_', '-')}"
|
||||
if val is None:
|
||||
@@ -199,8 +196,8 @@ class OcamlBridge:
|
||||
try:
|
||||
await self._send(f'(load-source "{_escape(defn)}")')
|
||||
await self._read_until_ok(ctx=None)
|
||||
except OcamlBridgeError:
|
||||
pass
|
||||
except OcamlBridgeError as e:
|
||||
_logger.warning("Shell static inject failed for %s: %s", key, e)
|
||||
self._shell_statics_injected = True
|
||||
_logger.info("Injected shell statics into OCaml kernel")
|
||||
|
||||
@@ -220,14 +217,16 @@ class OcamlBridge:
|
||||
async with self._lock:
|
||||
await self._inject_helpers_locked()
|
||||
await self._inject_shell_statics_locked()
|
||||
# Large blobs (component_defs, pages_sx, init_sx) use placeholders.
|
||||
# OCaml renders the shell with short tokens; Python splices in
|
||||
# the real values. This avoids piping ~1MB through stdin/stdout.
|
||||
PLACEHOLDER_KEYS = {"component_defs", "pages_sx", "init_sx"}
|
||||
# Large/complex blobs use placeholders — OCaml renders the shell
|
||||
# with short tokens; Python splices in the real values post-render.
|
||||
# This avoids piping large strings or strings with special chars
|
||||
# through the SX parser.
|
||||
PLACEHOLDER_KEYS = {"component_defs", "pages_sx", "init_sx",
|
||||
"sx_css", "inline_css", "inline_head_js"}
|
||||
placeholders = {}
|
||||
static_keys = {"component_hash", "sx_css_classes", "asset_url",
|
||||
"sx_js_hash", "body_js_hash", "sx_css",
|
||||
"head_scripts", "inline_css", "inline_head_js", "body_scripts"}
|
||||
"sx_js_hash", "body_js_hash",
|
||||
"head_scripts", "body_scripts"}
|
||||
parts = [f'(sx-page-full "{_escape(page_source)}"']
|
||||
for key, val in shell_kwargs.items():
|
||||
k = key.replace("_", "-")
|
||||
|
||||
Reference in New Issue
Block a user