fix: sync shared-name storage vars in the writing tab (#7567)

AN
6h ago
+337-6

Fixes #7567

All Submissions:

  • Have you followed the guidelines stated in CONTRIBUTING.md file?
  • Have you checked to ensure there aren't any other open Pull Requests for the desired changed?

Type of change

  • Bug fix (non-breaking change which fixes an issue)

New Feature Submission:

  • Does your submission pass the tests?
  • Have you linted your code locally prior to submission?

Changes To Core Features:

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your core changes, as applicable?
  • Have you successfully ran tests with your changes locally?

What

Several rx.LocalStorage(..., sync=True) vars can share one storage name (the common case is a named synced field on an rx.ComponentState used more than once). When the backend applies a write to one of them, applyClientStorageDelta updates localStorage, which raises a storage event in the other tabs only. The writing tab never hears it, so its sibling vars sharing the name stay stale until a reload.

Following the fix sketched in the issue: when a synced var's value is written to its storage name and the write actually changes storage, applyClientStorageDelta now also queues the same SYNC_LOCAL_STORAGE event for the other synced vars with that name in this tab, once per storage name per delta. The written var's own sync flag does not matter, matching the cross-tab path: storage writes raise the storage event either way. Deltas are per-session, so other tabs are unaffected and still sync through the real event. The sibling update records its sent values, so the echoing delta is recognized by takeEcho and neither writes localStorage again nor queues another sync; a genuinely newer backend value overwrites, re-syncs once, and its echo terminates the cycle.

Tests

Five new cases in tests/units/compiler/state_js.test.mjs (run through test_state_js_template.py): sibling sync on a synced write with three vars sharing a name; a write from an unsynced var still syncing its synced siblings; one sync per storage name when one delta writes two same-name vars; no writes or syncs from a not-yet-hydrated delta; and a newer backend value overwriting, re-syncing once, then terminating on its echo.

All five pass with the change; four of them fail against the template on main (the hydrate case guards existing behavior). The full file passes 37/37 and both files are prettier-clean. The test harness's localStorage mock gained setItem and its socket mock records emit so the delta path can be driven directly. I kept this to unit coverage since the existing state_js.test.mjs harness drives the real template with controlled browser APIs; happy to add a Playwright test if you'd like one.