Cache-bust wasm scripts, fix orchestration.sx paren balance
- Add wasm_hash (MD5 of sx_browser.bc.js) to shell template
- Script tags: /wasm/sx_browser.bc.js?v={hash}, /wasm/sx-platform.js?v={hash}
- Pass wasm_hash through helpers.py and ocaml_bridge.py
- Fix missing close paren in bind-sse-swap (broke SX parsing)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -300,7 +300,8 @@
|
|||||||
(with-transition use-transition
|
(with-transition use-transition
|
||||||
(fn ()
|
(fn ()
|
||||||
(let ((swap-result (swap-dom-nodes target content swap-style)))
|
(let ((swap-result (swap-dom-nodes target content swap-style)))
|
||||||
(post-swap (or swap-result target))))))))))))
|
(post-swap (or swap-result target)))))))))))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(define handle-html-response :effects [mutation io]
|
(define handle-html-response :effects [mutation io]
|
||||||
@@ -1083,7 +1084,7 @@
|
|||||||
(with-transition use-transition
|
(with-transition use-transition
|
||||||
(fn ()
|
(fn ()
|
||||||
(swap-html-string target trimmed swap-style)
|
(swap-html-string target trimmed swap-style)
|
||||||
(post-swap target))))))))
|
(post-swap target)))))))))
|
||||||
|
|
||||||
|
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ page elements (headers, search, etc.) from template context.
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
@@ -884,12 +885,14 @@ def _get_shell_static() -> dict[str, Any]:
|
|||||||
sx_css_classes=sx_css_classes,
|
sx_css_classes=sx_css_classes,
|
||||||
sx_js_hash=_script_hash("sx-browser.js"),
|
sx_js_hash=_script_hash("sx-browser.js"),
|
||||||
body_js_hash=_script_hash("body.js"),
|
body_js_hash=_script_hash("body.js"),
|
||||||
|
wasm_hash=_wasm_hash("sx_browser.bc.js"),
|
||||||
asset_url=_ca.config.get("ASSET_URL", "/static"),
|
asset_url=_ca.config.get("ASSET_URL", "/static"),
|
||||||
head_scripts=_shell_cfg.get("head_scripts"),
|
head_scripts=_shell_cfg.get("head_scripts"),
|
||||||
inline_css=_shell_cfg.get("inline_css"),
|
inline_css=_shell_cfg.get("inline_css"),
|
||||||
inline_head_js=_shell_cfg.get("inline_head_js"),
|
inline_head_js=_shell_cfg.get("inline_head_js"),
|
||||||
init_sx=_shell_cfg.get("init_sx"),
|
init_sx=_shell_cfg.get("init_sx"),
|
||||||
body_scripts=_shell_cfg.get("body_scripts"),
|
body_scripts=_shell_cfg.get("body_scripts"),
|
||||||
|
use_wasm=os.environ.get("SX_USE_WASM") == "1",
|
||||||
)
|
)
|
||||||
|
|
||||||
t1 = time.monotonic()
|
t1 = time.monotonic()
|
||||||
@@ -1081,8 +1084,11 @@ def sx_page_streaming_parts(ctx: dict, page_html: str, *,
|
|||||||
# Tail: bootstrap suspense resolver + scripts + close
|
# Tail: bootstrap suspense resolver + scripts + close
|
||||||
tail = (
|
tail = (
|
||||||
_SX_STREAMING_BOOTSTRAP + '\n'
|
_SX_STREAMING_BOOTSTRAP + '\n'
|
||||||
f'<script src="{asset_url}/scripts/sx-browser.js?v={sx_js_hash}"></script>\n'
|
+ (f'<script src="{asset_url}/wasm/sx_browser.bc.wasm.js"></script>\n'
|
||||||
f'<script src="{asset_url}/scripts/body.js?v={body_js_hash}"></script>\n'
|
f'<script src="{asset_url}/wasm/sx-platform.js"></script>\n'
|
||||||
|
if os.environ.get("SX_USE_WASM") == "1" else
|
||||||
|
f'<script src="{asset_url}/scripts/sx-browser.js?v={sx_js_hash}"></script>\n')
|
||||||
|
+ f'<script src="{asset_url}/scripts/body.js?v={body_js_hash}"></script>\n'
|
||||||
)
|
)
|
||||||
|
|
||||||
return shell, tail
|
return shell, tail
|
||||||
@@ -1120,6 +1126,18 @@ def _script_hash(filename: str) -> str:
|
|||||||
return _SCRIPT_HASH_CACHE[filename]
|
return _SCRIPT_HASH_CACHE[filename]
|
||||||
|
|
||||||
|
|
||||||
|
def _wasm_hash(filename: str) -> str:
|
||||||
|
"""Compute MD5 hash of a static wasm file, cached for process lifetime."""
|
||||||
|
key = "wasm/" + filename
|
||||||
|
if key not in _SCRIPT_HASH_CACHE:
|
||||||
|
try:
|
||||||
|
data = (Path(__file__).resolve().parent.parent / "static" / "wasm" / filename).read_bytes()
|
||||||
|
_SCRIPT_HASH_CACHE[key] = hashlib.md5(data).hexdigest()[:8]
|
||||||
|
except OSError:
|
||||||
|
_SCRIPT_HASH_CACHE[key] = "dev"
|
||||||
|
return _SCRIPT_HASH_CACHE[key]
|
||||||
|
|
||||||
|
|
||||||
def _get_csrf_token() -> str:
|
def _get_csrf_token() -> str:
|
||||||
"""Get the CSRF token from the current request context."""
|
"""Get the CSRF token from the current request context."""
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ class OcamlBridge:
|
|||||||
# Only inject small, safe values as kernel variables.
|
# Only inject small, safe values as kernel variables.
|
||||||
# Large/complex blobs use placeholder tokens at render time.
|
# Large/complex blobs use placeholder tokens at render time.
|
||||||
for key in ("component_hash", "sx_css_classes", "asset_url",
|
for key in ("component_hash", "sx_css_classes", "asset_url",
|
||||||
"sx_js_hash", "body_js_hash"):
|
"sx_js_hash", "body_js_hash", "wasm_hash"):
|
||||||
val = static.get(key) or ""
|
val = static.get(key) or ""
|
||||||
var = f"__shell-{key.replace('_', '-')}"
|
var = f"__shell-{key.replace('_', '-')}"
|
||||||
defn = f'(define {var} "{_escape(str(val))}")'
|
defn = f'(define {var} "{_escape(str(val))}")'
|
||||||
@@ -268,7 +268,7 @@ class OcamlBridge:
|
|||||||
"sx_css", "inline_css", "inline_head_js"}
|
"sx_css", "inline_css", "inline_head_js"}
|
||||||
placeholders = {}
|
placeholders = {}
|
||||||
static_keys = {"component_hash", "sx_css_classes", "asset_url",
|
static_keys = {"component_hash", "sx_css_classes", "asset_url",
|
||||||
"sx_js_hash", "body_js_hash",
|
"sx_js_hash", "body_js_hash", "wasm_hash",
|
||||||
"head_scripts", "body_scripts"}
|
"head_scripts", "body_scripts"}
|
||||||
# page_source is SX wire format that may contain \" escapes.
|
# page_source is SX wire format that may contain \" escapes.
|
||||||
# Send via binary blob protocol to avoid double-escaping
|
# Send via binary blob protocol to avoid double-escaping
|
||||||
|
|||||||
@@ -17,8 +17,10 @@
|
|||||||
(pages-sx :as string?) (page-sx :as string?)
|
(pages-sx :as string?) (page-sx :as string?)
|
||||||
(body-html :as string?)
|
(body-html :as string?)
|
||||||
(asset-url :as string) (sx-js-hash :as string) (body-js-hash :as string?)
|
(asset-url :as string) (sx-js-hash :as string) (body-js-hash :as string?)
|
||||||
|
(wasm-hash :as string?)
|
||||||
(head-scripts :as list?) (inline-css :as string?) (inline-head-js :as string?)
|
(head-scripts :as list?) (inline-css :as string?) (inline-head-js :as string?)
|
||||||
(init-sx :as string?) (body-scripts :as list?))
|
(init-sx :as string?) (body-scripts :as list?)
|
||||||
|
(use-wasm :as boolean?))
|
||||||
(<>
|
(<>
|
||||||
(raw! "<!doctype html>")
|
(raw! "<!doctype html>")
|
||||||
(html :lang "en"
|
(html :lang "en"
|
||||||
@@ -83,7 +85,12 @@ details.group{overflow:hidden}details.group>summary{list-style:none}details.grou
|
|||||||
(raw! (or pages-sx "")))
|
(raw! (or pages-sx "")))
|
||||||
(script :type "text/sx" :data-mount "#sx-root"
|
(script :type "text/sx" :data-mount "#sx-root"
|
||||||
(raw! (or page-sx "")))
|
(raw! (or page-sx "")))
|
||||||
(script :src (str asset-url "/scripts/sx-browser.js?v=" sx-js-hash))
|
(if use-wasm
|
||||||
|
(let ((wv (or wasm-hash "dev")))
|
||||||
|
(<>
|
||||||
|
(script :src (str asset-url "/wasm/sx_browser.bc.js?v=" wv))
|
||||||
|
(script :src (str asset-url "/wasm/sx-platform.js?v=" wv))))
|
||||||
|
(script :src (str asset-url "/scripts/sx-browser.js?v=" sx-js-hash)))
|
||||||
;; Body scripts — configurable per app
|
;; Body scripts — configurable per app
|
||||||
;; Pass a list (even empty) to override defaults; nil = use defaults
|
;; Pass a list (even empty) to override defaults; nil = use defaults
|
||||||
(if (not (nil? body-scripts))
|
(if (not (nil? body-scripts))
|
||||||
|
|||||||
@@ -300,7 +300,8 @@
|
|||||||
(with-transition use-transition
|
(with-transition use-transition
|
||||||
(fn ()
|
(fn ()
|
||||||
(let ((swap-result (swap-dom-nodes target content swap-style)))
|
(let ((swap-result (swap-dom-nodes target content swap-style)))
|
||||||
(post-swap (or swap-result target))))))))))))
|
(post-swap (or swap-result target)))))))))))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(define handle-html-response :effects [mutation io]
|
(define handle-html-response :effects [mutation io]
|
||||||
@@ -1083,7 +1084,7 @@
|
|||||||
(with-transition use-transition
|
(with-transition use-transition
|
||||||
(fn ()
|
(fn ()
|
||||||
(swap-html-string target trimmed swap-style)
|
(swap-html-string target trimmed swap-style)
|
||||||
(post-swap target))))))))
|
(post-swap target)))))))))
|
||||||
|
|
||||||
|
|
||||||
;; --------------------------------------------------------------------------
|
;; --------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user