From 6e1d28d1d7451dad33fbb313ccd8c973c9e499e3 Mon Sep 17 00:00:00 2001 From: giles Date: Mon, 23 Mar 2026 14:44:51 +0000 Subject: [PATCH] Load freeze.sx + browser API stubs for complete island SSR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All islands now render server-side: - freeze.sx loaded into kernel (freeze-scope for home/stepper) - Browser-only APIs stubbed (local-storage-get/set, dom-listen, dom-dispatch, dom-set-data, dom-get-data, promise-then) → return nil on server, client hydrates with real behavior Zero island failures. Both layouts/header and home/stepper render with hydration markers, CSS classes, and initial signal values. Co-Authored-By: Claude Opus 4.6 (1M context) --- hosts/ocaml/bin/sx_server.ml | 10 ++++++++++ shared/sx/ocaml_bridge.py | 8 ++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/hosts/ocaml/bin/sx_server.ml b/hosts/ocaml/bin/sx_server.ml index 4c56206..4689590 100644 --- a/hosts/ocaml/bin/sx_server.ml +++ b/hosts/ocaml/bin/sx_server.ml @@ -394,6 +394,16 @@ let make_server_env () = bind "set-render-active!" (fun _args -> Nil); bind "render-active?" (fun _args -> Bool true); + (* Browser APIs — no-op stubs for SSR *) + bind "local-storage-get" (fun _args -> Nil); + bind "local-storage-set" (fun _args -> Nil); + bind "dom-listen" (fun _args -> NativeFn ("noop", fun _ -> Nil)); + bind "dom-dispatch" (fun _args -> Nil); + bind "dom-set-data" (fun _args -> Nil); + bind "dom-get-data" (fun _args -> Nil); + bind "event-detail" (fun _args -> Nil); + bind "promise-then" (fun _args -> Nil); + (* Raw HTML — platform primitives for adapter-html.sx *) bind "make-raw-html" (fun args -> match args with [String s] -> RawHTML s | [v] -> RawHTML (value_to_string v) | _ -> Nil); diff --git a/shared/sx/ocaml_bridge.py b/shared/sx/ocaml_bridge.py index dc1e8fd..45eb572 100644 --- a/shared/sx/ocaml_bridge.py +++ b/shared/sx/ocaml_bridge.py @@ -397,14 +397,18 @@ class OcamlBridge: # All directories loaded into the Python env all_dirs = list(set(_watched_dirs) | _dirs_from_cache) - # Web adapters + signals (aser lives in adapter-sx.sx, - # signals.sx provides reactive primitives for island SSR) + # Isomorphic libraries: signals, rendering, freeze scopes web_dir = os.path.join(os.path.dirname(__file__), "../../web") if os.path.isdir(web_dir): for web_file in ["signals.sx", "adapter-html.sx", "adapter-sx.sx"]: path = os.path.normpath(os.path.join(web_dir, web_file)) if os.path.isfile(path): all_files.append(path) + # Spec library files (loaded after adapters) + for spec_lib in ["freeze.sx"]: + path = os.path.normpath(os.path.join(spec_dir, spec_lib)) + if os.path.isfile(path): + all_files.append(path) for directory in sorted(all_dirs): files = sorted(