Isomorphic SSR: server renders HTML body, client takes over with SX

Server now renders page content as HTML inside <div id="sx-root">,
visible immediately before JavaScript loads. The SX source is still
included in a <script data-mount="#sx-root"> tag for client hydration.

SSR pipeline: after aser produces the SX wire format, parse and
render-to-html it (~17ms for a 22KB page). Islands with reactive
state gracefully fall back to empty — client hydrates them.

Supporting changes:
- Load signals.sx into OCaml kernel (reactive primitives for island SSR)
- Add cek-call and context to kernel env (needed by signals/deref)
- Island-aware component accessors in sx_types.ml
- render-to-html handles Island values (renders as component with fallback)
- Fix 431 (Request Header Fields Too Large): replace SX-Components
  header (full component name list) with SX-Components-Hash (12 chars)
- CORS allow SX-Components-Hash header

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-23 14:01:41 +00:00
parent 9bd4863ce1
commit 894321db18
9 changed files with 103 additions and 28 deletions

View File

@@ -256,6 +256,18 @@ and render_list_to_html head args env =
let v = env_get env name in
(match v with
| Component _ -> render_component v args env
| Island i ->
(* Islands: render initial HTML server-side (like React SSR).
Log failures so we can fix them. *)
(try
let c = { c_name = i.i_name; c_params = i.i_params;
c_has_children = i.i_has_children; c_body = i.i_body;
c_closure = i.i_closure; c_affinity = "client";
c_compiled = None } in
render_component (Component c) args env
with e ->
Printf.eprintf "[ssr-island] ~%s FAILED: %s\n%!" i.i_name (Printexc.to_string e);
"")
| Macro m ->
let expanded = expand_macro m args env in
do_render_to_html expanded env