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:
@@ -640,6 +640,9 @@ what a valid business email is.
|
||||
### req: state/004
|
||||
004 SSR roots may carry a `data-slhx-st` base64url-encoded postcard blob on the `data-slhx-root` element. Runtime decodes it into the client atom store. Atoms computed from server state are immediately available to client-side handlers without a round-trip.
|
||||
|
||||
### req: state/006
|
||||
006 Malformed `data-slhx-st` bootstrap state must not stop the standard runtime from binding roots, handlers, navigation, or push. The runtime reports `slhx:state-error` and continues with an empty atom store for that root.
|
||||
|
||||
### req: state/005
|
||||
005 Not all state is an Atom. Ordinary Rust fields are preferred unless the value must be independently addressed, bootstrapped, synced, or subscribed. Atoms are explicit resources, not the default state container.
|
||||
|
||||
|
||||
@@ -668,8 +668,13 @@
|
||||
function bootstrapState(root) {
|
||||
const encoded = root.getAttribute(STATE);
|
||||
if (!encoded) return;
|
||||
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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user