New render_to_buf / render_to_html_streaming in sx_render.ml — writes
HTML directly to a Buffer.t instead of building intermediate strings.
Eliminates hundreds of string concatenations per page render.
Full parallel renderer: render_to_buf, render_element_buf,
render_component_buf, render_cond_buf, render_let_buf, render_map_buf,
render_for_each_buf — all buffer-native.
HTTP server SSR + shell now use streaming renderer.
Performance (warm, 2 worker domains, 2MB RSS):
Homepage: 138-195ms TTFB (Quart: 202ms) — faster
Geography: 218-286ms TTFB (Quart: 144ms)
Throughput: 6.85 req/s at c=5 (Quart: 6.8 req/s) — matched
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Switch SSR from SX adapter to native Sx_render.render_to_html.
Islands that fail SSR emit <span data-sx-island="name"> placeholders
instead of recursing into client-only bodies. Eliminates the <home
symbol error cascade that made the homepage render 23s.
Performance (warm, single process):
Homepage: 131-191ms TTFB (was 202ms on Quart) — faster
Geography: 203-229ms TTFB (was 144ms on Quart) — close
Reactive: 155-290ms TTFB (was 187ms on Quart) — similar
Memory: 2MB RSS (was 570MB on Quart) — 285x reduction
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Islands now render their initial state as HTML on the server, like
React SSR. The client hydrates with reactive behavior on boot.
Root causes fixed:
- is_signal/signal_value now recognize Dict-based signals (from
signals.sx) in addition to native Signal values
- Register "context" as a primitive so the CEK deref frame handler
can read scope stacks for reactive tracking
- Load adapter-html.sx into kernel for SX-level render-to-html
(islands use this instead of the OCaml render module)
- Component accessors (params, body, has-children?, affinity) handle
Island values with ? suffix aliases
- Add platform primitives: make-raw-html, raw-html-content,
empty-dict?, for-each-indexed, cek-call
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
- sx_render.ml: add raw! handler to HTML renderer (inject pre-rendered
content without HTML escaping)
- docker-compose.yml: move SX_USE_OCAML/SX_OCAML_BIN to shared env
(available to all services, not just sx_docs)
- hosts/ocaml/Dockerfile: OCaml kernel build stage
- shared/sx/tests/: golden test data + generator for OCaml render tests
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>