Fix navigation: deep URL routing, back button, render timeout

- request-handler.sx: replace all dots (not just `.(`) and auto-quote
  undefined symbols as strings so 3-level URLs like
  /sx/(geography.(reactive.(examples.counter))) resolve correctly
- sx-platform.js: register popstate handler (was missing from manual
  boot sequence) and fetch full HTML for back/forward navigation
- sx_ref.ml: add CEK step limit (10M steps) checked every 4096 steps
  so runaway renders return 500 instead of blocking the worker forever
- Rename test-runner.sx → runner-placeholder.sx to avoid `test-` skip
- Playwright config: pin testDir, single worker, ignore worktrees

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-29 17:54:33 +00:00
parent 20b3dfb8a0
commit 84a48f0de3
5 changed files with 120 additions and 85 deletions

View File

@@ -30,6 +30,12 @@ let _last_error_kont : value ref = ref Nil
(* === Transpiled from evaluator (frames + eval + CEK) === *)
(* Per-domain step limit (0 = no limit).
Set by the HTTP render worker before each page render.
Checked every 4096 CEK steps in cek_run. *)
let _step_limit : int Atomic.t = Atomic.make 0
let _step_count : int Atomic.t = Atomic.make 0
(* make-cek-state *)
let rec make_cek_state control env kont =
(CekState { cs_control = control; cs_env = env; cs_kont = kont; cs_phase = "eval"; cs_value = Nil })
@@ -372,13 +378,23 @@ and sf_provide args env =
and expand_macro mac raw_args env =
(let local = (env_merge ((macro_closure (mac))) (env)) in (let () = ignore ((List.iter (fun pair -> ignore ((env_bind local (sx_to_string (first (pair))) (if sx_truthy ((prim_call "<" [(nth (pair) ((Number 1.0))); (len (raw_args))])) then (nth (raw_args) ((nth (pair) ((Number 1.0))))) else Nil)))) (sx_to_list (List (List.mapi (fun i p -> let i = Number (float_of_int i) in (List [p; i])) (sx_to_list (macro_params (mac)))))); Nil)) in (let () = ignore ((if sx_truthy ((macro_rest_param (mac))) then (env_bind local (sx_to_string (macro_rest_param (mac))) (prim_call "slice" [raw_args; (len ((macro_params (mac))))])) else Nil)) in (trampoline ((eval_expr ((macro_body (mac))) (local)))))))
(* cek-run *)
(* cek-run — iterative loop to avoid JS stack overflow in WASM *)
and cek_run state =
(if sx_truthy ((cek_terminal_p (state))) then (cek_value (state)) else
try cek_run ((cek_step (state)))
with Eval_error msg ->
(if !_last_error_kont = Nil then _last_error_kont := cek_kont state);
raise (Eval_error msg))
let s = ref state in
(try
while not (match cek_terminal_p !s with Bool true -> true | _ -> false) do
s := cek_step !s;
let n = Atomic.fetch_and_add _step_count 1 in
if n land 4095 = 0 then begin
let lim = Atomic.get _step_limit in
if lim > 0 && n >= lim then
raise (Eval_error (Printf.sprintf "Render step limit exceeded (%d steps)" n))
end
done;
cek_value !s
with Eval_error msg ->
(if !_last_error_kont = Nil then _last_error_kont := cek_kont !s);
raise (Eval_error msg))
(* cek-step *)
and cek_step state =