Fix signal-add-sub! losing subscribers after remove, fix build pipeline

signal-add-sub! used (append! subscribers f) which returns a new list
for immutable List but discards the result — after signal-remove-sub!
replaces the subscribers list via dict-set!, re-adding subscribers
silently fails. Counter island only worked once (0→1 then stuck).

Fix: use (dict-set! s "subscribers" (append ...)) to explicitly update
the dict field, matching signal-remove-sub!'s pattern.

Build pipeline fixes:
- sx-build-all.sh now bundles spec→dist and recompiles .sxbc bytecode
- compile-modules.js syncs .sx source files alongside .sxbc to wasm/sx/
- Per-file cache busting: wasm, platform JS, and sxbc each get own hash
- bundle.sh adds cssx.sx to dist

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-31 07:36:36 +00:00
parent 5abc947ac7
commit 9ce8659f74
12 changed files with 439 additions and 48 deletions

View File

@@ -65,6 +65,9 @@ cp "$ROOT/web/engine.sx" "$DIST/sx/"
cp "$ROOT/web/orchestration.sx" "$DIST/sx/"
cp "$ROOT/web/boot.sx" "$DIST/sx/"
# 9. CSSX (stylesheet language)
cp "$ROOT/sx/sx/cssx.sx" "$DIST/sx/"
# Summary
WASM_SIZE=$(du -sh "$DIST/sx_browser.bc.wasm.assets" | cut -f1)
JS_SIZE=$(du -sh "$DIST/sx_browser.bc.js" | cut -f1)

View File

@@ -185,6 +185,7 @@ const staticSxDir = path.resolve(__dirname, '..', '..', '..', 'shared', 'static'
if (fs.existsSync(staticSxDir)) {
let copied = 0;
for (const file of FILES) {
// Copy bytecode
for (const ext of ['.sxbc', '.sxbc.json']) {
const src = path.join(sxDir, file.replace(/\.sx$/, ext));
const dst = path.join(staticSxDir, file.replace(/\.sx$/, ext));
@@ -193,6 +194,13 @@ if (fs.existsSync(staticSxDir)) {
copied++;
}
}
// Also sync .sx source files (fallback when .sxbc missing)
const sxSrc = path.join(sxDir, file);
const sxDst = path.join(staticSxDir, file);
if (fs.existsSync(sxSrc) && !fs.lstatSync(sxSrc).isSymbolicLink()) {
fs.copyFileSync(sxSrc, sxDst);
copied++;
}
}
console.log('Copied', copied, 'files to', staticSxDir);
}

View File

@@ -180,8 +180,12 @@
var _baseUrl = "";
// Detect base URL from current script
// Detect base URL and cache-bust params from current script tag.
// _cacheBust comes from the script's own ?v= query string (used for .sx source fallback).
// _sxbcCacheBust comes from data-sxbc-hash attribute — a separate content hash
// covering all .sxbc files so each file gets its own correct cache buster.
var _cacheBust = "";
var _sxbcCacheBust = "";
(function() {
if (typeof document !== "undefined") {
var scripts = document.getElementsByTagName("script");
@@ -191,6 +195,8 @@
_baseUrl = src.substring(0, src.lastIndexOf("/") + 1);
var qi = src.indexOf("?");
if (qi !== -1) _cacheBust = src.substring(qi);
var sxbcHash = scripts[i].getAttribute("data-sxbc-hash");
if (sxbcHash) _sxbcCacheBust = "?v=" + sxbcHash;
break;
}
}
@@ -233,7 +239,7 @@
*/
function loadBytecodeFile(path) {
var bcPath = path.replace(/\.sx$/, '.sxbc.json');
var url = _baseUrl + bcPath + _cacheBust;
var url = _baseUrl + bcPath + _sxbcCacheBust;
try {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, false);