Minify state var names and allow installing a name resolver after states exist

BE
6h ago
+9726-417

Stacked on #6100 — the diff includes its commits until that merges. The changes specific to this PR are in fb6992b and the merges of explicit-event-id-minification that follow it.

What this adds

Var name minification. REFLEX_MINIFY_VARS=1 shortens state var keys on the wire using a new optional vars section in minify.json (still schema version 1; older files load with no vars). A minified key is just its id, so a delta goes from {"a.b": {"items_rx_state_": []}} to {"a.b": {"c": []}}. reflex minify init/sync/validate/list/lookup cover vars, and the scheme digest includes them. Var ids that every JS object already has (constructor, __proto__, …) or that end in _rx_state_ are rejected.

A name resolver can be installed after states exist. RegistrationContext.set_name_resolver rebuilds the Vars every registered state holds. Backend dependency tracking and VarData.field_dependencies now key on classes and the resolver-independent default name, so they never go stale. This lets in-process AppHarness tests run fully minified. A Var built from state Vars before the install (e.g. a module-level component imported before minify.json was loaded) is not fixed; compiling fails with a message naming each stale reference instead of shipping a broken frontend.

One place builds wire keys. format_var_key / format_state_var (which reads through the prefixed local format_state_local from #6100) replace the ad-hoc name + FIELD_MARKER concatenations; update_vars_internal maps keys back through a per-state lookup, and state.js reads the hydrated flag's key from the compiled context instead of hardcoding it.

Notes

  • setvar still sends the Python var name: it dispatches through the class hierarchy, where a per-state id would be ambiguous.
  • REFLEX_PERF_MODE's check for stringified vars keys on the field marker, so it doesn't see minified vars.
  • Router fields and is_hydrated are minified too; nothing in the frontend reads them by name anymore.
  • New public API: NameResolver.resolve_var_name, reflex_base.utils.format.format_var_key / format_state_var.

Testing

  • Unit tests for var ids, config validation, sync/prune, the stale-name check, rebuilding Vars on a late install, dependency tracking and always-dirty substates under renamed states, client-storage compile, update_vars_internal with minified keys, and the CLI.
  • tests/integration/test_minification.py now runs in-process with framework states and vars minified.
  • A downstream app driven by Playwright in prod with all three modes on: inherited, computed and backend vars, background tasks, ComponentState, cookie/localStorage persistence across tabs, dynamic routes; websocket frames carry no var or state names.

🤖 Generated with Claude Code