Fix WASM kernel deploy: 3.6MB js_of_ocaml → 68KB wasm_of_ocaml

The deployed sx_browser.bc.wasm.js was actually the js_of_ocaml output
(pure JS), not the wasm_of_ocaml loader. Nothing synced the correct
build output from _build/ to shared/static/wasm/.

- sx_build target=ocaml now auto-syncs WASM kernel + JS fallback + assets
- sx-build-all.sh syncs after dune build
- Correct 68KB WASM loader replaces 3.6MB JS imposter

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-30 13:10:13 +00:00
parent e14947cedc
commit 03c2115f0d
5 changed files with 7693 additions and 45267 deletions

View File

@@ -581,7 +581,8 @@ let rec handle_tool name args =
in
let cmd = match target with
| "ocaml" ->
Printf.sprintf "cd %s/hosts/ocaml && eval $(opam env 2>/dev/null) && dune build 2>&1" project_dir
let abs_project = if Filename.is_relative project_dir then Sys.getcwd () ^ "/" ^ project_dir else project_dir in
Printf.sprintf "cd %s/hosts/ocaml && eval $(opam env 2>/dev/null) && dune build 2>&1 && cp _build/default/browser/sx_browser.bc.wasm.js %s/shared/static/wasm/sx_browser.bc.wasm.js && cp _build/default/browser/sx_browser.bc.js %s/shared/static/wasm/sx_browser.bc.js && cp -r _build/default/browser/sx_browser.bc.wasm.assets %s/shared/static/wasm/" abs_project abs_project abs_project abs_project
| "js" | _ ->
let extra = if full then " --extensions continuations --spec-modules types" else "" in
Printf.sprintf "cd %s && python3 hosts/javascript/cli.py%s --output shared/static/scripts/sx-browser.js 2>&1" project_dir extra

View File

@@ -6,6 +6,15 @@ cd "$(dirname "$0")/.."
echo "=== OCaml build ==="
(cd hosts/ocaml && eval $(opam env 2>/dev/null) && dune build) || { echo "FAIL: OCaml build"; exit 1; }
echo "=== Sync WASM kernel to shared/static/wasm/ ==="
OCAML_BUILD=hosts/ocaml/_build/default/browser
cp "$OCAML_BUILD/sx_browser.bc.wasm.js" shared/static/wasm/sx_browser.bc.wasm.js
cp "$OCAML_BUILD/sx_browser.bc.js" shared/static/wasm/sx_browser.bc.js
cp -r "$OCAML_BUILD/sx_browser.bc.wasm.assets" shared/static/wasm/
echo " WASM loader: $(du -sh shared/static/wasm/sx_browser.bc.wasm.js | cut -f1)"
echo " JS fallback: $(du -sh shared/static/wasm/sx_browser.bc.js | cut -f1)"
echo "=== JS browser build ==="
python3 hosts/javascript/cli.py --output shared/static/scripts/sx-browser.js || { echo "FAIL: JS build"; exit 1; }
echo "=== JS test build ==="

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -149,6 +149,53 @@ test.describe('Page Navigation', () => {
expect(afterNav).toBe(advanced);
});
test('sx-get link fetches SX not HTML and preserves layout', async ({ page }) => {
await loadPage(page, '');
// Track requests to detect what format the fetch uses
const fetches = [];
page.on('request', req => {
if (req.url().includes('/sx/') && req.resourceType() === 'xhr')
fetches.push({ url: req.url(), accept: req.headers()['accept'] || '' });
});
// Mark page so we can detect full reload vs SPA nav
await page.evaluate(() => window.__spa_marker = true);
// Click a nav link (Geography)
await page.click('a[sx-get*="(geography)"]');
await expect(page).toHaveURL(/geography/, { timeout: 5000 });
// Must be SPA navigation — marker survives
const marker = await page.evaluate(() => window.__spa_marker);
expect(marker).toBe(true);
// The fetch should have been XHR, not a document navigation
expect(fetches.length).toBeGreaterThan(0);
// After navigation, #sx-nav and #main-content should still be
// vertically stacked (not side-by-side). Check that nav is above content.
const layout = await page.evaluate(() => {
const nav = document.querySelector('#sx-nav');
const main = document.querySelector('#main-content, #main-panel');
if (!nav || !main) return { error: 'missing elements', nav: !!nav, main: !!main };
const navRect = nav.getBoundingClientRect();
const mainRect = main.getBoundingClientRect();
return {
navBottom: navRect.bottom,
mainTop: mainRect.top,
navRight: navRect.right,
mainLeft: mainRect.left,
// Nav should end before main starts (vertically stacked)
verticallyStacked: navRect.bottom <= mainRect.top + 5,
// Nav and main should overlap horizontally (not side-by-side)
horizontalOverlap: navRect.left < mainRect.right && mainRect.left < navRect.right
};
});
expect(layout.verticallyStacked).toBe(true);
expect(layout.horizontalOverlap).toBe(true);
});
test('header island renders with SSR', async ({ page }) => {
await loadPage(page, '(geography)');