sx-http: fix </script escaping in serialize_value, inline component-defs
escape_sx_string now escapes </ as <\\/ inside SX string literals, matching Python's serialize() behavior. This prevents the HTML parser from matching </script> inside component-defs strings while keeping the SX valid for the client parser. Component-defs back to inline <script data-components> (reverts external endpoint approach). init-sx triggers client render when sx-root is empty. Geography page: fully rendered with header, nav, content, styling. Header island hydration warning (Undefined symbol: let) is a pre-existing WASM kernel issue, not related to the HTTP server. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -25,14 +25,20 @@ open Sx_types
|
|||||||
|
|
||||||
(** Escape a string for embedding in an SX string literal. *)
|
(** Escape a string for embedding in an SX string literal. *)
|
||||||
let escape_sx_string s =
|
let escape_sx_string s =
|
||||||
let buf = Buffer.create (String.length s + 16) in
|
let len = String.length s in
|
||||||
String.iter (function
|
let buf = Buffer.create (len + 16) in
|
||||||
|
for i = 0 to len - 1 do
|
||||||
|
match s.[i] with
|
||||||
| '"' -> Buffer.add_string buf "\\\""
|
| '"' -> Buffer.add_string buf "\\\""
|
||||||
| '\\' -> Buffer.add_string buf "\\\\"
|
| '\\' -> Buffer.add_string buf "\\\\"
|
||||||
| '\n' -> Buffer.add_string buf "\\n"
|
| '\n' -> Buffer.add_string buf "\\n"
|
||||||
| '\r' -> Buffer.add_string buf "\\r"
|
| '\r' -> Buffer.add_string buf "\\r"
|
||||||
| '\t' -> Buffer.add_string buf "\\t"
|
| '\t' -> Buffer.add_string buf "\\t"
|
||||||
| c -> Buffer.add_char buf c) s;
|
| '<' when i + 1 < len && s.[i + 1] = '/' ->
|
||||||
|
(* Escape </ as <\\/ to prevent HTML parser matching </script> *)
|
||||||
|
Buffer.add_string buf "<\\\\/"
|
||||||
|
| c -> Buffer.add_char buf c
|
||||||
|
done;
|
||||||
Buffer.contents buf
|
Buffer.contents buf
|
||||||
|
|
||||||
(** Serialize a value to SX text (for io-request args). *)
|
(** Serialize a value to SX text (for io-request args). *)
|
||||||
@@ -1619,14 +1625,10 @@ let http_inject_shell_statics env static_dir sx_sxc =
|
|||||||
| _ -> ()
|
| _ -> ()
|
||||||
) env.bindings;
|
) env.bindings;
|
||||||
let raw_defs = Buffer.contents buf in
|
let raw_defs = Buffer.contents buf in
|
||||||
(* Don't inline component-defs — serve from /static/sx-components.sx.
|
(* Component-defs are inlined in <script type="text/sx">.
|
||||||
Inlining breaks because SX source can contain literal </script> which
|
The escape_sx_string function handles </ → <\\/ inside string
|
||||||
the HTML parser uses to close the tag prematurely. *)
|
literals, preventing the HTML parser from matching </script>. *)
|
||||||
let component_defs = "" in
|
let component_defs = raw_defs in
|
||||||
(* Cache the raw defs for the /static/sx-components.sx endpoint *)
|
|
||||||
Hashtbl.replace static_cache "/static/sx-components.sx"
|
|
||||||
(Printf.sprintf "HTTP/1.1 200 OK\r\nContent-Type: text/sx; charset=utf-8\r\nContent-Length: %d\r\nCache-Control: public, max-age=31536000, immutable\r\n\r\n%s"
|
|
||||||
(String.length raw_defs) raw_defs);
|
|
||||||
let component_hash = Digest.string component_defs |> Digest.to_hex in
|
let component_hash = Digest.string component_defs |> Digest.to_hex in
|
||||||
(* Compute file hashes for cache busting *)
|
(* Compute file hashes for cache busting *)
|
||||||
let sx_js_hash = file_hash (static_dir ^ "/scripts/sx-browser.js") in
|
let sx_js_hash = file_hash (static_dir ^ "/scripts/sx-browser.js") in
|
||||||
|
|||||||
Reference in New Issue
Block a user