This is a pre-release. djust 0.9.7 has shipped since: read the djust 0.9.7 release notes.
Changed
- Bundle init-order lint now does a deferral-pattern-aware depth-N call-graph walk (#1449, #1406).
scripts/check-bundle-init-order.mjspreviously caught only direct top-level reads of late-declaredlet/const. The walker now descends through synchronously-called function bodies (default depth 8) to catch the transitive TDZ class that PR #1370 hit —djustInit() → mountHooks() → _ensureHooksInit() → _activeHooks(read at top level via a transitive call chain, declared later in lexicographic concat order). Models deferral sites (addEventListener/removeEventListener,setTimeout/setInterval/setImmediate,requestAnimationFrame/queueMicrotask/requestIdleCallback, Promise.then/.catch/.finally,new XxxObserver(...)) so callbacks passed to those APIs are correctly treated as non-top-level. The walker uses an "effective-line" model: identifiers reached transitively through a top-level call at bundle line L are flagged only ifdecl.bundleLine > L, which eliminates the 16 false positives the naive depth-N version produced (per #1449). New CLI flags:--max-depth=N(default 8),--shallow-only(preserves v0.9.5 behavior). New env-var overrideBUNDLE_SRC_DIRfor synthetic-bundle tests. The runtime regression testtests/js/bundle-init-no-tdz.test.jsremains the simulate-bundle-init safety net — the two checks are complementary. Currentmainbundle is clean at default depth.
Tests
Wire-protocol snapshots: 12 final frames pinned (#1456 Batch 3 — closes #1456).
noop(with optional appends),rate_limit_exceeded,pong,error.messagevariant (wire-distinct fromerror.error),navigate,upload_registered,upload_progress,reload,hvr-applied(kebab-case type — the only one in the protocol),presence_event(presence.py),streaming.patch,streaming.html_update,streaming.stream. 14 new tests, 39 total. Across the 4 PRs (#1457 starter + #1461 Batch 1 + #1462 Batch 2 + this), the entiresend_jsonwire surface is now pinned.Wire-protocol snapshots: 5 optional-feature frames pinned (#1456 Batch 2, follow-up to Batch 1 PR #1461).
i18n,accessibility,focus,html_update(minimal + withreset_form/event_nameconditional appends),connect. 6 new tests, 25 total inpython/djust/tests/test_wire_protocol_snapshots.py. Closes Batch 2 of 3 in #1456; remaining ~12 shapes tracked there for Batch 3 (uploads, reload variants, control plane, presence, streaming).Wire-protocol snapshots: 5 lifecycle frames pinned (#1456 Batch 1, follow-up to PR #1457). Extends
python/djust/tests/test_wire_protocol_snapshots.pywithmount_batch(envelope + optionalnavigateappend),child_update,sticky_update,sticky_hold(with views + empty-list drop-all signal), andembedded_update. 7 new tests, 19 total. Closes Batch 1 of 3 in #1456; remaining ~17 shapes tracked there for Batches 2-3.Wire-protocol JSON snapshot pinning for 8 highest-value Python-emitted frame shapes (#1448 starter, follow-up to PR #1444). Generalizes PR #1444's Rust-side
Patch/VNodesnapshot pinning to the Pythonsend_jsonemit sites.python/djust/tests/test_wire_protocol_snapshots.pypinspush_event,flash,page_metadata,patch(envelope),mount(with + withoutpublic_state),layout,navigation(inner-type-to-actionpromotion), anderroragainst literal JSON strings. A field rename or default-value change at any of the 8 emit sites silently breaks deployed clients running older bundles — these snapshot tests catch it at test time. Key-order pinning is deliberate:mount.public_stateandpatch.htmlare appended after the initial dict literal, and Python 3.7+ preserves dict insertion order, so the JSON order is deterministic. The actual wire surface is ~30+ shapes; follow-up #1456 tracks the remaining ~22 in 2-3 grouped batches.VDOM cluster carryovers — 24 new tests across 5 files (#1413, #1416, #1417, #1418, #1420). Five P3 hardeners extending
crates/djust_vdom/tests/common/mod.rs(the harness from #1421). Test-only; no production code changes; none surfaced regressions onmain. Test count: djust_vdom 248 → 272.- #1413 —
proptest_round_trip_with_sync.rs: randomized counterpart to #1412's hand-crafted scenarios. 1 proptest fn × 64 cases × 5-20 steps of dj-if boundary toggles + inner mutations, assertsassert_handles_resolveinvariant on every cycle. - #1416 —
torture_html_round_trip.rs: 8 scenarios exercising the liveVDOM → to_html → parse_htmlround-trip (plain/nested elements, text, dj-if single + nested, dj-key keyed children, dj-update="ignore" subtrees, mixed attrs incl. data-/aria-/role/href entities, text-with-entities). - #1417 —
test_dj_update_ignore_dj_if_sync_ids_1417.rs: 3 scenarios for the dj-update="ignore" × dj-if × sync_ids three-way interaction. Verifies sync_ids preserves the ignored subtree's dj-ids across boundary swap-out → swap-in cycles. - #1418 —
torture_deep_cascade_dj_if_1418.rs: 4 scenarios with 10/12/15 levels of nested dj-if boundaries at start/middle/end positions of the children list. Toggles deepest boundary across cycles. - #1420 —
torture_patch_batch_ordering_1420.rs: 7 invariant scenarios + 1 snapshot. The canonical "SetAttr on kept child + RemoveChild on removed sibling" snapshot asserts RemoveChild comes at-or-after SetAttr in the batch (so any future emitter regression that reordered Remove ahead of Set would trip the test).
- #1413 —