Store primitives + event-bridge + client? for isomorphic nav

- def-store/use-store/clear-stores: OCaml primitives with global
  mutable registry. Bypasses env scoping issues that prevented SX-level
  stores from persisting across bytecode module boundaries.

- client? primitive: _is_client ref (false on server, true in browser).
  Registered in primitives table for CALL_PRIM compatibility.

- Event-bridge island: rewritten to use document-level addEventListener
  via effect + host-callback, fixing container-ref timing issue.

- Header island: uses def-store for idx/shade signals when client? is
  true, plain signals when false (SSR compatibility).

- web-signals.sx: SX store definitions removed, OCaml primitives replace.

Isomorphic nav still fixme — client? works from K.eval but the JIT
"Not callable: nil" bug prevents proper primitive resolution during
render-to-dom hydration. Needs JIT investigation.

100 passed, 1 skipped, 0 failed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-28 11:54:29 +00:00
parent 0cae1fbb6b
commit 9ac2e38c24
6 changed files with 84 additions and 98 deletions

View File

@@ -667,6 +667,25 @@ let () =
| _ -> raise (Eval_error "error: 1 arg"));
(* client? — false by default (server); sx_browser.ml sets _is_client := true *)
register "client?" (fun _args -> Bool !_is_client);
(* Named stores — global mutable registry, bypasses env scoping issues *)
let store_registry : (string, value) Hashtbl.t = Hashtbl.create 16 in
register "def-store" (fun args ->
match args with
| [String name; init_fn] ->
if not (Hashtbl.mem store_registry name) then begin
let store = !_sx_trampoline_fn (!_sx_call_fn init_fn []) in
Hashtbl.replace store_registry name store
end;
(match Hashtbl.find_opt store_registry name with Some v -> v | None -> Nil)
| _ -> raise (Eval_error "def-store: expected (name init-fn)"));
register "use-store" (fun args ->
match args with
| [String name] ->
(match Hashtbl.find_opt store_registry name with
| Some v -> v
| None -> raise (Eval_error ("Store not found: " ^ name)))
| _ -> raise (Eval_error "use-store: expected (name)"));
register "clear-stores" (fun _args -> Hashtbl.clear store_registry; Nil);
register "apply" (fun args ->
let call f a =
match f with