fix(js): tolerate malformed bootstrap state

Report malformed data-slhx-st with slhx:state-error and continue binding the root with an empty atom store instead of letting state decode failures stop handlers, navigation, or push.

req: state/004

req: state/006

req: runtime/001
This commit is contained in:
slhx agent
2026-06-01 21:35:49 +02:00
parent ad135d81fe
commit c69eb9932c
3 changed files with 23 additions and 2 deletions
+7 -2
View File
@@ -668,8 +668,13 @@
function bootstrapState(root) {
const encoded = root.getAttribute(STATE);
if (!encoded) return;
const store = atomStore(root);
for (const atom of decodeAtomState(encoded)) store.set(String(atom.id), atom.bytes);
try {
const store = atomStore(root);
for (const atom of decodeAtomState(encoded)) store.set(String(atom.id), atom.bytes);
} catch (error) {
atomStores.delete(root);
emit(root, "slhx:state-error", String(error));
}
}
function decodeAtomState(encoded) {
+13
View File
@@ -216,6 +216,19 @@ fn runtime_refuses_partial_updates_on_fingerprint_mismatch() {
assert!(source.contains("if (expected && String(batch.fingerprint) !== expected)"));
}
#[test]
fn runtime_malformed_bootstrap_state_reports_and_continues() {
// req: state/004 req: state/006 req: runtime/001
let source = slhx_js::RUNTIME_JS;
assert!(source.contains("function bootstrapState(root)"));
assert!(source.contains("try {\n const store = atomStore(root);"));
assert!(source.contains("atomStores.delete(root)"));
assert!(source.contains("emit(root, \"slhx:state-error\", String(error))"));
assert!(source.contains("bindRoot(root)"));
assert!(source.contains("bindSse(root)"));
}
#[test]
fn runtime_applies_sse_effect_batches_inside_roots() {
// req: push/001 req: push/003 req: push/006 req: runtime/001