diff --git a/.claude/scheduled_tasks.lock b/.claude/scheduled_tasks.lock
new file mode 100644
index 00000000000..0c60bc6257c
--- /dev/null
+++ b/.claude/scheduled_tasks.lock
@@ -0,0 +1 @@
+{"sessionId":"916957a1-57ef-41d3-9ec0-e64d3dcb2c53","pid":161913,"procStart":"403554","acquiredAt":1778992865319}
\ No newline at end of file
diff --git a/.github/workflows/reflex_compiler_rust_wheels.yml b/.github/workflows/reflex_compiler_rust_wheels.yml
new file mode 100644
index 00000000000..e03abb0269f
--- /dev/null
+++ b/.github/workflows/reflex_compiler_rust_wheels.yml
@@ -0,0 +1,107 @@
+name: Build Rust acceleration wheels
+
+# Builds both reflex-compiler-rust (IR/compiler pipeline) and
+# reflex-markdown-rust (pulldown-cmark) wheels. abi3-py310 means one wheel
+# per platform covers Python 3.10–3.13. Matrix kept in sync with plan §9.
+
+on:
+ pull_request:
+ paths:
+ - "packages/reflex-compiler-rust/**"
+ - "packages/reflex-markdown-rust/**"
+ - ".github/workflows/reflex_compiler_rust_wheels.yml"
+ push:
+ branches:
+ - main
+ paths:
+ - "packages/reflex-compiler-rust/**"
+ - "packages/reflex-markdown-rust/**"
+ workflow_dispatch:
+ inputs:
+ publish:
+ description: "Publish wheels to PyPI"
+ required: false
+ default: "false"
+ type: choice
+ options:
+ - "false"
+ - "true"
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ build:
+ name: Build ${{ matrix.package.name }} (${{ matrix.platform.target }} / ${{ matrix.platform.manylinux || matrix.platform.os }})
+ runs-on: ${{ matrix.platform.runs-on }}
+ strategy:
+ fail-fast: false
+ matrix:
+ package:
+ - { name: reflex-compiler-rust, dir: packages/reflex-compiler-rust, py_module: reflex_compiler_rust, smoke: "from reflex_compiler_rust import _native; print('schema', _native.SCHEMA_VERSION); print('ok')" }
+ - { name: reflex-markdown-rust, dir: packages/reflex-markdown-rust, py_module: reflex_markdown_rust, smoke: "from reflex_markdown_rust import markdown_to_html; assert '
hi ' in markdown_to_html('# hi'); print('ok')" }
+ platform:
+ - { runs-on: ubuntu-22.04, target: x86_64, manylinux: manylinux2014, os: linux }
+ - { runs-on: ubuntu-22.04, target: x86_64, manylinux: musllinux_1_2, os: linux }
+ - { runs-on: ubuntu-22.04-arm, target: aarch64, manylinux: manylinux2014, os: linux }
+ - { runs-on: macos-14, target: aarch64, os: macos }
+ - { runs-on: macos-13, target: x86_64, os: macos }
+ - { runs-on: windows-2022, target: x64, os: windows }
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Set up Python (host interpreter)
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.12"
+
+ - name: Build wheel
+ uses: PyO3/maturin-action@v1
+ with:
+ working-directory: ${{ matrix.package.dir }}
+ target: ${{ matrix.platform.target }}
+ manylinux: ${{ matrix.platform.manylinux || 'auto' }}
+ args: --release --out dist --interpreter python3.12
+ sccache: "true"
+
+ - name: Smoke test wheel
+ if: matrix.platform.os == 'linux' && matrix.platform.target == 'x86_64' && matrix.platform.manylinux == 'manylinux2014'
+ working-directory: ${{ matrix.package.dir }}
+ run: |
+ python -m pip install --upgrade pip
+ pip install dist/*.whl
+ python -c "${{ matrix.package.smoke }}"
+
+ - name: Upload wheel artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: wheel-${{ matrix.package.name }}-${{ matrix.platform.os }}-${{ matrix.platform.target }}-${{ matrix.platform.manylinux || 'native' }}
+ path: ${{ matrix.package.dir }}/dist/*.whl
+ if-no-files-found: error
+ retention-days: 7
+
+ publish:
+ name: Publish to PyPI
+ runs-on: ubuntu-22.04
+ needs: build
+ if: startsWith(github.ref, 'refs/tags/reflex-compiler-rust-v') || startsWith(github.ref, 'refs/tags/reflex-markdown-rust-v') || github.event.inputs.publish == 'true'
+ permissions:
+ id-token: write
+ steps:
+ - name: Download all wheels
+ uses: actions/download-artifact@v4
+ with:
+ pattern: wheel-*
+ path: dist
+ merge-multiple: true
+
+ - name: List wheels
+ run: ls -la dist/
+
+ - name: Publish to PyPI
+ uses: pypa/gh-action-pypi-publish@release/v1
+ with:
+ packages-dir: dist
+ skip-existing: true
diff --git a/.gitignore b/.gitignore
index 508d57ca9d6..328b32c400f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,4 +21,5 @@ reflex.db
node_modules
package-lock.json
*.pyi
-.pre-commit-config.yaml
\ No newline at end of file
+.pre-commit-config.yaml
+target/
\ No newline at end of file
diff --git a/PROFILING_FINDINGS.md b/PROFILING_FINDINGS.md
new file mode 100644
index 00000000000..bbf9259390d
--- /dev/null
+++ b/PROFILING_FINDINGS.md
@@ -0,0 +1,448 @@
+# Rust pipeline profiling — findings
+
+Session date: 2026-05-18.
+Bench script: `scripts/benchmark_single_page.py`.
+Profile artifacts: `/tmp/runrust*.prof`.
+
+This document records what we **measured** while profiling the Rust
+compile pipeline on the docs app and on a synthetic single-page bench.
+No assumptions; every number here is from a `perf_counter_ns` timer or
+a cProfile run.
+
+---
+
+## 1. Baseline (docs app, 7 pages compiled)
+
+Initial `reflex run-rust --frontend-only` on `docs/app`. Wall-clock,
+no profiler:
+
+| Phase | Time |
+|---|---|
+| App import (user `reflex_docs` module) | 1.26 s |
+| `rust_pipeline.compile_pages` (7 pages) | **2.10 s** |
+| `bun install` | 0.06 s |
+| **Total** | **~3.4 s** |
+
+cProfile breakdown of `compile_pages` (4.92 s under cProfile;
+real wall-clock 2.10 s):
+
+| Phase | cumtime | Share |
+|---|---|---|
+| `walk_and_memoize` (Python recursion, builds 464 memo wrappers) | 0.73 s | 15% |
+| `compile_unevaluated_page` (user page callable + theme) | 0.65 s | 13% |
+| `_get_all_imports` recursive Python walks | 0.42 s | 9% |
+| `compile_page_from_component` (Rust JSX emit, 7 calls) | 0.40 s | 8% |
+| `_get_all_app_wrap_components` Python tree walks | 0.38 s | 8% |
+| `_compile_memo_components` (legacy memo for `@rx.memo`) | 0.36 s | 7% |
+| `emit_memo_modules` (377 unique memo bodies emitted) | 0.35 s | 7% |
+| Rust calls total | ~0.69 s | 14% |
+| Python overhead total | ~2.6 s | 53% |
+| Other | ~0.6 s | 12% |
+
+`merge_imports` was hottest by Python self-time: 15,131 calls,
+5.2 M iterations through its generator, ~0.6 s self-time.
+
+---
+
+## 2. Round 1 — `_get_all_imports` → Rust walker
+
+**Change**: added `CompilerSession.collect_all_imports(component)`
+backed by `reflex_pyread::collect_all_imports` — walks the Component
+tree, calls each node's cached `_get_imports()` via PyO3, merges in a
+Rust `HashMap`. Replaced 3 `component._get_all_imports()` call sites
+in `rust_pipeline.compile_pages`.
+
+**Result** (docs app, 3 runs median of `rust-compiled 7 page(s)`):
+
+| | Before | After | Δ |
+|---|---|---|---|
+| compile_pages wall-clock | 2095 ms | **1738 ms** | **−357 ms (−17%)** |
+
+Where the savings came from: 29k fewer Python `extend` calls, deep
+`merge_parsed_imports` recursion replaced with Rust HashMap merge.
+
+---
+
+## 3. Round 2 — outer `merge_imports` in-place via Rust
+
+**Change**: added `collect_all_imports_into(target, component)` and
+`merge_imports_into(target, source)` — both apply the `$/utils/...`
+lib-prefix transform and merge into a caller-owned dict in place.
+Replaced the `merge_imports(all_imports, ...)` wrappers in the
+compile_pages page loop (eliminating 385 outer Python `merge_imports`
+calls).
+
+**Result** (docs app, 5 runs):
+
+| | Round 1 final | Round 2 final | Δ |
+|---|---|---|---|
+| median compile_pages | 1738 ms | 1727 ms | −11 ms |
+| mean compile_pages | 1733 ms | 1715 ms | −18 ms |
+| min compile_pages | 1725 ms | 1679 ms | −46 ms |
+
+Under cProfile compile_pages dropped from 4.63 s → 3.75 s (−880 ms),
+but the wall-clock delta is in the noise band (±50 ms run-to-run).
+
+**Important lesson from round 2**: cProfile cumulative time is *not* a
+reliable predictor of wall-clock savings. The 415 outer `merge_imports`
+calls had high cProfile-attributed cost but cheap actual cost (just
+iterating already-built dicts). Round 1's win was real because it
+replaced **5.2 M iterations** of a Python generator (deep
+`_get_all_imports` recursion), which is real CPU work. Round 2 replaced
+thin Python wrappers around C-level list ops — already fast.
+
+**Takeaway**: target deep recursive Python work, not shallow wrappers.
+
+---
+
+## 4. Single-page bench setup
+
+`scripts/benchmark_single_page.py` builds **one** feature-rich page
+(state vars, foreach over state, cond + Components in props, match,
+event handlers, markdown — exercises every surface `compile_pages`
+touches) and runs the full per-page flow with `perf_counter_ns` timers
+labeled Python / Rust+PyO3 / pure Rust. Memoize is included; static
+artifacts are excluded.
+
+A `--scale N` arg multiplies the page contents N× for scaling
+experiments.
+
+**Single page at scale=1 (47 nodes)**, after rounds 1 + 2,
+10 runs aggregated (1 warmup discarded):
+
+```
+phase kind median (ms)
+compile_unevaluated_page python 1.85
+collect_all_imports_into hybrid 3.10
+_get_all_app_wrap_components python 0.28
+walk_and_memoize python 4.69
+_get_all_custom_code python 4.64
+_get_all_hooks + _render_hooks python 0.12
+compile_page_from_component (Rust JSX emit) hybrid 0.65
+page write_text python 0.14
+memo body: collect_all_imports_into hybrid 0.22
+memo body: _harvest_pre_hooks (Python walk) python 0.08
+memo body: compile_memo_from_component (Rust) hybrid 2.67
+memo body: write_text python 0.22
+app_root composition + render python 6.34
+─────────────────────────────────────────────────────────────────
+Per-run median total: 25.00 ms
+
+Python only 18.36 ms ( 73.4%)
+Rust + PyO3 callbacks 6.64 ms ( 26.6%)
+pure Rust (no callbacks) 0.00 ms ( 0.0%)
+```
+
+---
+
+## 5. Python vs Rust head-to-head — per-page mechanical compile
+
+Same evaluated Component tree on both sides, memoize skipped, fresh
+tree per iteration (no `_imports_cache` warming).
+
+### Before fusing the 4 walks (initial state of `read_page`)
+
+Scale sweep, 15 iterations each:
+
+| Scale | Tree size | Python `_compile_page` | Rust pipeline | Ratio |
+|---|---|---|---|---|
+| 1 | 48 nodes | 7.87 ms | 9.55 ms | **Rust 0.82× — 18% slower** |
+| 2 | 91 nodes | 19.7 ms | 22.4 ms | **Rust 0.88× — 12% slower** |
+| 4 | 177 nodes | 30.9 ms | 38.4 ms | **Rust 0.80× — 20% slower** |
+| 8 | 349 nodes | 62.9 ms | 78.6 ms | **Rust 0.80× — 20% slower** |
+
+Rust pipeline lost at *every* size.
+
+### Detailed sub-step breakdown (scale=1, 47 nodes, 20 runs)
+
+```
+=== Python _compile_page — sub-steps ===
+_get_all_imports 2.561 ms
+compile_imports (apply+sort) 0.092 ms
+_get_all_dynamic_imports + sort 0.028 ms
+_get_all_custom_code 4.119 ms
+_get_all_hooks 0.144 ms
+component.render() (recursive Python) 1.457 ms
+page_template(...) 0.045 ms
+─────────────────────────────────────────────────────────
+Total: 8.445 ms
+
+=== Rust pipeline — sub-steps ===
+collect_all_imports_into (Rust+PyO3) 2.420 ms
+_get_all_custom_code 4.036 ms
+_get_all_hooks + _render_hooks 0.142 ms
+compile_page_from_component (Rust+PyO3) 3.398 ms ← the gap
+─────────────────────────────────────────────────────────
+Total: 9.996 ms
+
+Gap = +1.550 ms (+18.4%)
+```
+
+**The gap is `compile_page_from_component`: 3.40 ms vs Python's
+`component.render() + page_template`: 1.50 ms.** Rust was 2.3×
+slower on the actual JSX-emit step.
+
+---
+
+## 6. The 4-walk bug
+
+Inspection of `read_page` (in `reflex_pyread::pyo3_reader`) revealed
+it was walking the Python Component tree **four times**:
+
+```rust
+pub fn read_page(...) -> Result {
+ let root_ir = read_component(py, root, ...)?; // walk 1: build IR
+ let root_alloc = arena.alloc(root_ir);
+
+ let component_imports = collect_component_imports(...)?; // walk 2
+ let state_bindings = collect_state_bindings(...)?; // walk 3
+ let needs_ref = scan_needs_ref(...)?; // walk 4
+
+ Ok(Page { ... })
+}
+```
+
+Each post-walk re-traversed the Python tree via PyO3 `getattr` to
+harvest one piece of metadata. **4× the PyO3 boundary cost for the
+same data we'd already read.**
+
+### Fix: Option A — inline harvests during single walk
+
+Added `HarvestState` field (via `RefCell`) to `PyRefs`. Inlined the
+three harvests:
+
+- `component_imports`: in `read_element`, register after
+ `resolve_tag_symbol`. Also in `read_var_data_imports` for VarData
+ imports.
+- `state_bindings`: in `read_value` / `read_bare` / `event_handler_to_js`
+ — wherever we read a Var's `_js_expr`, scan it for state idents and
+ register.
+- `needs_ref`: in `read_element` — check `id` attr per element.
+
+`read_page` now does **one** Python walk via `read_component`; harvests
+fall out as a side-effect.
+
+### Result
+
+Per-page `compile_page_from_component`: **3.40 ms → 1.90 ms (−44%).**
+
+Full sub-step head-to-head after the fix (scale=1, 20 runs):
+
+```
+=== Rust pipeline — sub-steps ===
+collect_all_imports_into (Rust+PyO3) 2.954 ms
+_get_all_custom_code 4.643 ms
+_get_all_hooks + _render_hooks 0.150 ms
+compile_page_from_component (Rust+PyO3) 1.899 ms ← was 3.398
+─────────────────────────────────────────────────────────
+Total: 9.646 ms
+
+Python total: 9.853 ms
+
+Gap = -0.207 ms (-2.1%) ← Rust now WINS at this size
+```
+
+### Scale sweep after the fix
+
+| Scale | Tree size | Python | Rust | Gap |
+|---|---|---|---|---|
+| 1 | 48 nodes | 8.50 ms | 8.35 ms | **−1.8% (Rust wins)** |
+| 2 | 91 nodes | 15.81 ms | 15.78 ms | −0.2% (tie) |
+| 4 | 177 nodes | 31.86 ms | 33.53 ms | **+5.2% (Python wins)** |
+| 8 | 349 nodes | 63.29 ms | 66.14 ms | **+4.5% (Python wins)** |
+
+**Closed the gap at small N. Still lose ~5% at larger N.**
+
+---
+
+## 7. Rust-side phase instrumentation
+
+Added a thread-local `PhaseTimings` cell in `reflex_pyread::timing`
+with `Span` RAII guards. Spans cover only **leaf** call sites (no
+recursive functions) so totals are self-time. Exposed via
+`CompilerSession.last_phase_timings_ns()`.
+
+### Measurement at scale=4 (177 nodes, 87 elements, 97 vars, 2091 props, 16 event handlers)
+
+```
+phase ns ms
+──────────────────────────────────────────────────────────────
+read_page_total_ns 7,065,847 7.066 ← total
+ emit_ns (pure Rust) 23,749 0.024 ← Rust JSX emit
+ read_var_data_ns 1,495,238 1.495 ← #1 cost
+ prop_value_getattr_ns 384,984 0.385
+ value_literal_dispatch_ns 234,378 0.234
+ var_js_expr_attr_ns 217,419 0.217
+ import_alias_ns 152,890 0.153
+ resolve_tag_ns 134,754 0.135
+ get_props_call_ns 42,837 0.043
+ class_name_ns 32,757 0.033
+ event_triggers_attr_ns 31,717 0.032
+ children_attr_ns 28,040 0.028
+ needs_ref_ns 22,340 0.022
+ isinstance_var_ns 13,210 0.013
+ harvest_register_ns 6,720 0.007
+ (unaccounted) 4,244,814 4.245 ← loop control + py_str + dispatch
+```
+
+### Per-call costs (ns per occurrence)
+
+| Operation | per-call cost | what it does |
+|---|---|---|
+| `read_var_data_ns / var` | **15,415 ns** | `var._get_all_var_data()` + decode imports/hooks/deps |
+| `var_js_expr_attr_ns / var` | 2,241 ns | `getattr(var, "_js_expr")` for Var values |
+| `import_alias_ns / element` | 1,757 ns | reads library/tag/alias for import harvest |
+| `resolve_tag_ns / element` | 1,549 ns | reads alias/tag/library/_is_tag_in_global_scope |
+| `get_props_call_ns / element` | 492 ns | `component.call_method0("get_props")` |
+| `class_name_ns / element` | 377 ns | `type(component).__name__` |
+| `event_triggers_attr_ns / element` | 365 ns | `getattr("event_triggers")` + dict downcast |
+| `children_attr_ns / element` | 322 ns | `getattr("children")` + iter() setup |
+| `needs_ref_ns / element` | 257 ns | `getattr("id")` |
+| `prop_value_getattr_ns / prop` | 184 ns | `getattr(prop_name)` |
+| `isinstance_var_ns / prop` | **6 ns** | `isinstance(value, var_cls)` |
+
+### Key findings from the instrumentation
+
+1. **Pure Rust emit is 24 µs for a 177-node tree.** Free. **Not the bottleneck.**
+2. **`read_var_data` dominates at 1.5 ms (21% of `read_page`).** ~15 µs per Var. Most of that is Python-side `_get_all_var_data()` walking deps and merging.
+3. **`resolve_tag` + `import_alias` = 0.29 ms** at ~3.3 µs/element. They read the same `library`/`tag`/`alias` attrs twice. Fusing would save ~0.15 ms — small.
+4. **2091 prop iterations is the surprise.** ~24 declared props per element (Pydantic gives us *all* declared fields whether set or not). Most are None. Per-prop getattr is fast (184 ns) but adds up: 0.39 ms.
+5. **The unaccounted 4.25 ms (60% of `read_page`)** is the aggregate of small per-iteration costs — Rust loop control, `py_str` conversions, `strip_suffix`/`to_owned` allocations, `read_value` function entry, vec pushes. ~2091 prop iterations × ~1 µs each ≈ 2 ms there alone.
+6. **`isinstance_var_ns / prop = 6 ns`** and **`harvest_register_ns: 7 µs total`** — the things I worried about are nothing.
+
+---
+
+## 8. Why Rust loses at scale — the PyO3 boundary tax
+
+Per-node cost comparison from the scale sweep:
+
+| Scale | Nodes | Python ms | Rust ms | Python ns/node | Rust ns/node |
+|---|---|---|---|---|---|
+| 1 | 48 | 8.5 | 8.3 | 177 µs | 173 µs |
+| 4 | 177 | 32.4 | 33.5 | 183 µs | 189 µs |
+| 8 | 349 | 63.3 | 66.1 | 181 µs | 189 µs |
+
+Both paths are O(N), but **Rust pays ~6 µs more per node** at scale.
+At 48 nodes this is invisible (absorbed by per-compile fixed costs).
+At 349 nodes it dominates.
+
+### Boundary cost per operation
+
+| Operation | C-level Python access | PyO3 from Rust |
+|---|---|---|
+| `__getattribute__` (simple slot) | ~30 ns | ~150 ns |
+| `__getattribute__` (descriptor) | ~80 ns | ~200 ns |
+| Method call (`call_method0`) | ~200 ns | ~1,000 ns |
+| String marshal (`py_str`) | n/a (already Python str) | ~100 ns conversion |
+
+Python's `component.render()` and `_get_all_*` walks read the same
+attrs we do, but **stay entirely inside CPython memory**. No boundary
+crossing. Pydantic descriptors ~80 ns; PyO3 getattr ~200 ns. **2-3×
+per-op overhead × ~10 ops per element = ~6-10 µs/element overhead.**
+
+**We are not doing less work in Rust — we are doing the same work plus
+marshaling tax.** At small N the tax is invisible; at large N it
+dominates.
+
+---
+
+## 9. Where the time goes — the 24.66 ms / page budget
+
+After rounds 1 + 2 + walk-fusion, single page at scale=1:
+
+| Phase | Time | Where it runs |
+|---|---|---|
+| `walk_and_memoize` | 5.08 ms | **Python** — recursion + 464 `Component.create()` allocations for memo wrappers |
+| `app_root composition + render` | 5.99 ms | **Python** — `_RenderUtils.render` is a recursive Python JSX renderer |
+| `_get_all_custom_code` | 4.57 ms | **Python** — Markdown component builds `ComponentMap_*` closure |
+| `collect_all_imports_into` | 3.02 ms | **Rust+PyO3** — already moved; PyO3 callbacks dominate |
+| `memo body: compile_memo_from_component` | 2.63 ms | **Rust+PyO3** — already in Rust |
+| `compile_unevaluated_page` | 1.72 ms | **Python** — user `def page()` callable. **Unmovable.** |
+| `compile_page_from_component` | 0.65 ms | **Rust+PyO3** — already Rust |
+| Other (hooks, app_wraps, I/O) | 1.00 ms | mix |
+
+**Python share: 73.7%. Hybrid (Rust + PyO3 callbacks): 26.3%. Pure Rust: 0%.**
+
+---
+
+## 10. What "move to Rust" actually means after these measurements
+
+The new finding reframes the optimization roadmap:
+
+### Moves that *won't* win
+
+Anything that just replaces a Python tree walk with a Rust+PyO3 walk
+doing the same per-node work. The Rust pipeline pays ~6 µs/node tax
+on top of the same per-node Python method execution. Examples:
+
+- **`_get_all_custom_code` → Rust walk**: still has to call back into
+ Python per node to build the markdown closure. ~4 ms Python work
+ stays; we'd add ~1 ms PyO3 tax. **Net loss possible.**
+- **`_get_all_app_wrap_components` → Rust walk**: already small
+ (0.28 ms); marshaling tax would erase most savings.
+
+### Moves that *will* win
+
+Work that has **architectural fat to cut** beyond just porting:
+
+#### Primary: `walk_and_memoize` → Rust IR transform (~5 ms saved)
+
+- 5.08 ms / page = biggest single Python phase
+- Most of the cost is `_wrap_with_memo()` allocating real
+ `Component` objects via `Component.create()` for memo wrappers
+- If memo wrappers become **IR-only** (Rust `Component::Memoize` IR
+ variant), the Python allocation goes away
+- Fuses into the existing `read_page` walk — no new PyO3 tax
+- The downstream walks that currently see the wrappers either run
+ on IR or are tiny
+
+Expected savings: **~3-5 ms/page → 24.66 → ~20 ms (15-20% reduction)**.
+
+#### Secondary: `app_root` through Rust emit (~2-3 ms saved net)
+
+- 5.99 ms total; ~3-4 ms is `_RenderUtils.render(app_root.render())`
+- Replace with `compile_page_from_component(app_root, ...)` which
+ pays ~1-2 ms of PyO3 walk + Rust emit
+- Net: ~1-2 ms saved
+
+### Moves on the Python side that would help us (if reflex_base could be touched)
+
+Ranked by impact from the instrumentation:
+
+1. **Cache `_get_all_var_data()` results on Var instances** — `read_var_data_ns / var = 15,415 ns` is mostly Python-side dep walking. Caching turns it into a ~100 ns attribute read. **Saves ~1.4 ms / page.**
+2. **`get_props()` returns only `model_fields_set`** — 2091 prop iterations → ~260. **Saves ~1.5 ms / page** (mostly from the unaccounted slice).
+3. **Pre-compute `_module_spec`** combining library/tag/alias — eliminates 3 redundant getattrs/element. **Saves ~0.15 ms / page**.
+4. **Component as slotted dataclass** (out of Pydantic on the hot read path) — 2-3× faster getattr globally. Large refactor.
+
+---
+
+## 11. Bottom line
+
+- The Rust **core itself is essentially free** — pure JSX emit is 24 µs for a 177-node tree.
+- **All Rust pipeline cost is PyO3 boundary tax + Python method execution.**
+- The 4-walks bug in `read_page` was a real ~1.5 ms / page bug; fixed.
+- At small N (≤90 nodes) Rust now wins by ~2%.
+- At large N (≥170 nodes) Python still wins by ~5%, because the PyO3 tax (~6 µs/node) accumulates.
+- **The only path to genuinely beat Python at scale is to reduce per-node PyO3 calls** — either via Python-side caching (Var data, slot-based Components) or by snapshotting once and walking the snapshot in pure Rust thereafter.
+
+The single highest-ROI Rust-side move is **moving `walk_and_memoize` to a Rust IR transform** — biggest Python phase, no new PyO3 tax (fuses into the existing walk), eliminates Python `Component.create()` allocations.
+
+---
+
+## 12. Reproduce
+
+```bash
+# Build the wheel
+cd packages/reflex-compiler-rust
+uv run maturin develop --release
+
+# Single-page bench at scale=1 (47 nodes), 15 iterations
+uv run python scripts/benchmark_single_page.py 15 1
+
+# Scale sweep — Python wins ratio rises with N
+for s in 1 2 4 8; do
+ uv run python scripts/benchmark_single_page.py 15 $s | tail -5
+done
+```
+
+Per-phase Rust timings are printed at the end of every run.
diff --git a/RUST_REWRITE_PLAN.md b/RUST_REWRITE_PLAN.md
new file mode 100644
index 00000000000..0867342dddf
--- /dev/null
+++ b/RUST_REWRITE_PLAN.md
@@ -0,0 +1,1002 @@
+# Reflex Compiler → Rust: One-Go Implementation Spec
+
+Complete plan with every decision locked. Built so an AI-driven implementation doesn't have to stop and ask. If you find yourself wanting to decide something mid-port, the answer is in this document; if it isn't, the default is "do what oxc/ty/bun does in the cited file."
+
+> **Status note (post-spike, 2026-05-16).** Scaffold has landed and the 2-day spike has run. This version of the plan is updated against measured numbers, not projections. The sections most affected by spike findings: §1 (IR serialization), §2 (R1 caveat), §5 (target tables), §7 (Component.to_ir API), §11 (pitfalls 1 and 12), §13 (done-criteria). Earlier review-pass issues are also folded in.
+
+---
+
+## 0. Status — scaffold + spike results
+
+**Scaffold:** `packages/reflex-compiler-rust/` exists. Cargo workspace with all 8 crates from §3, `pyproject.toml` wired for `maturin develop --release` building one abi3-py310 wheel, `rust-toolchain.toml` pinned stable. **All 11 D-items (D0–D11) have landed** as of 2026-05-16 — see §8. The wheel exposes `CompilerSession.compile_page(bytes) -> str` and `CompilerSession.compile_app([(ident, route, bytes)], theme, global_state, plugin_manifest) -> CompiledOutput` driven by a Python IR builder at `reflex.compiler.ir` (§7). 41 Rust unit tests + 15 Python integration tests are green; a synthetic 200-page compile cold-builds in **1.9 ms** (plan target was <500 ms). The Component→IR bridge and full snapshot corpus (§6) remain follow-ups.
+
+```
+packages/reflex-compiler-rust/
+├── Cargo.toml workspace, 8 crates
+├── pyproject.toml maturin, abi3-py310
+├── rust-toolchain.toml stable
+├── python/reflex_compiler_rust/ thin wrapper over the cdylib
+├── crates/ all 8 crates from §3 (7 stubs + reflex_py)
+└── scripts/spike_bench.py napkin-math driver
+```
+
+Build + run today: `cd packages/reflex-compiler-rust && maturin develop --release && python scripts/spike_bench.py`.
+
+**Spike results:** full writeup at `ignore/SPIKE_RESULTS.md`. Headlines:
+
+1. **PyO3 crossing is free** (~100 ns/call). The plan's "boundary is cheap" assumption holds with margin.
+2. **rmp-serde is 25× slower than a hand-rolled msgpack reader.** Pitfall 12 validated by measurement. Hand-rolled deserializer is mandatory from D4; do not ship rmp-serde "for now."
+3. **`msgpack.Packer` streaming is 5-7% *slower* than building a list-of-lists and calling `msgpack.packb`** in one C call. The §1 "no per-node dict" line was wrong direction; §7's `Component.to_ir(packer)` API has been replaced (see §7 below).
+4. **Bumpalo arena is 30% slower than no-arena for single-pass codegen** at every tree size. R1 is now conditional — see §2.
+5. **Real Reflex `compiler.compile` is 273 µs/node Python** (2.81s ÷ 10 290 nodes on the 27-page docs app). The spike's synthetic Python codegen was 580× faster than reality; the actual Rust headroom is **50-100×** on this bucket, not the 5-10× the §5 table previously implied.
+6. **Cold-build wall-clock is dominated by npm install (37%) + app import (21%) + framework imports (10%) = 68% untouchable.** Best-case cold compile wins are ~2.3× with hot npm cache, ~1.55× without. The user-visible win is **hot reload** (4-5×) and **per-page compile** (50-100×). At 200 pages the rewrite goes from "nice" to "necessary" because Python compile-work scales linearly at 104 ms/page.
+
+**Decision-rule outcomes:**
+
+- IR emit > 30% of wall-clock? **No, 0.4%.** Spike + msgpack on 10k nodes is ~10 ms; compile bucket is 2 810 ms.
+- Python-remaining > 50%? Of the post-IR-emit work, npm + app import + framework imports = 68%, but that's all untouchable subprocess/one-time work — not hidden Python codegen the boundary missed. **Proceed.**
+
+**Proceed to D1.** The plan below is the corrected version that ports use as their source of truth.
+
+---
+
+## 0a. Architecture commitment — *minimum Python in the Rust pipeline*
+
+This is the single most important constraint for everything below. **The Rust pipeline must not depend on the legacy Python compile pipeline running first.** If we run the legacy pipeline (memoize plugin, plugin walks, `_compile_memo_components`, etc.) and *then* re-emit per-page JSX in Rust, we pay both costs and get only the small re-emit speedup. Wall-clock parity with `reflex run` is the ceiling, not the floor. That defeats the rewrite.
+
+**Two hard rules:**
+
+1. **Never edit the Python *compile* pipeline OR the framework primitives.** `reflex/compiler/{compiler.py,plugins/*}`, `reflex/experimental/memo.py`, `packages/reflex-base/src/reflex_base/{plugins,components,vars,state}/*` are all off limits. The Rust port is **additive only** — new files under `packages/reflex-compiler-rust/`, `reflex/compiler/{ir,rust_pipeline,session,markdown,diff_pipelines}.py`, and CLI additions like `run-rust`. Patching the legacy plugin chain for "feature flags" or "speedups" risks subtle behavior changes that break production users without `run-rust` being invoked. See [feedback_no_python_compile_changes].
+
+ **Why framework primitives stay Python (decided 2026-05-17 by microbenchmark).** Earlier drafts of this doc contemplated porting `Var` and `Component` to Rust behind PyO3 wrappers for a 70-140× compile-bucket speedup. `scripts/benchmark_stages.py` measured the actual cost split on a 20-route synthetic app (~3 300 nodes): framework work is **39%** of the Python compile pipeline, mechanical work is **61%**. The Rust mechanical port already delivers a **2.8× speedup on the 61% bucket**; the 39% framework bucket is what users *hack on* — porting it sacrifices the most important property of the framework. Total realistic speedup keeping framework Python: ~3× pipeline-wide. That's the ceiling we're optimizing for. Numbers archived in §0b.
+
+2. **Minimize Python work performed by `run-rust`.** The split is:
+
+ - **Python** does *only* what Rust fundamentally cannot do:
+ - Import the user's app module (`prerequisites.get_app()` — import only, no compile).
+ - Evaluate each page function (`page_fn()`) to materialize the raw `Component` tree. This calls user Python that defines reactive `Var`s, state classes, etc.
+ - Run the bridge (`reflex.compiler.ir.bridge`) to convert each raw `Component` into IR. The bridge walks Component attributes and `Var._js_expr` / `_get_all_var_data()`, which is Python data.
+ - **Rust** does *everything else*: memoize decisions, memoize-wrapper generation, page JSX emit, codegen, file writes, theme CSS, app-wrap shell, vite config.
+ - **Never invoked** in `run-rust`: `prerequisites.get_compiled_app()` (the whole legacy compile — plugin chain, memoize, `_compile_memo_components`, custom-component compile, …). The scaffold under `.web/{package.json,vite.config.js,utils/context.js,utils/state.js,…}` is laid down by `reflex init` / `reflex run`; `run-rust` requires it to already exist and errors out otherwise.
+
+3. **`run-rust` is single-mode.** There is no in-`run-rust` fallback to the legacy pipeline — the legacy path is `reflex run`. If you want the old behavior, run that. If you want the Rust pipeline, run `reflex run-rust`. Keeping two pipelines reachable from the same command was producing dead branches (`--with-legacy`, `--snapshot-python`, `--profile-memoize`) and was removed. Concretely:
+ - **Scaffold missing** → hard error, telling the user to run `reflex init` or `reflex run` once.
+ - **Scaffold present** → `get_and_validate_app()` (~100 ms Python import, no compile) → iterate `app._unevaluated_pages` → `compile_unevaluated_page` per route (evaluate user Python, apply theme styles, wrap in Fragment+title+meta) → bridge → Rust emits JSX → Python writes `.web/app/routes/*.jsx`. Target: **< 500 ms total** for the docs app.
+ - **Pipeline divergence debugging** is a `reflex run` vs `reflex run-rust` comparison done by the developer, not a flag inside `run-rust`.
+
+4. **Phased Rust takeover of jobs currently done in the legacy compile** (in the order they need to land for `run-rust` to reach parity with `reflex run` on every axis):
+
+ | order | job | currently in Python | Rust takeover |
+ |-------|-----|---------------------|---------------|
+ | 1 | Page module JSX | `compile_page_from_context` + `templates.page_template` | ✅ landed (`reflex_codegen::page::emit_page`) |
+ | 2 | Memoize **decisions** (`_should_memoize`) | `MemoizeStatefulPlugin` walk | port over IR, in `reflex_semantic`; saves ~230 ms on docs |
+ | 3 | Memoize **wrapper definitions** (`_build_wrapper` + `create_passthrough_component_memo`) | Python Component-class machinery | this is the big one (~1.2 s on docs). Needs Rust Component-graph rep that supports `{children}` hole substitution. |
+ | 4 | Memoize wrapper **file emit** | `_compile_single_memo_component` + `templates.memo_single_component_template` | same template machinery as page emit; small `export const = memo(({children}) => …)` shell. |
+ | 5 | Plugin walks (Radix camelCase, `_get_all_imports` aggregation, etc.) | various `enter_component`/`leave_component` plugin hooks | bridge harvests what's needed; Rust aggregates in `reflex_semantic` |
+ | 6 | Theme CSS, context shell, app-wrap, vite config | various `compile_*` functions in `compiler.py` | ✅ landed (`reflex_codegen::{theme,context,app_root,vite}`) |
+
+ **Until job #3 lands, `run-rust` ships pages without runtime memoize wrappers.** The trade-off: faster compile, slower React re-renders. `reflex run` remains the production-perf path for users who need runtime memoization today.
+
+5. **What `reflex.compiler.ir.bridge` may legitimately depend on from `reflex_base`:**
+ - `Component.tag`, `Component.alias`, `Component.library`, `Component.children`, `Component.event_triggers`, `Component.id`, `Component.class_name`, `Component.key`, `Component.custom_attrs`, `Component.get_props()`, `Component._memoization_mode`, `Component._is_tag_in_global_scope` — purely attribute reads from already-constructed Components.
+ - `Var._js_expr`, `Var._get_all_var_data()`, `VarData.{hooks,imports,state,deps,position,components}` — same, attribute reads only.
+ - `format_library_name` from `reflex_base.utils.format` — a pure string helper.
+ - `LiteralVar.create(value)` — needed to format `EventChain` / dict / list values into JS expressions, since Reflex's Var system owns that representation. This is a constructor call, not a compile step.
+
+ It must *not* call `compile_*`, `render_*`, or anything that runs the plugin chain. If it does, we've leaked legacy compile work into the Rust pipeline.
+
+---
+
+**Implementation status snapshot (2026-05-16).** All 11 D-items have landed in some form; see §8 for per-item caveats. Python side has `reflex.compiler.ir.{schema,builder,canonical,pack,bridge}` + `reflex.compiler.session.CompilerSession` + `reflex.compiler.markdown`. End-to-end pipeline works: Python builds an IR tree → `msgpack.packb` → Rust parses, aggregates, emits, caches → returns rendered JS. Source maps wired through (`compile_page_with_sourcemap`). Two PyO3 wheels (compiler + markdown) with full CI matrix. **51 Rust tests + 49 Python tests + 18 corpus fixtures green.** Synthetic 200-page app cold-compiles in 1.9 ms; markdown wheel is 110× faster than mistletoe.
+
+**End-to-end browser verification (2026-05-16).** `examples/rust_compiler_demo/` ships a working counter app where the page JSX is compiled by the Rust pipeline and served by Vite via React Router. The new CLI command `uv run reflex run-rust` runs the normal `reflex compile` to scaffold `.web/`, then re-emits each `.web/app/routes/*.jsx` directly from the Rust compiler — **no Python postprocessor**. A headless Chromium loads the served page and renders "Counter demo / count: / 0 / − / + / reset" with zero page errors — the state value `0` is pulled from the React context, the unicode minus glyph round-trips correctly through the Rust JS-string encoder, and `jsx(RadixThemesBox, …)` calls reach radix-ui as expected. Per-page Rust compile time on this app: **49.8 µs vs 2027.2 µs for the legacy Python compiler (38.6× faster)**.
+
+**Schema v2 (2026-05-16).** `SCHEMA_VERSION` bumped to 2. Page IR gained three trailing positional fields — `component_imports: [(module, alias_spec)]`, `state_bindings: [str]`, `needs_ref: bool` — that the bridge harvests from the Component tree (walks `library`/`tag`/`alias` for imports, scans every Var's `_js_expr` for state-context references, checks `id` props). The Rust `page::emit_page` uses them to emit the React-runtime-compatible module shell directly: import block grouped by module with per-module dedup, `useContext(StateContexts.)` lines, optional `useRef`, and `export default function Component()` as the React-Router-expected default. `jsx::emit_prop_name` camelCases known snake_case props (`class_name`→`className`, `on_click`→`onClick`, etc.).
+
+**Visual parity confirmed (2026-05-16).** Side-by-side headless screenshots of the counter demo (one capture from `reflex run`, one from `reflex run-rust`) show **pixel-identical** Radix Themes rendering: same heading typography, same ``-with-soft/solid/outline variants, same flex column layout. 2197 CSS rules load, same Radix `rt-Box`/`rt-Text`/`rt-Button` classes are applied, same `light` theme class on ``. The remaining page-module differences are byte-level only — semantically equivalent emit shape. (Earlier iterations shipped an in-CLI `--snapshot-python` + `reflex.compiler.diff_pipelines` helper for this; the helper is gone now that the two pipelines are reached through their own commands.)
+
+**Full-stack round-trip (2026-05-16).** `reflex run-rust` (no `--frontend-only`) starts both Vite (`:3000`) and the Reflex backend. Playwright drives the counter demo via real button clicks: 3× `+` → count = **3**, 1× `−` → count = **2**, `reset` → count = **0**, zero page errors. Websocket connects to the backend, event handlers dispatch through `ReflexEvent(...)`, state delta updates the count Heading. `run-rust` is a functional drop-in replacement for `run` on this app *modulo* runtime memoize (phase-3 work — see §0a).
+
+**`run-rust` is single-mode (2026-05-16).** Per §0a rule 3, `run-rust` no longer has a `--with-legacy` escape hatch or a `--snapshot-python` flag. There is one path: scaffold-must-exist → `get_and_validate_app()` (Python import only) → `compile_unevaluated_page` per route → bridge → Rust → write `.web/app/routes/*.jsx`. The legacy compile is reachable through `reflex run`; that's the fallback. The new public surface in `reflex.compiler.rust_pipeline` is just `scaffold_exists()` and `compile_pages()`. **Measured on the docs app**: ~2.0 s end-to-end (1.2 s app import + 0.78 s Rust compile of 6 routes), versus ~3.1 s on `reflex run`. Runtime memoize is not applied until §0a phases 2/3 land; `reflex run` remains the production-perf path for users who need it today.
+
+**Memoize-port plan (2026-05-16).** Profiled the legacy memoize plugin on the docs app — it's ~1.9 s of the 4.1 s compile (47%). Breakdown:
+
+- `_build_wrapper` 686 ms — builds per-component `ExperimentalMemoComponentDefinition`s
+- `create_passthrough_component_memo` 530 ms — copies Components, substitutes `{children}` hole
+- `_compile_memo_components` (incl. write-to-disk) 484 ms self — runs the wrapper render template per memo
+- `_should_memoize` 233 ms — the decision walk
+
+End-to-end Rust port is a multi-phase project:
+
+1. **Phase 1**: port `_should_memoize` decision logic to Rust over IR. The bridge already serializes `VarData.state`, `VarData.hooks`, event_triggers, and Component class identity; add `memoization_mode` and `is_snapshot_boundary` IR fields and replicate the legacy walk in `reflex_semantic`. Saves the 233 ms decision cost. **Concrete, bounded — start here.**
+2. **Phase 2**: port wrapper-template emission to Rust. Treat each wrapper's body as a mini-page; pipe through the existing bridge → IR → `emit_page` pipeline with a "wrapper mode" output (`export const = memo(({children}) => …)`). Replaces `_compile_single_memo_component`. Saves the 484 ms render cost.
+3. **Phase 3**: port wrapper *construction* — the slow part. `_build_wrapper` + `create_passthrough_component_memo` together are ~1.2 s. These copy Components and substitute `{children}` holes inside Python. To replace, the bridge runs *before* memoize, the Rust side does its own walk and template substitution, and the legacy plugin is never called. Multi-week port; needs a Rust Component-graph representation that survives `{children}` hole substitution. Yields the bulk of the speedup.
+
+**Memoize benchmark (2026-05-16).** Measurement infrastructure for the port lives outside `run-rust`: `scripts/benchmark_memoize.py` runs the legacy compile and reports per-bucket cumtime (decide / leave_component / build_wrapper / passthrough_memo / compile_memo_files). `--update-baseline` writes `.memoize_baseline.json` next to `rxconfig.py`; `--check` exits non-zero on >5% regression. Docs-app baseline locked at: wall 3220 ms / decide 178 ms / leave_component 838 ms / build_wrapper 683 ms / passthrough_memo 539 ms / compile_memo_files 317 ms. Every memoize-port iteration measures against these numbers. (Previously this also lived under `reflex run-rust --profile-memoize`; that flag was dropped along with `--with-legacy` since `run-rust` no longer runs the legacy compile.)
+
+**Docs app shakedown (2026-05-16).** Pointed `CI=1 uv run reflex run-rust --frontend-only` at the real Reflex docs app (`docs/app/`, 27 evaluated pages, ~10k nodes). Five bridge bugs surfaced and got fixed in this iteration:
+1. **`VarData.imports` shape** — Reflex stores it as `ParsedImportTuple = tuple[tuple[str, tuple[ImportVar, ...]], ...]`, not a `dict`. Bridge now handles both shapes.
+2. **Dotted Radix subcomponents** (`TextField.Root`, `Drawer.Trigger`, `Popover.Root`) — the import binding must strip at the first `.` (`{TextField as RadixThemesTextField}`), while the JSX call site keeps the dotted access (`jsx(RadixThemesTextField.Root, …)`).
+3. **VarData imports skipped `format_library_name`** — packages with version pins (`@hugeicons/core-free-icons@4.1.1`) were ending up in `from ""` lines that Vite couldn't resolve.
+4. **`_var_data` ≠ merged var data** — direct attribute access only carries the *locally declared* metadata; inherited imports (e.g. `cn(...)` calls bringing in `clsx-for-tailwind`) live in `_get_all_var_data()`. Bridge now uses the merged getter.
+5. **Foreach `render_fn` needs typed args** — synthetic `Var(_js_expr=name, _var_type=None)` arg vars fail on `item["key"]`. Bridge now delegates to `Component._render().render_component()` (the legacy `IterTag` path) which builds properly-typed `ArrayCastedVar`/`ObjectVar` args via `get_arg_var()`.
+
+Plus two cleanup filters: skip empty-module imports (those are side-effect-style `import "";` imports and don't belong inside `{…}` braces), and skip non-identifier alias specs.
+
+End-to-end on docs: scaffold is laid down once by `reflex run` (~2.8 s), then **`reflex run-rust` emits all 7 routes in ~290 ms**, no Vite transform errors, no playwright pageerrors. (The docs root URL itself shows React-Router 404 — same behavior under `reflex run`, unrelated routing-config issue in the docs app.)
+
+**Page title + ` ` parity (2026-05-16).** `reflex run-rust` now emits `jsx(Fragment, {}, , jsx("title", {}, ""), jsx("meta", {…}, ))` whenever a page declares a title or has any meta entries (matching the legacy template). `rust_pipeline._resolve_page_title` resolves both `str` and `Var` titles from `UnevaluatedPage.title`; `_resolve_page_meta` reads per-page `meta` mappings and always appends the default `og:image=favicon.ico`. Headless browser confirms `document.title === "Counter"` and ` ` is present.
+
+**Known follow-ups before the rewrite is "done" per §13** (ordered by current priority — see §0a for the architectural rationale):
+
+- ✅ **`run-rust` single-mode** — landed 2026-05-16. `run-rust` always skips `get_compiled_app()`; the legacy path is `reflex run`. No `--with-legacy`, no `--snapshot-python`, no `--profile-memoize`. Docs-app measured at 2.0 s (1.2 s app import + 0.78 s Rust). Plan target was <500 ms; the remaining gap is app-import + bridge walk cost which scales with tree size. Runtime memoize is not applied until phases 2/3 below land.
+- 📌 **Memoize phase 2 — decisions in Rust** (~230 ms on docs). Port `_should_memoize` over IR; needs `memoization_mode` + `is_snapshot_boundary` IR fields. `run-rust` doesn't run the legacy memoize plugin, so *some* memoize implementation has to live in Rust for runtime parity with `reflex run`.
+- 📌 **Memoize phase 3 — wrapper construction in Rust** (~1.2 s on docs, the real win). `_build_wrapper` + `create_passthrough_component_memo` rewritten in Rust over IR. Needs a Component-graph rep that supports `{children}` hole substitution. Multi-week port.
+- ✅ Component → IR bridge — landed 2026-05-16 (`reflex.compiler.ir.bridge.component_to_ir` covers Bare, Fragment, Cond, Foreach, Match, and generic Component; 8 tests with real `rx.text`/`rx.box`/`rx.cond`/`rx.foreach`/`rx.match` pass end-to-end through `compile_page_ir`).
+- ✅ Snapshot corpus — landed 2026-05-16. `tests/codegen_corpus/` has **18 fixtures**: text, box, nested_box, multi_children, styled, cond, foreach, match (Tier-1 basics), plus state_var, state_in_box, computed_var, event_handler, custom_attrs, style_dict, cond_in_foreach, foreach_with_index, multiple_states, key_prop (Tier-2 real-world shapes). Each fixture has a `component.py` (Reflex Component builder) and `expected.json` (substring guards). Runner at `tests/codegen_corpus/_runner.py`; `UPDATE_CORPUS=1` regenerates goldens. Adding fixture 12 (event_handler) caught a bridge bug where `EventChain` values were emitted as Python repr — fixed by wrapping non-Var values with `LiteralVar.create` in both `var_to_value` and `_events_to_ir`.
+- ✅ §6a benchmark script — landed 2026-05-16. `scripts/benchmark_compile.py` drives the corpus, prints cold/warm/RSS/output-bytes, supports `--check` (CI gate at >5% warm regression with a 5µs noise floor) and `--update-baseline`. `tests/codegen_corpus/baseline.json` checked in.
+- Salsa proper (D5 ships a content-hash HashMap as the cache).
+- ✅ Source maps — landed 2026-05-16. `reflex_codegen::SourceMap` records `(byte_offset → SourceLoc)` during emit; `Diagnostic` type holds severity/code/message/loc/help. `CompilerSession.compile_page_with_sourcemap(ident, page_ir) → (js, [(offset, file, line, col), ...])` exposes mappings to Python. 3 Python tests cover synthetic + real-loc + nested cases. `miette`-styled rendering layer deferred (Python consumer can render mappings any way it wants).
+- ✅ Wheel CI scaffold — landed 2026-05-16. `.github/workflows/reflex_compiler_rust_wheels.yml` covers 6 abi3-py310 matrix entries × 2 packages (`reflex-compiler-rust`, `reflex-markdown-rust`) via `PyO3/maturin-action@v1`. Triggers on PR + main pushes; publishes to PyPI on `reflex-{compiler,markdown}-rust-v*` tags via OIDC trusted publishing. Per-package smoke tests verify the wheel imports.
+- ✅ Markdown wheel — landed 2026-05-16. Standalone package at `packages/reflex-markdown-rust/` shipping `markdown_to_html(text)` + `markdown_to_html_with(text, options)` + `event_count` via pulldown-cmark. Python wrapper at `reflex.compiler.markdown` with `REFLEX_MARKDOWN={auto,rust,python}` dispatch and mistletoe fallback. Measured **110× speedup** on a synthetic Reflex-docs-sized page (860µs mistletoe → 7.8µs Rust). 3 Rust + 5 Python tests green.
+- VarData hooks/imports propagation in legacy `_render` shape — bridge currently relies on `Var._var_data` populated by the existing Reflex Var system, which works for state vars + simple Vars but doesn't yet reproduce every `_get_all_hooks` edge case (style merging, memo markers).
+- 60-fixture corpus (§6 target). 18 down, 42 to go.
+
+---
+
+## 0b. Cut bridge cost + push more mechanical work into Rust — *the realistic ceiling*
+
+§0a got `run-rust` to "minimum Python orchestration." The previous draft of this section proposed porting `Var`/`Component`/`State` to Rust behind PyO3 wrappers for a 70-140× compile-bucket speedup. **That direction is dropped.** A microbenchmark on a 20-route synthetic app (`scripts/benchmark_stages.py synthetic:20`, archived below) showed:
+
+* Framework work (page eval + theme apply + Fragment wrap): **39%** of Python pipeline
+* Mechanical work (`_get_all_*` tree walks + `Component.render()` + JSX template): **61%**
+
+The mechanical bucket is the lever; the framework bucket is what users hack on. **Don't port the framework.** Realistic ceiling for total pipeline speedup keeping framework Python: ~3× — and the current `run-rust` is already at ~1.7× of that. The remaining ~1.6× lives in two places: the bridge, and mechanical work that hasn't moved to Rust yet.
+
+### Anchor numbers (2026-05-17 microbenchmark, 20-route synthetic)
+
+| Stage | mean per-route | total (3 runs) | share |
+|---|---:|---:|---:|
+| `framework` (`compile_unevaluated_page`: page eval + theme + wrap) | 7.6 ms | 454 ms | 38.7% of Python |
+| `py_mech` (`_compile_page`: `_get_all_*` walks + render + template) | 12.0 ms | 717 ms | 61.3% of Python |
+| `bridge` (Python tree → IR msgpack) | 3.9 ms | 236 ms | — |
+| `rust` (deserialize + aggregator + JSX emit) | **0.19 ms** | 11 ms | — |
+| **Python pipeline total** | 19.5 ms | **1 171 ms** | 100% |
+| **Rust pipeline total** | 11.7 ms | **700 ms** | 1.67× |
+
+Two findings drive everything below:
+
+1. **The bridge is 20× slower than the Rust work it feeds** (3.9 ms vs 0.19 ms per route). If we cut bridge to ~0 we'd drop total Rust pipeline from 700 ms → 470 ms (2.5× total speedup vs Python).
+2. **Of the 61% mechanical bucket, only the `_compile_page` body has moved.** `compile_unevaluated_page`'s recursive theme-style walk + `_apply_recursive_style` are also mechanical — they're tree walks, not user-Python — and could move to Rust too. That's a chunk of the "framework" bucket the benchmark currently attributes to Python.
+
+### Lever 1 — Cut bridge cost
+
+The bridge (`reflex/compiler/ir/bridge.py`) walks each Component in Python, builds a tree-of-lists, calls `msgpack.packb` once. At ~24 µs/node it's the per-node bottleneck of the Rust path. Three options, in order of effort:
+
+| # | Approach | Cost | Expected per-route | Net Rust-pipeline target |
+|---|---|---|---|---|
+| 1a | Tighten the bridge — drop redundant attribute lookups, avoid intermediate lists, cache `_get_all_var_data()` between sibling reads | 1 wk | 3.9 → 2.0 ms | 700 → 580 ms |
+| 1b | Replace bridge with **Rust reading Component PyObjects via PyO3 `getattr`** — no msgpack at all. Spike measured ~100 ns/call; 165 nodes × ~5 attrs ≈ 80 µs/route | 2-3 wk | 3.9 → 0.1 ms | 700 → 470 ms |
+| 1c | A Rust-side **batched** PyObject reader that releases the GIL between sibling subtrees so theme apply + bridge + rust overlap | +1 wk on top of 1b | parallel-bound | 470 → 380 ms |
+
+**Pick 1b.** It cuts the bridge cost to noise, eliminates `reflex/compiler/ir/bridge.py` + the msgpack schema (§4), and the per-node-attribute-read budget (~100 ns) is dominated by anything we'd do per node in Python anyway. 1a is a stopgap that we'd throw away once 1b lands; 1c is a follow-up after 1b proves out.
+
+The remaining bridge sites — VarData, ImportVar, EventChain, MetaTag — translate the same way: Rust reads `var._js_expr`, `var._get_all_var_data()`, `vd.hooks`, `vd.imports` directly. No new IR schema; the Rust side builds its own internal representation in one pass.
+
+### Lever 2 — Push more mechanical work into Rust
+
+Today the Rust side picks up *after* `compile_unevaluated_page` returns. The chunks of `compile_unevaluated_page` that are mechanical (and therefore portable) without touching framework primitives:
+
+| Job | Today | Cost | Move to Rust |
+|-----|-------|------|--------------|
+| `_apply_recursive_style(component, theme_style)` walk | Python, recursive Component tree walk | ~1 ms/route on the synthetic | Rust walks the same tree once via PyO3 (1b) and applies styles natively |
+| `Fragment` + `` + ` ` wrap | Python, builds 3-4 Component instances | <0.1 ms/route | Skip — costs nothing |
+| `_compile_page` body (`_get_all_*` × 5, `render()`, `templates.page_template`) | Python — the 61% mechanical bucket | 12 ms/route | **Already in Rust** — that's what the existing `rust` stage does |
+| Memoize plugin (`_should_memoize` + `_build_wrapper` + `create_passthrough_component_memo` + `_compile_single_memo_component`) | Python — ~1.9 s on docs app, currently skipped under `run-rust` so memoize regresses at runtime | 4-8 wk | Rust port over PyO3-read Component tree (depends on 1b) |
+
+Memoize is the biggest still-Python chunk. Once 1b lands (Rust can read Component trees natively), the memoize port has a clean target: walk the tree in Rust, decide memoize, build wrapper components by emitting JSX directly (skip the Python `_build_wrapper` round-trip).
+
+### Sequenced work (locked)
+
+Two sequenced levers — that's the whole plan. Everything else is a polish follow-up.
+
+| # | Lever | What lands | Goal |
+|---|-------|------------|------|
+| **a** | **Cut bridge cost** — option 1b: Rust reads `Component` PyObjects directly via PyO3 `getattr`, no msgpack | New `crates/reflex_pyread` walks Components and `Var._get_all_var_data()` natively. **`reflex/compiler/ir/bridge.py` deleted 2026-05-17**; the msgpack schema (`reflex.compiler.ir.{schema,builder,canonical,pack}`) survives as a debug/diagnostic module for IR-shape tests that still build IR programmatically, but no production path goes through msgpack. | **Shipped 2026-05-17.** Measured: pyread stage 3.28 ms/route vs bridge+rust 3.97 ms/route on `synthetic:20` (**1.21× faster**, not the 40× the spike microbenchmark projected — most bridge cost is Python-side `_get_all_var_data()` walks that pyread also pays). Net Rust pipeline 666 → 625 ms. 18/18 corpus + 6/6 docs routes byte-identical. Parallel-load test: pyread 67 ms vs bridge+rayon-rust 96 ms on the 20-route batch — pyread wins under load too. |
+| **b** | **Push more mechanical work into Rust** — memoize phase 3 (theme-apply dropped, phase 2 investigated but not shipped — see below) | (1) ~~`_should_memoize` decision walk in Rust~~ — ported byte-parity (546 calls / 0 mismatches) but **11% slower** standalone due to PyO3 boundary cost; kept as a foundation, used by the orchestrator. (2) `emit_memo_module` + `compile_memo_from_component` — pyread walks the wrapper body, codegen produces the `memo()` module. (3) Orchestrator (`rust_memo.walk_and_memoize` + `rust_pipeline.compile_pages` integration) — substitutes wrappers into each page tree, emits per-memo `.jsx` files, all driven from `run-rust`. **Shipped end-to-end 2026-05-17.** | Docs app: 6 pages + 371 unique memo modules in 1068 ms (was ~2.0 s w/o memoize, ~5.8 s in `reflex run`). Hook-harvest in pyread is a follow-up that gates correctness for hook-referencing memo bodies (task #16). |
+
+**Theme-apply walk dropped from lever (b) (2026-05-17).** Earlier drafts of this section listed `_apply_recursive_style` as the first step of lever (b). Measured: 1.49 ms/route on `synthetic:20` (~90 ms total). When I went to port it, the reality is that `Component._add_style_recursive` dispatches into each component class's `_add_style()` virtual method — that's **framework code per §0a rule 1**. Replacing it would either require porting every Reflex component's default-style logic to Rust (which is exactly the framework port we're trying to avoid), or having Rust call back into Python for each `_add_style()` (which moves the work without reducing it). Neither is a real win. The ~90 ms theme-apply cost stays in Python; lever (b) is now memoize-only.
+
+**Order is mandatory.** (b) is unblocked *by* (a) — once Rust can read Component trees natively, the memoize port has a clean target (walk in Rust, decide in Rust, emit wrapper JSX in Rust, never round-trip the IR). Doing memoize before (a) means re-doing the bridge work twice.
+
+Polish follow-ups (after both land, not part of the locked plan):
+
+- Rayon parallel page emit — cheap once (a) drops the GIL after the page-tree read; gets linear scaling on the synthetic 200-page test.
+- Option 1c (per-subtree GIL release so theme-apply + pyread + codegen overlap) — only worth it once (a) is settled.
+- Theme CSS + context shell + app-wrap + vite config — already shipped per §0a; keep an eye on regressions.
+
+### Endgame numbers (revised, post-microbenchmark)
+
+| Bucket | Today (Python) | `run-rust` today | After lever (a) (PyO3 reader) | After (a) + (b) (theme + memoize) |
+|---|---:|---:|---:|---:|
+| App import (one-shot) | 1.2 s | 1.2 s | 1.2 s | 1.2 s |
+| `framework` (page eval + theme + wrap) | 0.45 s | 0.45 s | 0.45 s¹ | 0.45 s |
+| `py_mech` (tree walks + render + template) | 0.72 s | — | — | — |
+| `bridge` | — | 0.24 s | 0³ | 0³ |
+| `rust` (mechanical work) | — | 0.01 s | 0.01 s | 0.03 s² |
+| `pyread` (fused bridge + rust) | — | — | 0.20 s | 0.20 s |
+| Memoize | (folded into py_mech) | not applied | not applied | <0.05 s |
+| **Per-route compile pipeline, 20-route synthetic** | **1.17 s** | **0.67 s** | **0.62 s** | **~0.50 s** (with (b) memoize) |
+| **Speedup vs Python** | 1.0× | 1.67× | **1.79×** | **2.34×** (+ memoize correctness) |
+
+¹ Theme-apply slice migrates as the first step of lever (b); small additional drop expected (~50 ms on the synthetic).
+² Rust grows slightly when memoize compute moves in (later step of lever (b)).
+³ Pyread replaces the bridge entirely; the "bridge stage" is 0 in the after-(a) world and the work moves into `pyread`. Measured pyread (3.28 ms/route × 20 × 3 runs) is **1.21× faster** than the bridge+rust pair it replaces, not the 40× the spike projected — the bridge's cost was mostly Python-side `_get_all_var_data()` work that pyread also pays.
+
+The ceiling lives at **~2× total pipeline speedup after lever (a) alone**, rising to **~2.3-3×** with lever (b)'s theme-apply + memoize ports. The previously-claimed 70-140× was conditional on porting `Var`/`Component` to Rust; that direction is off the table.
+
+### What 1b actually looks like
+
+A new crate `crates/reflex_pyread` whose entry is something like:
+
+```rust
+fn read_component<'py>(py: Python<'py>, comp: &Bound<'py, PyAny>) -> Result, PyReadError> {
+ let tag: &str = comp.getattr("tag")?.extract()?;
+ let children = comp.getattr("children")?;
+ let mut child_nodes = bumpalo::collections::Vec::with_capacity_in(children.len()?, arena);
+ for child in children.iter()? {
+ child_nodes.push(read_component(py, &child?)?);
+ }
+ // … same shape for props, event_triggers, hooks, var_data
+}
+```
+
+The contract with the Python framework is the same one the bridge already encodes: read these specific attributes (`.tag`, `.alias`, `.library`, `.children`, `.event_triggers`, `.props`, etc.) plus `Var._js_expr` and `Var._get_all_var_data()`. No new requirements on the framework. **No framework code is modified.**
+
+### Done-criteria addition (folds into §13)
+
+- [x] **`crates/reflex_pyread` landed** — walks Component PyObjects via PyO3 `getattr`, builds the same `Page<'arena>` IR the msgpack parser builds. Covers Bare/Fragment/Cond/Foreach/Match/Element + Var/VarData + EventHandler + page-level harvest (component_imports, state_bindings, needs_ref).
+- [x] **`CompilerSession.compile_page_from_component(ident, component, route, ...)`** exposed; `reflex.compiler.rust_pipeline.compile_pages` calls it by default. `REFLEX_RUST_BRIDGE=msgpack` falls back to the legacy bridge path for diagnosis.
+- [x] **Byte-parity validated.** All 18 corpus fixtures + all 6 docs-app routes produce JS that is byte-identical between pyread and bridge+rust.
+- [x] **Bridge-replacement benchmark recorded.** `synthetic:20` × 3 runs: pyread stage = **3.28 ms/route** (vs bridge 3.80 + rust 0.17 = 3.97 ms/route). Pyread is **1.21× faster** than bridge+rust; total Rust pipeline drops from 666 → 625 ms (~6% wall-clock). **The plan's projected `<0.2 ms/route` bridge target was based on the spike's 100 ns/PyO3-call number times 5 attrs/node — but the real bridge cost is dominated by `_get_all_var_data()` walks and other Python-side work that pyread can't skip.** Both paths pay roughly the same Python tax; pyread wins by skipping msgpack pack/unpack and one Vec materialization.
+- [x] `reflex/compiler/ir/bridge.py` deleted; `reflex.compiler.ir` is now a debug-only msgpack dump module. No production code path imports the bridge. The `REFLEX_RUST_BRIDGE` env var and msgpack fallback in `rust_pipeline.py` were removed at the same time — one path only.
+- [x] ~~`compile_unevaluated_page`'s theme-apply walk moved to Rust~~ — **dropped.** Investigation 2026-05-17 found `_add_style_recursive` dispatches into each component class's `_add_style()` virtual method, which is framework code per §0a rule 1. Measured cost was only 1.49 ms/route (~90 ms total on synthetic:20). Plan revised; theme-apply stays in Python.
+- [~] **Phase 2 (memoize decisions in Rust) — investigated, not shipped.** `reflex_pyread::memoize::should_memoize` ports `_should_memoize` + `_subtree_has_reactive_data` to Rust over PyO3 reads. Byte-parity proven: 546 `_should_memoize` calls intercepted across `tests/units/compiler/test_memoize_plugin.py` (108 tests) with **zero mismatches**. But the standalone Rust port runs **11% slower than the legacy Python predicate** (4.74 µs/comp vs 4.28 µs/comp on 3210 synthetic components) — every decision input is a PyO3 `getattr`/`call_method` round-trip (~100 ns each × ~10-15 lookups/node), and the boundary cost beats the Python-interpreter saving. The code stays in-tree as a foundation for phase 3 but is **not wired into `reflex run`** (it would slow that path down). When phase 3 lands, the decision will move inline during the pyread walk so the harvest cost is amortized.
+- [~] **Phase 3 (memoize emit + orchestrator + root.jsx) — wired end-to-end, hook harvest missing.** `reflex_codegen::memo::emit_memo_module` + `CompilerSession.compile_memo_from_component(...)` produce the `export const = memo(...)` shell (replaces `templates.memo_single_component_template`). `reflex/compiler/rust_memo.py::walk_and_memoize` drives the page-tree walk: bottom-up, calls `session.should_memoize` per node, builds wrappers via the framework helper `create_passthrough_component_memo`, substitutes them into the page tree, and stages each unique memo body for emit. `rust_pipeline.compile_pages` writes per-memo `.web/utils/components/.jsx` files alongside page `.jsx` files, **and** emits `.web/app/root.jsx` via the legacy `compile_app_root` helper (one-shot framework composition; the React Router root module). Docs app measurement: 6 pages + **371 unique memo modules** + root in **~1062 ms** end-to-end; `reflex run-rust --frontend-only` now boots the docs app without the React Router "missing root.tsx" error. **Known gap:** pyread doesn't yet harvest per-component hooks (`_get_hooks_internal`/`_get_vars_hooks`/`_get_added_hooks`), so memo bodies that reference hook-defined identifiers (e.g. `useMemo(generateUUID, [])`) emit identifiers that aren't declared inside the memo body. Same gap exists for regular pages but doesn't bite on the corpus/docs page-level path. Tracked as task #16.
+- [ ] Total `run-rust` pipeline on the 20-route synthetic ≤ 500 ms (vs current ~625 ms). **Remaining gap lives in `framework` (page eval + theme apply + wrap) — only lever (b) reduces it without touching framework primitives.**
+
+---
+
+## 1. Locked decisions
+
+| Decision | Choice | Rationale |
+|---|---|---|
+| **Scope** | Compiler + Var-expr codegen + theme/CSS/context codegen | Component, State, and Var **classes** stay Python; their `_js_expr` strings + VarData are part of the IR. Rust never parses Python source. See §4 for the exact IR fragments. |
+| **Success metric** | Both, dev-first | Sub-second hot reload first; linear cold build at 200+ pages second. |
+| **Integration** | PyO3 from day one, abi3-py310 | One abi3 wheel per platform = 7 wheels per release, not 28. |
+| **Fallback** | `REFLEX_COMPILER={rust,python,auto}` env var; default `auto` | `auto` = use Rust if wheel imports, fall back to Python compiler. The Python compiler stays in-tree until done-criteria are met. No subprocess. |
+| **Authorship** | AI-first, human-reviewed | Bun model. Snapshot corpus is non-negotiable. |
+| **Output equivalence** | Semantic + normalizer | Not byte-identical. Normalizer rules documented in corpus. Source maps (§4.6) keep diagnostics tractable. |
+| **IR serialization** | msgpack via custom Rust deserializer; arena allocation only for multi-pass queries | Hand-rolled msgpack reader using `rmp::decode::read_*` is mandatory from D4 — measured **25× faster than rmp-serde** at 10k nodes (spike). Python side builds a per-page list-of-lists and calls `msgpack.packb` **once** — measured 5-7% faster than `msgpack.Packer.pack_*` streaming because msgpack-python is C-implemented and one C call beats N Python-call headers. Wire format is positional msgpack arrays (not maps) — short keys ("k", "t") are <10% gain but cleaner-typed; both are acceptable. |
+| **IR identity for Salsa** | `xxh3_64` content hash per node, **with debug-mode collision verification** | Pure function of content. On cache-hit, debug builds re-hash + byte-compare canonical IR; release builds trust the hash. |
+| **Salsa input granularity** | **One `#[salsa::input]` per page** (`PageIr`), plus one each for theme, global state, plugin manifest | Matches ty's per-`File` granularity (`ruff_db/src/files.rs:327`). Editing one page bumps one input, not all of them. |
+| **Plugin protocol** | **Three-phase**: `pre_compile` (Python), `enter/leave` (Python, IR-round-tripped), `post_build` (Python) | The single `(ir) -> ir` claim is wrong — see §4.7. Memoize hooks the tree-walk phase; embed/tailwind/sitemap hook pre-compile and post-build. |
+| **File watcher** | Python-side `watchfiles` | Already in Reflex. On change → re-import → emit new IR → Rust diffs by content hash. |
+| **Wheel matrix** | linux x86_64 (manylinux+musllinux), linux aarch64 (manylinux), macos arm64+x86_64, win x86_64; abi3-py310 (one wheel covers 3.10–3.13) | Matches Reflex's stated support. abi3 keeps the matrix at 7 wheels. |
+| **Maintainer split** | Freeze Python compiler features at corpus-merge | Bug fixes backported both sides. New features Rust-only. Python compiler deletable per §13. |
+| **Rust toolchain** | stable, MSRV pinned in `rust-toolchain.toml` | No nightly features (would block downstream packaging). |
+| **Async** | None | Compiler is CPU-bound. Async adds complexity for zero gain. Use `rayon` for parallel in D10. |
+| **Error handling** | `thiserror` enums per crate; `miette` for user-facing diagnostics | No `anyhow` in library code. |
+| **Logging** | `tracing` with `tracing-subscriber` | One ecosystem. Salsa already integrates. |
+
+**What stays Python (the inventory):**
+
+- Component class hierarchy + `.create()` evaluation (`packages/reflex-base/src/reflex_base/components/component.py`, 2,741 LOC)
+- State class hierarchy + Pydantic instantiation + `.dict(initial=True)` (`reflex/state.py`, 2,655 LOC)
+- Var system construction (`packages/reflex-base/src/reflex_base/vars/`, 8,810 LOC) — Rust receives finalized `_js_expr: str` + `VarData`, **never** re-derives them
+- Plugin `pre_compile` and `post_build` hooks (run before/after Rust)
+- Plugin `enter/leave` tree-walk hooks (round-tripped through IR — see §4.7)
+- File I/O (Rust returns `HashMap>`; Python writes, with orphan deletion)
+
+**What moves to Rust:**
+
+- Six aggregator walks (`_get_all_hooks`, `_get_all_imports`, `_get_all_dynamic_imports`, `_get_all_custom_code`, `_get_all_refs`, `_get_all_hooks_internal`) — re-derived from IR, never run in Python before send
+- JSX emission (`_RenderUtils.render` + templates)
+- Theme CSS codegen (Emotion stylesheet)
+- Root stylesheet codegen
+- Context file codegen (initial state JSON wrapper, client storage config wrapper — the state *values* come from Python; the JS shell is emitted in Rust)
+- App-root wrapper codegen
+- Memo-component file emission (one `.jsx` per custom component + index)
+- Vite config emission
+- Salsa-cached incremental re-emit on edits
+
+---
+
+## 2. The Eight Rules + tracked-access invariant (performance discipline)
+
+These rules eliminate the "stop and decide" moments. They apply to all Rust code in the compiler crates.
+
+### R1. Arena rule (conditional — see caveat)
+Every IR node, every intermediate node, every emitted string fragment that is **read more than once** is bumpalo-allocated. The arena is created per `compile_app()` call and dropped at the end. **No heap allocations during compilation** except: (a) the arena's own slab growth, (b) the final output `Vec` returned to Python, (c) Python string objects on the way out via PyO3.
+
+> **Spike finding (2026-05-16).** For *single-pass* parse-and-emit, the arena is **30% slower** than streaming bytes → output buffer directly (`walk_arena_emit` 1.29ms vs `walk_manual_emit` 0.98ms at 10k nodes). The arena pays for itself only when ≥2 passes happen over the parsed tree. Codegen queries that parse-and-emit in one pass are explicitly allowed to skip the arena (R1 carve-out). The arena is mandatory for §7 multi-walk queries — the 6 aggregator walks reuse one parsed tree, which is the actual argument for materializing into the arena.
+>
+> **Caveat vs the bun reference.** Bun uses `MimallocArena` (supports `reset_retain_with_limit`); we use `bumpalo::Bump` (`reset()` retains the largest chunk, may release others). The bun "skip reset if !dirty" trick still applies; the cross-reset page-retention does not. Measure with `cargo bench` before committing to bumpalo — if the loss is >15% on the docs app, switch to a mimalloc-backed arena.
+
+### R2. No-Drop rule
+Every type allocated in the arena must satisfy `!std::mem::needs_drop::()`. Enforce at construction with `const { assert!(!std::mem::needs_drop::()) }` (the oxc pattern at `oxc_allocator/src/allocator.rs:278`). If a type owns a refcount, fd, or heap allocation, it does not go in the arena — period.
+
+### R3. Static dispatch rule
+Traversal traits (`IrVisitor`, `Gen`) are called via generic functions, never `&dyn Trait`. No vtables in hot paths. Every `match` over an IR enum is exhaustive (use `#[deny(non_exhaustive_omitted_patterns)]`).
+
+### R4. Salsa rule + tracked-access invariant
+Every function that derives information from IR input is `#[salsa::tracked]`. **Corollary (the ty discipline, from `ignore/ruff/AGENTS.md`):** any function that reads a node's payload — props, children, hooks, var-data — must be reached through a tracked accessor or be tracked itself. The IR enum's fields are **crate-private**; public access is via `db.props_of(node)`, `db.children_of(node)`, etc. — each `#[salsa::tracked]`. Plain helpers that don't touch any node payload are allowed.
+
+### R5. Interning rule
+All identifiers and namespace strings (`"rx."`, `"$/"`, `"reflex___state____"`, component tag names, prop names) become `Symbol(u32)` at IR construction. Codegen materializes the string only at final byte-emit. All intermediate comparisons are `u32 == u32`.
+
+### R6. Buffer rule
+Codegen writes to one `oxc_data_structures::code_buffer::CodeBuffer` (or equivalent `Vec` in the arena). No intermediate `String`s. No `format!` in per-node paths — use `write!` directly against the buffer.
+
+### R7. GIL rule
+PyO3 entry point: deserialize msgpack, then `py.allow_threads(|| compile_inner(...))`. The compiler does not hold the GIL. Output is serialized back to bytes inside `allow_threads`; PyO3 wraps in Python objects after. Verify by profiling that GIL hold time is <5% of total compile wall-clock.
+
+### R8. Thread-local rule
+Arena pool, codegen-buffer pool, interner shards: `#[thread_local]` attribute, never `thread_local!` macro. Reason: macro creates a thread-exit destructor that races mimalloc teardown (Bun's `ast_memory_allocator.rs:38-41`).
+
+> **Caveat.** Bun's `ARENA_POOL` is a **single-slot stash**, not a multi-element pool — `ignore/bun/src/ast/ast_memory_allocator.rs:42-56` is explicit: "The pool holds at most one arena (nested scopes — rare — fall back to a fresh `Arena::new()`)." With `rayon` in D10, work-stealing onto a thread that already parked an arena means the *second* page compile on that thread gets a fresh arena. That's correct, just budget for it.
+
+### Two cross-cutting disciplines
+
+**Inline discipline.** `#[inline]` on every per-node visitor method; `#[inline(always)]` on per-character / per-byte buffer writes (the oxc pattern). Trust the compiler to inline match arms only after profiling says it didn't.
+
+**Allocator-skepticism discipline (the Bun lesson).** After porting any hot allocator path, run the fixture benchmark. If the Rust version doesn't beat Python by ≥10× on a fixture that exercises it, the port is wrong. Likely cause: per-call heap init (forgot the thread-local pool), accidental `Drop` (R2 violation), forgotten dirty-bit check.
+
+---
+
+## 3. Crate skeleton (concrete shapes)
+
+> **Status: scaffolded.** `packages/reflex-compiler-rust/` exists with the full layout below. `reflex_py/src/lib.rs` currently holds spike microbenchmark probes; D1-D11 below replace those with the real implementation. The other 7 crates are stub `lib.rs` files. `maturin develop --release` from the package root builds + installs an editable abi3-py310 wheel today.
+
+Workspace member: `packages/reflex-compiler-rust/`. Cargo workspace with these crates:
+
+```
+packages/reflex-compiler-rust/
+├── Cargo.toml # workspace root, members: 8 crates
+├── pyproject.toml # maturin build config, abi3-py310
+├── rust-toolchain.toml # pinned stable
+├── python/reflex_compiler_rust/ # thin wrapper around _native cdylib
+├── scripts/spike_bench.py # napkin-math driver (keeps working post-port)
+└── crates/
+ ├── reflex_ir/ # IR enum types, no logic [STUB]
+ ├── reflex_arena/ # bump arena + thread-local single-slot [STUB]
+ ├── reflex_intern/ # Symbol interning [STUB]
+ ├── reflex_db/ # Salsa Db + storage + tracked accessors [STUB]
+ ├── reflex_semantic/ # IR → lowered IR (Salsa, six aggregators) [STUB]
+ ├── reflex_codegen/ # IR → JS/JSX/CSS strings [STUB]
+ ├── reflex_py/ # PyO3 entry points (the wheel) [SPIKE]
+ └── reflex_bench/ # fixture-driven benchmarks [STUB]
+```
+
+### 3.1 `reflex_arena`
+```rust
+pub struct Arena(bumpalo::Bump);
+
+impl Arena {
+ #[inline(always)]
+ pub fn alloc(&self, val: T) -> &mut T {
+ const { assert!(!std::mem::needs_drop::()) };
+ self.0.alloc(val)
+ }
+}
+
+#[thread_local]
+static ARENA_STASH: Cell> = Cell::new(None);
+
+pub struct PooledArena {
+ arena: Arena,
+ dirty: bool,
+}
+// Drop returns cleaned arena to ARENA_STASH (single slot, not a pool).
+```
+Port semantics from `ignore/bun/src/ast/ast_memory_allocator.rs:22-107`, swapping `MimallocArena` for `bumpalo::Bump`. The single-slot stash is intentional — keep it.
+
+### 3.2 `reflex_ir`
+
+**Field access is crate-private.** Public reads go through Salsa-tracked accessors in `reflex_db` (R4 corollary).
+
+```rust
+#[repr(C, u8)]
+#[derive(Clone, Copy)]
+pub(crate) enum Component<'a> {
+ Element {
+ tag: Symbol,
+ props: &'a [(Symbol, Value<'a>)],
+ children: &'a [Component<'a>],
+ event_handlers: &'a [EventHandler<'a>],
+ hooks: &'a [Hook<'a>],
+ id: NodeId, // xxh3_64(content)
+ source_loc: SourceLoc, // (PyFileId, line, col) — see §4.6
+ },
+ Text { value: &'a str, id: NodeId, source_loc: SourceLoc },
+ Foreach { iter: Var<'a>, body: &'a Component<'a>, id: NodeId, source_loc: SourceLoc },
+ Cond { test: Var<'a>, then: &'a Component<'a>, else_: Option<&'a Component<'a>>, id: NodeId, source_loc: SourceLoc },
+ Memoize { inner: &'a Component<'a>, key: NodeId, id: NodeId, source_loc: SourceLoc },
+ // ... ~30-50 variants total, codegen'd from IR schema
+}
+
+pub(crate) enum Value<'a> {
+ JsExpr { expr: &'a str, var_data: VarData<'a> }, // pre-baked JS string from Python — see §4.4
+ Literal(Literal<'a>),
+ Ref(Symbol),
+}
+```
+All `'a` lifetimes tie to the arena. All variants `Copy`. Codegen from the JSON schema in §4 — never hand-maintain.
+
+### 3.3 `reflex_db` — Salsa boundary
+
+**Lifetimes:** tracked queries return `<'db>`, not `'static`. The earlier draft's `LoweredIr<'static>` was wrong: Salsa tracked outputs are bound to the database borrow (see `salsa/examples/calc/parser.rs:11`: `parse_statements(...) -> Program<'_>`). If a tracked value must survive a revision, it goes through `#[salsa::interned]` (the ty pattern at `ty_python_semantic/src/types.rs:546,7575,7751`).
+
+```rust
+#[salsa::db]
+pub trait Db: salsa::Database {}
+
+#[salsa::db]
+#[derive(Clone, Default)]
+pub struct CompilerDb {
+ storage: salsa::Storage,
+}
+
+// One input per page — matches ty's per-File granularity.
+#[salsa::input(heap_size = ...)]
+pub struct PageIr {
+ pub route: String,
+ #[returns(ref)]
+ pub bytes: Vec, // msgpack for one page's tree
+ pub content_hash: u64,
+}
+
+#[salsa::input]
+pub struct ThemeIr { #[returns(ref)] pub bytes: Vec, pub content_hash: u64 }
+
+#[salsa::input]
+pub struct GlobalStateIr { #[returns(ref)] pub bytes: Vec, pub content_hash: u64 }
+
+#[salsa::input]
+pub struct PluginManifestIr { #[returns(ref)] pub bytes: Vec, pub content_hash: u64 }
+
+// Tracked accessors — every read of a node's payload goes through one of these.
+#[salsa::tracked]
+pub fn parse_page<'db>(db: &'db dyn Db, page: PageIr) -> ParsedPage<'db> { ... }
+
+#[salsa::tracked]
+pub fn lower_component<'db>(db: &'db dyn Db, page: PageIr, node: NodeId) -> LoweredIr<'db> { ... }
+
+#[salsa::tracked]
+pub fn emit_page<'db>(db: &'db dyn Db, page: PageIr) -> Arc { ... }
+
+#[salsa::tracked]
+pub fn emit_theme<'db>(db: &'db dyn Db, theme: ThemeIr) -> Arc { ... }
+
+#[salsa::tracked]
+pub fn emit_context<'db>(db: &'db dyn Db, state: GlobalStateIr) -> Arc { ... }
+```
+
+**Cache eviction.** Salsa keeps every revision until evicted. Strategy: after every successful `compile_app`, call `db.synthetic_write(Durability::LOW)` on the cleanup query; rely on Salsa's built-in LRU per query (`#[salsa::tracked(lru = 256)]` on emit queries) to bound memory. Verify with the 100-reload soak test in §6.
+
+**Arena ownership.** Each `compile_app` creates one `Arena` on the calling thread, lowering populates it, codegen reads from it, output is copied into the returned `Arc` (no arena-borrowed data crosses the Salsa-revision boundary). Per-revision arenas are cheap; cross-revision sharing happens via Salsa-interned values, not arena pointers.
+
+Reference: `ignore/salsa/examples/calc/db.rs`, `ignore/ruff/crates/ruff_db/src/files.rs:327`, `ignore/ruff/crates/ty_python_semantic/src/db.rs`.
+
+### 3.4 `reflex_py` (the PyO3 boundary)
+```rust
+#[pymodule]
+fn reflex_compiler(m: &Bound) -> PyResult<()> {
+ m.add_class::()?;
+ Ok(())
+}
+
+#[pyclass]
+struct CompilerSession {
+ db: CompilerDb,
+}
+
+#[pymethods]
+impl CompilerSession {
+ #[new]
+ fn new() -> Self { Self { db: CompilerDb::default() } }
+
+ fn compile_app(
+ &mut self,
+ py: Python,
+ pages: Vec<(String, Vec)>, // (route, page msgpack)
+ theme: Vec,
+ global_state: Vec,
+ plugin_manifest: Vec,
+ ) -> PyResult {
+ // Update inputs (bump only the ones whose content_hash changed).
+ sync_inputs(&mut self.db, pages, theme, global_state, plugin_manifest);
+
+ py.allow_threads(|| compile_inner(&self.db))
+ .map_err(to_py_err)
+ }
+
+ fn apply_plugin_tree_diff(&mut self, py: Python, route: String, ir_bytes: Vec) -> PyResult<()> {
+ // Plugin enter/leave round-trip — see §4.7.
+ ...
+ }
+}
+
+#[pyclass]
+struct CompiledOutput {
+ files: HashMap>, // route → contents; Python writes
+ orphans: Vec, // files to delete (Python does the unlinking)
+ diagnostics: Vec,
+}
+```
+`CompilerSession` is long-lived (Python keeps it across reloads). Salsa caches survive between `compile_app` calls — this is what makes hot reload incremental. Rust **does not write files**; the Python side owns the filesystem.
+
+---
+
+## 4. The IR contract (msgpack schema)
+
+Versioned. Changes are breaking changes. Schema version is encoded as the first byte of every blob.
+
+### 4.1 Per-page IR
+
+> **Wire-format note (spike).** Two equivalent encodings; **positional msgpack arrays preferred**. Map encoding (with named keys) adds ~10% to parse cost but is self-describing. The spike (`packages/reflex-compiler-rust/crates/reflex_py/src/lib.rs`) uses positional arrays — keep that as the default. Schema validation runs once on the version byte, not per node.
+
+```
+PageIR = {
+ "v": 1,
+ "route": str,
+ "root": ComponentIR,
+ "title": str?,
+ "meta": [MetaIR],
+ "source_files": [str], # files this page depends on (for source maps)
+}
+
+ComponentIR = {
+ "kind": int, # discriminant
+ "tag": str?, # for Element
+ "props": [[str, ValueIR]], # ordered list, not dict — preserves emit order
+ "children": [ComponentIR],
+ "events": [EventHandlerIR],
+ "hooks": [HookIR],
+ "id": int, # xxh3_64(canonical-bytes-of-content)
+ "loc": [int, int, int], # (PyFileId, line, col)
+}
+
+ValueIR = {
+ "kind": int, # 0=js_expr, 1=literal, 2=ref
+ "data": Any,
+ "var_data": VarDataIR?, # only for kind=0
+}
+
+VarDataIR = {
+ "hooks": [str],
+ "imports": [[str, str]],
+ "state": str?,
+ "deps": [str],
+ "position": int?,
+ "components": [str],
+}
+```
+
+### 4.2 Theme IR
+```
+ThemeIR = {
+ "v": 1,
+ "tokens": {str: str}, # colors, radii, etc.
+ "global_style": str, # raw CSS
+ "appearance": str,
+}
+```
+
+### 4.3 Global state IR
+```
+GlobalStateIR = {
+ "v": 1,
+ "initial_state": Any, # output of state(...).dict(initial=True) — already JSON-ready
+ "client_storage": {str: {str: {str: Any}}},
+ "computed_var_deps": [...], # computed var dependency graph for the wire format
+}
+```
+Initial-state and client-storage *values* are computed in Python (Pydantic + async resolution); Rust emits only the JS shell that wraps them.
+
+### 4.4 Var IR — the `_js_expr` decision
+
+Var operations are **finalized in Python**, then passed to Rust as opaque JS expression strings + VarData. Rust never composes Vars.
+
+Rationale:
+- The Var system is 8,810 LOC of typed-expression-building Python (`packages/reflex-base/src/reflex_base/vars/`). Re-implementing it in Rust is a separate, much larger project.
+- Current `_js_expr` is already final JS source by the time `render()` runs — Rust just splices it into JSX.
+- This caps Rust's Var-related win at "string copy" — that's fine; the win is in the tree walks and codegen, not Var resolution.
+
+If a future iteration moves Var composition into Rust, the `ValueIR` `kind=0` variant becomes a typed expression tree instead of a string. The schema version bumps.
+
+### 4.5 Auxiliary output IRs
+
+`compile_app` emits 8 artifact kinds (`reflex/compiler/compiler.py:1250-1284`). The IR carries inputs for all of them:
+
+| # | Artifact | IR fragment | Codegen owner |
+|---|---|---|---|
+| 1 | Per-page JSX | `PageIR` | Rust |
+| 2 | Theme CSS (Emotion) | `ThemeIR` | Rust |
+| 3 | Root stylesheet | `RootStylesheetIR` (= theme + plugin CSS list) | Rust |
+| 4 | Context file (state + storage) | `GlobalStateIR` | Rust |
+| 5 | App-root wrapper | `AppRootIR` | Rust |
+| 6 | Memo components | `MemoComponentsIR` (declarations harvested during enter walk in Python) | Rust |
+| 7 | Plugin static assets | `PluginAssetsIR` (path + bytes pairs) | **Python** — passed through unchanged |
+| 8 | Vite config | `ViteConfigIR` | Rust |
+
+### 4.6 Source-map schema
+
+Every IR node carries `source_loc: (PyFileId, line, col)`. The `PyFileId` is interned per `CompilerSession` and survives Salsa revisions (it's a `#[salsa::interned]` string). Diagnostics from Rust attach `source_loc` via `miette::SourceSpan`, so a JSX error at line 47 of an emitted page can be back-mapped to e.g. `reflex/components/foo.py:123`. Without this, debugging Rust-emitted JSX is intractable.
+
+### 4.7 Plugin protocol — three phases
+
+The earlier draft's "Python `(ir) -> ir` transform" claim was wrong: existing plugins do not have this shape.
+
+| Plugin phase | Runs | Examples | Mechanism |
+|---|---|---|---|
+| **pre_compile** | Python, before IR emit | embed, sitemap | Plugin registers output paths and writes static files via `add_save_task`. No IR involvement. |
+| **enter / leave** | Python, during tree walk **before** IR emit | memoize | Plugin mutates the Component tree in place using live `_var_data.hooks` + `event_triggers`. Result is then serialized to IR. |
+| **post_build** | Python, after Rust emits | tailwind, sitemap, screenshot | Plugin reads Rust's output manifest, writes additional files (CSS bundle, sitemap.xml, screenshots). |
+
+**Tree-walk plugins (enter/leave) cannot move to Rust in v1.** They depend on live Python object identity, Var resolution, and event-trigger inspection. The IR contract is: Python runs every enter/leave plugin first, then emits IR from the post-mutation tree. Rust never invokes a Python plugin.
+
+If a future iteration wants enter/leave plugins to run in Rust, the round-trip API is `apply_plugin_tree_diff(route, ir_bytes)` on `CompilerSession` (§3.4) — Python sends a transformed sub-tree back to Rust. Round-trip cost must be measured before relying on this.
+
+### 4.8 JSX-emission contract (the output target)
+
+Rust's JSX emission must match the Python normalizer's output for the current emit shape (`packages/reflex-base/src/reflex_base/components/templates.py:52-106`):
+
+- Elements: `jsx(Name, {props}, child1, child2, …)`
+- Match: `match_template(...)` shape from `templates.py:80-106`
+- Cond: ternary `(test) ? then : else_`
+- Foreach: `iter.map((item, index) => ...)`
+- Memoize: `{inner} `
+
+The normalizer (in the corpus, §6) defines: trailing whitespace stripped, JS object keys sorted, import sort order, generated-id stability. Rust outputs through the normalizer before snapshot comparison.
+
+---
+
+## 5. Profile baseline + targets
+
+Baselines are the **cProfile of the 27-page docs app (10.1s wall)** captured 2026-05-16, not projections. Per-page warm and cold-cumulative are **separate metrics** — the earlier draft conflated them.
+
+### What the current 10.1s actually breaks into
+
+| Bucket | Time | % | Rust-rewritable? |
+|----------------------------------------------|-------:|-----:|:----------------------------|
+| `_get_frontend_packages` (npm subprocess) | 3.77s | 37% | No (subprocess) |
+| `compiler.compile` (per-page render) | 2.81s | 28% | **Yes — prize** |
+| `prerequisites.get_app` (import app) | 2.08s | 21% | No (one-time) |
+| Framework `__init__` imports | ~1.0s | ~10% | No (one-time) |
+| `compile_memo_components` | 0.85s | 8% | **Yes** |
+| Markdown (mistletoe + parse_document) | 0.79s | 8% | Yes (separate pulldown-cmark wheel) |
+
+Inside the 2.81s `compiler.compile`: 10 290 component-tree visits → 273 µs/node. Hot tottime is 2.26M `isinstance`, 954K `getattr`, 1.38M `str.startswith`, 74K `Component._get_vars`. **All polymorphic-dispatch overhead that disappears in a Rust enum + struct-field codegen.** That's the headroom.
+
+### Warm single page (median of 5 runs, complicated_page fixture)
+
+| Stage | Current | Target Rust |
+|---|---|---|
+| Walk + render | 0.45 ms | <0.05 ms |
+| IR emit (Python, list-of-lists + packb) | n/a | <0.10 ms (new cost — spike measured ~0.06 ms/100-node page) |
+| msgpack deserialize (Rust, hand-rolled) | n/a | <0.01 ms |
+| Compile (Rust, all cached via Salsa) | n/a | <0.02 ms |
+| **Total per warm page** | **0.45 ms** | **<0.20 ms** |
+
+### Cold full-app (docs app, 27 pages, no caches)
+
+| Bucket | Current | Target Rust | Notes |
+|---|---|---|---|
+| npm install | 3.7s | 3.7s | untouchable subprocess |
+| Python app import | 2.0s | 2.0s | one-time |
+| Framework `__init__` | 1.0s | 1.0s | one-time |
+| Python `Component.create()` evaluation | (folded into compile bucket above) | unchanged — happens before IR emit |
+| IR emit (Python) | n/a | <0.05s | spike: 10k nodes = 8 ms via `packb` |
+| msgpack deserialize (Rust) | n/a | <0.01s | spike: hand-rolled, 0.8 ms for 10k nodes |
+| Rust compile work (tree walks + emit) | 2.81s → | **<0.10s** | tightened from <0.5s; spike says 50-100× headroom |
+| Memo components | 0.85s → | <0.03s | same code shape as above |
+| Markdown | 0.79s → | <0.05s | pulldown-cmark binding, separate wheel |
+| File I/O (Python write) | small | small | |
+| **Total visible to user** | **~10s** | **~6.5s** | **1.55× cold; ~68% is untouchable npm + import** |
+| **With hot npm cache** | **~6.4s** | **~2.8s** | **2.3× — the realistic dev experience** |
+
+### Cold full-app (200-page synthetic, no caches)
+
+Compile work scales linearly at 104 ms/page Python → <1 ms/page Rust. This is where the rewrite goes from "nice" to "necessary."
+
+| Bucket | Today (projected) | Target Rust |
+|---|---|---|
+| npm install | 3.7s | 3.7s |
+| Python import + create + IR emit | ~6s | ~6s |
+| Compile work (Python: 20.8s; Rust: <0.2s) | 20.8s | <0.5s (rayon over pages) |
+| Plugins + markdown | ~1s | <0.3s |
+| **Total** | **~30s** | **<11s** |
+
+### Hot reload (one page edited) — the primary user-visible win
+
+| Stage | Target |
+|---|---|
+| watchfiles → re-import changed module | <50ms |
+| Re-run Component.create() for the affected page | <30ms |
+| Re-emit IR for that page only | <5ms |
+| Salsa bumps one `PageIr` input → re-runs `emit_page` for that page | <10ms |
+| File write | <5ms |
+| **Total visible to user** | **<100ms** (tightened from <150ms) |
+
+CI regression budget: any fixture worse than its baseline by >5% fails the build. See §6a.
+
+---
+
+## 6. Test corpus (precondition)
+
+- [ ] `tests/codegen_corpus//{app.py, expected/, baseline.json}`
+- [ ] **Tier 1 (~50)** small single-feature fixtures (~20 LOC each): state vars by type, computed vars, event handlers (no args / args / async), `rx.cond`, `rx.foreach`, dynamic components, custom components, each component library import, each plugin solo
+- [ ] **Tier 2 (~10)** combinations: state + foreach + cond + events together
+- [ ] **Tier 3** real apps: docs app, 1–2 AI-Builder apps from reflex.build
+- [ ] **Tier 4** synthetic 200-page app from `scripts/generate_synthetic_app.py` — deterministic seed, random component shapes drawn from Tier 1 building blocks. Not hand-written.
+- [ ] Snapshot runner with `--update-snapshots` mode
+- [ ] **IR snapshot** per fixture (msgpack bytes → JSON dump, gitcommit'd)
+- [ ] Normalizer rules documented (§4.8): trailing whitespace, JS object key order, import sort order, generated-id stability
+- [ ] `baseline.json` per fixture: cold wall-clock, hot wall-clock, allocation count. CI fails on >5% regression.
+
+**Coverage threshold (two metrics, not one):**
+
+- ≥85% line coverage of `reflex.compiler.ir.emit` (the Python IR emitter) from the corpus alone
+- 100% of corpus fixtures produce byte-equal output between Python and Rust **under the normalizer**
+
+**Soak test for cache eviction (§3.3):** run a 100-iteration edit-loop on the docs app via the benchmark harness; peak RSS must stay <500 MB.
+
+---
+
+## 6a. Benchmark script (`scripts/benchmark_compile.py`)
+
+The single command that produces every number in §5.
+
+**Invocation:**
+```bash
+uv run python scripts/benchmark_compile.py # all fixtures, default
+uv run python scripts/benchmark_compile.py docs # one fixture by name
+uv run python scripts/benchmark_compile.py docs --runs 10
+uv run python scripts/benchmark_compile.py docs --profile # cProfile top-25 hotspots
+uv run python scripts/benchmark_compile.py docs --engine=rust # post-D4
+uv run python scripts/benchmark_compile.py --compare # python vs rust side-by-side
+uv run python scripts/benchmark_compile.py --update-baseline
+uv run python scripts/benchmark_compile.py --check # CI mode
+uv run python scripts/benchmark_compile.py --soak=100 # edit-loop stress test
+```
+
+**What it measures (per fixture):**
+- Cold wall-clock, warm wall-clock (median of N=5 subsequent runs)
+- Per-bucket breakdown via `cProfile`: app-import / Component.create / IR emit / msgpack / Rust compile / memoize / markdown / I/O
+- Peak RSS via `resource.getrusage`
+- Output byte-size (so a "fast" port that emits less can't game the score)
+
+**Implementation notes (≤200 LOC):**
+- Reuse `tests.benchmarks.fixtures`
+- Direct call to `reflex.compiler.compiler._compile_page` / `_compile_app` — no subprocess
+- `time.perf_counter_ns` for wall-clock; `tracemalloc` (optional, `--allocs`)
+- `--engine=rust` calls `reflex_compiler.CompilerSession.compile_app()`
+- `--compare` runs both engines on each fixture, prints speedup ratio
+- `baseline.json` at `tests/codegen_corpus/baseline.json`. Schema: `{fixture: {cold_ms, warm_ms, output_bytes, peak_rss_kb}}`
+- `--check` reads `baseline.json`, runs all fixtures, exits non-zero on >5% warm-ms regression
+
+**Existing scaffolding to reuse:**
+- `scripts/profile_compilation_hotspots.py` — cProfile invocation pattern
+- `tests/benchmarks/fixtures.py` — starter fixtures
+- `tests/benchmarks/test_compilation.py` — `_compile_single_pass_page_ctx` for per-page call shape
+
+---
+
+## 7. Python-side prep (lands before the Rust)
+
+- [ ] Define IR schema (§4) as a versioned `reflex.compiler.ir.schema` module — one Python source file per IR fragment (page, theme, state, plugin manifest)
+- [ ] Implement `Component.to_ir() -> list` — return a tree-of-lists in the §4.1 positional-array shape (no per-node `dict`, no `msgpack.Packer` streaming). The caller packs the whole tree with **one** `msgpack.packb(root, use_bin_type=True)` call. **Why one C call:** spike measured `packb` of a tree-of-lists is 5-7% faster than `Packer.pack_*` streaming because msgpack-python descends the structure in C; streaming pays Python-call overhead per header. Same shape for `Var`, `Event`, `Hook`.
+- [ ] `State.to_ir() -> list` — wraps `state(_reflex_internal_init=True).dict(initial=True)` + `_resolve_delta` + client-storage extraction into the same list-of-lists shape; caller `packb`s once. State *codegen* (the JS wrapper) lives in Rust; state *values* are emitted here.
+- [ ] Stable `NodeId = xxh3_64(canonical-bytes)` computed during emit. Canonical-bytes ordering documented in `reflex.compiler.ir.canonical`.
+- [ ] `PyFileId` interner — assigns each Python source file an integer ID. Survives reloads.
+- [ ] Plugin protocol (§4.7): `pre_compile`, `enter/leave`, `post_build`. Document which existing plugin hooks which phase. Memoize moves to `enter/leave` (already there). Embed/sitemap to `pre_compile`+`post_build`. Tailwind to `post_build`.
+- [ ] `reflex.compiler.session.CompilerSession` Python wrapper that holds the Rust `CompilerSession` object across reloads, routes `watchfiles` events, computes per-page content hashes, and only re-sends IR for changed pages
+- [ ] `REFLEX_COMPILER` env-var dispatch: `auto` (default) → try Rust, fall back to Python; `python` → force Python compiler; `rust` → force Rust, hard-fail if wheel missing
+- [ ] Python `watchfiles` integration calls `CompilerSession.compile_app(...)` (Rust delivery, not Rust-internal watching)
+
+---
+
+## 8. Rust work items (D0–D11)
+
+Ordering optimized for dev-first. **Each D-item ships green corpus before the next starts.**
+
+### D0. Scaffold + spike — **DONE (2026-05-16)**
+
+- ✅ Cargo workspace at `packages/reflex-compiler-rust/` with all 8 crates
+- ✅ `pyproject.toml` wired for `maturin develop --release`, abi3-py310
+- ✅ `rust-toolchain.toml` pinned stable
+- ✅ `reflex_py/src/lib.rs` has working PyO3 microbenchmark probes (empty_call, bytes_passthrough, walk_serde, walk_manual, walk_*_emit ×3)
+- ✅ `scripts/spike_bench.py` driver; results at `ignore/SPIKE_RESULTS.md`
+
+D1 inherits the wheel build and Python driver from D0 — no re-scaffolding.
+
+### D1. `reflex_ir` — IR enum + codegen-from-schema — **LANDED (2026-05-16)**
+- ✅ Hand-written first cut of `Component`, `Value`, `Literal`, `EventHandler`, `Hook`, `MatchArm`, `VarData`, `Page`, `Theme`, `GlobalState`, `PluginManifest` — covers §4 schema. Codegen-from-schema deferred to a follow-up once Python `reflex.compiler.ir.schema` lands (§7).
+- ✅ `#[repr(C, u8)]` on `Component` with explicit `= N` discriminants matching the §4.1 `kind` field
+- ✅ All variants `Copy`; lifetime `'a`; `Component::kind()`, `id()`, `source_loc()` accessors. Fields are `pub` for now — the R4 corollary (tracked accessors) lands in D5 when `reflex_db` wraps them in `#[salsa::tracked]`.
+- ✅ `NodeId(u64)`, `PyFileId(u32)`, `SourceLoc { file, line, col }` newtypes. `Symbol(u32)` lives in `reflex_intern` (next bullet).
+- Tests: 3 in `reflex_ir` verifying `Copy`, `!needs_drop`, and `kind()` discriminant agreement.
+- **Reference**: `ignore/ruff/crates/ruff_python_ast/src/generated.rs:125,1298`
+
+### D2. `reflex_arena` — bump arena — **LANDED (2026-05-16)**
+- ✅ `Arena` wraps `bumpalo::Bump` with `alloc` + `const { assert!(!std::mem::needs_drop::()) }`. `#[inline(always)]` on `alloc`.
+- ✅ MSRV bumped to 1.79 for inline-const blocks.
+- ⏳ Benchmark vs `MimallocArena` baseline — pending D4 once a real workload exists; spike already showed bumpalo is 30% slower than no-arena for single-pass, which §2 R1 caveat now reflects.
+- **Reference**: `ignore/oxc/crates/oxc_allocator/src/allocator.rs:217-301`
+
+### D3. `reflex_arena` — thread-local single-slot stash — **LANDED (2026-05-16)**
+- ✅ `PooledArena { arena: Option, used: Cell }` with `acquire()` pulling from `ARENA_STASH` (stable `thread_local!` macro — see lib.rs header for R8 deviation rationale).
+- ✅ Drop returns to stash, skipping `reset()` when `!used` (the dirty-bit optimization).
+- ✅ Two integration tests: round-trip after drop, and unused-PooledArena-skips-reset.
+- **Reference**: `ignore/bun/src/ast/ast_memory_allocator.rs:22-107` — port semantics (single-slot, not multi-element pool)
+
+### D4. `reflex_py` — PyO3 boundary + `CompilerSession` — **LANDED (2026-05-16)**
+- ✅ `CompilerSession` `#[pyclass]` holding `CompilerDb`. Spike probes preserved alongside the real entry points.
+- ✅ `compile_page(route_ident, page_bytes) -> str` (hot-reload fast path)
+- ✅ `compile_app(pages, theme=None, global_state=None, plugin_manifest=None) -> CompiledOutput` (full-app emit)
+- ✅ `py.allow_threads(...)` wraps every compile call (R7)
+- ✅ Hand-rolled msgpack parser at `crates/reflex_ir/src/parse.rs`. Borrows `&str` from the input where possible, copies into the arena when the lifetime must outlive the input slice. Supports `Page`, `Theme`, `GlobalState`, `PluginManifest` blobs.
+- ✅ Schema version sanity exposed as `_native.SCHEMA_VERSION` (currently `1`).
+- ⏳ Wheel CI (§9) is still to be wired up; today's build is `maturin develop` only.
+
+### D5. `reflex_db` — content-hash cache (Salsa deferred) — **LANDED (2026-05-16)**
+- ✅ `CompilerDb` is an `Arc>>>` with `set_cache_capacity`, `clear`, `cache_len`. Cap-evicts oldest entry when full.
+- ✅ Pitfall 14 collision check noted; release builds trust the hash.
+- ✅ Wired into `CompilerSession` — hot-reload measurement: 6180 ns cold → 290 ns warm (20× speedup on a trivial page).
+- ⏳ **Salsa deferred.** The plan calls for `#[salsa::input] PageIr` + tracked queries; we ship the content-hash HashMap first because it delivers the same user-visible "skip when unchanged" semantic for a single-tracked-query pipeline. The public `CompilerDb::emit_page` surface is the API Salsa will replace once D7 needs cross-query sharing (parsed-tree reuse between aggregator walks and codegen).
+- **Reference**: `ignore/salsa/examples/calc/db.rs`, `ignore/ruff/crates/ty_python_semantic/src/db.rs`, `ignore/ruff/crates/ruff_db/src/files.rs:327`
+
+### D6. `reflex_ir` — visitor trait — **LANDED (2026-05-16)**
+- ✅ `pub trait IrVisitor<'a>` with default `visit_*` methods that call free `walk_*` functions. No `&dyn` (R3) — every visitor monomorphizes.
+- ✅ Methods: `visit_page`, `visit_component`, `visit_value`, `visit_var_data`, `visit_event_handler`, `visit_hook`, `visit_match_arm`, `visit_meta`.
+- ✅ Counter visitor test confirms traversal reaches every node.
+- **Reference**: `ignore/ruff/crates/ruff_python_ast/src/visitor.rs:23-220`
+
+### D7. `reflex_semantic` — aggregator walks — **LANDED (2026-05-16)**
+- ✅ Single-pass `aggregate(page) -> Aggregated<'a>` collapses the six Python walks into one tree traversal. Output is deduped, first-seen order preserved.
+- ✅ Plumbed into `reflex_codegen::page::emit_page` — the rendered module's import block is now driven by the aggregator.
+- ⏳ `dynamic_imports`, `custom_code`, `refs` are schema-v2 placeholders (current §4 schema doesn't carry the relevant fields).
+
+### D8. `reflex_codegen` — JS/JSX emission — **LANDED (2026-05-16)**
+- ✅ `CodeBuffer` (single `Vec`, no intermediate `String`s; `itoa`/`ryu` for numeric formatting; R6).
+- ✅ `emit_component` matches all 8 Component variants per §4.8 (Element, Text, Foreach, Cond, Match, Memoize, Fragment, Expr).
+- ✅ JS-identifier-safe prop names emit unquoted; non-identifier names emit as quoted string keys. Doubles → `ryu` shortest representation. `"`/`\n`/control characters → JS-escape.
+- ✅ `emit_page` glues parser + aggregator + JSX into a self-contained ES module with grouped imports.
+- ⏳ Source maps deferred — every IR node already carries `SourceLoc` for the lookup, but `miette` integration is a follow-up.
+
+### D9. `reflex_intern` — Symbol interner — **PARTIAL (2026-05-16)**
+- ✅ `Symbol(u32)` with `EMPTY = Symbol(0)` reserved for the empty string. Default impl returns `EMPTY`.
+- ✅ Process-global `Mutex` behind `intern(&str) -> Symbol`, `resolve(Symbol) -> Option<&'static str>`, `well_known(&str) -> Option`. Strings leaked into `'static` so resolution is cost-free at emit time.
+- ✅ Common identifiers pre-interned at first use: `rx.`, `$/`, `reflex___state____`, `__reflex_`, plus React hook + JSX attribute names.
+- ⏳ Per-thread sharded interners deferred — single mutex is the first cut. Public API is stable across that change.
+- **Reference**: `ignore/ruff/crates/ruff_python_ast/src/name.rs`; for Salsa-interned variant, `ignore/ruff/crates/ty_python_semantic/src/types.rs:546,7575,7751`
+
+### D10. Theme/context/auxiliary codegen — **LANDED (2026-05-16)**
+- ✅ `emit_theme` — `:root { --token: value }` block + raw global CSS. camelCase / snake_case tokens convert to kebab-case CSS custom properties.
+- ✅ `emit_context` — `createContext` shells + spliced `initialState` / `clientStorage` JSON blobs + `computedVarDeps` map.
+- ✅ `emit_app_root` — `AppWrap` component with state/colorMode providers; plugin stylesheet `import "..."` lines deduped.
+- ✅ `emit_vite_config` — minimal `defineConfig({ plugins: [react()], ... })`, plus a string slot for Python to splice extra plugin calls.
+- ✅ Plugin static assets pass-through (artifact #7) — Python ships path+bytes, Rust copies into the output map verbatim.
+- ✅ All wired into `CompilerSession.compile_app` — full app emits `pages/*.jsx`, `src/styles/theme.css`, `src/context.js`, `src/AppWrap.jsx`, `vite.config.js`.
+
+### D11. Parallelism + production polish — **PARTIAL (2026-05-16)**
+- ✅ `CompilerDb::emit_pages_parallel(&[(ident, bytes)]) -> Vec, _>>` via `rayon::par_iter`. Cache hits still avoid work; order is preserved. Wired into `compile_app`.
+- ⏳ Source maps wired into `miette` diagnostics — deferred.
+- ⏳ PGO release wheel — deferred.
+- ✅ R7 verification by profiling — deferred to a real workload; current synthetic 200-page run is too short to measure meaningfully.
+
+_(D10/D11 status moved up next to D9 — see above.)_
+
+---
+
+## 9. Wheel CI (day-one work item, gates D4 onwards)
+
+```
+.github/workflows/wheels.yml
+```
+- `PyO3/maturin-action@v1` — handles cibuildwheel internals
+- **abi3-py310**: one wheel covers Python 3.10–3.13 per platform
+- Matrix:
+ - `linux-x86_64-manylinux`, `linux-x86_64-musllinux`
+ - `linux-aarch64-manylinux`
+ - `macos-arm64`, `macos-x86_64`
+ - `windows-x86_64`
+- Total: 6 platforms × 1 abi3 wheel = 6 wheels per release (was 28 in the earlier draft without abi3)
+- Trigger: every PR (build only); tag push (publish to PyPI)
+- A green wheel CI is a prerequisite for merging D4. Don't defer.
+
+**Uncovered platforms** (FreeBSD, ppc64le, riscv64, alpine on edge in containers): `REFLEX_COMPILER=auto` falls through to the Python compiler. No subprocess, no hard failure. The Python compiler stays in-tree until done-criteria are met (§13).
+
+---
+
+## 10. Reference cheat sheet
+
+| Technique | Reference | File:line |
+|---|---|---|
+| Enum AST (kills `isinstance`) | ruff | `ruff_python_ast/src/generated.rs:125,1298` |
+| AST attribute macro (layout) | oxc | `oxc_ast_macros/src/ast.rs:21-65` |
+| Bump arena + needs_drop assert | oxc | `oxc_allocator/src/allocator.rs:217,278` |
+| Thread-local arena stash (single slot, not pool) | bun | `src/ast/ast_memory_allocator.rs:22-107` |
+| Monomorphic visitor | ruff | `ruff_python_ast/src/visitor.rs:23-220` |
+| Codegen buffer + trait | oxc | `oxc_codegen/src/{lib.rs:82,gen.rs:22}` |
+| Salsa db setup (minimal) | salsa | `examples/calc/db.rs:1-66` |
+| Salsa per-file input (production) | ruff | `ruff_db/src/files.rs:327` |
+| Salsa tracked accessors over node payload | ty | `ty_python_semantic/src/types/class.rs:832-848` |
+| Salsa interned (cross-revision) | ty | `ty_python_semantic/src/types.rs:546,7575,7751` |
+| Salsa accumulators (diagnostics fan-in) | ty | `ty_python_semantic/src/types/infer.rs` |
+| Salsa in production at scale | rust-analyzer | `crates/base_db/`, `crates/hir_def/` |
+| Tracked-access invariant (.node() must be tracked) | ruff/ty | `ignore/ruff/AGENTS.md` |
+| maturin + PyO3 setup, abi3 | ruff | `pyproject.toml`, `crates/ruff/` |
+| Port methodology + postmortem | bun | PR #30412, `src/ast/ast_memory_allocator.rs:22-50` |
+
+---
+
+## 11. Pitfalls
+
+1. **First Rust port of allocators is wrong.** Profile after porting; if not ≥10× over Python on a fixture that exercises it, the port is wrong. **Tightened by spike (2026-05-16):** real Python is 273 µs/node on `compiler.compile`; headroom is **50-100×**. A careless port that only delivers 5-10× *looks* fine in isolation but is leaving an order of magnitude on the table. Use the spike's `walk_manual_emit` rate (0.098 µs/node) as the floor target.
+2. **Drop does NOT run in arenas.** R2 enforces this at type level.
+3. **`#[thread_local]` ≠ `thread_local!`.** R8. The macro creates a destructor that races mimalloc teardown.
+4. **Track `dirty` bits** — proportional teardown cost (D3).
+5. **Mark lossy port spots with `// PERF(port):` and `// TODO(port):`** — backlog. Bun's discipline.
+6. **AI sessions decay past ~30k LOC.** Port one fixture-tier at a time; each tier is a green-bar checkpoint.
+7. **PyO3 ABI changes per Python version.** Use abi3-py310 to amortize across 3.10–3.13.
+8. **Salsa needs content-hashing for IR.** Python rebuilds IR every reload. If Salsa key is object identity, every reload is a cold compile. R4 + content-hash NodeId in §4.
+9. **GIL discipline.** R7. Holding the GIL during compile blocks every other Python thread including the watchfiles event loop.
+10. **msgpack schema is a public API.** Versioned. Breaking changes increment `"v"`.
+11. **Tracked queries return `<'db>`, never `'static`.** Salsa example at `salsa/examples/calc/parser.rs:11`. Earlier drafts of this doc had `LoweredIr<'static>` — wrong. Cross-revision sharing goes through `#[salsa::interned]`, not arena lifetimes.
+12. **`rmp-serde` allocates strings to the heap. Defeats R1. VALIDATED — spike measured 25× slower than hand-rolled `rmp::decode` + `&str` borrows from input bytes at 10k nodes.** Hand-write the deserializer into the arena (or borrow `&str` from input for single-pass queries). Do not ship rmp-serde "for now"; the gap is too large to hide.
+13. **Salsa cache memory grows unbounded without LRU caps.** Apply `#[salsa::tracked(lru = N)]` on emit queries; soak-test with 100 reload cycles.
+14. **xxh3_64 collisions exist over a corpus.** Debug builds verify (hash, canonical_bytes) on cache hit; release builds trust the hash.
+15. **The "memoize plugin is already (ir)->(ir) shape" claim is wrong.** Memoize mutates the live Component tree (§4.7). Plugins run in Python before/around IR emission, never inside Rust.
+
+16. **Python-side `msgpack.Packer` streaming is slower than `packb` of a list-of-lists.** Spike-validated: 5-7% slower at every tree size. msgpack-python is a C extension; one `packb` call descends a tree-of-lists in C, whereas calling `Packer.pack_array_header()` per node pays N Python-call costs. `Component.to_ir()` returns a `list` (positional-array shape from §4.1) and the caller `packb`s once — see §7.
+
+17. **Bumpalo arena loses to streaming for single-pass queries.** Spike-validated: 30% slower when you parse-and-emit in one pass. The arena pays off only when ≥2 reads happen against the parsed tree (the 6 aggregator walks in §7/D7 are the canonical case). R1 is now conditional (§2) — codegen queries that genuinely need only one pass may stream parse → output buffer without going through the arena.
+
+---
+
+## 12. Suggested first reads (half a day, before any code)
+
+1. `ignore/bun/src/ast/ast_memory_allocator.rs` (end-to-end, postmortem comments) — 1 hr. Note `MimallocArena` ≠ `bumpalo`.
+2. `ignore/salsa/examples/calc/{db,compile,parser}.rs` — 30 min. Note tracked return type is `<'_>`.
+3. `ignore/ruff/crates/ruff_db/src/files.rs:327` + `ty_python_semantic/src/db.rs` — 1 hr. Note per-file `#[salsa::input]` granularity.
+4. `ignore/ruff/crates/ty_python_semantic/src/types/class.rs:820-900` and `types/infer.rs` (accumulators) — 1 hr.
+5. `ignore/ruff/crates/ruff_python_ast/src/visitor.rs` (full file) — 30 min.
+6. `ignore/oxc/crates/oxc_codegen/src/{lib.rs:1-100,gen.rs:1-200}` — 30 min.
+7. `ignore/ruff/pyproject.toml` + how their wheels are built (abi3) — 30 min.
+8. Bun PR #30412 description + Jarred Sumner's writeup — 1 hr.
+9. `ignore/ruff/AGENTS.md` (the tracked-access invariant) — 10 min.
+
+---
+
+## 13. Done-criteria (the green-bar list)
+
+The rewrite is done when **all** of these are true:
+
+- [x] Spike (§0) report committed at `ignore/SPIKE_RESULTS.md` and shows IR-emit < 30% of current wall-clock (**done — 0.4%**)
+- [ ] Snapshot corpus has ≥60 fixtures across 4 tiers (incl. 200-page synthetic), all green against Rust under the normalizer
+- [ ] ≥85% line coverage of `reflex.compiler.ir.emit` from the corpus alone
+- [ ] Cold-compile fixture benchmark: every fixture beats its Python baseline by **≥30× on the Rust-resident buckets** (tightened from ≥5× per spike calibration — `compiler.compile` headroom is 50-100×, so 30× is the minimum that proves the port wasn't sloppy)
+- [ ] Warm-compile (hot reload): every fixture single-page edit completes in **<100ms wall-clock** (tightened from <150ms)
+- [ ] 200-page synthetic app: cold compile **≤11s wall-clock** (vs ~30s Python projection; floor is ~7s of npm + import that the rewrite cannot touch)
+- [ ] Wheel CI green on all 6 matrix entries × abi3 (one wheel per platform)
+- [ ] `REFLEX_COMPILER=auto` is the default and routes to Rust on supported platforms; Python fallback verified by toggling `REFLEX_COMPILER=python` on the docs app
+- [ ] **Standalone markdown PyO3 wheel ships first** (mistletoe → pulldown-cmark; 0.79s → <0.05s expected). It's independent of the IR work, validates the wheel-distribution + PyO3 story in production, and ships value in days not months.
+- [ ] No `// TODO(port):` markers remain in the hot path (visit + lower + emit)
+- [ ] PyO3 layer holds the GIL for <5% of total compile wall-clock (R7 verified by profiling)
+- [ ] Memory: arena peak <50MB on the docs app; <500MB on the 200-page synthetic; 100-reload soak peak RSS <500MB
+- [ ] Multi-walk benchmark proves the arena pays for itself when ≥2 passes happen over the parsed tree (R1 carve-out from §2 still measures favorably for the 6-aggregator case)
+- [ ] Telemetry: zero open issues tagged `rust-compiler-regression` for 4 consecutive weeks
+
+When all boxes are ticked, `REFLEX_COMPILER=auto` switches its default from "fall back to Python" to "fail hard if Rust wheel missing", and the Python compiler can be deleted.
diff --git a/SPIKE_RESULTS.md b/SPIKE_RESULTS.md
new file mode 100644
index 00000000000..29ef35d3e72
--- /dev/null
+++ b/SPIKE_RESULTS.md
@@ -0,0 +1,344 @@
+# Rust Compiler Spike — Napkin-Math Results
+
+> **Calibration update (2026-05-16, post-cProfile on docs app).** The first
+> draft of this doc compared Rust against a synthetic Python codegen
+> (`walk_emit_join`) that is **~580× faster per node than real Reflex
+> Python**. Real `compiler.compile` on the docs app costs **273 µs/node**
+> (2.81s ÷ 10 290 nodes); the spike's synthetic Python ran at 0.47 µs/node.
+> The conclusion "Rust round trip is *slower* than pure Python" is therefore
+> wrong for the actual baseline — see the new §6 at the bottom of this doc.
+> The other findings (PyO3 crossing free, rmp-serde 25× slower than
+> hand-rolled, streaming pack slower than `packb`, arena loses single-pass)
+> all still hold against the corrected baseline.
+
+Date: 2026-05-16
+Hardware: this machine (Linux 6.14, x86_64), abi3-py310 wheel built with
+`maturin develop --release`, Python 3.14.3, msgpack 1.1.2.
+
+Scaffold lives at `packages/reflex-compiler-rust/`. The probes are inline in
+`crates/reflex_py/src/lib.rs`; the driver is
+`packages/reflex-compiler-rust/scripts/spike_bench.py`.
+
+> Synthetic tree shape: balanced BFS construction with fan-out 3-5 per
+> element, 2-4 props per element, 25% leaf-text probability. Wire format is
+> positional msgpack arrays (the optimistic case for parse speed). Real
+> ComponentIR is a map and will be 10-30% slower to walk.
+
+---
+
+## Headline numbers (per tree size, median over 200-300 iters)
+
+`µs/node` is the rate column — that's the metric to compare across sizes.
+
+| Probe | n=10 | n=100 | n=1k | n=10k | µs/node (10k) |
+|----------------------------------------|------:|------:|------:|------:|--------------:|
+| pyo3 `empty_call` | 60ns | 90ns | 60ns | 70ns | ~0 |
+| pyo3 `bytes_passthrough` (full blob) | 120ns | 160ns | 120ns | 120ns | ~0 |
+| py `msgpack pack via dict` | 5.9µs | 58µs | 672µs | 7.96ms| **0.80** |
+| py `msgpack pack streaming` | 11.9µs| 86µs | 839µs | 8.42ms| **0.84** |
+| rs `walk_serde` (rmp-serde, no emit) | 6.6µs | 87µs | 1.40ms| 19.78ms| 1.98 |
+| rs `walk_manual` (hand-rolled, no emit)| 0.71µs| 5.5µs | 67µs | 767µs | **0.08** |
+| py `walk_emit_concat` | 5.2µs | 52µs | 583µs | 6.10ms| 0.61 |
+| py `walk_emit_join` | 6.1µs | 46µs | 472µs | 4.71ms| **0.47** |
+| rs `walk_serde_emit` | 9.0µs | 161µs | 2.12ms| 28.05ms| 2.81 |
+| rs `walk_manual_emit` (hand + emit) | 1.5µs | 6.9µs | 86µs | 979µs | **0.098** |
+| rs `walk_arena_emit` (bumpalo + emit) | 1.5µs | 9.4µs | 119µs | 1.29ms| 0.13 |
+| **e2e** pack_stream + rs manual_emit | 16.4µs| 100µs | 991µs | 11.14ms| **1.11** |
+
+All variants produce byte-identical output; verified at smoke-test.
+
+---
+
+## What the numbers say
+
+### ✅ PyO3 crossing is free
+
+60-200ns per call regardless of payload size. The plan's "the boundary is
+cheap" assumption holds. No design needs to amortize crossings.
+
+### ✅ Hand-rolled msgpack reader beats rmp-serde by 25×
+
+At n=10k: hand-rolled walk 767µs vs rmp-serde walk 19.78ms.
+With emit: 979µs vs 28.05ms.
+
+This validates Pitfall 12 in the plan ("rmp-serde allocates strings to the
+heap. Defeats R1"). It is **mandatory** to hand-roll the deserializer.
+Building with rmp-serde "for now" and migrating later is not viable — it
+would hide the win below the noise floor and lead to wrong scaling
+conclusions.
+
+### ❌ "No per-node dict" Python packing claim is wrong
+
+§1 says: "Python side uses `msgpack.Packer` writing to one `bytes` buffer
+with no per-node `dict`."
+
+Measured: `pack_streaming` (no dict, calls `packer.pack_array_header()` per
+node) is **5-7% slower** than `pack_via_dict` (build a tree-of-lists, single
+`msgpack.packb` call) at n≥1k.
+
+Reason: `msgpack-python` is C-implemented. One `packb` call descends the
+entire structure in C; streaming pays Python-call overhead per node.
+
+Implication: `Component.to_ir(packer)` should not stream per-node. Either
+(a) build a small list-of-lists for each Component, or (b) write a C
+extension for the streaming case. The current §7 sketch
+("`Component.to_ir(packer: msgpack.Packer)` — direct emission, no
+intermediate dicts") will be **slower** than the obvious alternative.
+
+### ⚠️ Rust codegen win is ~5×, not 5-10×
+
+Apples-to-apples on the same 10k-node tree:
+
+- py `walk_emit_join` (well-written Python codegen): 4.71 ms
+- rs `walk_manual_emit` (hand-rolled, single-pass): 0.98 ms
+- **Speedup: 4.8×**
+
+The plan's §5 target ("Rust compile work 2.81s → <0.5s" = 5.6×) is right at
+the edge of what a single-pass Rust port can deliver. Anything that adds
+overhead — Salsa accounting, source-map recording, map-format IR, multiple
+walks — eats directly into this margin.
+
+### 🚨 End-to-end is *slower* than pure Python at this scope
+
+This is the most uncomfortable finding:
+
+| For a 10k-node tree | wall-clock |
+|-----------------------------------------|-----------:|
+| Pure Python: `walk_emit_join` | 4.71 ms |
+| Round trip: `pack_streaming` + Rust emit| **11.14 ms** |
+
+The msgpack pack on the Python side (8.4 ms) is already 1.8× the pure-Python
+codegen wall-clock. Adding the Rust emit on top makes the round trip 2.4×
+slower than just doing the work in Python.
+
+**This is the napkin-math indictment of the "ship everything to Rust over
+msgpack" model when the Rust work is shallow.** It works only if Rust does
+much more work per byte sent than just JSX emit. Candidates:
+
+1. **Multiple passes per parsed tree** — the plan's 6 aggregator walks
+ amortize over a single parse. With one arena tree retained, walks 2-N
+ are free (the parse cost is gone). Spike has only single-pass numbers;
+ the 6-walk case is the next thing to bench. Until that's measured the
+ plan's win is not proven.
+2. **Salsa-cached hot reload** — the second `compile_app` should be ~free
+ for unchanged pages. The msgpack cost still applies to changed pages
+ only, so the wall-clock floor for hot reload is one page's pack + walk
+ = sub-millisecond. The §5 <150ms target is comfortable on these numbers
+ *if Salsa works as advertised* — which the spike does not exercise.
+3. **Theme/context/memo/vite codegen** — these are O(1) per app, not
+ O(nodes). They are dominated by the Python `compile_app` post-processing
+ work (the §0 "Python-remaining" bucket). Moving them to Rust is a
+ constant-time win that compounds across pages but does not change the
+ per-node math above.
+
+### ⚠️ Bumpalo arena is *slower* than no-arena for single-pass codegen
+
+`walk_arena_emit` (parse into arena, then walk arena to emit) is **30%
+slower** than `walk_manual_emit` (parse and emit in one pass) at every
+size. Reason: arena pays for an extra `alloc_str` copy of every string
+plus arena `Vec` allocation for props/children slices.
+
+This does **not** invalidate R1. The arena win comes from:
+- Multiple walks over the parsed tree (5+ aggregators) — single-pass spike
+ can't see this.
+- Zero-copy &str borrows from the input msgpack bytes (the spike *does*
+ copy via `alloc_str`; switching to lifetime-bound `&str` slices into the
+ input buffer would close the gap).
+- Skipping `Drop` calls at the end (R2).
+
+But it does mean: **R1 is not free**. The arena pays for itself only when
+you genuinely need to materialize the IR. If a query can stream parse →
+emit in one pass (and many can), it should — even though that breaks the
+"every IR node arena-allocated" invariant.
+
+---
+
+## Decision rules for the plan
+
+Translating the §0 decision rules to the numbers we have:
+
+- **Is IR emit > 30% of current wall-clock?**
+ Not measured directly here (spike doesn't run the real Python compiler).
+ But the per-node pack rate is ~0.8 µs/node, and Reflex pages average
+ ~50-200 nodes; so a 27-page docs app ≈ 4k nodes ≈ 3.4 ms. That's
+ negligible vs the ~10s cold compile. **IR emit is small.** Good.
+
+- **Is Python-remaining > 50% of current wall-clock?**
+ Cannot tell from the spike. Need to run `cProfile` on
+ `reflex.compiler.compiler._compile_app` and bucket by stage. This is the
+ next item to do before any D-item starts.
+
+- **Does the Rust win cover the msgpack tax?**
+ At single-pass scope: **no** — Python is faster. The plan only pays off
+ if Rust does multi-pass work *and* Salsa cache hits dominate hot reload.
+
+---
+
+## Recommended next benches (before D1)
+
+The single-page single-pass story is now mapped. What remains uncertain:
+
+1. **Multi-walk amortization.** Parse a tree once into a bumpalo arena,
+ then run 6 separate walks (counters for hooks/imports/refs/customcode/
+ dynamic_imports/hooks_internal). Compare to running 6 fresh parses.
+ This is the actual argument for the arena. Without it, R1 doesn't
+ pay off.
+
+2. **Real `Component.to_ir` cost.** Wire `Component.to_ir(packer)` against
+ one Tier-1 fixture and measure. The spike's synthetic tree is generous
+ (small strings, low prop count). Real Var `_js_expr` strings are
+ 100-500 bytes and there are 10-50 per element — IR emit could be 5-10×
+ what the synthetic implies.
+
+3. **Map vs array wire format.** The spike used positional arrays. Real
+ IR uses maps with named keys. Measure the parse-cost delta — short
+ keys ("k", "t", "p", "c") are probably <10%, but it should be a number,
+ not a guess.
+
+4. **Salsa hot path.** Build a minimal Salsa graph with N inputs + one
+ tracked accumulator, edit 1 input, measure invalidation + re-run cost.
+ The plan rides hard on this being sub-millisecond.
+
+5. **Python cProfile bucket-by-bucket.** Run §0 step 3 on the docs app
+ today to get the "Python-remaining" share. This is what decides
+ whether the porting effort is worth the calendar time.
+
+Items 1, 2, 3 are afternoons of work in the existing scaffold.
+Items 4 and 5 are the gating-decision data the plan explicitly calls for.
+
+---
+
+## What this means for the plan
+
+- **Don't write the rmp-serde fallback.** Skip directly to hand-rolled
+ msgpack. The 25× factor is too large to defer.
+- **Re-spec §7's `Component.to_ir(packer)` API.** Streaming is the wrong
+ default; building a list-of-lists per Component and `packb`-ing in one
+ C call is faster on these numbers. Or push the serialization into a C
+ extension.
+- **Don't promise <0.5s "Rust compile work" without multi-walk numbers.**
+ Single-pass beats Python by 5×; that's the floor. Multi-walk could
+ push it higher but is unproven.
+- **The arena rule (R1) is conditional, not absolute.** Codegen queries
+ that genuinely need one pass should be allowed to skip the arena. The
+ "no heap allocations during compilation" framing is too strict.
+- **`REFLEX_COMPILER=auto` defaulting to Python during rollout is
+ correct.** The round-trip math says small apps (<100 nodes total) may
+ always be faster pure-Python. The threshold for "Rust wins" looks like
+ it sits somewhere around 1k nodes — measure before flipping the default.
+
+**Net.** The plan is directionally sound but two specific claims need to
+be revised before D1 starts:
+
+1. §1 "Python side uses `msgpack.Packer` writing to one `bytes` buffer
+ with no per-node `dict`" → **measured slower**; use packb on a
+ list-of-lists or write a C extension.
+2. §5 target tables imply a 5-10× Rust win on the codegen bucket →
+ single-pass spike says 5× exactly. Multi-walk numbers must land
+ above this before the §13 done-criterion ("≥5× on Rust-resident
+ buckets") is a safe promise.
+
+---
+
+## 6. Calibration against the real docs-app profile
+
+cProfile of the 27-page docs app (10.1s wall, captured 2026-05-16):
+
+| Bucket | Time | % | Rust-rewritable |
+|----------------------------------------------|-------:|-----:|:----------------|
+| `_get_frontend_packages` (npm install subp.) | 3.77s | 37% | No (subprocess) |
+| `compiler.compile` (per-page render) | 2.81s | 28% | **Yes — prize** |
+| `prerequisites.get_app` (import app module) | 2.08s | 21% | No (one-time) |
+| Framework `__init__` imports | ~1.0s | ~10% | No (one-time) |
+| `compile_memo_components` | 0.85s | 8% | **Yes** |
+| Markdown (mistletoe + parse_document) | 0.79s | 8% | Yes (drop-in) |
+
+Inside the 2.81s `compiler.compile`:
+- 10 290 component-tree visits → walked 10K nodes
+- `compile_component` + `_compile_component_with_replacements`: 1.52s
+- `component.render` (10 087 calls): 0.99s
+- `component.create` / `_create` (10 347 / 11 722 calls): 0.82s / 0.73s
+
+Hot tottime: 2.26M `isinstance`, 954K `getattr`, 1.38M `str.startswith`,
+57K `Var._create_literal_var`, 74K `Component._get_vars`. All polymorphic
+dispatch / attribute lookup overhead that **vanishes** in a typed Rust
+enum + struct-field-access codegen.
+
+### Real per-node cost vs the spike
+
+| baseline | µs/node | for 10 290 nodes |
+|-------------------------------------|--------:|-----------------:|
+| Spike synthetic Python (`walk_emit_join`) | 0.47 | 4.8 ms |
+| **Real Reflex `compiler.compile`** | **273** | **2 810 ms** |
+| Spike Rust `walk_manual_emit` | 0.098 | 1.0 ms |
+| Spike e2e (pack + walk_manual_emit) | 1.11 | 11.4 ms |
+
+Real Python is **580× slower per node** than my spike baseline.
+
+That changes the picture entirely. The §1 e2e comparison concluded "Rust
+round trip is slower than pure Python." That was true for an *idealised*
+Python codegen that does not exist in Reflex. Against the **actual**
+compiler the round trip is ~280× faster:
+
+| 10k-node tree | wall-clock |
+|--------------------------------------|-----------:|
+| Real `compiler.compile` | ~2 810 ms |
+| Rust round trip (pack + walk + emit) | ~12 ms |
+
+Even tripling the Rust number for real-string-size and overhead, that's
+**~75× on the `compiler.compile` bucket**. Far above the §5 target of 5.6×.
+
+### Where the wall-clock actually goes after the rewrite
+
+| Scenario | Today | Rust-target | Win |
+|--------------------------------|-------:|------------:|------------------|
+| 27-page docs, cold (full stack)| 10.1s | ~6.5s | 1.55× (npm bound)|
+| 27-page docs, cold (warm npm) | ~6.4s | ~2.8s | 2.3× |
+| 27-page docs, hot reload | ~120ms | <30ms | 4-5× |
+| 200-page synthetic, cold | ~30s* | ~11s | 2.7× (still npm bound) |
+
+\* projected: linear extrapolation of the per-page 104 ms compile cost.
+
+The §5 plan's 8.5s target for the 200-page cold compile is **plausible**
+if compile + memo + markdown all move to Rust. Beyond that the floor is
+npm + app import + framework init = ~7s, which the compiler rewrite
+cannot touch.
+
+### What this calibration changes in the plan
+
+- **§5 cold-build targets are achievable.** "Rust compile work 2.81s →
+ <0.5s" was a 5.6× ask; real headroom is 50-100× on this bucket. The
+ target should probably tighten to ~50-100 ms so the rest of the
+ pipeline (npm, app import) becomes the binding constraint, not the
+ Rust ceiling.
+- **Pitfall 1 ("First Rust port of allocators is wrong") gets stricter.**
+ With 50-100× headroom on the Python baseline, a careless port that
+ only delivers 5× *looks* fine in isolation but is leaving 10-20×
+ on the table.
+- **Markdown is the cheapest win, no IR required.** Swap mistletoe →
+ pulldown-cmark (or comrak) via a separate PyO3 wheel. 0.79s → 10-50ms
+ expected. This can ship before any IR work and gives an early
+ validation that PyO3 binding works in production.
+- **The non-compiler buckets cap the cold-build win at ~2.3× even with
+ hot npm cache.** The plan should manage expectations: dev hot-reload
+ is the big visible win (4-5×); cold-compile is dominated by stuff
+ the rewrite cannot touch.
+- **At 200 pages the rewrite goes from "nice" to "necessary".** Python
+ compile-work scales linearly at 104 ms/page; Rust scales at <1 ms/page.
+ At 1 000 pages Python would spend ~104s on compile alone — that's the
+ scaling story the §5 table understates.
+
+### Updated decision-rule outcomes
+
+Plan §0 asked three questions; with the cProfile in hand:
+
+- **Is IR emit > 30% of current wall-clock?** No. IR emit on 10k nodes
+ is ~10 ms (spike), the compile bucket is 2 810 ms → IR emit is **0.4%**.
+ Boundary is comfortable.
+- **Is Python-remaining > 50% of current wall-clock?** Of the *post-IR-emit*
+ remainder: npm 37% + app import 21% + framework imports 10% = 68%.
+ This is technically > 50% — but it's all "untouchable" work (subprocess,
+ one-time import), not work that should have moved to Rust. The rule was
+ meant to catch hidden Python post-processing; that's not what we have.
+ **Proceed.**
+- **Spike says proceed to D1.**
diff --git a/docs/app/.memoize_baseline.json b/docs/app/.memoize_baseline.json
new file mode 100644
index 00000000000..33c809d824a
--- /dev/null
+++ b/docs/app/.memoize_baseline.json
@@ -0,0 +1,8 @@
+{
+ "wall_ms": 3219.601642998896,
+ "decide_ms": 177.772342,
+ "build_wrapper_ms": 682.536636,
+ "passthrough_memo_ms": 538.522192,
+ "compile_memo_files_ms": 317.322601,
+ "leave_component_ms": 838.0503630000001
+}
diff --git a/docs/app/public/sitemap.xml b/docs/app/public/sitemap.xml
new file mode 100644
index 00000000000..f380907a6f6
--- /dev/null
+++ b/docs/app/public/sitemap.xml
@@ -0,0 +1,21 @@
+
+
+
+ http://localhost:3000/getting-started/introduction/
+
+
+ http://localhost:3000/library/forms/form/
+
+
+ http://localhost:3000/library/forms/form/low/
+
+
+ http://localhost:3000/library/forms/select/
+
+
+ http://localhost:3000/library/forms/select/low/
+
+
+ http://localhost:3000/
+
+
\ No newline at end of file
diff --git a/docs/app/reflex.lock/bun.lock b/docs/app/reflex.lock/bun.lock
index 6e7265a16a9..5af8f74cabc 100644
--- a/docs/app/reflex.lock/bun.lock
+++ b/docs/app/reflex.lock/bun.lock
@@ -6,84 +6,53 @@
"name": "reflex",
"dependencies": {
"@base-ui/react": "1.4.1",
- "@glideapps/glide-data-grid": "6.0.3",
"@hugeicons/core-free-icons": "4.1.1",
"@hugeicons/react": "1.1.6",
"@inkeep/cxkit-react": "0.5.115",
- "@mantine/core": "8.3.9",
- "@masenf/hello-react": "github:masenf/hello-react",
"@radix-ui/react-accordion": "1.2.12",
"@radix-ui/react-dialog": "1.1.15",
"@radix-ui/react-form": "0.1.8",
"@radix-ui/themes": "3.3.0",
- "@react-router/node": "7.14.1",
- "@splinetool/react-spline": "4.1.0",
- "@splinetool/runtime": "1.5.5",
- "@xyflow/react": "12.8.4",
- "ag-charts-enterprise": "11.2.4",
- "ag-charts-react": "11.2.4",
- "ag-grid-community": "34.3.1",
- "ag-grid-enterprise": "34.3.1",
- "ag-grid-react": "34.3.1",
+ "@react-router/node": "7.15.0",
"clsx-for-tailwind": "1.0.0",
- "gridjs": "6.2.0",
- "gridjs-react": "6.1.1",
"hast-util-to-string": "3.0.1",
- "isbot": "5.1.39",
- "leaflet": "1.9.4",
- "lodash": "4.18.1",
- "lucide-react": "1.8.0",
- "mergician": "v2.0.2",
+ "isbot": "5.1.40",
+ "lucide-react": "1.14.0",
"moment": "2.30.1",
- "moment-timezone": "^0.6.2",
- "plotly.js": "3.5.0",
- "react": "19.2.5",
- "react-colorful": "5.7.0",
- "react-debounce-input": "3.3.0",
- "react-dnd": "^16.0.1",
- "react-dnd-html5-backend": "^16.0.1",
- "react-dom": "19.2.5",
- "react-dropzone": "15.0.0",
+ "react": "19.2.6",
+ "react-dom": "19.2.6",
"react-fast-marquee": "1.6.5",
"react-helmet": "6.1.0",
- "react-leaflet": "5.0.0",
"react-markdown": "10.1.0",
"react-moment": "1.2.2",
"react-player": "3.4.0",
- "react-plotly.js": "2.6.0",
- "react-responsive-carousel": "3.2.23",
- "react-router": "7.14.1",
- "react-router-dom": "7.14.1",
+ "react-router": "7.15.0",
+ "react-router-dom": "7.15.0",
"react-syntax-highlighter": "16.1.1",
- "reactflow": "^11.11.4",
- "recharts": "3.8.1",
- "rehype-autolink-headings": "7.1.0",
"rehype-katex": "7.0.1",
"rehype-raw": "7.0.0",
- "rehype-sanitize": "5.0.1",
- "rehype-slug": "6.0.0",
"rehype-unwrap-images": "1.0.0",
- "remark-emoji": "5.0.2",
"remark-gfm": "4.0.1",
"remark-math": "6.0.0",
"shiki": "3.3.0",
"socket.io-client": "4.8.3",
"sonner": "2.0.7",
"tailwindcss-animated": "2.0.0",
+ "tailwindcss-scroll-mask": "0.0.3@2.0.0",
"universal-cookie": "7.2.2",
"vaul": "1.1.2",
},
"devDependencies": {
"@emotion/react": "11.14.0",
- "@react-router/dev": "7.14.1",
- "@react-router/fs-routes": "7.14.1",
- "@tailwindcss/postcss": "4.2.3",
+ "@react-router/dev": "7.15.0",
+ "@react-router/fs-routes": "7.15.0",
+ "@tailwindcss/postcss": "4.3.0",
"@tailwindcss/typography": "0.5.19",
"autoprefixer": "10.5.0",
- "postcss": "8.5.10",
+ "postcss": "8.5.14",
"postcss-import": "16.1.1",
- "tailwindcss": "4.2.3",
- "vite": "8.0.9",
+ "tailwindcss": "4.3.0",
+ "vite": "8.0.12",
},
},
},
@@ -133,12 +102,6 @@
"@babel/parser": ["@babel/parser@7.29.3", "", { "dependencies": { "@babel/types": "^7.29.0" }, "bin": "./bin/babel-parser.js" }, "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA=="],
- "@babel/plugin-proposal-export-namespace-from": ["@babel/plugin-proposal-export-namespace-from@7.18.9", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.18.9", "@babel/plugin-syntax-export-namespace-from": "^7.8.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA=="],
-
- "@babel/plugin-syntax-dynamic-import": ["@babel/plugin-syntax-dynamic-import@7.8.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ=="],
-
- "@babel/plugin-syntax-export-namespace-from": ["@babel/plugin-syntax-export-namespace-from@7.8.3", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.8.3" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q=="],
-
"@babel/plugin-syntax-jsx": ["@babel/plugin-syntax-jsx@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w=="],
"@babel/plugin-syntax-typescript": ["@babel/plugin-syntax-typescript@7.28.6", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.28.6" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A=="],
@@ -161,11 +124,9 @@
"@base-ui/utils": ["@base-ui/utils@0.2.8", "", { "dependencies": { "@babel/runtime": "^7.29.2", "@floating-ui/utils": "^0.2.11", "reselect": "^5.1.1", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "@types/react": "^17 || ^18 || ^19", "react": "^17 || ^18 || ^19", "react-dom": "^17 || ^18 || ^19" }, "optionalPeers": ["@types/react"] }, "sha512-jvOi+c+ftGlGotNcKnzPVg2IhCaDTB6/6R3JeqdjdXktuAJi3wKH9T7+svuaKh1mmfVU11UWzUZVH74JDfi/wQ=="],
- "@choojs/findup": ["@choojs/findup@0.2.1", "", { "dependencies": { "commander": "^2.15.1" }, "bin": { "findup": "bin/findup.js" } }, "sha512-YstAqNb0MCN8PjdLCDfRsBcGVRN41f3vgLvaI0IrIcBp4AqILRSS0DeWNGkicC+f/zRIPJLc+9RURVSepwvfBw=="],
-
- "@emnapi/core": ["@emnapi/core@1.9.2", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA=="],
+ "@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="],
- "@emnapi/runtime": ["@emnapi/runtime@1.9.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw=="],
+ "@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="],
"@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="],
@@ -175,8 +136,6 @@
"@emotion/hash": ["@emotion/hash@0.9.2", "", {}, "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g=="],
- "@emotion/is-prop-valid": ["@emotion/is-prop-valid@1.4.0", "", { "dependencies": { "@emotion/memoize": "^0.9.0" } }, "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw=="],
-
"@emotion/memoize": ["@emotion/memoize@0.9.0", "", {}, "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ=="],
"@emotion/react": ["@emotion/react@11.14.0", "", { "dependencies": { "@babel/runtime": "^7.18.3", "@emotion/babel-plugin": "^11.13.5", "@emotion/cache": "^11.14.0", "@emotion/serialize": "^1.3.3", "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", "@emotion/utils": "^1.4.2", "@emotion/weak-memoize": "^0.4.0", "hoist-non-react-statics": "^3.3.1" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA=="],
@@ -249,14 +208,10 @@
"@floating-ui/dom": ["@floating-ui/dom@1.7.6", "", { "dependencies": { "@floating-ui/core": "^1.7.5", "@floating-ui/utils": "^0.2.11" } }, "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ=="],
- "@floating-ui/react": ["@floating-ui/react@0.27.19", "", { "dependencies": { "@floating-ui/react-dom": "^2.1.8", "@floating-ui/utils": "^0.2.11", "tabbable": "^6.0.0" }, "peerDependencies": { "react": ">=17.0.0", "react-dom": ">=17.0.0" } }, "sha512-31B8h5mm8YxotlE7/AU/PhNAl8eWxAmjL/v2QOxroDNkTFLk3Uu82u63N3b6TXa4EGJeeZLVcd/9AlNlVqzeog=="],
-
"@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.8", "", { "dependencies": { "@floating-ui/dom": "^1.7.6" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A=="],
"@floating-ui/utils": ["@floating-ui/utils@0.2.11", "", {}, "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg=="],
- "@glideapps/glide-data-grid": ["@glideapps/glide-data-grid@6.0.3", "", { "dependencies": { "@linaria/react": "^4.5.3", "canvas-hypertxt": "^1.0.3", "react-number-format": "^5.0.0" }, "peerDependencies": { "lodash": "^4.17.19", "marked": "^4.0.10", "react": "^16.12.0 || 17.x || 18.x", "react-dom": "^16.12.0 || 17.x || 18.x", "react-responsive-carousel": "^3.2.7" } }, "sha512-YXKggiNOaEemf0jP0jORq2EQKz+zXms+6mGzZc+q0mLMjmgzzoGLOQC1uYcynXSj1R61bd27JcPFsoH+Gj37Vg=="],
-
"@hugeicons/core-free-icons": ["@hugeicons/core-free-icons@4.1.1", "", {}, "sha512-teqIBvPHl90ygIwKyJwTxOH8aNp1X1PjDTcMvLkEwdPxPD+8mssrZ5kXKIAJJFYPsz69a8LYQY0UPid4PAdavg=="],
"@hugeicons/react": ["@hugeicons/react@1.1.6", "", { "peerDependencies": { "react": ">=16.0.0" } }, "sha512-c2LhXJMAW5wN1pC/smBXG0YPqUON6ceR/ZdXHCjEI9KvB+hjtqYjmzIxok5hAQOeXGz0WtORgCQMzqewFKAZwg=="],
@@ -283,42 +238,6 @@
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
- "@linaria/core": ["@linaria/core@4.5.4", "", { "dependencies": { "@linaria/logger": "^4.5.0", "@linaria/tags": "^4.5.4", "@linaria/utils": "^4.5.3" } }, "sha512-vMs/5iU0stxjfbBCxobIgY+wSQx4G8ukNwrhjPVD+6bF9QrTwi5rl0mKaCMxaGMjnfsLRiiM3i+hnWLIEYLdSg=="],
-
- "@linaria/logger": ["@linaria/logger@4.5.0", "", { "dependencies": { "debug": "^4.1.1", "picocolors": "^1.0.0" } }, "sha512-XdQLk242Cpcsc9a3Cz1ktOE5ysTo2TpxdeFQEPwMm8Z/+F/S6ZxBDdHYJL09srXWz3hkJr3oS2FPuMZNH1HIxw=="],
-
- "@linaria/react": ["@linaria/react@4.5.4", "", { "dependencies": { "@emotion/is-prop-valid": "^1.2.0", "@linaria/core": "^4.5.4", "@linaria/tags": "^4.5.4", "@linaria/utils": "^4.5.3", "minimatch": "^9.0.3", "react-html-attributes": "^1.4.6", "ts-invariant": "^0.10.3" }, "peerDependencies": { "react": ">=16" } }, "sha512-/dhCVCsfdGPfQCPV0q5yy+DDlFXepvfXrw/os2fC+Xo1v9J/9gyiaBBWHzcumauvNNFj8aN6vRkj89fMujPHew=="],
-
- "@linaria/tags": ["@linaria/tags@4.5.4", "", { "dependencies": { "@babel/generator": "^7.22.9", "@linaria/logger": "^4.5.0", "@linaria/utils": "^4.5.3" } }, "sha512-HPxLB6HlJWLi6o8+8lTLegOmDnbMbuzEE+zzunaPZEGSoIIYx8HAv5VbY/sG/zNyxDElk6laiAwEVWN8h5/zxg=="],
-
- "@linaria/utils": ["@linaria/utils@4.5.3", "", { "dependencies": { "@babel/core": "^7.22.9", "@babel/generator": "^7.22.9", "@babel/plugin-proposal-export-namespace-from": "^7.18.9", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/plugin-transform-modules-commonjs": "^7.22.5", "@babel/template": "^7.22.5", "@babel/traverse": "^7.22.8", "@babel/types": "^7.22.5", "@linaria/logger": "^4.5.0", "babel-merge": "^3.0.0", "find-up": "^5.0.0", "minimatch": "^9.0.3" } }, "sha512-tSpxA3Zn0DKJ2n/YBnYAgiDY+MNvkmzAHrD8R9PKrpGaZ+wz1jQEmE1vGn1cqh8dJyWK0NzPAA8sf1cqa+RmAg=="],
-
- "@mantine/core": ["@mantine/core@8.3.9", "", { "dependencies": { "@floating-ui/react": "^0.27.16", "clsx": "^2.1.1", "react-number-format": "^5.4.4", "react-remove-scroll": "^2.7.1", "react-textarea-autosize": "8.5.9", "type-fest": "^4.41.0" }, "peerDependencies": { "@mantine/hooks": "8.3.9", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "sha512-ivj0Crn5N521cI2eWZBsBGckg0ZYRqfOJz5vbbvYmfj65bp0EdsyqZuOxXzIcn2aUScQhskfvzyhV5XIUv81PQ=="],
-
- "@mantine/hooks": ["@mantine/hooks@8.3.9", "", { "peerDependencies": { "react": "^18.x || ^19.x" } }, "sha512-Dfz7W0+K1cq4Gb1WFQCZn8tsMXkLH6MV409wZR/ToqsxdNDUMJ/xxbfnwEXWEZjXNJd1wDETHgc+cZG2lTe3Xw=="],
-
- "@mapbox/geojson-rewind": ["@mapbox/geojson-rewind@0.5.2", "", { "dependencies": { "get-stream": "^6.0.1", "minimist": "^1.2.6" }, "bin": { "geojson-rewind": "geojson-rewind" } }, "sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA=="],
-
- "@mapbox/geojson-types": ["@mapbox/geojson-types@1.0.2", "", {}, "sha512-e9EBqHHv3EORHrSfbR9DqecPNn+AmuAoQxV6aL8Xu30bJMJR1o8PZLZzpk1Wq7/NfCbuhmakHTPYRhoqLsXRnw=="],
-
- "@mapbox/jsonlint-lines-primitives": ["@mapbox/jsonlint-lines-primitives@2.0.2", "", {}, "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ=="],
-
- "@mapbox/mapbox-gl-supported": ["@mapbox/mapbox-gl-supported@1.5.0", "", { "peerDependencies": { "mapbox-gl": ">=0.32.1 <2.0.0" } }, "sha512-/PT1P6DNf7vjEEiPkVIRJkvibbqWtqnyGaBz3nfRdcxclNSnSdaLU5tfAgcD7I8Yt5i+L19s406YLl1koLnLbg=="],
-
- "@mapbox/point-geometry": ["@mapbox/point-geometry@0.1.0", "", {}, "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ=="],
-
- "@mapbox/tiny-sdf": ["@mapbox/tiny-sdf@1.2.5", "", {}, "sha512-cD8A/zJlm6fdJOk6DqPUV8mcpyJkRz2x2R+/fYcWDYG3oWbG7/L7Yl/WqQ1VZCjnL9OTIMAn6c+BC5Eru4sQEw=="],
-
- "@mapbox/unitbezier": ["@mapbox/unitbezier@0.0.0", "", {}, "sha512-HPnRdYO0WjFjRTSwO3frz1wKaU649OBFPX3Zo/2WZvuRi6zMiRGui8SnPQiQABgqCf8YikDe5t3HViTVw1WUzA=="],
-
- "@mapbox/vector-tile": ["@mapbox/vector-tile@1.3.1", "", { "dependencies": { "@mapbox/point-geometry": "~0.1.0" } }, "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw=="],
-
- "@mapbox/whoots-js": ["@mapbox/whoots-js@3.1.0", "", {}, "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q=="],
-
- "@maplibre/maplibre-gl-style-spec": ["@maplibre/maplibre-gl-style-spec@20.4.0", "", { "dependencies": { "@mapbox/jsonlint-lines-primitives": "~2.0.2", "@mapbox/unitbezier": "^0.0.1", "json-stringify-pretty-compact": "^4.0.0", "minimist": "^1.2.8", "quickselect": "^2.0.0", "rw": "^1.3.3", "tinyqueue": "^3.0.0" }, "bin": { "gl-style-format": "dist/gl-style-format.mjs", "gl-style-migrate": "dist/gl-style-migrate.mjs", "gl-style-validate": "dist/gl-style-validate.mjs" } }, "sha512-AzBy3095fTFPjDjmWpR2w6HVRAZJ6hQZUCwk5Plz6EyfnfuQW1odeW5i2Ai47Y6TBA2hQnC+azscjBSALpaWgw=="],
-
- "@masenf/hello-react": ["@masenf/hello-react@github:masenf/hello-react#fce6758", { "dependencies": { "lodash": "^4.17.21" }, "peerDependencies": { "react": ">=18.2.0", "react-dom": ">=18.2.0" } }, "masenf-hello-react-fce6758", "sha512-zx6XH9cfGCPj3ymDNhWAIa9XryMGdhKyOQjpqlxCZl4osTzDZIhhJQ2eCPzepR4pKYOb+RGKEj3NEJ8D7M4ZGw=="],
-
"@mjackson/node-fetch-server": ["@mjackson/node-fetch-server@0.2.0", "", {}, "sha512-EMlH1e30yzmTpGLQjlFmaDAjyOeZhng1/XCd7DExR8PNAnG/G1tyruZxEoUe11ClnwGhGrtsdnyyUx1frSzjng=="],
"@mux/mux-data-google-ima": ["@mux/mux-data-google-ima@0.3.17", "", { "dependencies": { "mux-embed": "5.18.1" } }, "sha512-4wpH6dYybyZhqLn9qGn/+67Z8MZnQRAdqTFEEZw2bx61M9q01uPYYHxd8qwOnYtUGEeafsdTwVHVxKHGD3oc1A=="],
@@ -333,19 +252,7 @@
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="],
- "@oxc-project/types": ["@oxc-project/types@0.126.0", "", {}, "sha512-oGfVtjAgwQVVpfBrbtk4e1XDyWHRFta6BS3GWVzrF8xYBT2VGQAk39yJS/wFSMrZqoiCU4oghT3Ch0HaHGIHcQ=="],
-
- "@plotly/d3": ["@plotly/d3@3.8.2", "", {}, "sha512-wvsNmh1GYjyJfyEBPKJLTMzgf2c2bEbSIL50lmqVUi+o1NHaLPi1Lb4v7VxXXJn043BhNyrxUrWI85Q+zmjOVA=="],
-
- "@plotly/d3-sankey": ["@plotly/d3-sankey@0.7.2", "", { "dependencies": { "d3-array": "1", "d3-collection": "1", "d3-shape": "^1.2.0" } }, "sha512-2jdVos1N3mMp3QW0k2q1ph7Gd6j5PY1YihBrwpkFnKqO+cqtZq3AdEYUeSGXMeLsBDQYiqTVcihYfk8vr5tqhw=="],
-
- "@plotly/d3-sankey-circular": ["@plotly/d3-sankey-circular@0.33.1", "", { "dependencies": { "d3-array": "^1.2.1", "d3-collection": "^1.0.4", "d3-shape": "^1.2.0", "elementary-circuits-directed-graph": "^1.0.4" } }, "sha512-FgBV1HEvCr3DV7RHhDsPXyryknucxtfnLwPtCKKxdolKyTFYoLX/ibEfX39iFYIL7DYbVeRtP43dbFcrHNE+KQ=="],
-
- "@plotly/mapbox-gl": ["@plotly/mapbox-gl@1.13.4", "", { "dependencies": { "@mapbox/geojson-rewind": "^0.5.2", "@mapbox/geojson-types": "^1.0.2", "@mapbox/jsonlint-lines-primitives": "^2.0.2", "@mapbox/mapbox-gl-supported": "^1.5.0", "@mapbox/point-geometry": "^0.1.0", "@mapbox/tiny-sdf": "^1.1.1", "@mapbox/unitbezier": "^0.0.0", "@mapbox/vector-tile": "^1.3.1", "@mapbox/whoots-js": "^3.1.0", "csscolorparser": "~1.0.3", "earcut": "^2.2.2", "geojson-vt": "^3.2.1", "gl-matrix": "^3.2.1", "grid-index": "^1.1.0", "murmurhash-js": "^1.0.0", "pbf": "^3.2.1", "potpack": "^1.0.1", "quickselect": "^2.0.0", "rw": "^1.3.3", "supercluster": "^7.1.0", "tinyqueue": "^2.0.3", "vt-pbf": "^3.1.1" } }, "sha512-sR3/Pe5LqT/fhYgp4rT4aSFf1rTsxMbGiH6Hojc7PH36ny5Bn17iVFUjpzycafETURuFbLZUfjODO8LvSI+5zQ=="],
-
- "@plotly/point-cluster": ["@plotly/point-cluster@3.1.9", "", { "dependencies": { "array-bounds": "^1.0.1", "binary-search-bounds": "^2.0.4", "clamp": "^1.0.1", "defined": "^1.0.0", "dtype": "^2.0.0", "flatten-vertex-data": "^1.0.2", "is-obj": "^1.0.1", "math-log2": "^1.0.1", "parse-rect": "^1.2.0", "pick-by-alias": "^1.2.0" } }, "sha512-MwaI6g9scKf68Orpr1pHZ597pYx9uP8UEFXLPbsCmuw3a84obwz6pnMXGc90VhgDNeNiLEdlmuK7CPo+5PIxXw=="],
-
- "@plotly/regl": ["@plotly/regl@2.1.2", "", {}, "sha512-Mdk+vUACbQvjd0m/1JJjOOafmkp/EpmHjISsopEz5Av44CBq7rPC05HHNbYGKVyNUF2zmEoBS/TT0pd0SPFFyw=="],
+ "@oxc-project/types": ["@oxc-project/types@0.129.0", "", {}, "sha512-3oz8m3FGdr2nDXVqmFUw7jolKliC4MoyXYIG2c7gpjBnzUWQpUGIYcXYKxTdTi+N2jusvt610ckTMkxdwHkYEg=="],
"@radix-ui/colors": ["@radix-ui/colors@3.0.0", "", {}, "sha512-FUOsGBkHrYJwCSEtWRCIfQbZG7q1e6DgxCIOe1SUQzDe/7rXXeA47s8yCn6fuTNQAj1Zq4oTFi9Yjp3wzElcxg=="],
@@ -471,67 +378,45 @@
"@radix-ui/themes": ["@radix-ui/themes@3.3.0", "", { "dependencies": { "@radix-ui/colors": "^3.0.0", "classnames": "^2.3.2", "radix-ui": "^1.1.3", "react-remove-scroll-bar": "^2.3.8" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-I0/h2CRNTpYNB7Mi3xFIvSsQq5a108d7kK8dTO5zp5b9HR5QJXKag6B8tjpz2ITkVYkFdkGk45doNkSr7OxwNw=="],
- "@react-dnd/asap": ["@react-dnd/asap@5.0.2", "", {}, "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A=="],
-
- "@react-dnd/invariant": ["@react-dnd/invariant@4.0.2", "", {}, "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw=="],
-
- "@react-dnd/shallowequal": ["@react-dnd/shallowequal@4.0.2", "", {}, "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA=="],
-
- "@react-leaflet/core": ["@react-leaflet/core@3.0.0", "", { "peerDependencies": { "leaflet": "^1.9.0", "react": "^19.0.0", "react-dom": "^19.0.0" } }, "sha512-3EWmekh4Nz+pGcr+xjf0KNyYfC3U2JjnkWsh0zcqaexYqmmB5ZhH37kz41JXGmKzpaMZCnPofBBm64i+YrEvGQ=="],
-
- "@react-router/dev": ["@react-router/dev@7.14.1", "", { "dependencies": { "@babel/core": "^7.27.7", "@babel/generator": "^7.27.5", "@babel/parser": "^7.27.7", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/preset-typescript": "^7.27.1", "@babel/traverse": "^7.27.7", "@babel/types": "^7.27.7", "@react-router/node": "7.14.1", "@remix-run/node-fetch-server": "^0.13.0", "arg": "^5.0.1", "babel-dead-code-elimination": "^1.0.6", "chokidar": "^4.0.0", "dedent": "^1.5.3", "es-module-lexer": "^1.3.1", "exit-hook": "2.2.1", "isbot": "^5.1.11", "jsesc": "3.0.2", "lodash": "^4.17.21", "p-map": "^7.0.3", "pathe": "^1.1.2", "picocolors": "^1.1.1", "pkg-types": "^2.3.0", "prettier": "^3.6.2", "react-refresh": "^0.14.0", "semver": "^7.3.7", "tinyglobby": "^0.2.14", "valibot": "^1.2.0", "vite-node": "^3.2.2" }, "peerDependencies": { "@react-router/serve": "^7.14.1", "@vitejs/plugin-rsc": "~0.5.21", "react-router": "^7.14.1", "react-server-dom-webpack": "^19.2.3", "typescript": "^5.1.0 || ^6.0.0", "vite": "^5.1.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", "wrangler": "^3.28.2 || ^4.0.0" }, "optionalPeers": ["@react-router/serve", "@vitejs/plugin-rsc", "react-server-dom-webpack", "typescript", "wrangler"], "bin": { "react-router": "bin.js" } }, "sha512-ZBEwods1TxqPVY2SrXDuDCfoaE5VoTMBYrfa/+3MesprY3foSo1jhin9mh4FwmXPXhhmDYKXi2z5UR+oMj8Qjg=="],
-
- "@react-router/fs-routes": ["@react-router/fs-routes@7.14.1", "", { "dependencies": { "minimatch": "^9.0.0" }, "peerDependencies": { "@react-router/dev": "^7.14.1", "typescript": "^5.1.0 || ^6.0.0" }, "optionalPeers": ["typescript"] }, "sha512-7eLiNkrAypIwfOEdeeUGckV5Hi3k0jIiLJaMmT93gbREGEYggd4EJ/HvL1sJN5KqD4ZNLKoBbGrJ0LbcZhlLuA=="],
-
- "@react-router/node": ["@react-router/node@7.14.1", "", { "dependencies": { "@mjackson/node-fetch-server": "^0.2.0" }, "peerDependencies": { "react-router": "7.14.1", "typescript": "^5.1.0 || ^6.0.0" }, "optionalPeers": ["typescript"] }, "sha512-SthTjCwW7otzEAcZwF0RAPMRrDT47B4qHDxZM45rM5K1Gp86ANK/xlXF+DgpLq9qKZf9FbKzxS9hT7FqDeBAOg=="],
-
- "@reactflow/background": ["@reactflow/background@11.3.14", "", { "dependencies": { "@reactflow/core": "11.11.4", "classcat": "^5.0.3", "zustand": "^4.4.1" }, "peerDependencies": { "react": ">=17", "react-dom": ">=17" } }, "sha512-Gewd7blEVT5Lh6jqrvOgd4G6Qk17eGKQfsDXgyRSqM+CTwDqRldG2LsWN4sNeno6sbqVIC2fZ+rAUBFA9ZEUDA=="],
-
- "@reactflow/controls": ["@reactflow/controls@11.2.14", "", { "dependencies": { "@reactflow/core": "11.11.4", "classcat": "^5.0.3", "zustand": "^4.4.1" }, "peerDependencies": { "react": ">=17", "react-dom": ">=17" } }, "sha512-MiJp5VldFD7FrqaBNIrQ85dxChrG6ivuZ+dcFhPQUwOK3HfYgX2RHdBua+gx+40p5Vw5It3dVNp/my4Z3jF0dw=="],
-
- "@reactflow/core": ["@reactflow/core@11.11.4", "", { "dependencies": { "@types/d3": "^7.4.0", "@types/d3-drag": "^3.0.1", "@types/d3-selection": "^3.0.3", "@types/d3-zoom": "^3.0.1", "classcat": "^5.0.3", "d3-drag": "^3.0.0", "d3-selection": "^3.0.0", "d3-zoom": "^3.0.0", "zustand": "^4.4.1" }, "peerDependencies": { "react": ">=17", "react-dom": ">=17" } }, "sha512-H4vODklsjAq3AMq6Np4LE12i1I4Ta9PrDHuBR9GmL8uzTt2l2jh4CiQbEMpvMDcp7xi4be0hgXj+Ysodde/i7Q=="],
-
- "@reactflow/minimap": ["@reactflow/minimap@11.7.14", "", { "dependencies": { "@reactflow/core": "11.11.4", "@types/d3-selection": "^3.0.3", "@types/d3-zoom": "^3.0.1", "classcat": "^5.0.3", "d3-selection": "^3.0.0", "d3-zoom": "^3.0.0", "zustand": "^4.4.1" }, "peerDependencies": { "react": ">=17", "react-dom": ">=17" } }, "sha512-mpwLKKrEAofgFJdkhwR5UQ1JYWlcAAL/ZU/bctBkuNTT1yqV+y0buoNVImsRehVYhJwffSWeSHaBR5/GJjlCSQ=="],
-
- "@reactflow/node-resizer": ["@reactflow/node-resizer@2.2.14", "", { "dependencies": { "@reactflow/core": "11.11.4", "classcat": "^5.0.4", "d3-drag": "^3.0.0", "d3-selection": "^3.0.0", "zustand": "^4.4.1" }, "peerDependencies": { "react": ">=17", "react-dom": ">=17" } }, "sha512-fwqnks83jUlYr6OHcdFEedumWKChTHRGw/kbCxj0oqBd+ekfs+SIp4ddyNU0pdx96JIm5iNFS0oNrmEiJbbSaA=="],
+ "@react-router/dev": ["@react-router/dev@7.15.0", "", { "dependencies": { "@babel/core": "^7.27.7", "@babel/generator": "^7.27.5", "@babel/parser": "^7.27.7", "@babel/plugin-syntax-jsx": "^7.27.1", "@babel/preset-typescript": "^7.27.1", "@babel/traverse": "^7.27.7", "@babel/types": "^7.27.7", "@react-router/node": "7.15.0", "@remix-run/node-fetch-server": "^0.13.0", "arg": "^5.0.1", "babel-dead-code-elimination": "^1.0.6", "chokidar": "^4.0.0", "dedent": "^1.5.3", "es-module-lexer": "^1.3.1", "exit-hook": "2.2.1", "isbot": "^5.1.11", "jsesc": "3.0.2", "lodash": "^4.17.21", "p-map": "^7.0.3", "pathe": "^1.1.2", "picocolors": "^1.1.1", "pkg-types": "^2.3.0", "prettier": "^3.6.2", "react-refresh": "^0.14.0", "semver": "^7.3.7", "tinyglobby": "^0.2.14", "valibot": "^1.2.0", "vite-node": "^3.2.2" }, "peerDependencies": { "@react-router/serve": "^7.15.0", "@vitejs/plugin-rsc": "~0.5.21", "react-router": "^7.15.0", "react-server-dom-webpack": "^19.2.3", "typescript": "^5.1.0 || ^6.0.0", "vite": "^5.1.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", "wrangler": "^3.28.2 || ^4.0.0" }, "optionalPeers": ["@react-router/serve", "@vitejs/plugin-rsc", "react-server-dom-webpack", "typescript", "wrangler"], "bin": { "react-router": "bin.js" } }, "sha512-ZwUQu4KNZrViFqdeFWqh00Bk/QbLNvoWRDfjsqOp3oyuG3jSRLYnqRD3VAMK/FYMpL+s37ByT7XqqLXaF7Nw1g=="],
- "@reactflow/node-toolbar": ["@reactflow/node-toolbar@1.3.14", "", { "dependencies": { "@reactflow/core": "11.11.4", "classcat": "^5.0.3", "zustand": "^4.4.1" }, "peerDependencies": { "react": ">=17", "react-dom": ">=17" } }, "sha512-rbynXQnH/xFNu4P9H+hVqlEUafDCkEoCy0Dg9mG22Sg+rY/0ck6KkrAQrYrTgXusd+cEJOMK0uOOFCK2/5rSGQ=="],
+ "@react-router/fs-routes": ["@react-router/fs-routes@7.15.0", "", { "dependencies": { "minimatch": "^9.0.0" }, "peerDependencies": { "@react-router/dev": "^7.15.0", "typescript": "^5.1.0 || ^6.0.0" }, "optionalPeers": ["typescript"] }, "sha512-Bn0PvYQCpFm547UBc2hP6bI9Rv6i5ewHiVG+hZsPxkYH/txvTWwwF7b4lle7TaO/UeHA7J5rhlB2a8C4ubXO7w=="],
- "@reduxjs/toolkit": ["@reduxjs/toolkit@2.11.2", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@standard-schema/utils": "^0.3.0", "immer": "^11.0.0", "redux": "^5.0.1", "redux-thunk": "^3.1.0", "reselect": "^5.1.0" }, "peerDependencies": { "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" }, "optionalPeers": ["react", "react-redux"] }, "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ=="],
+ "@react-router/node": ["@react-router/node@7.15.0", "", { "dependencies": { "@mjackson/node-fetch-server": "^0.2.0" }, "peerDependencies": { "react-router": "7.15.0", "typescript": "^5.1.0 || ^6.0.0" }, "optionalPeers": ["typescript"] }, "sha512-SgvWaWF1n3u+bpXXZUW9BSd2p/NwkIYLz4SSeDYqoX5RkYX5rcI4cHHuNJXszPu+Dm9QIri4J9g/4EV3KfgiXQ=="],
"@remix-run/node-fetch-server": ["@remix-run/node-fetch-server@0.13.1", "", {}, "sha512-dOL+A/C84EA47gO/ps52KGrVSiYy96512rwtbXmJfWKYFm1FbrbjA3jao1hcIfao+jwVNEaZ1kTMwFjiino+HQ=="],
- "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.16", "", { "os": "android", "cpu": "arm64" }, "sha512-rhY3k7Bsae9qQfOtph2Pm2jZEA+s8Gmjoz4hhmx70K9iMQ/ddeae+xhRQcM5IuVx5ry1+bGfkvMn7D6MJggVSA=="],
+ "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0", "", { "os": "android", "cpu": "arm64" }, "sha512-TWMZnRLMe63C2Lhyicviu7ZHaU4kxa6PS3rofvc9GmcvptzNN11BcfQ4Sl7MwTOsisQoa2keB/EBdNCAnUo8vA=="],
- "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-rc.16", "", { "os": "darwin", "cpu": "arm64" }, "sha512-rNz0yK078yrNn3DrdgN+PKiMOW8HfQ92jQiXxwX8yW899ayV00MLVdaCNeVBhG/TbH3ouYVObo8/yrkiectkcQ=="],
+ "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-6XcD+8k0gPVItNagEw78/qqcBDwKcwDYS8V2hRmVsfUSIrd8cWe/CBvRDI5toqFyPfj+FJr6t8U6Xj2P2prEew=="],
- "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-rc.16", "", { "os": "darwin", "cpu": "x64" }, "sha512-r/OmdR00HmD4i79Z//xO06uEPOq5hRXdhw7nzkxQxwSavs3PSHa1ijntdpOiZ2mzOQ3fVVu8C1M19FoNM+dMUQ=="],
+ "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-iN/tWVXRQDWvmZlKdceP1Dwug9GDpEymhb9p4xnEe6zvCg5lFmzVljl+1qR1NVx3yfGpr2Na+CuLmv5IU8uzfQ=="],
- "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-rc.16", "", { "os": "freebsd", "cpu": "x64" }, "sha512-KcRE5w8h0OnjUatG8pldyD14/CQ5Phs1oxfR+3pKDjboHRo9+MkqQaiIZlZRpsxC15paeXme/I127tUa9TXJ6g=="],
+ "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jjQMDvvwSOuhOwMszD/klSOjyWMM3zI64hWTj9KT5x4MxRbZAf+7vLQ6qouRhtsLVFHr3f0ILaJAfgENPiQdAQ=="],
- "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.16", "", { "os": "linux", "cpu": "arm" }, "sha512-bT0guA1bpxEJ/ZhTRniQf7rNF8ybvXOuWbNIeLABaV5NGjx4EtOWBTSRGWFU9ZWVkPOZ+HNFP8RMcBokBiZ0Kg=="],
+ "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0", "", { "os": "linux", "cpu": "arm" }, "sha512-d//Dtg2x6/m3mbV64yUGNnDGNZaDGRpDLLNGerHQUVObuNaIQaaDp25yUiqGXtHEXX+NP2d0wAlmKgpYgIAJ2A=="],
- "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-rc.16", "", { "os": "linux", "cpu": "arm64" }, "sha512-+tHktCHWV8BDQSjemUqm/Jl/TPk3QObCTIjmdDy/nlupcujZghmKK2962LYrqFpWu+ai01AN/REOH3NEpqvYQg=="],
+ "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-n7Ofp0mx+aB2cC+Sdy5YtMnXtY9lchnHbY+3Yt0uq9JsWQExf4f5Whu0tK0R8Jdc9S6RchTHjIFY7uc92puOVQ=="],
- "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-rc.16", "", { "os": "linux", "cpu": "arm64" }, "sha512-3fPzdREH806oRLxpTWW1Gt4tQHs0TitZFOECB2xzCFLPKnSOy90gwA7P29cksYilFO6XVRY1kzga0cL2nRjKPg=="],
+ "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-EIVjy2cgd7uuMMo94FVkBp7F6DhcZAUwNURkSG3RwUmvAXR6s0ISxM81U+IydcZByPG0pZIHsf1b6kTxoFDgJA=="],
- "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.16", "", { "os": "linux", "cpu": "ppc64" }, "sha512-EKwI1tSrLs7YVw+JPJT/G2dJQ1jl9qlTTTEG0V2Ok/RdOenRfBw2PQdLPyjhIu58ocdBfP7vIRN/pvMsPxs/AQ=="],
+ "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-JEwwOPcwTLAcpDQlqSmjEmfs63xJnSiUNIGvLcDLUHCWK4XowpS/7c7tUsUH6uT/ct6bMUTdXKfI8967FYj6mg=="],
- "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.0-rc.16", "", { "os": "linux", "cpu": "s390x" }, "sha512-Uknladnb3Sxqu6SEcqBldQyJUpk8NleooZEc0MbRBJ4inEhRYWZX0NJu12vNf2mqAq7gsofAxHrGghiUYjhaLQ=="],
+ "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-0wjCFhLrihtAubnT9iA0N++0pSV0z5Hg7tNGdNJ4RFaINceHadoF+kiFGyY1qSSNVIAZtLotG8Ju1bgDPkjnFA=="],
- "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-rc.16", "", { "os": "linux", "cpu": "x64" }, "sha512-FIb8+uG49sZBtLTn+zt1AJ20TqVcqWeSIyoVt0or7uAWesgKaHbiBh6OpA/k9v0LTt+PTrb1Lao133kP4uVxkg=="],
+ "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0", "", { "os": "linux", "cpu": "x64" }, "sha512-Dfn7iak9BcMMePxcoJfpSbWqnEyrp/dRF63/8qW/eHBdOZov6x5aShLLEYGYdIeSJ6vMLK/XCVB+lGIxm41bQA=="],
- "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-rc.16", "", { "os": "linux", "cpu": "x64" }, "sha512-RuERhF9/EgWxZEXYWCOaViUWHIboceK4/ivdtQ3R0T44NjLkIIlGIAVAuCddFxsZ7vnRHtNQUrt2vR2n2slB2w=="],
+ "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0", "", { "os": "linux", "cpu": "x64" }, "sha512-5/utzzDmD/pD/bmuaUcbTf/sZYy0aztwIVlfpoW1fTjCZ0BaPOMVWGZL1zvgxyi7ZIVYWlxKONHmSbHuiOh8Jw=="],
- "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-rc.16", "", { "os": "none", "cpu": "arm64" }, "sha512-mXcXnvd9GpazCxeUCCnZ2+YF7nut+ZOEbE4GtaiPtyY6AkhZWbK70y1KK3j+RDhjVq5+U8FySkKRb/+w0EeUwA=="],
+ "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0", "", { "os": "none", "cpu": "arm64" }, "sha512-ouJs8VcUomfLfpbUECqFMRqdV4x6aeAK3MA4m6vTrJJjKyWTV5KnxZx7Jd9G+GlDaQQxubcba00x16OyJ1meig=="],
- "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-rc.16", "", { "dependencies": { "@emnapi/core": "1.9.2", "@emnapi/runtime": "1.9.2", "@napi-rs/wasm-runtime": "^1.1.4" }, "cpu": "none" }, "sha512-3Q2KQxnC8IJOLqXmUMoYwyIPZU9hzRbnHaoV3Euz+VVnjZKcY8ktnNP8T9R4/GGQtb27C/UYKABxesKWb8lsvQ=="],
+ "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0", "", { "dependencies": { "@emnapi/core": "1.10.0", "@emnapi/runtime": "1.10.0", "@napi-rs/wasm-runtime": "^1.1.4" }, "cpu": "none" }, "sha512-E+oHKGiDA+lsKMmFtffDDw91EryDT7uJocrIuCHqhm6bCTM6xFK+3gaCkYOHfPwQr0cCNarSM2xaELoQDz9jJg=="],
- "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-rc.16", "", { "os": "win32", "cpu": "arm64" }, "sha512-tj7XRemQcOcFwv7qhpUxMTBbI5mWMlE4c1Omhg5+h8GuLXzyj8HviYgR+bB2DMDgRqUE+jiDleqSCRjx4aYk/Q=="],
+ "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-yYK02n8Rngo+gbm1y6G0+7jk1sJ/2Wt7K0me0Y7k/ErBpyf+LJ2gFpqWVTcRV1rUepBlQRmpgWkTQCiiwrK0Ow=="],
- "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-rc.16", "", { "os": "win32", "cpu": "x64" }, "sha512-PH5DRZT+F4f2PTXRXR8uJxnBq2po/xFtddyabTJVJs/ZYVHqXPEgNIr35IHTEa6bpa0Q8Awg+ymkTaGnKITw4g=="],
+ "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0", "", { "os": "win32", "cpu": "x64" }, "sha512-14bpChMahXRRXiTwahSl+zzHPW6qQTXtkMuJBFlbo+pqSAews2d4BdCSHfrJ/MBsCZtpmTafsY+1QhBzitcmdg=="],
- "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.16", "", {}, "sha512-45+YtqxLYKDWQouLKCrpIZhke+nXxhsw+qAHVzHDVwttyBlHNBVs2K25rDXrZzhpTp9w1FlAlvweV1H++fdZoA=="],
+ "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0", "", {}, "sha512-aKs/3GSWyV0mrhNmt/96/Z3yczC3yvrzYATCiCXQebBsGyYzjNdUphRVLeJQ67ySKVXRfMxt2lm12pmXvbPFQQ=="],
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.60.3", "", { "os": "android", "cpu": "arm" }, "sha512-x35CNW/ANXG3hE/EZpRU8MXX1JDN86hBb2wMGAtltkz7pc6cxgjpy1OMMfDosOQ+2hWqIkag/fGok1Yady9nGw=="],
@@ -597,18 +482,8 @@
"@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
- "@sindresorhus/is": ["@sindresorhus/is@4.6.0", "", {}, "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw=="],
-
"@socket.io/component-emitter": ["@socket.io/component-emitter@3.1.2", "", {}, "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA=="],
- "@splinetool/react-spline": ["@splinetool/react-spline@4.1.0", "", { "dependencies": { "blurhash": "2.0.5", "lodash.debounce": "4.0.8", "react-merge-refs": "2.1.1", "thumbhash": "0.1.1" }, "peerDependencies": { "@splinetool/runtime": "*", "next": ">=14.2.0", "react": "*", "react-dom": "*" }, "optionalPeers": ["next"] }, "sha512-Y379gm17gw+1nxT/YXTCJnVIWuu7tsUH1tp/YxsYb0pZnc9Gljk7Om4Kpq7WPq0bZ4zidVCxf6xn6jgDcbHifQ=="],
-
- "@splinetool/runtime": ["@splinetool/runtime@1.5.5", "", { "dependencies": { "on-change": "^4.0.0", "semver-compare": "^1.0.0" } }, "sha512-TQf7rWVGi7qFuhJDfECQ7XSTMEAdT3P7nrHx0a5B940y5RNJRBIZ6aKwVfouF0QHnzyARtJJPFA9I8lEAVF1Ig=="],
-
- "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
-
- "@standard-schema/utils": ["@standard-schema/utils@0.3.0", "", {}, "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g=="],
-
"@svta/cml-608": ["@svta/cml-608@1.0.1", "", {}, "sha512-Y/Ier9VPUSOBnf0bJqdDyTlPrt4dDB+jk5mYHa1bnD2kcRl8qn7KkW3PRuj4w1aVN+BS2eHmsLxodt7P2hylUg=="],
"@svta/cml-cmcd": ["@svta/cml-cmcd@1.0.1", "", { "peerDependencies": { "@svta/cml-cta": "1.0.1", "@svta/cml-structured-field-values": "1.0.1", "@svta/cml-utils": "1.0.1" } }, "sha512-eox305g+QUJgXqOLVrbgxeQHCgl90ewwQ9O2bIoo7m+hanR8Xswu5CknFnT5qqIbLOHfw80ug+raycoAFHTQ+w=="],
@@ -629,134 +504,54 @@
"@svta/cml-xml": ["@svta/cml-xml@1.0.1", "", { "peerDependencies": { "@svta/cml-utils": "1.0.1" } }, "sha512-11LkJa5kDEcsRMWkVI1ABH3KLCxGoiSVe4kQ293ItVj8ncTTQ7htmCGiJDjS+Cmy35UgF3e/vc0ysJIiWRTx2g=="],
- "@tailwindcss/node": ["@tailwindcss/node@4.2.3", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.3" } }, "sha512-dhXFXkW2dGvX4r/fi24gyXM0t1mFMrpykQjqrdA4SuavaMagm4SY1u5G2SCJwu1/0x/5RlZJ2VPjP3mKYQfCkA=="],
+ "@tailwindcss/node": ["@tailwindcss/node@4.3.0", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.21.0", "jiti": "^2.6.1", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.3.0" } }, "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g=="],
- "@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.3", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.3", "@tailwindcss/oxide-darwin-arm64": "4.2.3", "@tailwindcss/oxide-darwin-x64": "4.2.3", "@tailwindcss/oxide-freebsd-x64": "4.2.3", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.3", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.3", "@tailwindcss/oxide-linux-arm64-musl": "4.2.3", "@tailwindcss/oxide-linux-x64-gnu": "4.2.3", "@tailwindcss/oxide-linux-x64-musl": "4.2.3", "@tailwindcss/oxide-wasm32-wasi": "4.2.3", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.3", "@tailwindcss/oxide-win32-x64-msvc": "4.2.3" } }, "sha512-YyhwSBcxHLS3CU2Mk3dXDuVm8/Ia0+XvfpT8s9YQoICppkUeoobB3hgyGMYbyQ4vn6VgWH9bdv5UnzhTz2NPTQ=="],
+ "@tailwindcss/oxide": ["@tailwindcss/oxide@4.3.0", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.3.0", "@tailwindcss/oxide-darwin-arm64": "4.3.0", "@tailwindcss/oxide-darwin-x64": "4.3.0", "@tailwindcss/oxide-freebsd-x64": "4.3.0", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0", "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0", "@tailwindcss/oxide-linux-arm64-musl": "4.3.0", "@tailwindcss/oxide-linux-x64-gnu": "4.3.0", "@tailwindcss/oxide-linux-x64-musl": "4.3.0", "@tailwindcss/oxide-wasm32-wasi": "4.3.0", "@tailwindcss/oxide-win32-arm64-msvc": "4.3.0", "@tailwindcss/oxide-win32-x64-msvc": "4.3.0" } }, "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg=="],
- "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.2.3", "", { "os": "android", "cpu": "arm64" }, "sha512-0Jmt1U/zPqeKp1+fvgI3qMqrV5b/EcFIbE5Dl5KdPl5Ri6e+95nlYNjfB3w8hJBeASI4IQSnIMz0tdVP1AVO4g=="],
+ "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.3.0", "", { "os": "android", "cpu": "arm64" }, "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng=="],
- "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.2.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c+/Etn/nghKBhd9fh2diG+3SEV1VTTPLlqH209yleofi28H87Cy6g1vsd3W3kf6r/dR5g4G4TEwHxo2Ydn6yFw=="],
+ "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.3.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ=="],
- "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.2.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-1DrKKsdJTLuLWVdpaLZ0j/g9YbCZyP9xnwSqEvl3gY4ZHdXmX7TwVAHkoWUljOq7JK5zvzIGhrYmfE/2DJ5qaA=="],
+ "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.3.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA=="],
- "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.2.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-HE6HHZYF8k7m80eVQ0RBvRGBdvvLvCpHiT38IRH9JSnBlt1T7gDzWoslWjmpXQFuqlRpzkCpbdKJa3NxWMfgVA=="],
+ "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.3.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ=="],
- "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.2.3", "", { "os": "linux", "cpu": "arm" }, "sha512-Li2wVd2kkKlKkTdpo7ujHSv6kxD1UYMvulAraikyvVf6AKNZ/VHbm8XoSNimZ+dF7SOFaDD2VAT64SK7WKcbjQ=="],
+ "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0", "", { "os": "linux", "cpu": "arm" }, "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA=="],
- "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.2.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-otIiImZaHj9MiDK02ItoWxIVcMTZVAX2F1c32bg9y7ecV0AnN5JHDZqIO8LxWsTuig1d+Bjg0cBWn4A9sGJO9Q=="],
+ "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.3.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg=="],
- "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.2.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-MmIA32rNEOrjh6wnevlR3OjjlCuwgZ4JMJo7Vrhk4Fk56Vxi7EeF7cekSKwvlrnfcn/ERC1LdcG3sFneU8WdoA=="],
+ "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.3.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ=="],
- "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.2.3", "", { "os": "linux", "cpu": "x64" }, "sha512-BiCy1YV0IKO+xbD7gyZnENU4jdwDygeGQjncJoeIE5Kp4UqWHFsKUSJ3pp7vYURrqVzwJX2xD5gQeGnoXp4xPQ=="],
+ "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ=="],
- "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.2.3", "", { "os": "linux", "cpu": "x64" }, "sha512-venvyAu0AMKdr0c1Oz23IJJdZ72zSwKyHrLvqQV1cn49vPAJk3AuVtDkJ1ayk1sYI4M4j8Jv6ZGflpaP0QVSXQ=="],
+ "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.3.0", "", { "os": "linux", "cpu": "x64" }, "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg=="],
- "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.2.3", "", { "dependencies": { "@emnapi/core": "^1.8.1", "@emnapi/runtime": "^1.8.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.1", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-e3kColrZZCdtbwIOc07cNQ2zNf1sTPXTYLjjPlsgsaf+ttzAg/hOlDyEgHoOlBGxM88nPxeVaOGe9ThqVzPncg=="],
+ "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.3.0", "", { "dependencies": { "@emnapi/core": "^1.10.0", "@emnapi/runtime": "^1.10.0", "@emnapi/wasi-threads": "^1.2.1", "@napi-rs/wasm-runtime": "^1.1.4", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA=="],
- "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.2.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-qpwoUPzfu71cppxOtcz4LXMR1brljS13yOcAAnVHKIL++NJvSQKZBKlP39pVowd+G6Mq34YAbf4CUUYdLWL9gQ=="],
+ "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.3.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ=="],
- "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.2.3", "", { "os": "win32", "cpu": "x64" }, "sha512-dTRIlLRC5lCRHqO5DLb+A18HCvS394axmzqfnRNLptKVw7WuckpUwo1Z87Yw74mesbeIhnQTA2SZbRcIfVlwxg=="],
+ "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.3.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA=="],
- "@tailwindcss/postcss": ["@tailwindcss/postcss@4.2.3", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.2.3", "@tailwindcss/oxide": "4.2.3", "postcss": "^8.5.6", "tailwindcss": "4.2.3" } }, "sha512-MehdHOQRVFf300r8F430s4cf2QL+nSjFUNIndX5ZMqDLyMwTnyL4RDZsoDsDU+ThzT5eCj1+erSDKBWdn462Nw=="],
+ "@tailwindcss/postcss": ["@tailwindcss/postcss@4.3.0", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.3.0", "@tailwindcss/oxide": "4.3.0", "postcss": "^8.5.10", "tailwindcss": "4.3.0" } }, "sha512-Jm05Tjx+9yCLGv5qw1c+84Psds8MnyrEQYCB+FFk2lgGiUjlRqdxke4mVTuYrj2xnVZqKim2Apr5ySuQRYAw/w=="],
"@tailwindcss/typography": ["@tailwindcss/typography@0.5.19", "", { "dependencies": { "postcss-selector-parser": "6.0.10" }, "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg=="],
"@tanem/svg-injector": ["@tanem/svg-injector@10.1.68", "", { "dependencies": { "@babel/runtime": "^7.23.2", "content-type": "^1.0.5", "tslib": "^2.6.2" } }, "sha512-UkJajeR44u73ujtr5GVSbIlELDWD/mzjqWe54YMK61ljKxFcJoPd9RBSaO7xj02ISCWUqJW99GjrS+sVF0UnrA=="],
- "@turf/area": ["@turf/area@7.3.5", "", { "dependencies": { "@turf/helpers": "7.3.5", "@turf/meta": "7.3.5", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-sSn80wPT7XfBIDN3vurCPxhk9W4U8ozS/XImSqeLN8qveTICOxzZkhsGDMp0CuncaN+plWut4a2TdNM7mzZB6Q=="],
-
- "@turf/bbox": ["@turf/bbox@7.3.5", "", { "dependencies": { "@turf/helpers": "7.3.5", "@turf/meta": "7.3.5", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-oG1ya/HtBjAIg4TimbWx+nOYPbY0bCvt82Bq8tm6sBw3qqtbOyRSfDz79Sq90TnH7DXJprJ1qnVGKNtZ6jemfw=="],
-
- "@turf/centroid": ["@turf/centroid@7.3.5", "", { "dependencies": { "@turf/helpers": "7.3.5", "@turf/meta": "7.3.5", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-hkWaqwGFdOn6Tf0EWfn2yn1XZ1FWE1h2C5ZWstDMu/FxYO5DB+YjlmOFPl4K6SmSOEgdV07eK2vDCyPeTHqKGA=="],
-
- "@turf/helpers": ["@turf/helpers@7.3.5", "", { "dependencies": { "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-E/NMGV5MwbjjP7AJXBtsanC3yY8N2MQ87IGdIgkB2ji5AtBpwnH4L3gEqpYN4RlCJJWbLbzO91BbKv2waUd0eg=="],
-
- "@turf/meta": ["@turf/meta@7.3.5", "", { "dependencies": { "@turf/helpers": "7.3.5", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-r+ohqxoyqeigFB0oFrQx/YEHIkOKqcKpCjvZkvZs7Tkv+IFco5MezAd2zd4rzK+0DfFgDP3KpJc7HqrYjvEjhg=="],
-
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="],
"@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="],
- "@types/d3": ["@types/d3@7.4.3", "", { "dependencies": { "@types/d3-array": "*", "@types/d3-axis": "*", "@types/d3-brush": "*", "@types/d3-chord": "*", "@types/d3-color": "*", "@types/d3-contour": "*", "@types/d3-delaunay": "*", "@types/d3-dispatch": "*", "@types/d3-drag": "*", "@types/d3-dsv": "*", "@types/d3-ease": "*", "@types/d3-fetch": "*", "@types/d3-force": "*", "@types/d3-format": "*", "@types/d3-geo": "*", "@types/d3-hierarchy": "*", "@types/d3-interpolate": "*", "@types/d3-path": "*", "@types/d3-polygon": "*", "@types/d3-quadtree": "*", "@types/d3-random": "*", "@types/d3-scale": "*", "@types/d3-scale-chromatic": "*", "@types/d3-selection": "*", "@types/d3-shape": "*", "@types/d3-time": "*", "@types/d3-time-format": "*", "@types/d3-timer": "*", "@types/d3-transition": "*", "@types/d3-zoom": "*" } }, "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww=="],
-
- "@types/d3-array": ["@types/d3-array@3.2.2", "", {}, "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw=="],
-
- "@types/d3-axis": ["@types/d3-axis@3.0.6", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw=="],
-
- "@types/d3-brush": ["@types/d3-brush@3.0.6", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A=="],
-
- "@types/d3-chord": ["@types/d3-chord@3.0.6", "", {}, "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg=="],
-
- "@types/d3-color": ["@types/d3-color@3.1.3", "", {}, "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A=="],
-
- "@types/d3-contour": ["@types/d3-contour@3.0.6", "", { "dependencies": { "@types/d3-array": "*", "@types/geojson": "*" } }, "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg=="],
-
- "@types/d3-delaunay": ["@types/d3-delaunay@6.0.4", "", {}, "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw=="],
-
- "@types/d3-dispatch": ["@types/d3-dispatch@3.0.7", "", {}, "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA=="],
-
- "@types/d3-drag": ["@types/d3-drag@3.0.7", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ=="],
-
- "@types/d3-dsv": ["@types/d3-dsv@3.0.7", "", {}, "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g=="],
-
- "@types/d3-ease": ["@types/d3-ease@3.0.2", "", {}, "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA=="],
-
- "@types/d3-fetch": ["@types/d3-fetch@3.0.7", "", { "dependencies": { "@types/d3-dsv": "*" } }, "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA=="],
-
- "@types/d3-force": ["@types/d3-force@3.0.10", "", {}, "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw=="],
-
- "@types/d3-format": ["@types/d3-format@3.0.4", "", {}, "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g=="],
-
- "@types/d3-geo": ["@types/d3-geo@3.1.0", "", { "dependencies": { "@types/geojson": "*" } }, "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ=="],
-
- "@types/d3-hierarchy": ["@types/d3-hierarchy@3.1.7", "", {}, "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg=="],
-
- "@types/d3-interpolate": ["@types/d3-interpolate@3.0.4", "", { "dependencies": { "@types/d3-color": "*" } }, "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA=="],
-
- "@types/d3-path": ["@types/d3-path@3.1.1", "", {}, "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg=="],
-
- "@types/d3-polygon": ["@types/d3-polygon@3.0.2", "", {}, "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA=="],
-
- "@types/d3-quadtree": ["@types/d3-quadtree@3.0.6", "", {}, "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg=="],
-
- "@types/d3-random": ["@types/d3-random@3.0.3", "", {}, "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ=="],
-
- "@types/d3-scale": ["@types/d3-scale@4.0.9", "", { "dependencies": { "@types/d3-time": "*" } }, "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw=="],
-
- "@types/d3-scale-chromatic": ["@types/d3-scale-chromatic@3.1.0", "", {}, "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ=="],
-
- "@types/d3-selection": ["@types/d3-selection@3.0.11", "", {}, "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w=="],
-
- "@types/d3-shape": ["@types/d3-shape@3.1.8", "", { "dependencies": { "@types/d3-path": "*" } }, "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w=="],
-
- "@types/d3-time": ["@types/d3-time@3.0.4", "", {}, "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g=="],
-
- "@types/d3-time-format": ["@types/d3-time-format@4.0.3", "", {}, "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg=="],
-
- "@types/d3-timer": ["@types/d3-timer@3.0.2", "", {}, "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw=="],
-
- "@types/d3-transition": ["@types/d3-transition@3.0.9", "", { "dependencies": { "@types/d3-selection": "*" } }, "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg=="],
-
- "@types/d3-zoom": ["@types/d3-zoom@3.0.8", "", { "dependencies": { "@types/d3-interpolate": "*", "@types/d3-selection": "*" } }, "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw=="],
-
"@types/debug": ["@types/debug@4.1.13", "", { "dependencies": { "@types/ms": "*" } }, "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw=="],
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
"@types/estree-jsx": ["@types/estree-jsx@1.0.5", "", { "dependencies": { "@types/estree": "*" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="],
- "@types/geojson": ["@types/geojson@7946.0.16", "", {}, "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg=="],
-
- "@types/geojson-vt": ["@types/geojson-vt@3.2.5", "", { "dependencies": { "@types/geojson": "*" } }, "sha512-qDO7wqtprzlpe8FfQ//ClPV9xiuoh2nkIgiouIptON9w5jvD/fA4szvP9GBlDVdJ5dldAl0kX/sy3URbWwLx0g=="],
-
"@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="],
"@types/katex": ["@types/katex@0.16.8", "", {}, "sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg=="],
- "@types/mapbox__point-geometry": ["@types/mapbox__point-geometry@0.1.4", "", {}, "sha512-mUWlSxAmYLfwnRBmgYV86tgYmMIICX4kza8YnE/eIlywGe2XoOxlpVnXWwir92xRLjwyarqwpu2EJKD2pk0IUA=="],
-
- "@types/mapbox__vector-tile": ["@types/mapbox__vector-tile@1.3.4", "", { "dependencies": { "@types/geojson": "*", "@types/mapbox__point-geometry": "*", "@types/pbf": "*" } }, "sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg=="],
-
"@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="],
"@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
@@ -767,28 +562,18 @@
"@types/parse-json": ["@types/parse-json@4.0.2", "", {}, "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="],
- "@types/pbf": ["@types/pbf@3.0.5", "", {}, "sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA=="],
-
"@types/prismjs": ["@types/prismjs@1.26.6", "", {}, "sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw=="],
"@types/prop-types": ["@types/prop-types@15.7.15", "", {}, "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw=="],
"@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="],
- "@types/supercluster": ["@types/supercluster@7.1.3", "", { "dependencies": { "@types/geojson": "*" } }, "sha512-Z0pOY34GDFl3Q6hUFYf3HkTwKEE02e7QgtJppBt+beEAxnyOpJua+voGFvxINBHa06GwLFFym7gRPY2SiKIfIA=="],
-
"@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
- "@types/use-sync-external-store": ["@types/use-sync-external-store@0.0.6", "", {}, "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg=="],
-
"@ungap/structured-clone": ["@ungap/structured-clone@1.3.1", "", {}, "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ=="],
"@vimeo/player": ["@vimeo/player@2.29.0", "", { "dependencies": { "native-promise-only": "0.8.1", "weakmap-polyfill": "2.0.4" } }, "sha512-9JjvjeqUndb9otCCFd0/+2ESsLk7VkDE6sxOBy9iy2ukezuQbplVRi+g9g59yAurKofbmTi/KcKxBGO/22zWRw=="],
- "@xyflow/react": ["@xyflow/react@12.8.4", "", { "dependencies": { "@xyflow/system": "0.0.68", "classcat": "^5.0.3", "zustand": "^4.4.0" }, "peerDependencies": { "react": ">=17", "react-dom": ">=17" } }, "sha512-bqUu4T5QSHiCFPkoH+b+LROKwQJdLvcjhGbNW9c1dLafCBRjmH1IYz0zPE+lRDXCtQ9kRyFxz3tG19+8VORJ1w=="],
-
- "@xyflow/system": ["@xyflow/system@0.0.68", "", { "dependencies": { "@types/d3-drag": "^3.0.7", "@types/d3-interpolate": "^3.0.4", "@types/d3-selection": "^3.0.10", "@types/d3-transition": "^3.0.8", "@types/d3-zoom": "^3.0.8", "d3-drag": "^3.0.0", "d3-interpolate": "^3.0.1", "d3-selection": "^3.0.0", "d3-zoom": "^3.0.0" } }, "sha512-QDG2wxIG4qX+uF8yzm1ULVZrcXX3MxPBoxv7O52FWsX87qIImOqifUhfa/TwsvLdzn7ic2DDBH1uI8TKbdNTYA=="],
-
"@zag-js/anatomy": ["@zag-js/anatomy@1.40.0", "", {}, "sha512-oiB4uAaV//L38JluLVPtOHO3xvqambrfrXVOoq4kmNrBv1LLlCmFvrXA2HOR9lakn4ExK27XSUrKhUN7YlKjfQ=="],
"@zag-js/collection": ["@zag-js/collection@1.40.0", "", { "dependencies": { "@zag-js/utils": "1.40.0" } }, "sha512-+3o1nvbcA9Kz2hDDFf8Kngpd+of33S4TS5Tb9KvrHlU5ieQdvEUtc7/pWG2aCTkGpmgda+j91akB6ZB8+oVkvA=="],
@@ -823,28 +608,6 @@
"abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="],
- "abs-svg-path": ["abs-svg-path@0.1.1", "", {}, "sha512-d8XPSGjfyzlXC3Xx891DJRyZfqk5JU0BJrDQcsWomFIV1/BIzPW5HDH5iDdWpqWaav0YVIEzT1RHTwWr0FFshA=="],
-
- "acorn": ["acorn@7.4.1", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="],
-
- "ag-charts-community": ["ag-charts-community@11.2.4", "", { "dependencies": { "ag-charts-core": "11.2.4", "ag-charts-locale": "11.2.4", "ag-charts-types": "11.2.4" } }, "sha512-L4DDGpCP1IVjjgacADRMPSucUiH7CU4nLJFFI/eWkv+ee0WOte7XIgIiwzUci+UD4IhLHu5zwbOoHdZ9LX/E5w=="],
-
- "ag-charts-core": ["ag-charts-core@11.2.4", "", { "dependencies": { "ag-charts-types": "11.2.4" } }, "sha512-RiN2GXSBuYBAGhbfxiQjhM+pDnJ+2iT/W0r/2KHBHXI8Bv89l+jYKBunb/4SqcP6/DBaxhh+yESk25dYj391Bg=="],
-
- "ag-charts-enterprise": ["ag-charts-enterprise@11.2.4", "", { "dependencies": { "ag-charts-community": "11.2.4", "ag-charts-core": "11.2.4" } }, "sha512-4msY1swe0GwEytQI9H3ANM+pMWVeAa8u8yGcZJ8kMzTFB3urNjXXGmYLZ7rQCiPfzLUuY25iGKf36ToUMHrrNA=="],
-
- "ag-charts-locale": ["ag-charts-locale@11.2.4", "", {}, "sha512-hArufzKISb/5UJtIPfTKu3TvSQ3ZAuwMrOsn8F6I4rOwJnNxbZYeQvm2yiKXx9D7hMhr3d1lNZxNIlKOrMZY/w=="],
-
- "ag-charts-react": ["ag-charts-react@11.2.4", "", { "dependencies": { "ag-charts-community": "11.2.4" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0" } }, "sha512-fZgshFe6qZ9f85RYz84rWNbtvCm136jqARu3z/GfQsUP/Y8uNJreZCBjiJpUkU3N0/HJ9Moq8W2fE9opp1MVWA=="],
-
- "ag-charts-types": ["ag-charts-types@12.3.1", "", {}, "sha512-5216xYoawnvMXDFI6kTpPku+mH0Csiwu/FE7lsAm8Z22HEN6ciSG/V7g+IrpLWncELqksgENebCTP75PZ3CsHA=="],
-
- "ag-grid-community": ["ag-grid-community@34.3.1", "", { "dependencies": { "ag-charts-types": "12.3.1" } }, "sha512-PwlrPudsFOzGumphi2y9ihWeaUlIwKhOra/MXu2LjeV2U8DgLLcYS8CartE5Hszhn1poJHawwI9HWrxlKliwdw=="],
-
- "ag-grid-enterprise": ["ag-grid-enterprise@34.3.1", "", { "dependencies": { "ag-grid-community": "34.3.1" }, "optionalDependencies": { "ag-charts-community": "12.3.1", "ag-charts-enterprise": "12.3.1" } }, "sha512-pee4Zh0gLeQED+RM+ofxNun9JSrYrE0ZVw90BibuBkhBCLXrtXtjGPiwM+Ylntl+GwJUuCXrTcVLXRHpdXVYbQ=="],
-
- "ag-grid-react": ["ag-grid-react@34.3.1", "", { "dependencies": { "ag-grid-community": "34.3.1", "prop-types": "^15.8.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-1UTlBT+xJkjNZAuf7RxK61mgxKGTPB+6XR99oIHq7cYC89kJmLbWqhHt/1XqRWF5cAgSKk8u+HtOQaN8tAZStw=="],
-
"agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="],
"altcha-lib": ["altcha-lib@1.4.1", "", {}, "sha512-MAXP9tkQOA2SE9Gwoe3LAcZbcDpp3XzYc5GDVej/y3eMNaFG/eVnRY1/7SGFW0RPsViEjPf+hi5eANjuZrH1xA=="],
@@ -853,32 +616,18 @@
"aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="],
- "array-bounds": ["array-bounds@1.0.1", "", {}, "sha512-8wdW3ZGk6UjMPJx/glyEt0sLzzwAE1bhToPsO1W2pbpR2gULyxe3BjSiuJFheP50T/GgODVPz2fuMUmIywt8cQ=="],
-
- "array-find-index": ["array-find-index@1.0.2", "", {}, "sha512-M1HQyIXcBGtVywBt8WVdim+lrNaK7VHp99Qt5pSNziXznKHViIBbXWtfRTpEFpF/c4FdfxNAsCCwPp5phBYJtw=="],
-
- "array-normalize": ["array-normalize@1.1.4", "", { "dependencies": { "array-bounds": "^1.0.0" } }, "sha512-fCp0wKFLjvSPmCn4F5Tiw4M3lpMZoHlCjfcs7nNzuj3vqQQ1/a8cgB9DXcpDSn18c+coLnaW7rqfcYCvKbyJXg=="],
-
- "array-range": ["array-range@1.0.1", "", {}, "sha512-shdaI1zT3CVNL2hnx9c0JMc0ZogGaxDs5e85akgHWKYa0yVbIyp06Ind3dVkTj/uuFrzaHBOyqFzo+VV6aXgtA=="],
-
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
- "attr-accept": ["attr-accept@2.2.5", "", {}, "sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ=="],
-
"autoprefixer": ["autoprefixer@10.5.0", "", { "dependencies": { "browserslist": "^4.28.2", "caniuse-lite": "^1.0.30001787", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong=="],
"babel-dead-code-elimination": ["babel-dead-code-elimination@1.0.12", "", { "dependencies": { "@babel/core": "^7.23.7", "@babel/parser": "^7.23.6", "@babel/traverse": "^7.23.7", "@babel/types": "^7.23.6" } }, "sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig=="],
- "babel-merge": ["babel-merge@3.0.0", "", { "dependencies": { "deepmerge": "^2.2.1", "object.omit": "^3.0.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "sha512-eBOBtHnzt9xvnjpYNI5HmaPp/b2vMveE5XggzqHnQeHJ8mFIBrBv6WZEVIj5jJ2uwTItkqKo9gWzEEcBxEq0yw=="],
-
"babel-plugin-macros": ["babel-plugin-macros@3.1.0", "", { "dependencies": { "@babel/runtime": "^7.12.5", "cosmiconfig": "^7.0.0", "resolve": "^1.19.0" } }, "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg=="],
"bail": ["bail@2.0.2", "", {}, "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw=="],
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
- "base64-arraybuffer": ["base64-arraybuffer@1.0.2", "", {}, "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ=="],
-
"baseline-browser-mapping": ["baseline-browser-mapping@2.10.29", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-Asa2krT+XTPZINCS+2QcyS8WTkObE77RwkydwF7h6DmnKqbvlalz93m/dnphUyCa6SWSP51VgtEUf2FN+gelFQ=="],
"bcp-47": ["bcp-47@2.1.0", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-9IIS3UPrvIa1Ej+lVDdDwO7zLehjqsaByECw0bu2RRGP73jALm6FYbzI5gWbgHLvNdkvfXB5YrSbocZdOS0c0w=="],
@@ -887,22 +636,10 @@
"bcp-47-normalize": ["bcp-47-normalize@2.3.0", "", { "dependencies": { "bcp-47": "^2.0.0", "bcp-47-match": "^2.0.0" } }, "sha512-8I/wfzqQvttUFz7HVJgIZ7+dj3vUaIyIxYXaTRP1YWoSDfzt6TUmxaKZeuXR62qBmYr+nvuWINFRl6pZ5DlN4Q=="],
- "binary-search-bounds": ["binary-search-bounds@2.0.5", "", {}, "sha512-H0ea4Fd3lS1+sTEB2TgcLoK21lLhwEJzlQv3IN47pJS976Gx4zoWe0ak3q+uYh60ppQxg9F16Ri4tS1sfD4+jA=="],
-
- "bit-twiddle": ["bit-twiddle@1.0.2", "", {}, "sha512-B9UhK0DKFZhoTFcfvAzhqsjStvGJp9vYWf3+6SNTtdSQnvIgfkHbgHrg/e4+TH71N2GDu8tpmCVoyfrL1d7ntA=="],
-
- "bitmap-sdf": ["bitmap-sdf@1.0.4", "", {}, "sha512-1G3U4n5JE6RAiALMxu0p1XmeZkTeCwGKykzsLTCqVzfSDaN6S7fKnkIkfejogz+iwqBWc0UYAIKnKHNN7pSfDg=="],
-
- "bl": ["bl@2.2.1", "", { "dependencies": { "readable-stream": "^2.3.5", "safe-buffer": "^5.1.1" } }, "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g=="],
-
- "blurhash": ["blurhash@2.0.5", "", {}, "sha512-cRygWd7kGBQO3VEhPiTgq4Wc43ctsM+o46urrmPOiuAe+07fzlSB9OJVdpgDL0jPqXUVQ9ht7aq7kxOeJHRK+w=="],
-
"brace-expansion": ["brace-expansion@2.1.0", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w=="],
"browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="],
- "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="],
-
"cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="],
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
@@ -911,18 +648,12 @@
"caniuse-lite": ["caniuse-lite@1.0.30001792", "", {}, "sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw=="],
- "canvas-fit": ["canvas-fit@1.5.0", "", { "dependencies": { "element-size": "^1.1.1" } }, "sha512-onIcjRpz69/Hx5bB5HGbYKUF2uC6QT6Gp+pfpGm3A7mPfcluSLV5v4Zu+oflDUwLdUw0rLIBhUbi0v8hM4FJQQ=="],
-
- "canvas-hypertxt": ["canvas-hypertxt@1.0.3", "", {}, "sha512-+VsMpRr64jYgKq2IeFUNel3vCZH/IzS+iXSHxmUV3IUH5dXlC9xHz4AwtPZisDxZ5MWcuK0V+TXgPKFPiZnxzg=="],
-
"castable-video": ["castable-video@1.1.16", "", { "dependencies": { "custom-media-element": "~1.4.6" } }, "sha512-wBhe2dZu2afhewL3EaGgVYTyDsa9HvNhY98clMZkNzDrLelOValSrTaoMos9YX7PPBCrgpd1j6YmNyyI2Vbq3w=="],
"ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
"ce-la-react": ["ce-la-react@0.3.2", "", { "peerDependencies": { "react": ">=17.0.0" } }, "sha512-QJ6k4lOD/btI08xG8jBPxRCGXvCnusGGkTsiXk0u3NqUu/W+BXRnFD4PYjwtqh8AWmGa5LDbGk0fLQsqr0nSMA=="],
- "char-regex": ["char-regex@1.0.2", "", {}, "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw=="],
-
"character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="],
"character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="],
@@ -933,12 +664,8 @@
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
- "clamp": ["clamp@1.0.1", "", {}, "sha512-kgMuFyE78OC6Dyu3Dy7vcx4uy97EIbVxJB/B0eJ3bUNAkwdNcxYzgKltnyADiYwsR7SEqkkUPsEUT//OVS6XMA=="],
-
"class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="],
- "classcat": ["classcat@5.0.5", "", {}, "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w=="],
-
"classnames": ["classnames@2.5.1", "", {}, "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow=="],
"cloudflare-video-element": ["cloudflare-video-element@1.3.5", "", {}, "sha512-zj9gjJa6xW8MNrfc4oKuwgGS0njRLpOlQjdifbuNxvy8k4Y3pKCyKCMG2XIsjd2iQGhgjS57b1P5VWdJlxcXBw=="],
@@ -949,29 +676,13 @@
"codem-isoboxer": ["codem-isoboxer@0.3.10", "", {}, "sha512-eNk3TRV+xQMJ1PEj0FQGY8KD4m0GPxT487XJ+Iftm7mVa9WpPFDMWqPt+46buiP5j5Wzqe5oMIhqBcAeKfygSA=="],
- "color-alpha": ["color-alpha@1.0.4", "", { "dependencies": { "color-parse": "^1.3.8" } }, "sha512-lr8/t5NPozTSqli+duAN+x+no/2WaKTeWvxhHGN+aXT6AJ8vPlzLa7UriyjWak0pSC2jHol9JgjBYnnHsGha9A=="],
-
- "color-id": ["color-id@1.1.0", "", { "dependencies": { "clamp": "^1.0.1" } }, "sha512-2iRtAn6dC/6/G7bBIo0uupVrIne1NsQJvJxZOBCzQOfk7jRq97feaDZ3RdzuHakRXXnHGNwglto3pqtRx1sX0g=="],
-
- "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
-
- "color-normalize": ["color-normalize@1.5.0", "", { "dependencies": { "clamp": "^1.0.1", "color-rgba": "^2.1.1", "dtype": "^2.0.0" } }, "sha512-rUT/HDXMr6RFffrR53oX3HGWkDOP9goSAQGBkUaAYKjOE2JxozccdGyufageWDlInRAjm/jYPrf/Y38oa+7obw=="],
-
- "color-parse": ["color-parse@2.0.0", "", { "dependencies": { "color-name": "^1.0.0" } }, "sha512-g2Z+QnWsdHLppAbrpcFWo629kLOnOPtpxYV69GCqm92gqSgyXbzlfyN3MXs0412fPBkFmiuS+rXposgBgBa6Kg=="],
-
- "color-rgba": ["color-rgba@3.0.0", "", { "dependencies": { "color-parse": "^2.0.0", "color-space": "^2.0.0" } }, "sha512-PPwZYkEY3M2THEHHV6Y95sGUie77S7X8v+h1r6LSAPF3/LL2xJ8duUXSrkic31Nzc4odPwHgUbiX/XuTYzQHQg=="],
-
- "color-space": ["color-space@2.3.2", "", {}, "sha512-BcKnbOEsOarCwyoLstcoEztwT0IJxqqQkNwDuA3a65sICvvHL2yoeV13psoDFh5IuiOMnIOKdQDwB4Mk3BypiA=="],
-
"colorjs.io": ["colorjs.io@0.5.2", "", {}, "sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw=="],
"combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
"comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="],
- "commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="],
-
- "concat-stream": ["concat-stream@1.6.2", "", { "dependencies": { "buffer-from": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^2.2.2", "typedarray": "^0.0.6" } }, "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw=="],
+ "commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="],
"confbox": ["confbox@0.2.4", "", {}, "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ=="],
@@ -981,134 +692,38 @@
"cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="],
- "core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="],
-
"cosmiconfig": ["cosmiconfig@7.1.0", "", { "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", "parse-json": "^5.0.0", "path-type": "^4.0.0", "yaml": "^1.10.0" } }, "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA=="],
- "country-regex": ["country-regex@1.1.0", "", {}, "sha512-iSPlClZP8vX7MC3/u6s3lrDuoQyhQukh5LyABJ3hvfzbQ3Yyayd4fp04zjLnfi267B/B2FkumcWWgrbban7sSA=="],
-
- "css-font": ["css-font@1.2.0", "", { "dependencies": { "css-font-size-keywords": "^1.0.0", "css-font-stretch-keywords": "^1.0.1", "css-font-style-keywords": "^1.0.1", "css-font-weight-keywords": "^1.0.0", "css-global-keywords": "^1.0.1", "css-system-font-keywords": "^1.0.0", "pick-by-alias": "^1.2.0", "string-split-by": "^1.0.0", "unquote": "^1.1.0" } }, "sha512-V4U4Wps4dPDACJ4WpgofJ2RT5Yqwe1lEH6wlOOaIxMi0gTjdIijsc5FmxQlZ7ZZyKQkkutqqvULOp07l9c7ssA=="],
-
- "css-font-size-keywords": ["css-font-size-keywords@1.0.0", "", {}, "sha512-Q+svMDbMlelgCfH/RVDKtTDaf5021O486ZThQPIpahnIjUkMUslC+WuOQSWTgGSrNCH08Y7tYNEmmy0hkfMI8Q=="],
-
- "css-font-stretch-keywords": ["css-font-stretch-keywords@1.0.1", "", {}, "sha512-KmugPO2BNqoyp9zmBIUGwt58UQSfyk1X5DbOlkb2pckDXFSAfjsD5wenb88fNrD6fvS+vu90a/tsPpb9vb0SLg=="],
-
- "css-font-style-keywords": ["css-font-style-keywords@1.0.1", "", {}, "sha512-0Fn0aTpcDktnR1RzaBYorIxQily85M2KXRpzmxQPgh8pxUN9Fcn00I8u9I3grNr1QXVgCl9T5Imx0ZwKU973Vg=="],
-
- "css-font-weight-keywords": ["css-font-weight-keywords@1.0.0", "", {}, "sha512-5So8/NH+oDD+EzsnF4iaG4ZFHQ3vaViePkL1ZbZ5iC/KrsCY+WHq/lvOgrtmuOQ9pBBZ1ADGpaf+A4lj1Z9eYA=="],
-
- "css-global-keywords": ["css-global-keywords@1.0.1", "", {}, "sha512-X1xgQhkZ9n94WDwntqst5D/FKkmiU0GlJSFZSV3kLvyJ1WC5VeyoXDOuleUD+SIuH9C7W05is++0Woh0CGfKjQ=="],
-
- "css-system-font-keywords": ["css-system-font-keywords@1.0.0", "", {}, "sha512-1umTtVd/fXS25ftfjB71eASCrYhilmEsvDEI6wG/QplnmlfmVM5HkZ/ZX46DT5K3eblFPgLUHt5BRCb0YXkSFA=="],
-
- "csscolorparser": ["csscolorparser@1.0.3", "", {}, "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w=="],
-
"cssesc": ["cssesc@3.0.0", "", { "bin": { "cssesc": "bin/cssesc" } }, "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="],
"csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="],
"custom-media-element": ["custom-media-element@1.4.6", "", {}, "sha512-/HRYqJOa1ob5ik4q7FIJVYxTJCFs/FL3+cQPAJjUf2uiqrDEzbTgB315gQ2rG8oK3w094W9m5tcB8S5Qah+caA=="],
- "d": ["d@1.0.2", "", { "dependencies": { "es5-ext": "^0.10.64", "type": "^2.7.2" } }, "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw=="],
-
- "d3-array": ["d3-array@1.2.4", "", {}, "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw=="],
-
- "d3-collection": ["d3-collection@1.0.7", "", {}, "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A=="],
-
- "d3-color": ["d3-color@3.1.0", "", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="],
-
- "d3-dispatch": ["d3-dispatch@1.0.6", "", {}, "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA=="],
-
- "d3-drag": ["d3-drag@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-selection": "3" } }, "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg=="],
-
- "d3-ease": ["d3-ease@3.0.1", "", {}, "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w=="],
-
- "d3-force": ["d3-force@1.2.1", "", { "dependencies": { "d3-collection": "1", "d3-dispatch": "1", "d3-quadtree": "1", "d3-timer": "1" } }, "sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg=="],
-
- "d3-format": ["d3-format@1.4.5", "", {}, "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ=="],
-
- "d3-geo": ["d3-geo@1.12.1", "", { "dependencies": { "d3-array": "1" } }, "sha512-XG4d1c/UJSEX9NfU02KwBL6BYPj8YKHxgBEw5om2ZnTRSbIcego6dhHwcxuSR3clxh0EpE38os1DVPOmnYtTPg=="],
-
- "d3-geo-projection": ["d3-geo-projection@2.9.0", "", { "dependencies": { "commander": "2", "d3-array": "1", "d3-geo": "^1.12.0", "resolve": "^1.1.10" }, "bin": { "geo2svg": "bin/geo2svg", "geograticule": "bin/geograticule", "geoproject": "bin/geoproject", "geoquantize": "bin/geoquantize", "geostitch": "bin/geostitch" } }, "sha512-ZULvK/zBn87of5rWAfFMc9mJOipeSo57O+BBitsKIXmU4rTVAnX1kSsJkE0R+TxY8pGNoM1nbyRRE7GYHhdOEQ=="],
-
- "d3-hierarchy": ["d3-hierarchy@1.1.9", "", {}, "sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ=="],
-
- "d3-interpolate": ["d3-interpolate@3.0.1", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="],
-
- "d3-path": ["d3-path@1.0.9", "", {}, "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="],
-
- "d3-quadtree": ["d3-quadtree@1.0.7", "", {}, "sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA=="],
-
- "d3-scale": ["d3-scale@4.0.2", "", { "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", "d3-interpolate": "1.2.0 - 3", "d3-time": "2.1.1 - 3", "d3-time-format": "2 - 4" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="],
-
- "d3-selection": ["d3-selection@3.0.0", "", {}, "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ=="],
-
- "d3-shape": ["d3-shape@1.3.7", "", { "dependencies": { "d3-path": "1" } }, "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw=="],
-
- "d3-time": ["d3-time@1.1.0", "", {}, "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA=="],
-
- "d3-time-format": ["d3-time-format@2.3.0", "", { "dependencies": { "d3-time": "1" } }, "sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ=="],
-
- "d3-timer": ["d3-timer@1.0.10", "", {}, "sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw=="],
-
- "d3-transition": ["d3-transition@3.0.1", "", { "dependencies": { "d3-color": "1 - 3", "d3-dispatch": "1 - 3", "d3-ease": "1 - 3", "d3-interpolate": "1 - 3", "d3-timer": "1 - 3" }, "peerDependencies": { "d3-selection": "2 - 3" } }, "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w=="],
-
- "d3-zoom": ["d3-zoom@3.0.0", "", { "dependencies": { "d3-dispatch": "1 - 3", "d3-drag": "2 - 3", "d3-interpolate": "1 - 3", "d3-selection": "2 - 3", "d3-transition": "2 - 3" } }, "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw=="],
-
"dash-video-element": ["dash-video-element@0.3.2", "", { "dependencies": { "custom-media-element": "^1.4.6", "dashjs": "^5.0.3", "media-tracks": "^0.3.5" } }, "sha512-eN1IqgtTAbq4zVkbt82BDwQtybQ6WOXyQU5HbftUSnqo+SHAPbZCif1Y7uViAhgvuEPvZtbiXDiacvvTeGxc/g=="],
"dashjs": ["dashjs@5.1.1", "", { "dependencies": { "@svta/cml-608": "1.0.1", "@svta/cml-cmcd": "1.0.1", "@svta/cml-cmsd": "1.0.1", "@svta/cml-dash": "1.0.1", "@svta/cml-id3": "1.0.1", "@svta/cml-request": "1.0.1", "@svta/cml-xml": "1.0.1", "bcp-47-match": "^2.0.3", "bcp-47-normalize": "^2.3.0", "codem-isoboxer": "0.3.10", "fast-deep-equal": "3.1.3", "html-entities": "^2.5.2", "imsc": "^1.1.5", "localforage": "^1.10.0", "path-browserify": "^1.0.1", "ua-parser-js": "^1.0.37" } }, "sha512-BzNXlUgzEjhuZ5M5hlSp1qIyQHZ7NpXAR0loP9DAAFVZj/ntL1DHeZ7qp/L3bvI4rq50X5indkAZQ3zEHWJoCA=="],
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
- "decimal.js-light": ["decimal.js-light@2.5.1", "", {}, "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg=="],
-
"decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="],
"dedent": ["dedent@1.7.2", "", { "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, "optionalPeers": ["babel-plugin-macros"] }, "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA=="],
- "deepmerge": ["deepmerge@2.2.1", "", {}, "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA=="],
-
- "defined": ["defined@1.0.1", "", {}, "sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q=="],
-
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
"dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
- "detect-kerning": ["detect-kerning@2.1.2", "", {}, "sha512-I3JIbrnKPAntNLl1I6TpSQQdQ4AutYzv/sKMFKbepawV/hlH0GmYKhUoOEMd4xqaUHT+Bm0f4127lh5qs1m1tw=="],
-
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
"detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="],
"devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="],
- "dnd-core": ["dnd-core@16.0.1", "", { "dependencies": { "@react-dnd/asap": "^5.0.1", "@react-dnd/invariant": "^4.0.1", "redux": "^4.2.0" } }, "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng=="],
-
- "draw-svg-path": ["draw-svg-path@1.0.0", "", { "dependencies": { "abs-svg-path": "~0.1.1", "normalize-svg-path": "~0.1.0" } }, "sha512-P8j3IHxcgRMcY6sDzr0QvJDLzBnJJqpTG33UZ2Pvp8rw0apCHhJCWqYprqrXjrgHnJ6tuhP1iTJSAodPDHxwkg=="],
-
- "dtype": ["dtype@2.0.0", "", {}, "sha512-s2YVcLKdFGS0hpFqJaTwscsyt0E8nNFdmo73Ocd81xNPj4URI4rj6D60A+vFMIw7BXWlb4yRkEwfBqcZzPGiZg=="],
-
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
- "dup": ["dup@1.0.0", "", {}, "sha512-Bz5jxMMC0wgp23Zm15ip1x8IhYRqJvF3nFC0UInJUDkN1z4uNPk9jTnfCUJXbOGiQ1JbXLQsiV41Fb+HXcj5BA=="],
-
- "duplexify": ["duplexify@3.7.1", "", { "dependencies": { "end-of-stream": "^1.0.0", "inherits": "^2.0.1", "readable-stream": "^2.0.0", "stream-shift": "^1.0.0" } }, "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g=="],
-
- "earcut": ["earcut@2.2.4", "", {}, "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ=="],
-
"electron-to-chromium": ["electron-to-chromium@1.5.354", "", {}, "sha512-JaBHwWcfIdmSAfWM5l3uwjGd431j8YEMikZ+K/2nXVuBqJKyZ0f+2h4n4JY5AyNiZmnY9qQr2RU3v9DxDmHMNg=="],
- "element-size": ["element-size@1.1.1", "", {}, "sha512-eaN+GMOq/Q+BIWy0ybsgpcYImjGIdNLyjLFJU4XsLHXYQao5jCNb36GyN6C2qwmDDYSfIBmKpPpr4VnBdLCsPQ=="],
-
- "elementary-circuits-directed-graph": ["elementary-circuits-directed-graph@1.3.1", "", { "dependencies": { "strongly-connected-components": "^1.0.1" } }, "sha512-ZEiB5qkn2adYmpXGnJKkxT8uJHlW/mxmBpmeqawEHzPxh9HkLD4/1mFYX5l0On+f6rcPIt8/EWlRU2Vo3fX6dQ=="],
-
- "emojilib": ["emojilib@2.4.0", "", {}, "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw=="],
-
- "emoticon": ["emoticon@4.1.0", "", {}, "sha512-VWZfnxqwNcc51hIy/sbOdEem6D+cVtpPzEEtVAFdaas30+1dgkyaOQ4sQ6Bp0tOMqWO1v+HQfYaoodOkdhK6SQ=="],
-
- "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="],
-
"engine.io-client": ["engine.io-client@6.6.4", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1", "engine.io-parser": "~5.2.1", "ws": "~8.18.3", "xmlhttprequest-ssl": "~2.1.1" } }, "sha512-+kjUJnZGwzewFDw951CDWcwj35vMNf2fcj7xQWOctq1F2i1jkDdVvdFG9kM/BEChymCH36KgjnW0NsL58JYRxw=="],
"engine.io-parser": ["engine.io-parser@5.2.3", "", {}, "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q=="],
@@ -1129,72 +744,30 @@
"es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
- "es-toolkit": ["es-toolkit@1.46.1", "", {}, "sha512-5eNtXOs3tbfxXOj04tjjseeWkRWaoCjdEI+96DgwzZoe6c9juL49pXlzAFTI72aWC9Y8p7168g6XIKjh7k6pyQ=="],
-
- "es5-ext": ["es5-ext@0.10.64", "", { "dependencies": { "es6-iterator": "^2.0.3", "es6-symbol": "^3.1.3", "esniff": "^2.0.1", "next-tick": "^1.1.0" } }, "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg=="],
-
- "es6-iterator": ["es6-iterator@2.0.3", "", { "dependencies": { "d": "1", "es5-ext": "^0.10.35", "es6-symbol": "^3.1.1" } }, "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g=="],
-
- "es6-symbol": ["es6-symbol@3.1.4", "", { "dependencies": { "d": "^1.0.2", "ext": "^1.7.0" } }, "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg=="],
-
- "es6-weak-map": ["es6-weak-map@2.0.3", "", { "dependencies": { "d": "1", "es5-ext": "^0.10.46", "es6-iterator": "^2.0.3", "es6-symbol": "^3.1.1" } }, "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA=="],
-
"esbuild": ["esbuild@0.27.7", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.7", "@esbuild/android-arm": "0.27.7", "@esbuild/android-arm64": "0.27.7", "@esbuild/android-x64": "0.27.7", "@esbuild/darwin-arm64": "0.27.7", "@esbuild/darwin-x64": "0.27.7", "@esbuild/freebsd-arm64": "0.27.7", "@esbuild/freebsd-x64": "0.27.7", "@esbuild/linux-arm": "0.27.7", "@esbuild/linux-arm64": "0.27.7", "@esbuild/linux-ia32": "0.27.7", "@esbuild/linux-loong64": "0.27.7", "@esbuild/linux-mips64el": "0.27.7", "@esbuild/linux-ppc64": "0.27.7", "@esbuild/linux-riscv64": "0.27.7", "@esbuild/linux-s390x": "0.27.7", "@esbuild/linux-x64": "0.27.7", "@esbuild/netbsd-arm64": "0.27.7", "@esbuild/netbsd-x64": "0.27.7", "@esbuild/openbsd-arm64": "0.27.7", "@esbuild/openbsd-x64": "0.27.7", "@esbuild/openharmony-arm64": "0.27.7", "@esbuild/sunos-x64": "0.27.7", "@esbuild/win32-arm64": "0.27.7", "@esbuild/win32-ia32": "0.27.7", "@esbuild/win32-x64": "0.27.7" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w=="],
"escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="],
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
- "escodegen": ["escodegen@2.1.0", "", { "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", "esutils": "^2.0.2" }, "optionalDependencies": { "source-map": "~0.6.1" }, "bin": { "esgenerate": "bin/esgenerate.js", "escodegen": "bin/escodegen.js" } }, "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w=="],
-
- "esniff": ["esniff@2.0.1", "", { "dependencies": { "d": "^1.0.1", "es5-ext": "^0.10.62", "event-emitter": "^0.3.5", "type": "^2.7.2" } }, "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg=="],
-
- "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="],
-
- "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
-
"estree-util-is-identifier-name": ["estree-util-is-identifier-name@3.0.0", "", {}, "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg=="],
- "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
-
- "event-emitter": ["event-emitter@0.3.5", "", { "dependencies": { "d": "1", "es5-ext": "~0.10.14" } }, "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA=="],
-
"event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="],
- "eventemitter3": ["eventemitter3@5.0.4", "", {}, "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw=="],
-
- "events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="],
-
"exit-hook": ["exit-hook@2.2.1", "", {}, "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw=="],
"exsolve": ["exsolve@1.0.8", "", {}, "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA=="],
- "ext": ["ext@1.7.0", "", { "dependencies": { "type": "^2.7.2" } }, "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw=="],
-
"extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
- "falafel": ["falafel@2.2.5", "", { "dependencies": { "acorn": "^7.1.1", "isarray": "^2.0.1" } }, "sha512-HuC1qF9iTnHDnML9YZAdCDQwT0yKl/U55K4XSUXqGAA2GLoafFgWRqdAbhWJxXaYD4pyoVxAJ8wH670jMpI9DQ=="],
-
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
- "fast-isnumeric": ["fast-isnumeric@1.1.4", "", { "dependencies": { "is-string-blank": "^1.0.1" } }, "sha512-1mM8qOr2LYz8zGaUdmiqRDiuue00Dxjgcb1NQR7TnhLVh6sQyngP9xvLo7Sl7LZpP/sk5eb+bcyWXw530NTBZw=="],
-
"fault": ["fault@1.0.4", "", { "dependencies": { "format": "^0.2.0" } }, "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA=="],
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
- "file-selector": ["file-selector@2.1.2", "", { "dependencies": { "tslib": "^2.7.0" } }, "sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig=="],
-
"find-root": ["find-root@1.1.0", "", {}, "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="],
- "find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
-
- "flatten-vertex-data": ["flatten-vertex-data@1.0.2", "", { "dependencies": { "dtype": "^2.0.0" } }, "sha512-BvCBFK2NZqerFTdMDgqfHBwxYWnxeCkwONsw6PvBMcUXqo8U/KDWwmXhqx1x2kLIg7DqIsJfOaJFOmlua3Lxuw=="],
-
- "font-atlas": ["font-atlas@2.1.0", "", { "dependencies": { "css-font": "^1.0.0" } }, "sha512-kP3AmvX+HJpW4w3d+PiPR2X6E1yvsBXt2yhuCw+yReO9F1WYhvZwx3c95DGZGwg9xYzDGrgJYa885xmVA+28Cg=="],
-
- "font-measure": ["font-measure@1.2.2", "", { "dependencies": { "css-font": "^1.2.0" } }, "sha512-mRLEpdrWzKe9hbfaF3Qpr06TAjquuBVP5cHy4b3hyeNdjc9i0PO6HniGsX5vjL5OWv7+Bd++NiooNpT/s8BvIA=="],
-
"form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="],
"form-data-encoder": ["form-data-encoder@1.7.2", "", {}, "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A=="],
@@ -1205,82 +778,22 @@
"fraction.js": ["fraction.js@5.3.4", "", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="],
- "from2": ["from2@2.3.0", "", { "dependencies": { "inherits": "^2.0.1", "readable-stream": "^2.0.0" } }, "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g=="],
-
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
"gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="],
- "geojson-vt": ["geojson-vt@3.2.1", "", {}, "sha512-EvGQQi/zPrDA6zr6BnJD/YhwAkBP8nnJ9emh3EnHQKVMfg/MRVtPbMYdgVy/IaEmn4UfagD2a6fafPDL5hbtwg=="],
-
- "get-canvas-context": ["get-canvas-context@1.0.2", "", {}, "sha512-LnpfLf/TNzr9zVOGiIY6aKCz8EKuXmlYNV7CM2pUjBa/B+c2I15tS7KLySep75+FuerJdmArvJLcsAXWEy2H0A=="],
-
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
"get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="],
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
- "get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="],
-
- "github-slugger": ["github-slugger@2.0.0", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="],
-
- "gl-mat4": ["gl-mat4@1.2.0", "", {}, "sha512-sT5C0pwB1/e9G9AvAoLsoaJtbMGjfd/jfxo8jMCKqYYEnjZuFvqV5rehqar0538EmssjdDeiEWnKyBSTw7quoA=="],
-
- "gl-matrix": ["gl-matrix@3.4.4", "", {}, "sha512-latSnyDNt/8zYUB6VIJ6PCh2jBjJX6gnDsoCZ7LyW7GkqrD51EWwa9qCoGixj8YqBtETQK/xY7OmpTF8xz1DdQ=="],
-
- "gl-text": ["gl-text@1.4.0", "", { "dependencies": { "bit-twiddle": "^1.0.2", "color-normalize": "^1.5.0", "css-font": "^1.2.0", "detect-kerning": "^2.1.2", "es6-weak-map": "^2.0.3", "flatten-vertex-data": "^1.0.2", "font-atlas": "^2.1.0", "font-measure": "^1.2.2", "gl-util": "^3.1.2", "is-plain-obj": "^1.1.0", "object-assign": "^4.1.1", "parse-rect": "^1.2.0", "parse-unit": "^1.0.1", "pick-by-alias": "^1.2.0", "regl": "^2.0.0", "to-px": "^1.0.1", "typedarray-pool": "^1.1.0" } }, "sha512-o47+XBqLCj1efmuNyCHt7/UEJmB9l66ql7pnobD6p+sgmBUdzfMZXIF0zD2+KRfpd99DJN+QXdvTFAGCKCVSmQ=="],
-
- "gl-util": ["gl-util@3.1.3", "", { "dependencies": { "is-browser": "^2.0.1", "is-firefox": "^1.0.3", "is-plain-obj": "^1.1.0", "number-is-integer": "^1.0.1", "object-assign": "^4.1.0", "pick-by-alias": "^1.2.0", "weak-map": "^1.0.5" } }, "sha512-dvRTggw5MSkJnCbh74jZzSoTOGnVYK+Bt+Ckqm39CVcl6+zSsxqWk4lr5NKhkqXHL6qvZAU9h17ZF8mIskY9mA=="],
-
- "global-prefix": ["global-prefix@4.0.0", "", { "dependencies": { "ini": "^4.1.3", "kind-of": "^6.0.3", "which": "^4.0.0" } }, "sha512-w0Uf9Y9/nyHinEk5vMJKRie+wa4kR5hmDbEhGGds/kG1PwGLLHKRoNMeJOyCQjjBkANlnScqgzcFwGHgmgLkVA=="],
-
- "glsl-inject-defines": ["glsl-inject-defines@1.0.3", "", { "dependencies": { "glsl-token-inject-block": "^1.0.0", "glsl-token-string": "^1.0.1", "glsl-tokenizer": "^2.0.2" } }, "sha512-W49jIhuDtF6w+7wCMcClk27a2hq8znvHtlGnrYkSWEr8tHe9eA2dcnohlcAmxLYBSpSSdzOkRdyPTrx9fw49+A=="],
-
- "glsl-resolve": ["glsl-resolve@0.0.1", "", { "dependencies": { "resolve": "^0.6.1", "xtend": "^2.1.2" } }, "sha512-xxFNsfnhZTK9NBhzJjSBGX6IOqYpvBHxxmo+4vapiljyGNCY0Bekzn0firQkQrazK59c1hYxMDxYS8MDlhw4gA=="],
-
- "glsl-token-assignments": ["glsl-token-assignments@2.0.2", "", {}, "sha512-OwXrxixCyHzzA0U2g4btSNAyB2Dx8XrztY5aVUCjRSh4/D0WoJn8Qdps7Xub3sz6zE73W3szLrmWtQ7QMpeHEQ=="],
-
- "glsl-token-defines": ["glsl-token-defines@1.0.0", "", { "dependencies": { "glsl-tokenizer": "^2.0.0" } }, "sha512-Vb5QMVeLjmOwvvOJuPNg3vnRlffscq2/qvIuTpMzuO/7s5kT+63iL6Dfo2FYLWbzuiycWpbC0/KV0biqFwHxaQ=="],
-
- "glsl-token-depth": ["glsl-token-depth@1.1.2", "", {}, "sha512-eQnIBLc7vFf8axF9aoi/xW37LSWd2hCQr/3sZui8aBJnksq9C7zMeUYHVJWMhFzXrBU7fgIqni4EhXVW4/krpg=="],
-
- "glsl-token-descope": ["glsl-token-descope@1.0.2", "", { "dependencies": { "glsl-token-assignments": "^2.0.0", "glsl-token-depth": "^1.1.0", "glsl-token-properties": "^1.0.0", "glsl-token-scope": "^1.1.0" } }, "sha512-kS2PTWkvi/YOeicVjXGgX5j7+8N7e56srNDEHDTVZ1dcESmbmpmgrnpjPcjxJjMxh56mSXYoFdZqb90gXkGjQw=="],
-
- "glsl-token-inject-block": ["glsl-token-inject-block@1.1.0", "", {}, "sha512-q/m+ukdUBuHCOtLhSr0uFb/qYQr4/oKrPSdIK2C4TD+qLaJvqM9wfXIF/OOBjuSA3pUoYHurVRNao6LTVVUPWA=="],
-
- "glsl-token-properties": ["glsl-token-properties@1.0.1", "", {}, "sha512-dSeW1cOIzbuUoYH0y+nxzwK9S9O3wsjttkq5ij9ZGw0OS41BirKJzzH48VLm8qLg+au6b0sINxGC0IrGwtQUcA=="],
-
- "glsl-token-scope": ["glsl-token-scope@1.1.2", "", {}, "sha512-YKyOMk1B/tz9BwYUdfDoHvMIYTGtVv2vbDSLh94PT4+f87z21FVdou1KNKgF+nECBTo0fJ20dpm0B1vZB1Q03A=="],
-
- "glsl-token-string": ["glsl-token-string@1.0.1", "", {}, "sha512-1mtQ47Uxd47wrovl+T6RshKGkRRCYWhnELmkEcUAPALWGTFe2XZpH3r45XAwL2B6v+l0KNsCnoaZCSnhzKEksg=="],
-
- "glsl-token-whitespace-trim": ["glsl-token-whitespace-trim@1.0.0", "", {}, "sha512-ZJtsPut/aDaUdLUNtmBYhaCmhIjpKNg7IgZSfX5wFReMc2vnj8zok+gB/3Quqs0TsBSX/fGnqUUYZDqyuc2xLQ=="],
-
- "glsl-tokenizer": ["glsl-tokenizer@2.1.5", "", { "dependencies": { "through2": "^0.6.3" } }, "sha512-XSZEJ/i4dmz3Pmbnpsy3cKh7cotvFlBiZnDOwnj/05EwNp2XrhQ4XKJxT7/pDt4kp4YcpRSKz8eTV7S+mwV6MA=="],
-
- "glslify": ["glslify@7.1.1", "", { "dependencies": { "bl": "^2.2.1", "concat-stream": "^1.5.2", "duplexify": "^3.4.5", "falafel": "^2.1.0", "from2": "^2.3.0", "glsl-resolve": "0.0.1", "glsl-token-whitespace-trim": "^1.0.0", "glslify-bundle": "^5.0.0", "glslify-deps": "^1.2.5", "minimist": "^1.2.5", "resolve": "^1.1.5", "stack-trace": "0.0.9", "static-eval": "^2.0.5", "through2": "^2.0.1", "xtend": "^4.0.0" }, "bin": { "glslify": "bin.js" } }, "sha512-bud98CJ6kGZcP9Yxcsi7Iz647wuDz3oN+IZsjCRi5X1PI7t/xPKeL0mOwXJjo+CRZMqvq0CkSJiywCcY7kVYog=="],
-
- "glslify-bundle": ["glslify-bundle@5.1.1", "", { "dependencies": { "glsl-inject-defines": "^1.0.1", "glsl-token-defines": "^1.0.0", "glsl-token-depth": "^1.1.1", "glsl-token-descope": "^1.0.2", "glsl-token-scope": "^1.1.1", "glsl-token-string": "^1.0.1", "glsl-token-whitespace-trim": "^1.0.0", "glsl-tokenizer": "^2.0.2", "murmurhash-js": "^1.0.0", "shallow-copy": "0.0.1" } }, "sha512-plaAOQPv62M1r3OsWf2UbjN0hUYAB7Aph5bfH58VxJZJhloRNbxOL9tl/7H71K7OLJoSJ2ZqWOKk3ttQ6wy24A=="],
-
- "glslify-deps": ["glslify-deps@1.3.2", "", { "dependencies": { "@choojs/findup": "^0.2.0", "events": "^3.2.0", "glsl-resolve": "0.0.1", "glsl-tokenizer": "^2.0.0", "graceful-fs": "^4.1.2", "inherits": "^2.0.1", "map-limit": "0.0.1", "resolve": "^1.0.0" } }, "sha512-7S7IkHWygJRjcawveXQjRXLO2FTjijPDYC7QfZyAQanY+yGLCFHYnPtsGT9bdyHiwPTw/5a1m1M9hamT2aBpag=="],
-
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
- "grid-index": ["grid-index@1.1.0", "", {}, "sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA=="],
-
- "gridjs": ["gridjs@6.2.0", "", { "dependencies": { "preact": "^10.11.3" } }, "sha512-EAGGfHjyEXWh12Txs6DjTGnWTo226wbowtMrLI+yNZQaJpvs0m7yDcyM7r+D4RA7rZQVj/cfmwEaRz/rlxg8LA=="],
-
- "gridjs-react": ["gridjs-react@6.1.1", "", { "peerDependencies": { "gridjs": "^6.1.0", "react": ">=18.x" } }, "sha512-MOu/t4naXR6vIeLdcE+vUFtyUcYuRn75sThJnShlMntQcrVCSKSBApmNWU1MzLuNrqOH5sbDImqk7pIXYumKxA=="],
-
- "has-hover": ["has-hover@1.0.1", "", { "dependencies": { "is-browser": "^2.0.1" } }, "sha512-0G6w7LnlcpyDzpeGUTuT0CEw05+QlMuGVk1IHNAlHrGJITGodjZu3x8BNDUMfKJSZXNB2ZAclqc1bvrd+uUpfg=="],
-
- "has-passive-events": ["has-passive-events@1.0.0", "", { "dependencies": { "is-browser": "^2.0.1" } }, "sha512-2vSj6IeIsgvsRMyeQ0JaCX5Q3lX4zMn5HpoVc7MEhQ6pv8Iq9rsXjsp+E5ZwaT7T0xhMT0KmU8gtt1EFVdbJiw=="],
-
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
"has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
@@ -1297,8 +810,6 @@
"hast-util-has-property": ["hast-util-has-property@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA=="],
- "hast-util-heading-rank": ["hast-util-heading-rank@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA=="],
-
"hast-util-interactive": ["hast-util-interactive@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-has-property": "^3.0.0" } }, "sha512-9VFa3kP6AT40BNYcPmn3jpsG+1KPDF0rUFCrFVQDUsuUXZ3YLODm8UGV0tmYzFpcOIQXTAOi2ccS3ywlj2dQTA=="],
"hast-util-is-element": ["hast-util-is-element@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g=="],
@@ -1307,8 +818,6 @@
"hast-util-raw": ["hast-util-raw@9.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "@ungap/structured-clone": "^1.0.0", "hast-util-from-parse5": "^8.0.0", "hast-util-to-parse5": "^8.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "parse5": "^7.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" } }, "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw=="],
- "hast-util-sanitize": ["hast-util-sanitize@4.1.0", "", { "dependencies": { "@types/hast": "^2.0.0" } }, "sha512-Hd9tU0ltknMGRDv+d6Ro/4XKzBqQnP/EZrpiTbpFYfXv/uOhWeKc+2uajcbEvAEH98VZd7eII2PiXm13RihnLw=="],
-
"hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="],
"hast-util-to-jsx-runtime": ["hast-util-to-jsx-runtime@2.3.6", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "vfile-message": "^4.0.0" } }, "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg=="],
@@ -1333,8 +842,6 @@
"hoist-non-react-statics": ["hoist-non-react-statics@3.3.2", "", { "dependencies": { "react-is": "^16.7.0" } }, "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw=="],
- "html-element-attributes": ["html-element-attributes@1.3.1", "", {}, "sha512-UrRKgp5sQmRnDy4TEwAUsu14XBUlzKB8U3hjIYDjcZ3Hbp86Jtftzxfgrv6E/ii/h78tsaZwAnAE8HwnHr0dPA=="],
-
"html-entities": ["html-entities@2.6.0", "", {}, "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ=="],
"html-url-attributes": ["html-url-attributes@3.0.1", "", {}, "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ=="],
@@ -1345,69 +852,31 @@
"humps": ["humps@2.0.1", "", {}, "sha512-E0eIbrFWUhwfXJmsbdjRQFQPrl5pTEoKlz163j1mTqqUnU9PgR4AgB8AIITzuB3vLBdxZXyZ9TDIrwB2OASz4g=="],
- "iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="],
-
- "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
-
"immediate": ["immediate@3.0.6", "", {}, "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="],
- "immer": ["immer@10.2.0", "", {}, "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw=="],
-
"import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
"imsc": ["imsc@1.1.5", "", { "dependencies": { "sax": "1.2.1" } }, "sha512-V8je+CGkcvGhgl2C1GlhqFFiUOIEdwXbXLiu1Fcubvvbo+g9inauqT3l0pNYXGoLPBj3jxtZz9t+wCopMkwadQ=="],
- "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
-
- "ini": ["ini@4.1.3", "", {}, "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg=="],
-
"inline-style-parser": ["inline-style-parser@0.2.7", "", {}, "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="],
- "internmap": ["internmap@2.0.3", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="],
-
"is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="],
"is-alphanumerical": ["is-alphanumerical@2.0.1", "", { "dependencies": { "is-alphabetical": "^2.0.0", "is-decimal": "^2.0.0" } }, "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw=="],
"is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="],
- "is-browser": ["is-browser@2.1.0", "", {}, "sha512-F5rTJxDQ2sW81fcfOR1GnCXT6sVJC104fCyfj+mjpwNEwaPYSn5fte5jiHmBg3DHsIoL/l8Kvw5VN5SsTRcRFQ=="],
-
- "is-buffer": ["is-buffer@2.0.5", "", {}, "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ=="],
-
"is-core-module": ["is-core-module@2.16.2", "", { "dependencies": { "hasown": "^2.0.3" } }, "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA=="],
"is-decimal": ["is-decimal@2.0.1", "", {}, "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A=="],
- "is-extendable": ["is-extendable@1.0.1", "", { "dependencies": { "is-plain-object": "^2.0.4" } }, "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA=="],
-
- "is-finite": ["is-finite@1.1.0", "", {}, "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w=="],
-
- "is-firefox": ["is-firefox@1.0.3", "", {}, "sha512-6Q9ITjvWIm0Xdqv+5U12wgOKEM2KoBw4Y926m0OFkvlCxnbG94HKAsVz8w3fWcfAS5YA2fJORXX1dLrkprCCxA=="],
-
"is-hexadecimal": ["is-hexadecimal@2.0.1", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="],
- "is-mobile": ["is-mobile@4.0.0", "", {}, "sha512-mlcHZA84t1qLSuWkt2v0I2l61PYdyQDt4aG1mLIXF5FDMm4+haBCxCPYSr/uwqQNRk1MiTizn0ypEuRAOLRAew=="],
-
- "is-obj": ["is-obj@1.0.1", "", {}, "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg=="],
-
- "is-plain-obj": ["is-plain-obj@1.1.0", "", {}, "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg=="],
-
- "is-plain-object": ["is-plain-object@2.0.4", "", { "dependencies": { "isobject": "^3.0.1" } }, "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og=="],
-
- "is-string-blank": ["is-string-blank@1.0.1", "", {}, "sha512-9H+ZBCVs3L9OYqv8nuUAzpcT9OTgMD1yAWrG7ihlnibdkbtB850heAmYWxHuXc4CHy4lKeK69tN+ny1K7gBIrw=="],
-
- "is-svg-path": ["is-svg-path@1.0.2", "", {}, "sha512-Lj4vePmqpPR1ZnRctHv8ltSh1OrSxHkhUkd7wi+VQdcdP15/KvQFyk7LhNuM7ZW0EVbJz8kZLVmL9quLrfq4Kg=="],
+ "is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
"is-what": ["is-what@4.1.16", "", {}, "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A=="],
- "isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="],
-
- "isbot": ["isbot@5.1.39", "", {}, "sha512-obH0yYahGXdzNxo+djmHhBYThUKDkz565cxkIlt2L9hXfv1NlaLKoDBHo6KxXsYrIXx2RK3x5vY36CfZcobxEw=="],
-
- "isexe": ["isexe@3.1.5", "", {}, "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w=="],
-
- "isobject": ["isobject@3.0.1", "", {}, "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg=="],
+ "isbot": ["isbot@5.1.40", "", {}, "sha512-yNeeynhhtIVRBk12tBV4eHNxwB42HzR4Q3Ea7vCOiJhImGaAIdIMrbJtacQlBizGLjUPw+akkFI5Dn9T70XoVQ=="],
"jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="],
@@ -1417,18 +886,10 @@
"json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="],
- "json-stringify-pretty-compact": ["json-stringify-pretty-compact@4.0.0", "", {}, "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q=="],
-
"json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="],
"katex": ["katex@0.16.45", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-pQpZbdBu7wCTmQUh7ufPmLr0pFoObnGUoL/yhtwJDgmmQpbkg/0HSVti25Fu4rmd1oCR6NGWe9vqTWuWv3GcNA=="],
- "kdbush": ["kdbush@4.0.2", "", {}, "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA=="],
-
- "kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="],
-
- "leaflet": ["leaflet@1.9.4", "", {}, "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA=="],
-
"lie": ["lie@3.1.1", "", { "dependencies": { "immediate": "~3.0.5" } }, "sha512-RiNhHysUjhrDQntfYSfY4MU24coXXdEOgw9WGcKHNeEwffDYbF//u87M1EWaMGzuFoSbqW0C9C6lEEhDOAswfw=="],
"lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="],
@@ -1459,14 +920,8 @@
"localforage": ["localforage@1.10.0", "", { "dependencies": { "lie": "3.1.1" } }, "sha512-14/H1aX7hzBBmmh7sGPd+AOMkkIrHM3Z1PAyGgZigA1H1p5O5ANnMyWzvpAETtG68/dC4pC0ncy3+PPGzXZHPg=="],
- "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
-
"lodash": ["lodash@4.18.1", "", {}, "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q=="],
- "lodash.debounce": ["lodash.debounce@4.0.8", "", {}, "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="],
-
- "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
-
"longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="],
"loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
@@ -1475,24 +930,16 @@
"lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
- "lucide-react": ["lucide-react@1.8.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-WuvlsjngSk7TnTBJ1hsCy3ql9V9VOdcPkd3PKcSmM34vJD8KG6molxz7m7zbYFgICwsanQWmJ13JlYs4Zp7Arw=="],
+ "lucide-react": ["lucide-react@1.14.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-+1mdWcfSJVUsaTIjN9zoezmUhfXo5l0vP7ekBMPo3jcS/aIkxHnXqAPsByszMZx/Y8oQBRJxJx5xg+RH3urzxA=="],
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
- "map-limit": ["map-limit@0.0.1", "", { "dependencies": { "once": "~1.3.0" } }, "sha512-pJpcfLPnIF/Sk3taPW21G/RQsEEirGaFpCW3oXRwH9dnFHPHNGjNyvh++rdmC2fNqEaTw2MhYJraoJWAHx8kEg=="],
-
- "mapbox-gl": ["mapbox-gl@1.13.3", "", { "dependencies": { "@mapbox/geojson-rewind": "^0.5.2", "@mapbox/geojson-types": "^1.0.2", "@mapbox/jsonlint-lines-primitives": "^2.0.2", "@mapbox/mapbox-gl-supported": "^1.5.0", "@mapbox/point-geometry": "^0.1.0", "@mapbox/tiny-sdf": "^1.1.1", "@mapbox/unitbezier": "^0.0.0", "@mapbox/vector-tile": "^1.3.1", "@mapbox/whoots-js": "^3.1.0", "csscolorparser": "~1.0.3", "earcut": "^2.2.2", "geojson-vt": "^3.2.1", "gl-matrix": "^3.2.1", "grid-index": "^1.1.0", "murmurhash-js": "^1.0.0", "pbf": "^3.2.1", "potpack": "^1.0.1", "quickselect": "^2.0.0", "rw": "^1.3.3", "supercluster": "^7.1.0", "tinyqueue": "^2.0.3", "vt-pbf": "^3.1.1" } }, "sha512-p8lJFEiqmEQlyv+DQxFAOG/XPWN0Wp7j/Psq93Zywz7qt9CcUKFYDBOoOEKzqe6gudHVJY8/Bhqw6VDpX2lSBg=="],
-
- "maplibre-gl": ["maplibre-gl@4.7.1", "", { "dependencies": { "@mapbox/geojson-rewind": "^0.5.2", "@mapbox/jsonlint-lines-primitives": "^2.0.2", "@mapbox/point-geometry": "^0.1.0", "@mapbox/tiny-sdf": "^2.0.6", "@mapbox/unitbezier": "^0.0.1", "@mapbox/vector-tile": "^1.3.1", "@mapbox/whoots-js": "^3.1.0", "@maplibre/maplibre-gl-style-spec": "^20.3.1", "@types/geojson": "^7946.0.14", "@types/geojson-vt": "3.2.5", "@types/mapbox__point-geometry": "^0.1.4", "@types/mapbox__vector-tile": "^1.3.4", "@types/pbf": "^3.0.5", "@types/supercluster": "^7.1.3", "earcut": "^3.0.0", "geojson-vt": "^4.0.2", "gl-matrix": "^3.4.3", "global-prefix": "^4.0.0", "kdbush": "^4.0.2", "murmurhash-js": "^1.0.0", "pbf": "^3.3.0", "potpack": "^2.0.0", "quickselect": "^3.0.0", "supercluster": "^8.0.1", "tinyqueue": "^3.0.0", "vt-pbf": "^3.1.3" } }, "sha512-lgL7XpIwsgICiL82ITplfS7IGwrB1OJIw/pCvprDp2dhmSSEBgmPzYRvwYYYvJGJD7fxUv1Tvpih4nZ6VrLuaA=="],
-
"markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="],
"marked": ["marked@15.0.12", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA=="],
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
- "math-log2": ["math-log2@1.0.1", "", {}, "sha512-9W0yGtkaMAkf74XGYVy4Dqw3YUMnTNB2eeiw9aQbUl4A3KmuCEHTt2DgAB07ENzOYAjsYSAYufkAq0Zd+jU7zA=="],
-
"mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="],
"mdast-util-from-markdown": ["mdast-util-from-markdown@2.0.3", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "mdast-util-to-string": "^4.0.0", "micromark": "^4.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-decode-string": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q=="],
@@ -1533,8 +980,6 @@
"merge-anything": ["merge-anything@5.1.7", "", { "dependencies": { "is-what": "^4.1.8" } }, "sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ=="],
- "mergician": ["mergician@2.0.2", "", {}, "sha512-1GDF4LuMcc7UpE7XitfSm822yDtTxLoOVB+9QFnb3o/+zoMAVKxM67UjvmEXOA7FQnz9209K0ZyHAoD+5Ibe4A=="],
-
"micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="],
"micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="],
@@ -1599,98 +1044,48 @@
"minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="],
- "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
-
"moment": ["moment@2.30.1", "", {}, "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how=="],
- "moment-timezone": ["moment-timezone@0.6.2", "", { "dependencies": { "moment": "^2.29.4" } }, "sha512-lDsQv8FoGdBUdf0+TjGsq2orxKuXdwFlQ6Zw6TX3xIcTwTfEpCLyKqvEauvCHJ8iu3KBV8+uPhlv70YsNGdUBQ=="],
-
- "mouse-change": ["mouse-change@1.4.0", "", { "dependencies": { "mouse-event": "^1.0.0" } }, "sha512-vpN0s+zLL2ykyyUDh+fayu9Xkor5v/zRD9jhSqjRS1cJTGS0+oakVZzNm5n19JvvEj0you+MXlYTpNxUDQUjkQ=="],
-
- "mouse-event": ["mouse-event@1.0.5", "", {}, "sha512-ItUxtL2IkeSKSp9cyaX2JLUuKk2uMoxBg4bbOWVd29+CskYJR9BGsUqtXenNzKbnDshvupjUewDIYVrOB6NmGw=="],
-
- "mouse-event-offset": ["mouse-event-offset@3.0.2", "", {}, "sha512-s9sqOs5B1Ykox3Xo8b3Ss2IQju4UwlW6LSR+Q5FXWpprJ5fzMLefIIItr3PH8RwzfGy6gxs/4GAmiNuZScE25w=="],
-
- "mouse-wheel": ["mouse-wheel@1.2.0", "", { "dependencies": { "right-now": "^1.0.0", "signum": "^1.0.0", "to-px": "^1.0.1" } }, "sha512-+OfYBiUOCTWcTECES49neZwL5AoGkXE+lFjIvzwNCnYRlso+EnfvovcBxGoyQ0yQt806eSPjS675K0EwWknXmw=="],
-
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
- "murmurhash-js": ["murmurhash-js@1.0.0", "", {}, "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw=="],
-
"mux-embed": ["mux-embed@5.18.1", "", {}, "sha512-ePsHjiEKY+FgrSBiMmaF+LOtTQSSBWv/1zqpREQFN96JE93xlsArT/MEi30yKOE06MgjOlL70YI750molu3y7g=="],
"nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="],
"native-promise-only": ["native-promise-only@0.8.1", "", {}, "sha512-zkVhZUA3y8mbz652WrL5x0fB0ehrBkulWT3TomAQ9iDtyXZvzKeEA6GPxAItBYeNYl5yngKRX612qHOhvMkDeg=="],
- "needle": ["needle@2.9.1", "", { "dependencies": { "debug": "^3.2.6", "iconv-lite": "^0.4.4", "sax": "^1.2.4" }, "bin": { "needle": "./bin/needle" } }, "sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ=="],
-
- "next-tick": ["next-tick@1.1.0", "", {}, "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="],
-
"node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
- "node-emoji": ["node-emoji@2.2.0", "", { "dependencies": { "@sindresorhus/is": "^4.6.0", "char-regex": "^1.0.2", "emojilib": "^2.4.0", "skin-tone": "^2.0.0" } }, "sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw=="],
-
"node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
"node-releases": ["node-releases@2.0.44", "", {}, "sha512-5WUyunoPMsvvEhS8AxHtRzP+oA8UCkJ7YRxatWKjngndhDGLiqEVAQKWjFAiAiuL8zMRGzGSJxFnLetoa43qGQ=="],
- "normalize-svg-path": ["normalize-svg-path@0.1.0", "", {}, "sha512-1/kmYej2iedi5+ROxkRESL/pI02pkg0OBnaR4hJkSIX6+ORzepwbuUXfrdZaPjysTsJInj0Rj5NuX027+dMBvA=="],
-
- "number-is-integer": ["number-is-integer@1.0.1", "", { "dependencies": { "is-finite": "^1.0.1" } }, "sha512-Dq3iuiFBkrbmuQjGFFF3zckXNCQoSD37/SdSbgcBailUx6knDvDwb5CympBgcoWHy36sfS12u74MHYkXyHq6bg=="],
-
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
- "object.omit": ["object.omit@3.0.0", "", { "dependencies": { "is-extendable": "^1.0.0" } }, "sha512-EO+BCv6LJfu+gBIF3ggLicFebFLN5zqzz/WWJlMFfkMyGth+oBkhxzDl0wx2W4GkLzuQs/FsSkXZb2IMWQqmBQ=="],
-
- "on-change": ["on-change@4.0.2", "", {}, "sha512-cMtCyuJmTx/bg2HCpHo3ZLeF7FZnBOapLqZHr2AlLeJ5Ul0Zu2mUJJz051Fdwu/Et2YW04ZD+TtU+gVy0ACNCA=="],
-
- "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
-
"oniguruma-parser": ["oniguruma-parser@0.12.2", "", {}, "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw=="],
"oniguruma-to-es": ["oniguruma-to-es@4.3.6", "", { "dependencies": { "oniguruma-parser": "^0.12.2", "regex": "^6.1.0", "regex-recursion": "^6.0.2" } }, "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA=="],
"openai": ["openai@4.78.1", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" }, "peerDependencies": { "zod": "^3.23.8" }, "optionalPeers": ["zod"], "bin": { "openai": "bin/cli" } }, "sha512-drt0lHZBd2lMyORckOXFPQTmnGLWSLt8VK0W9BhOKWpMFBEoHMoz5gxMPmVq5icp+sOrsbMnsmZTVHUlKvD1Ow=="],
- "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
-
- "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
-
"p-map": ["p-map@7.0.4", "", {}, "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ=="],
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
- "parenthesis": ["parenthesis@3.1.8", "", {}, "sha512-KF/U8tk54BgQewkJPvB4s/US3VQY68BRDpH638+7O/n58TpnwiwnOtGIOsT2/i+M78s61BBpeC83STB88d8sqw=="],
-
"parse-entities": ["parse-entities@4.0.2", "", { "dependencies": { "@types/unist": "^2.0.0", "character-entities-legacy": "^3.0.0", "character-reference-invalid": "^2.0.0", "decode-named-character-reference": "^1.0.0", "is-alphanumerical": "^2.0.0", "is-decimal": "^2.0.0", "is-hexadecimal": "^2.0.0" } }, "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw=="],
"parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="],
- "parse-rect": ["parse-rect@1.2.0", "", { "dependencies": { "pick-by-alias": "^1.2.0" } }, "sha512-4QZ6KYbnE6RTwg9E0HpLchUM9EZt6DnDxajFZZDSV4p/12ZJEvPO702DZpGvRYEPo00yKDys7jASi+/w7aO8LA=="],
-
- "parse-svg-path": ["parse-svg-path@0.1.2", "", {}, "sha512-JyPSBnkTJ0AI8GGJLfMXvKq42cj5c006fnLz6fXy6zfoVjJizi8BNTpu8on8ziI1cKy9d9DGNuY17Ce7wuejpQ=="],
-
- "parse-unit": ["parse-unit@1.0.1", "", {}, "sha512-hrqldJHokR3Qj88EIlV/kAyAi/G5R2+R56TBANxNMy0uPlYcttx0jnMW6Yx5KsKPSbC3KddM/7qQm3+0wEXKxg=="],
-
"parse5": ["parse5@7.3.0", "", { "dependencies": { "entities": "^6.0.0" } }, "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw=="],
"path-browserify": ["path-browserify@1.0.1", "", {}, "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="],
- "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="],
-
"path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
"path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="],
"pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="],
- "pbf": ["pbf@3.3.0", "", { "dependencies": { "ieee754": "^1.1.12", "resolve-protobuf-schema": "^2.1.0" }, "bin": { "pbf": "bin/pbf" } }, "sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q=="],
-
- "performance-now": ["performance-now@2.1.0", "", {}, "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="],
-
- "pick-by-alias": ["pick-by-alias@1.2.0", "", {}, "sha512-ESj2+eBxhGrcA1azgHs7lARG5+5iLakc/6nlfbpjcLl00HuuUOIuORhYXN4D1HfvMSKuVtFQjAlnwi1JHEeDIw=="],
-
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
"picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="],
@@ -1701,13 +1096,7 @@
"player.style": ["player.style@0.3.4", "", { "dependencies": { "media-chrome": "~4.19.0" } }, "sha512-5O9bkbq0APQIkhptyZwp0gUgheCeImGPFo4hvPF2M5xBmgsUYzsUPHCfMye2xyeRE1zvQBKtziaC3jPgnHE1tw=="],
- "plotly.js": ["plotly.js@3.5.0", "", { "dependencies": { "@plotly/d3": "3.8.2", "@plotly/d3-sankey": "0.7.2", "@plotly/d3-sankey-circular": "0.33.1", "@plotly/mapbox-gl": "1.13.4", "@plotly/regl": "^2.1.2", "@turf/area": "^7.1.0", "@turf/bbox": "^7.1.0", "@turf/centroid": "^7.1.0", "base64-arraybuffer": "^1.0.2", "canvas-fit": "^1.5.0", "color-alpha": "1.0.4", "color-normalize": "1.5.0", "color-parse": "2.0.0", "color-rgba": "3.0.0", "country-regex": "^1.1.0", "d3-force": "^1.2.1", "d3-format": "^1.4.5", "d3-geo": "^1.12.1", "d3-geo-projection": "^2.9.0", "d3-hierarchy": "^1.1.9", "d3-interpolate": "^3.0.1", "d3-time": "^1.1.0", "d3-time-format": "^2.2.3", "fast-isnumeric": "^1.1.4", "gl-mat4": "^1.2.0", "gl-text": "^1.4.0", "has-hover": "^1.0.1", "has-passive-events": "^1.0.0", "is-mobile": "^4.0.0", "maplibre-gl": "^4.7.1", "mouse-change": "^1.4.0", "mouse-event-offset": "^3.0.2", "mouse-wheel": "^1.2.0", "native-promise-only": "^0.8.1", "parse-svg-path": "^0.1.2", "point-in-polygon": "^1.1.0", "polybooljs": "^1.2.2", "probe-image-size": "^7.2.3", "regl-error2d": "^2.0.12", "regl-line2d": "^3.1.3", "regl-scatter2d": "^3.3.1", "regl-splom": "^1.0.14", "strongly-connected-components": "^1.0.1", "superscript-text": "^1.0.0", "svg-path-sdf": "^1.1.3", "tinycolor2": "^1.4.2", "to-px": "1.0.1", "topojson-client": "^3.1.0", "webgl-context": "^2.2.0", "world-calendars": "^1.0.4" } }, "sha512-a3AYQIMG7OdZmrJ/fJ65HSt3g1l5qDeludKqjjafU1dh5E+fwqDhsEBndW7VCYwjlducCfN6KtPdWdiWFcoBWw=="],
-
- "point-in-polygon": ["point-in-polygon@1.1.0", "", {}, "sha512-3ojrFwjnnw8Q9242TzgXuTD+eKiutbzyslcq1ydfu82Db2y+Ogbmyrkpv0Hgj31qwT3lbS9+QAAO/pIQM35XRw=="],
-
- "polybooljs": ["polybooljs@1.2.2", "", {}, "sha512-ziHW/02J0XuNuUtmidBc6GXE8YohYydp3DWPWXYsd7O721TjcmN+k6ezjdwkDqep+gnWnFY+yqZHvzElra2oCg=="],
-
- "postcss": ["postcss@8.5.10", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ=="],
+ "postcss": ["postcss@8.5.14", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg=="],
"postcss-import": ["postcss-import@16.1.1", "", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-2xVS1NCZAfjtVdvXiyegxzJ447GyqCeEI5V7ApgQVOWnros1p5lGNovJNapwPpMombyFBfqDwt7AD3n2l0KOfQ=="],
@@ -1715,49 +1104,23 @@
"postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
- "potpack": ["potpack@1.0.2", "", {}, "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ=="],
-
- "preact": ["preact@10.29.1", "", {}, "sha512-gQCLc/vWroE8lIpleXtdJhTFDogTdZG9AjMUpVkDf2iTCNwYNWA+u16dL41TqUDJO4gm2IgrcMv3uTpjd4Pwmg=="],
-
"prettier": ["prettier@3.8.3", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw=="],
"prism-react-renderer": ["prism-react-renderer@2.4.1", "", { "dependencies": { "@types/prismjs": "^1.26.0", "clsx": "^2.0.0" }, "peerDependencies": { "react": ">=16.0.0" } }, "sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig=="],
"prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="],
- "probe-image-size": ["probe-image-size@7.3.0", "", { "dependencies": { "lodash.merge": "^4.6.2", "needle": "^2.5.2", "stream-parser": "~0.3.1" } }, "sha512-7CaDeBwiAbh6ohXsvLbAZhO7wzsZAmaevfxe39qvCwRh8LyaZfDlBGGLU1CCTgrTLtCOdwBBhjOrIHaIIimHfQ=="],
-
- "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="],
-
"prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
"property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="],
- "protocol-buffers-schema": ["protocol-buffers-schema@3.6.1", "", {}, "sha512-VG2K63Igkiv9p76tk1lilczEK1cT+kCjKtkdhw1dQZV3k3IXJbd3o6Ho8b9zJZaHSnT2hKe4I+ObmX9w6m5SmQ=="],
-
"proxy-compare": ["proxy-compare@3.0.1", "", {}, "sha512-V9plBAt3qjMlS1+nC8771KNf6oJ12gExvaxnNzN/9yVRLdTv/lc+oJlnSzrdYDAvBfTStPCoiaCOTmTs0adv7Q=="],
- "quickselect": ["quickselect@2.0.0", "", {}, "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw=="],
-
"radix-ui": ["radix-ui@1.4.3", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-accessible-icon": "1.1.7", "@radix-ui/react-accordion": "1.2.12", "@radix-ui/react-alert-dialog": "1.1.15", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-aspect-ratio": "1.1.7", "@radix-ui/react-avatar": "1.1.10", "@radix-ui/react-checkbox": "1.3.3", "@radix-ui/react-collapsible": "1.1.12", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-context-menu": "2.2.16", "@radix-ui/react-dialog": "1.1.15", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-dropdown-menu": "2.1.16", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-form": "0.1.8", "@radix-ui/react-hover-card": "1.1.15", "@radix-ui/react-label": "2.1.7", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-menubar": "1.1.16", "@radix-ui/react-navigation-menu": "1.2.14", "@radix-ui/react-one-time-password-field": "0.1.8", "@radix-ui/react-password-toggle-field": "0.1.3", "@radix-ui/react-popover": "1.1.15", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-progress": "1.1.7", "@radix-ui/react-radio-group": "1.3.8", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-scroll-area": "1.2.10", "@radix-ui/react-select": "2.2.6", "@radix-ui/react-separator": "1.1.7", "@radix-ui/react-slider": "1.3.6", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-switch": "1.2.6", "@radix-ui/react-tabs": "1.1.13", "@radix-ui/react-toast": "1.2.15", "@radix-ui/react-toggle": "1.1.10", "@radix-ui/react-toggle-group": "1.1.11", "@radix-ui/react-toolbar": "1.1.11", "@radix-ui/react-tooltip": "1.2.8", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-effect-event": "0.0.2", "@radix-ui/react-use-escape-keydown": "1.1.1", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-aWizCQiyeAenIdUbqEpXgRA1ya65P13NKn/W8rWkcN0OPkRDxdBVLWnIEDsS2RpwCK2nobI7oMUSmexzTDyAmA=="],
- "raf": ["raf@3.4.1", "", { "dependencies": { "performance-now": "^2.1.0" } }, "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA=="],
-
- "react": ["react@19.2.5", "", {}, "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA=="],
-
- "react-colorful": ["react-colorful@5.7.0", "", { "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-fuesYIemttah97XmsIHmz4OORDHiSFzyc9HMAIrCHJou2jaRQmL8cFJ76K4zQhhj8jzwOBlOi4BaGTjjOZCfTg=="],
-
- "react-debounce-input": ["react-debounce-input@3.3.0", "", { "dependencies": { "lodash.debounce": "^4", "prop-types": "^15.8.1" }, "peerDependencies": { "react": "^15.3.0 || 16 || 17 || 18" } }, "sha512-VEqkvs8JvY/IIZvh71Z0TC+mdbxERvYF33RcebnodlsUZ8RSgyKe2VWaHXv4+/8aoOgXLxWrdsYs2hDhcwbUgA=="],
+ "react": ["react@19.2.6", "", {}, "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q=="],
- "react-dnd": ["react-dnd@16.0.1", "", { "dependencies": { "@react-dnd/invariant": "^4.0.1", "@react-dnd/shallowequal": "^4.0.1", "dnd-core": "^16.0.1", "fast-deep-equal": "^3.1.3", "hoist-non-react-statics": "^3.3.2" }, "peerDependencies": { "@types/hoist-non-react-statics": ">= 3.3.1", "@types/node": ">= 12", "@types/react": ">= 16", "react": ">= 16.14" }, "optionalPeers": ["@types/hoist-non-react-statics", "@types/node", "@types/react"] }, "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q=="],
-
- "react-dnd-html5-backend": ["react-dnd-html5-backend@16.0.1", "", { "dependencies": { "dnd-core": "^16.0.1" } }, "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw=="],
-
- "react-dom": ["react-dom@19.2.5", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.5" } }, "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag=="],
-
- "react-dropzone": ["react-dropzone@15.0.0", "", { "dependencies": { "attr-accept": "^2.2.4", "file-selector": "^2.1.0", "prop-types": "^15.8.1" }, "peerDependencies": { "react": ">= 16.8 || 18.0.0" } }, "sha512-lGjYV/EoqEjEWPnmiSvH4v5IoIAwQM2W4Z1C0Q/Pw2xD0eVzKPS359BQTUMum+1fa0kH2nrKjuavmTPOGhpLPg=="],
-
- "react-easy-swipe": ["react-easy-swipe@0.0.21", "", { "dependencies": { "prop-types": "^15.5.8" } }, "sha512-OeR2jAxdoqUMHIn/nS9fgreI5hSpgGoL5ezdal4+oO7YSSgJR8ga+PkYGJrSrJ9MKlPcQjMQXnketrD7WNmNsg=="],
+ "react-dom": ["react-dom@19.2.6", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.6" } }, "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g=="],
"react-error-boundary": ["react-error-boundary@6.1.1", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0" } }, "sha512-BrYwPOdXi5mqkk5lw+Uvt0ThHx32rCt3BkukS4X23A2AIWDPSGX6iaWTc0y9TU/mHDA/6qOSGel+B2ERkOvD1w=="],
@@ -1769,37 +1132,23 @@
"react-hook-form": ["react-hook-form@7.54.2", "", { "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19" } }, "sha512-eHpAUgUjWbZocoQYUHposymRb4ZP6d0uwUnooL2uOybA9/3tPUvoAKqEWK1WaSiTxxOfTpffNZP7QwlnM3/gEg=="],
- "react-html-attributes": ["react-html-attributes@1.4.6", "", { "dependencies": { "html-element-attributes": "^1.0.0" } }, "sha512-uS3MmThNKFH2EZUQQw4k5pIcU7XIr208UE5dktrj/GOH1CMagqxDl4DCLpt3o2l9x+IB5nVYBeN3Cr4IutBXAg=="],
-
"react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
- "react-leaflet": ["react-leaflet@5.0.0", "", { "dependencies": { "@react-leaflet/core": "^3.0.0" }, "peerDependencies": { "leaflet": "^1.9.0", "react": "^19.0.0", "react-dom": "^19.0.0" } }, "sha512-CWbTpr5vcHw5bt9i4zSlPEVQdTVcML390TjeDG0cK59z1ylexpqC6M1PJFjV8jD7CF+ACBFsLIDs6DRMoLEofw=="],
-
"react-markdown": ["react-markdown@10.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "html-url-attributes": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" }, "peerDependencies": { "@types/react": ">=18", "react": ">=18" } }, "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ=="],
- "react-merge-refs": ["react-merge-refs@2.1.1", "", {}, "sha512-jLQXJ/URln51zskhgppGJ2ub7b2WFKGq3cl3NYKtlHoTG+dN2q7EzWrn3hN3EgPsTMvpR9tpq5ijdp7YwFZkag=="],
-
"react-moment": ["react-moment@1.2.2", "", { "peerDependencies": { "moment": "^2.29.0", "prop-types": "^15.7.0", "react": "^16.0 || ^17.0.0 || ^18.0.0" } }, "sha512-F5/YLsygduvR5+mpYdHMD9HzyljQFudcoAfL5XmJhGBkZVzJANIx/5PN+kvWO5KELifXXtndXeMCUxH4eB+E9Q=="],
- "react-number-format": ["react-number-format@5.4.5", "", { "peerDependencies": { "react": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-y8O2yHHj3w0aE9XO8d2BCcUOOdQTRSVq+WIuMlLVucAm5XNjJAy+BoOJiuQMldVYVOKTMyvVNfnbl2Oqp+YxGw=="],
-
"react-player": ["react-player@3.4.0", "", { "dependencies": { "@mux/mux-player-react": "^3.8.0", "cloudflare-video-element": "^1.3.4", "dash-video-element": "^0.3.0", "hls-video-element": "^1.5.9", "spotify-audio-element": "^1.0.3", "tiktok-video-element": "^0.1.1", "twitch-video-element": "^0.1.5", "vimeo-video-element": "^1.6.1", "wistia-video-element": "^1.3.5", "youtube-video-element": "^1.8.0" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18 || ^19", "react": "^17.0.2 || ^18 || ^19", "react-dom": "^17.0.2 || ^18 || ^19" } }, "sha512-QpQSHXtnMBKjQVNeaCYMtTVcynWQ0DDDhz/FJu1OR9PHLC1Aih94UqNstywzSHbJ6Oc7lI8/7kDDqcIvyTI6zQ=="],
- "react-plotly.js": ["react-plotly.js@2.6.0", "", { "dependencies": { "prop-types": "^15.8.1" }, "peerDependencies": { "plotly.js": ">1.34.0", "react": ">0.13.0" } }, "sha512-g93xcyhAVCSt9kV1svqG1clAEdL6k3U+jjuSzfTV7owaSU9Go6Ph8bl25J+jKfKvIGAEYpe4qj++WHJuc9IaeA=="],
-
- "react-redux": ["react-redux@9.2.0", "", { "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" }, "peerDependencies": { "@types/react": "^18.2.25 || ^19", "react": "^18.0 || ^19", "redux": "^5.0.0" }, "optionalPeers": ["@types/react", "redux"] }, "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g=="],
-
"react-refresh": ["react-refresh@0.14.2", "", {}, "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA=="],
"react-remove-scroll": ["react-remove-scroll@2.7.2", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q=="],
"react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="],
- "react-responsive-carousel": ["react-responsive-carousel@3.2.23", "", { "dependencies": { "classnames": "^2.2.5", "prop-types": "^15.5.8", "react-easy-swipe": "^0.0.21" } }, "sha512-pqJLsBaKHWJhw/ItODgbVoziR2z4lpcJg+YwmRlSk4rKH32VE633mAtZZ9kDXjy4wFO+pgUZmDKPsPe1fPmHCg=="],
-
- "react-router": ["react-router@7.14.1", "", { "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" }, "optionalPeers": ["react-dom"] }, "sha512-5BCvFskyAAVumqhEKh/iPhLOIkfxcEUz8WqFIARCkMg8hZZzDYX9CtwxXA0e+qT8zAxmMC0x3Ckb9iMONwc5jg=="],
+ "react-router": ["react-router@7.15.0", "", { "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" }, "optionalPeers": ["react-dom"] }, "sha512-HW9vYwuM8f4yx66Izy8xfrzCM+SBJluoZcCbww9A1TySax11S5Vgw6fi3ZjMONw9J4gQwngL7PzkyIpJJpJ7RQ=="],
- "react-router-dom": ["react-router-dom@7.14.1", "", { "dependencies": { "react-router": "7.14.1" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" } }, "sha512-ZkrQuwwhGibjQLqH1eCdyiZyLWglPxzxdl5tgwgKEyCSGC76vmAjleGocRe3J/MLfzMUIKwaFJWpFVJhK3d2xA=="],
+ "react-router-dom": ["react-router-dom@7.15.0", "", { "dependencies": { "react-router": "7.15.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" } }, "sha512-VcrVg64Fo8nwBvDscajG8gRTLIuTC6N50nb22l2HOOV4PTOHgoGp8mUjy9wLiHYoYTSYI36tUnXZgasSRFZorQ=="],
"react-side-effect": ["react-side-effect@2.1.2", "", { "peerDependencies": { "react": "^16.3.0 || ^17.0.0 || ^18.0.0" } }, "sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw=="],
@@ -1809,22 +1158,12 @@
"react-syntax-highlighter": ["react-syntax-highlighter@16.1.1", "", { "dependencies": { "@babel/runtime": "^7.28.4", "highlight.js": "^10.4.1", "highlightjs-vue": "^1.0.0", "lowlight": "^1.17.0", "prismjs": "^1.30.0", "refractor": "^5.0.0" }, "peerDependencies": { "react": ">= 0.14.0" } }, "sha512-PjVawBGy80C6YbC5DDZJeUjBmC7skaoEUdvfFQediQHgCL7aKyVHe57SaJGfQsloGDac+gCpTfRdtxzWWKmCXA=="],
- "react-textarea-autosize": ["react-textarea-autosize@8.5.9", "", { "dependencies": { "@babel/runtime": "^7.20.13", "use-composed-ref": "^1.3.0", "use-latest": "^1.2.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A=="],
-
- "reactflow": ["reactflow@11.11.4", "", { "dependencies": { "@reactflow/background": "11.3.14", "@reactflow/controls": "11.2.14", "@reactflow/core": "11.11.4", "@reactflow/minimap": "11.7.14", "@reactflow/node-resizer": "2.2.14", "@reactflow/node-toolbar": "1.3.14" }, "peerDependencies": { "react": ">=17", "react-dom": ">=17" } }, "sha512-70FOtJkUWH3BAOsN+LU9lCrKoKbtOPnz2uq0CV2PLdNSwxTXOhCbsZr50GmZ+Rtw3jx8Uv7/vBFtCGixLfd4Og=="],
+ "react-textarea-autosize": ["react-textarea-autosize@8.5.7", "", { "dependencies": { "@babel/runtime": "^7.20.13", "use-composed-ref": "^1.3.0", "use-latest": "^1.2.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-2MqJ3p0Jh69yt9ktFIaZmORHXw4c4bxSIhCeWiFwmJ9EYKgLmuNII3e9c9b2UO+ijl4StnpZdqpxNIhTdHvqtQ=="],
"read-cache": ["read-cache@1.0.0", "", { "dependencies": { "pify": "^2.3.0" } }, "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA=="],
- "readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="],
-
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
- "recharts": ["recharts@3.8.1", "", { "dependencies": { "@reduxjs/toolkit": "^1.9.0 || 2.x.x", "clsx": "^2.1.1", "decimal.js-light": "^2.5.1", "es-toolkit": "^1.39.3", "eventemitter3": "^5.0.1", "immer": "^10.1.1", "react-redux": "8.x.x || 9.x.x", "reselect": "5.1.1", "tiny-invariant": "^1.3.3", "use-sync-external-store": "^1.2.2", "victory-vendor": "^37.0.2" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-mwzmO1s9sFL0TduUpwndxCUNoXsBw3u3E/0+A+cLcrSfQitSG62L32N69GhqUrrT5qKcAE3pCGVINC6pqkBBQg=="],
-
- "redux": ["redux@4.2.1", "", { "dependencies": { "@babel/runtime": "^7.9.2" } }, "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w=="],
-
- "redux-thunk": ["redux-thunk@3.1.0", "", { "peerDependencies": { "redux": "^5.0.0" } }, "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw=="],
-
"refractor": ["refractor@5.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/prismjs": "^1.0.0", "hastscript": "^9.0.0", "parse-entities": "^4.0.0" } }, "sha512-QXOrHQF5jOpjjLfiNk5GFnWhRXvxjUVnlFxkeDmewR5sXkr3iM46Zo+CnRR8B+MDVqkULW4EcLVcRBNOPXHosw=="],
"regex": ["regex@6.1.0", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="],
@@ -1833,30 +1172,12 @@
"regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="],
- "regl": ["regl@2.1.1", "", {}, "sha512-+IOGrxl3FZ8ZM9ixCWQZzFRiRn7Rzn9bu3iFHwg/yz4tlOUQgbO4PHLgG+1ZT60zcIV8tief6Qrmyl8qcoJP0g=="],
-
- "regl-error2d": ["regl-error2d@2.0.12", "", { "dependencies": { "array-bounds": "^1.0.1", "color-normalize": "^1.5.0", "flatten-vertex-data": "^1.0.2", "object-assign": "^4.1.1", "pick-by-alias": "^1.2.0", "to-float32": "^1.1.0", "update-diff": "^1.1.0" } }, "sha512-r7BUprZoPO9AbyqM5qlJesrSRkl+hZnVKWKsVp7YhOl/3RIpi4UDGASGJY0puQ96u5fBYw/OlqV24IGcgJ0McA=="],
-
- "regl-line2d": ["regl-line2d@3.1.3", "", { "dependencies": { "array-bounds": "^1.0.1", "array-find-index": "^1.0.2", "array-normalize": "^1.1.4", "color-normalize": "^1.5.0", "earcut": "^2.1.5", "es6-weak-map": "^2.0.3", "flatten-vertex-data": "^1.0.2", "object-assign": "^4.1.1", "parse-rect": "^1.2.0", "pick-by-alias": "^1.2.0", "to-float32": "^1.1.0" } }, "sha512-fkgzW+tTn4QUQLpFKsUIE0sgWdCmXAM3ctXcCgoGBZTSX5FE2A0M7aynz7nrZT5baaftLrk9te54B+MEq4QcSA=="],
-
- "regl-scatter2d": ["regl-scatter2d@3.4.0", "", { "dependencies": { "@plotly/point-cluster": "^3.1.9", "array-bounds": "^1.0.1", "color-id": "^1.1.0", "color-normalize": "^1.5.0", "flatten-vertex-data": "^1.0.2", "glslify": "^7.0.0", "parse-rect": "^1.2.0", "pick-by-alias": "^1.2.0", "to-float32": "^1.1.0", "update-diff": "^1.1.0" } }, "sha512-DavKQlHsI+iHZuLgOL+yGkg+sPd94CS+7FCBWkcQ6s/TbaNfUsF9eN591fjjSWIoKrGNfb/SEGhsXR5lXjqZ2w=="],
-
- "regl-splom": ["regl-splom@1.0.14", "", { "dependencies": { "array-bounds": "^1.0.1", "array-range": "^1.0.1", "color-alpha": "^1.0.4", "flatten-vertex-data": "^1.0.2", "parse-rect": "^1.2.0", "pick-by-alias": "^1.2.0", "raf": "^3.4.1", "regl-scatter2d": "^3.2.3" } }, "sha512-OiLqjmPRYbd7kDlHC6/zDf6L8lxgDC65BhC8JirhP4ykrK4x22ZyS+BnY8EUinXKDeMgmpRwCvUmk7BK4Nweuw=="],
-
- "rehype-autolink-headings": ["rehype-autolink-headings@7.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@ungap/structured-clone": "^1.0.0", "hast-util-heading-rank": "^3.0.0", "hast-util-is-element": "^3.0.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-rItO/pSdvnvsP4QRB1pmPiNHUskikqtPojZKJPPPAVx9Hj8i8TwMBhofrrAYRhYOOBZH9tgmG5lPqDLuIWPWmw=="],
-
"rehype-katex": ["rehype-katex@7.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/katex": "^0.16.0", "hast-util-from-html-isomorphic": "^2.0.0", "hast-util-to-text": "^4.0.0", "katex": "^0.16.0", "unist-util-visit-parents": "^6.0.0", "vfile": "^6.0.0" } }, "sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA=="],
"rehype-raw": ["rehype-raw@7.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-raw": "^9.0.0", "vfile": "^6.0.0" } }, "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww=="],
- "rehype-sanitize": ["rehype-sanitize@5.0.1", "", { "dependencies": { "@types/hast": "^2.0.0", "hast-util-sanitize": "^4.0.0", "unified": "^10.0.0" } }, "sha512-da/jIOjq8eYt/1r9GN6GwxIR3gde7OZ+WV8pheu1tL8K0D9KxM2AyMh+UEfke+FfdM3PvGHeYJU0Td5OWa7L5A=="],
-
- "rehype-slug": ["rehype-slug@6.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "github-slugger": "^2.0.0", "hast-util-heading-rank": "^3.0.0", "hast-util-to-string": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A=="],
-
"rehype-unwrap-images": ["rehype-unwrap-images@1.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-interactive": "^3.0.0", "hast-util-whitespace": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-wzW5Mk9IlVF2UwXC5NtIZsx1aHYbV8+bLWjJnlZaaamz5QU52RppWtq1uEZJqGo8d9Y4RuDqidB6r9RFpKugIg=="],
- "remark-emoji": ["remark-emoji@5.0.2", "", { "dependencies": { "@types/mdast": "^4.0.4", "emoticon": "^4.0.1", "mdast-util-find-and-replace": "^3.0.1", "node-emoji": "^2.1.3", "unified": "^11.0.4" } }, "sha512-IyIqGELcyK5AVdLFafoiNww+Eaw/F+rGrNSXoKucjo95uL267zrddgxGM83GN1wFIb68pyDuAsY3m5t2Cav1pQ=="],
-
"remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="],
"remark-math": ["remark-math@6.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-math": "^3.0.0", "micromark-extension-math": "^3.0.0", "unified": "^11.0.0" } }, "sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA=="],
@@ -1873,38 +1194,20 @@
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
- "resolve-protobuf-schema": ["resolve-protobuf-schema@2.1.0", "", { "dependencies": { "protocol-buffers-schema": "^3.3.1" } }, "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ=="],
-
- "right-now": ["right-now@1.0.0", "", {}, "sha512-DA8+YS+sMIVpbsuKgy+Z67L9Lxb1p05mNxRpDPNksPDEFir4vmBlUtuN9jkTGn9YMMdlBuK7XQgFiz6ws+yhSg=="],
-
- "rolldown": ["rolldown@1.0.0-rc.16", "", { "dependencies": { "@oxc-project/types": "=0.126.0", "@rolldown/pluginutils": "1.0.0-rc.16" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.16", "@rolldown/binding-darwin-arm64": "1.0.0-rc.16", "@rolldown/binding-darwin-x64": "1.0.0-rc.16", "@rolldown/binding-freebsd-x64": "1.0.0-rc.16", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.16", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.16", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.16", "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.16", "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.16", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.16", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.16", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.16", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.16", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.16", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.16" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-rzi5WqKzEZw3SooTt7cgm4eqIoujPIyGcJNGFL7iPEuajQw7vxMHUkXylu4/vhCkJGXsgRmxqMKXUpT6FEgl0g=="],
+ "rolldown": ["rolldown@1.0.0", "", { "dependencies": { "@oxc-project/types": "=0.129.0", "@rolldown/pluginutils": "1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0", "@rolldown/binding-darwin-arm64": "1.0.0", "@rolldown/binding-darwin-x64": "1.0.0", "@rolldown/binding-freebsd-x64": "1.0.0", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0", "@rolldown/binding-linux-arm64-gnu": "1.0.0", "@rolldown/binding-linux-arm64-musl": "1.0.0", "@rolldown/binding-linux-ppc64-gnu": "1.0.0", "@rolldown/binding-linux-s390x-gnu": "1.0.0", "@rolldown/binding-linux-x64-gnu": "1.0.0", "@rolldown/binding-linux-x64-musl": "1.0.0", "@rolldown/binding-openharmony-arm64": "1.0.0", "@rolldown/binding-wasm32-wasi": "1.0.0", "@rolldown/binding-win32-arm64-msvc": "1.0.0", "@rolldown/binding-win32-x64-msvc": "1.0.0" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-yD986aXDESFGS95spT1LAv0jssywP4npMEjmMHyN2/5+eE8qQJUype2AaKkRiLgBgyD0LFlubwAht7VmY8rGoA=="],
"rollup": ["rollup@4.60.3", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.60.3", "@rollup/rollup-android-arm64": "4.60.3", "@rollup/rollup-darwin-arm64": "4.60.3", "@rollup/rollup-darwin-x64": "4.60.3", "@rollup/rollup-freebsd-arm64": "4.60.3", "@rollup/rollup-freebsd-x64": "4.60.3", "@rollup/rollup-linux-arm-gnueabihf": "4.60.3", "@rollup/rollup-linux-arm-musleabihf": "4.60.3", "@rollup/rollup-linux-arm64-gnu": "4.60.3", "@rollup/rollup-linux-arm64-musl": "4.60.3", "@rollup/rollup-linux-loong64-gnu": "4.60.3", "@rollup/rollup-linux-loong64-musl": "4.60.3", "@rollup/rollup-linux-ppc64-gnu": "4.60.3", "@rollup/rollup-linux-ppc64-musl": "4.60.3", "@rollup/rollup-linux-riscv64-gnu": "4.60.3", "@rollup/rollup-linux-riscv64-musl": "4.60.3", "@rollup/rollup-linux-s390x-gnu": "4.60.3", "@rollup/rollup-linux-x64-gnu": "4.60.3", "@rollup/rollup-linux-x64-musl": "4.60.3", "@rollup/rollup-openbsd-x64": "4.60.3", "@rollup/rollup-openharmony-arm64": "4.60.3", "@rollup/rollup-win32-arm64-msvc": "4.60.3", "@rollup/rollup-win32-ia32-msvc": "4.60.3", "@rollup/rollup-win32-x64-gnu": "4.60.3", "@rollup/rollup-win32-x64-msvc": "4.60.3", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-pAQK9HalE84QSm4Po3EmWIZPd3FnjkShVkiMlz1iligWYkWQ7wHYd1PF/T7QZ5TVSD6uSTon5gBVMSM4JfBV+A=="],
- "rw": ["rw@1.3.3", "", {}, "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="],
-
- "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
-
- "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
-
- "sax": ["sax@1.6.0", "", {}, "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA=="],
+ "sax": ["sax@1.2.1", "", {}, "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA=="],
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
"semver": ["semver@7.8.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA=="],
- "semver-compare": ["semver-compare@1.0.0", "", {}, "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow=="],
-
"set-cookie-parser": ["set-cookie-parser@2.7.2", "", {}, "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="],
- "shallow-copy": ["shallow-copy@0.0.1", "", {}, "sha512-b6i4ZpVuUxB9h5gfCxPiusKYkqTMOjEbBs4wMaFbkfia4yFv92UKZ6Df8WXcKbn08JNL/abvg3FnMAOfakDvUw=="],
-
"shiki": ["shiki@3.3.0", "", { "dependencies": { "@shikijs/core": "3.3.0", "@shikijs/engine-javascript": "3.3.0", "@shikijs/engine-oniguruma": "3.3.0", "@shikijs/langs": "3.3.0", "@shikijs/themes": "3.3.0", "@shikijs/types": "3.3.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-j0Z1tG5vlOFGW8JVj0Cpuatzvshes7VJy5ncDmmMaYcmnGW0Js1N81TOW98ivTFNZfKRn9uwEg/aIm638o368g=="],
- "signum": ["signum@1.0.0", "", {}, "sha512-yodFGwcyt59XRh7w5W3jPcIQb3Bwi21suEfT7MAWnBX3iCdklJpgDgvGT9o04UonglZN5SNMfJFkHIR/jO8GHw=="],
-
- "skin-tone": ["skin-tone@2.0.0", "", { "dependencies": { "unicode-emoji-modifier-base": "^1.0.0" } }, "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA=="],
-
"socket.io-client": ["socket.io-client@4.8.3", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1", "engine.io-client": "~6.6.1", "socket.io-parser": "~4.2.4" } }, "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g=="],
"socket.io-parser": ["socket.io-parser@4.2.6", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1" } }, "sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg=="],
@@ -1919,22 +1222,8 @@
"spotify-audio-element": ["spotify-audio-element@1.0.4", "", {}, "sha512-QdKrJPkYCzaNwwz2vN2eDGyoW0KmQFmnwVprB41mpMzj4qujbqr6pegEchQeTn0b5PceKiLoVu0pp2QDpTcWnw=="],
- "stack-trace": ["stack-trace@0.0.9", "", {}, "sha512-vjUc6sfgtgY0dxCdnc40mK6Oftjo9+2K8H/NG81TMhgL392FtiPA9tn9RLyTxXmTLPJPjF3VyzFp6bsWFLisMQ=="],
-
- "static-eval": ["static-eval@2.1.1", "", { "dependencies": { "escodegen": "^2.1.0" } }, "sha512-MgWpQ/ZjGieSVB3eOJVs4OA2LT/q1vx98KPCTTQPzq/aLr0YUXTsgryTXr4SLfR0ZfUUCiedM9n/ABeDIyy4mA=="],
-
- "stream-parser": ["stream-parser@0.3.1", "", { "dependencies": { "debug": "2" } }, "sha512-bJ/HgKq41nlKvlhccD5kaCr/P+Hu0wPNKPJOH7en+YrJu/9EgqUF+88w5Jb6KNcjOFMhfX4B2asfeAtIGuHObQ=="],
-
- "stream-shift": ["stream-shift@1.0.3", "", {}, "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ=="],
-
- "string-split-by": ["string-split-by@1.0.0", "", { "dependencies": { "parenthesis": "^3.1.5" } }, "sha512-KaJKY+hfpzNyet/emP81PJA9hTVSfxNLS9SFTWxdCnnW1/zOOwiV248+EfoX7IQFcBaOp4G5YE6xTJMF+pLg6A=="],
-
- "string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="],
-
"stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="],
- "strongly-connected-components": ["strongly-connected-components@1.0.1", "", {}, "sha512-i0TFx4wPcO0FwX+4RkLJi1MxmcTv90jNZgxMu9XRnMXMeFUY1VJlIoXpZunPUvUUqbCT1pg5PEkFqqpcaElNaA=="],
-
"style-to-js": ["style-to-js@1.1.21", "", { "dependencies": { "style-to-object": "1.0.14" } }, "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ=="],
"style-to-object": ["style-to-object@1.0.14", "", { "dependencies": { "inline-style-parser": "0.2.7" } }, "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw=="],
@@ -1943,74 +1232,36 @@
"super-media-element": ["super-media-element@1.4.2", "", {}, "sha512-9pP/CVNp4NF2MNlRzLwQkjiTgKKe9WYXrLh9+8QokWmMxz+zt2mf1utkWLco26IuA3AfVcTb//qtlTIjY3VHxA=="],
- "supercluster": ["supercluster@7.1.5", "", { "dependencies": { "kdbush": "^3.0.0" } }, "sha512-EulshI3pGUM66o6ZdH3ReiFcvHpM3vAigyK+vcxdjpJyEbIIrtbmBdY23mGgnI24uXiGFvrGq9Gkum/8U7vJWg=="],
-
- "superscript-text": ["superscript-text@1.0.0", "", {}, "sha512-gwu8l5MtRZ6koO0icVTlmN5pm7Dhh1+Xpe9O4x6ObMAsW+3jPbW14d1DsBq1F4wiI+WOFjXF35pslgec/G8yCQ=="],
-
"supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
- "svg-arc-to-cubic-bezier": ["svg-arc-to-cubic-bezier@3.2.0", "", {}, "sha512-djbJ/vZKZO+gPoSDThGNpKDO+o+bAeA4XQKovvkNCqnIS2t+S4qnLAGQhyyrulhCFRl1WWzAp0wUDV8PpTVU3g=="],
-
- "svg-path-bounds": ["svg-path-bounds@1.0.2", "", { "dependencies": { "abs-svg-path": "^0.1.1", "is-svg-path": "^1.0.1", "normalize-svg-path": "^1.0.0", "parse-svg-path": "^0.1.2" } }, "sha512-H4/uAgLWrppIC0kHsb2/dWUYSmb4GE5UqH06uqWBcg6LBjX2fu0A8+JrO2/FJPZiSsNOKZAhyFFgsLTdYUvSqQ=="],
-
- "svg-path-sdf": ["svg-path-sdf@1.1.3", "", { "dependencies": { "bitmap-sdf": "^1.0.0", "draw-svg-path": "^1.0.0", "is-svg-path": "^1.0.1", "parse-svg-path": "^0.1.2", "svg-path-bounds": "^1.0.1" } }, "sha512-vJJjVq/R5lSr2KLfVXVAStktfcfa1pNFjFOgyJnzZFXlO/fDZ5DmM8FpnSKKzLPfEYTVeXuVBTHF296TpxuJVg=="],
-
- "tabbable": ["tabbable@6.4.0", "", {}, "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg=="],
-
"tailwind-merge": ["tailwind-merge@2.6.1", "", {}, "sha512-Oo6tHdpZsGpkKG88HJ8RR1rg/RdnEkQEfMoEk2x1XRI3F1AxeU+ijRXpiVUF4UbLfcxxRGw6TbUINKYdWVsQTQ=="],
- "tailwindcss": ["tailwindcss@4.2.3", "", {}, "sha512-fA/NX5gMf0ooCLISgB0wScaWgaj6rjTN2SVAwleURjiya7ITNkV+VMmoHtKkldP6CIZoYCZyxb8zP/e2TWoEtQ=="],
+ "tailwindcss": ["tailwindcss@4.3.0", "", {}, "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q=="],
"tailwindcss-animated": ["tailwindcss-animated@2.0.0", "", { "peerDependencies": { "tailwindcss": ">=3.1.0 || >=4.0.0" } }, "sha512-anNNGpxNgjydD8p1lcJjxxH+XbjW6KR8Xs29owTrbcf3tOJ6IRblpyFob43HBkfxFJJTAfFQqugoEG2b1EsR0A=="],
- "tapable": ["tapable@2.3.3", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="],
+ "tailwindcss-scroll-mask": ["tailwindcss-scroll-mask@0.0.3", "", { "peerDependencies": { "tailwindcss": "^4" } }, "sha512-vPuacFs5yHJRZ8MFP/qqKKRbnWlVDPAJ5VfRzOwrmtEJQ4pHm5K1xw/dXdF4v6Sx1QdbtCWJuGs9ucSTc+YanQ=="],
- "through2": ["through2@2.0.5", "", { "dependencies": { "readable-stream": "~2.3.6", "xtend": "~4.0.1" } }, "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ=="],
-
- "thumbhash": ["thumbhash@0.1.1", "", {}, "sha512-kH5pKeIIBPQXAOni2AiY/Cu/NKdkFREdpH+TLdM0g6WA7RriCv0kPLgP731ady67MhTAqrVG/4mnEeibVuCJcg=="],
+ "tapable": ["tapable@2.3.3", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="],
"tiktok-video-element": ["tiktok-video-element@0.1.2", "", {}, "sha512-w6TboLm236XJKKiIXIhCbYCnUxbixBbaAoty0etaEAZ/2kHkVIdfZdv2oouMU/HGMsWCHI/VjQ3wU3MJ+s192Q=="],
- "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="],
-
- "tinycolor2": ["tinycolor2@1.6.0", "", {}, "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="],
-
"tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="],
- "tinyqueue": ["tinyqueue@2.0.3", "", {}, "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA=="],
-
- "to-float32": ["to-float32@1.1.0", "", {}, "sha512-keDnAusn/vc+R3iEiSDw8TOF7gPiTLdK1ArvWtYbJQiVfmRg6i/CAvbKq3uIS0vWroAC7ZecN3DjQKw3aSklUg=="],
-
- "to-px": ["to-px@1.0.1", "", { "dependencies": { "parse-unit": "^1.0.1" } }, "sha512-2y3LjBeIZYL19e5gczp14/uRWFDtDUErJPVN3VU9a7SJO+RjGRtYR47aMN2bZgGlxvW4ZcEz2ddUPVHXcMfuXw=="],
-
- "topojson-client": ["topojson-client@3.1.0", "", { "dependencies": { "commander": "2" }, "bin": { "topo2geo": "bin/topo2geo", "topomerge": "bin/topomerge", "topoquantize": "bin/topoquantize" } }, "sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw=="],
-
"tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
"trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="],
"trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="],
- "ts-invariant": ["ts-invariant@0.10.3", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ=="],
-
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"twitch-video-element": ["twitch-video-element@0.1.6", "", {}, "sha512-X7l8gy+DEFKJ/EztUwaVnAYwQN9fUJxPkOVJj2sE62sGvGU4DNLyvmOsmVulM+8Plc5dMg6hYIMNRAPaH+39Uw=="],
- "type": ["type@2.7.3", "", {}, "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ=="],
-
- "type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
-
- "typedarray": ["typedarray@0.0.6", "", {}, "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="],
-
- "typedarray-pool": ["typedarray-pool@1.2.0", "", { "dependencies": { "bit-twiddle": "^1.0.0", "dup": "^1.0.0" } }, "sha512-YTSQbzX43yvtpfRtIDAYygoYtgT+Rpjuxy9iOpczrjpXLgGoyG7aS5USJXV2d3nn8uHTeb9rXDvzS27zUg5KYQ=="],
-
"ua-parser-js": ["ua-parser-js@1.0.41", "", { "bin": { "ua-parser-js": "script/cli.js" } }, "sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug=="],
"undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
- "unicode-emoji-modifier-base": ["unicode-emoji-modifier-base@1.0.0", "", {}, "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g=="],
-
"unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="],
"unist-util-find-after": ["unist-util-find-after@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ=="],
@@ -2029,12 +1280,8 @@
"universal-cookie": ["universal-cookie@7.2.2", "", { "dependencies": { "@types/cookie": "^0.6.0", "cookie": "^0.7.2" } }, "sha512-fMiOcS3TmzP2x5QV26pIH3mvhexLIT0HmPa3V7Q7knRfT9HG6kTwq02HZGLPw0sAOXrAmotElGRvTLCMbJsvxQ=="],
- "unquote": ["unquote@1.1.1", "", {}, "sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg=="],
-
"update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="],
- "update-diff": ["update-diff@1.1.0", "", {}, "sha512-rCiBPiHxZwT4+sBhEbChzpO5hYHjm91kScWgdHf4Qeafs6Ba7MBl+d9GlGv72bcTZQO0sLmtQS1pHSWoCLtN/A=="],
-
"use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="],
"use-composed-ref": ["use-composed-ref@1.4.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w=="],
@@ -2059,54 +1306,34 @@
"vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="],
- "victory-vendor": ["victory-vendor@37.3.6", "", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ=="],
-
"vimeo-video-element": ["vimeo-video-element@1.7.1", "", { "dependencies": { "@vimeo/player": "2.29.0", "media-played-ranges-mixin": "^0.1.0" } }, "sha512-GSlKdqPoa133f2YbuO4JObPWuZ/3+ShIesXhpqtli4p0+STL4YsZ73ixolLK49+VHkOFMfnjeN9Cghh+bGjkhA=="],
- "vite": ["vite@8.0.9", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.10", "rolldown": "1.0.0-rc.16", "tinyglobby": "^0.2.16" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-t7g7GVRpMXjNpa67HaVWI/8BWtdVIQPCL2WoozXXA7LBGEFK4AkkKkHx2hAQf5x1GZSlcmEDPkVLSGahxnEEZw=="],
+ "vite": ["vite@8.0.12", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.14", "rolldown": "1.0.0", "tinyglobby": "^0.2.16" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.18", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w2dDofOWv2QB09ZITZBsvKTVAlYvPR4IAmrY/v0ir9KvLs0xybR7i48wxhM1/oyBWO34wPns+bPGw5ZrZqDpZg=="],
"vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="],
- "vt-pbf": ["vt-pbf@3.1.3", "", { "dependencies": { "@mapbox/point-geometry": "0.1.0", "@mapbox/vector-tile": "^1.3.1", "pbf": "^3.2.1" } }, "sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA=="],
-
- "weak-map": ["weak-map@1.0.8", "", {}, "sha512-lNR9aAefbGPpHO7AEnY0hCFjz1eTkWCXYvkTRrTHs9qv8zJp+SkVYpzfLIFXQQiG3tVvbNFQgVg2bQS8YGgxyw=="],
-
"weakmap-polyfill": ["weakmap-polyfill@2.0.4", "", {}, "sha512-ZzxBf288iALJseijWelmECm/1x7ZwQn3sMYIkDr2VvZp7r6SEKuT8D0O9Wiq6L9Nl5mazrOMcmiZE/2NCenaxw=="],
"web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="],
"web-streams-polyfill": ["web-streams-polyfill@4.0.0-beta.3", "", {}, "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug=="],
- "webgl-context": ["webgl-context@2.2.0", "", { "dependencies": { "get-canvas-context": "^1.0.1" } }, "sha512-q/fGIivtqTT7PEoF07axFIlHNk/XCPaYpq64btnepopSWvKNFkoORlQYgqDigBIuGA1ExnFd/GnSUnBNEPQY7Q=="],
-
"webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
"whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
- "which": ["which@4.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg=="],
-
"wistia-video-element": ["wistia-video-element@1.4.0", "", { "dependencies": { "super-media-element": "~1.4.2" } }, "sha512-udI8/yiMZ+KIGwYK1qNOFiXl+s/wffiY+XkwTXlBFICZylFalOS0QYEQqYOmuBsm3jNfGUS4O50tLuN7Ejqm3A=="],
- "world-calendars": ["world-calendars@1.0.4", "", { "dependencies": { "object-assign": "^4.1.0" } }, "sha512-VGRnLJS+xJmGDPodgJRnGIDwGu0s+Cr9V2HB3EzlDZ5n0qb8h5SJtGUEkjrphZYAglEiXZ6kiXdmk0H/h/uu/w=="],
-
- "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
-
"ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
"xmlhttprequest-ssl": ["xmlhttprequest-ssl@2.1.2", "", {}, "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ=="],
- "xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="],
-
"yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
"yaml": ["yaml@1.10.3", "", {}, "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA=="],
- "yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
-
"youtube-video-element": ["youtube-video-element@1.9.0", "", { "dependencies": { "media-played-ranges-mixin": "^0.1.0" } }, "sha512-Hh0dbQM+FVlUaYUbpYkZNUvdKxTNcSNvTGzkQKYShltnX+LRHEp2eYvC2Zm43eU8Np+CBZuoNR2i+seCYzzAyg=="],
- "zustand": ["zustand@4.5.7", "", { "dependencies": { "use-sync-external-store": "^1.2.2" }, "peerDependencies": { "@types/react": ">=16.8", "immer": ">=9.0.6", "react": ">=16.8" }, "optionalPeers": ["@types/react", "immer", "react"] }, "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw=="],
-
"zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
"@babel/core/convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
@@ -2131,22 +1358,10 @@
"@inkeep/cxkit-primitives/react-markdown": ["react-markdown@9.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "devlop": "^1.0.0", "hast-util-to-jsx-runtime": "^2.0.0", "html-url-attributes": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.0.0", "unified": "^11.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" }, "peerDependencies": { "@types/react": ">=18", "react": ">=18" } }, "sha512-Yk7Z94dbgYTOrdk41Z74GoKA7rThnsbbqBTRYuxoe08qvfQ9tJVhmAKw6BJS/ZORG7kTy/s1QvYzSuaoBA1qfw=="],
- "@inkeep/cxkit-primitives/react-textarea-autosize": ["react-textarea-autosize@8.5.7", "", { "dependencies": { "@babel/runtime": "^7.20.13", "use-composed-ref": "^1.3.0", "use-latest": "^1.2.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-2MqJ3p0Jh69yt9ktFIaZmORHXw4c4bxSIhCeWiFwmJ9EYKgLmuNII3e9c9b2UO+ijl4StnpZdqpxNIhTdHvqtQ=="],
-
"@inkeep/cxkit-react/lucide-react": ["lucide-react@0.503.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-HGGkdlPWQ0vTF8jJ5TdIqhQXZi6uh3LnNgfZ8MHiuxFfX3RZeA79r2MW2tHAZKlAVfoNE8esm3p+O6VkIvpj6w=="],
"@inkeep/cxkit-styled/tailwind-merge": ["tailwind-merge@2.6.0", "", {}, "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA=="],
- "@maplibre/maplibre-gl-style-spec/@mapbox/unitbezier": ["@mapbox/unitbezier@0.0.1", "", {}, "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw=="],
-
- "@maplibre/maplibre-gl-style-spec/tinyqueue": ["tinyqueue@3.0.0", "", {}, "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g=="],
-
- "@react-router/dev/isbot": ["isbot@5.1.40", "", {}, "sha512-yNeeynhhtIVRBk12tBV4eHNxwB42HzR4Q3Ea7vCOiJhImGaAIdIMrbJtacQlBizGLjUPw+akkFI5Dn9T70XoVQ=="],
-
- "@reduxjs/toolkit/immer": ["immer@11.1.8", "", {}, "sha512-/tbkHMW7y10Lx6i1crLjD4/OhNkRG+Fo7byZHtah0547nIeXYcpIXaUh0IAQY6gO5459qpGGYapcEOHtFXkIuA=="],
-
- "@reduxjs/toolkit/redux": ["redux@5.0.1", "", {}, "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w=="],
-
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="],
@@ -2159,92 +1374,12 @@
"@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
- "ag-charts-community/ag-charts-types": ["ag-charts-types@11.2.4", "", {}, "sha512-a1aQjtQ9ZH+J8BF3YcWAIHUt9ZLiL9IVZ8R5h7z2CkTNu2TPq5UlsCcF0/YZKc1M8CM0CdSNLSGQuWUwDNxAcA=="],
-
- "ag-charts-core/ag-charts-types": ["ag-charts-types@11.2.4", "", {}, "sha512-a1aQjtQ9ZH+J8BF3YcWAIHUt9ZLiL9IVZ8R5h7z2CkTNu2TPq5UlsCcF0/YZKc1M8CM0CdSNLSGQuWUwDNxAcA=="],
-
- "ag-grid-enterprise/ag-charts-community": ["ag-charts-community@12.3.1", "", { "dependencies": { "ag-charts-core": "12.3.1", "ag-charts-locale": "12.3.1", "ag-charts-types": "12.3.1" } }, "sha512-uRaUFmCl8e0Y3KxjaHUYlkCPS5OtwtfTChkdpoZuBKDXqerCPTjPu+uvlun3rYUSYxScwVf2LZCI0Hfw4Vc+WQ=="],
-
- "ag-grid-enterprise/ag-charts-enterprise": ["ag-charts-enterprise@12.3.1", "", { "dependencies": { "ag-charts-community": "12.3.1", "ag-charts-core": "12.3.1" } }, "sha512-2sQIwLfksRTcI4JVRjgQkyGq6z68UVTO/I91HKmtpukrQLsW/o0TzzHmgTtVJOXd9yO9gx2KWa2DMg0+FKdXEQ=="],
-
- "color-alpha/color-parse": ["color-parse@1.4.3", "", { "dependencies": { "color-name": "^1.0.0" } }, "sha512-BADfVl/FHkQkyo8sRBwMYBqemqsgnu7JZAwUgvBvuwwuNUZAhSvLTbsEErS5bQXzOjDR0dWzJ4vXN2Q+QoPx0A=="],
-
- "color-normalize/color-rgba": ["color-rgba@2.4.0", "", { "dependencies": { "color-parse": "^1.4.2", "color-space": "^2.0.0" } }, "sha512-Nti4qbzr/z2LbUWySr7H9dk3Rl7gZt7ihHAxlgT4Ho90EXWkjtkL1avTleu9yeGuqrt/chxTB6GKK8nZZ6V0+Q=="],
-
- "d3-scale/d3-array": ["d3-array@3.2.4", "", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="],
-
- "d3-scale/d3-time": ["d3-time@3.1.0", "", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="],
-
- "d3-transition/d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="],
-
- "escodegen/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
-
- "glsl-resolve/resolve": ["resolve@0.6.3", "", {}, "sha512-UHBY3viPlJKf85YijDUcikKX6tmF4SokIDp518ZDVT92JNDcG5uKIthaT/owt3Sar0lwtOafsQuwrg22/v2Dwg=="],
-
- "glsl-resolve/xtend": ["xtend@2.2.0", "", {}, "sha512-SLt5uylT+4aoXxXuwtQp5ZnMMzhDb1Xkg4pEqc00WUJCQifPfV9Ub1VrNhp9kXkrjZD2I2Hl8WnjP37jzZLPZw=="],
-
- "glsl-tokenizer/through2": ["through2@0.6.5", "", { "dependencies": { "readable-stream": ">=1.0.33-1 <1.1.0-0", "xtend": ">=4.0.0 <4.1.0-0" } }, "sha512-RkK/CCESdTKQZHdmKICijdKKsCRVHs5KsLZ6pACAmF/1GPUQhonHSXWNERctxEp7RmvjdNbZTL5z9V7nSCXKcg=="],
-
- "hast-util-sanitize/@types/hast": ["@types/hast@2.3.10", "", { "dependencies": { "@types/unist": "^2" } }, "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw=="],
-
- "imsc/sax": ["sax@1.2.1", "", {}, "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA=="],
-
- "katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="],
-
- "map-limit/once": ["once@1.3.3", "", { "dependencies": { "wrappy": "1" } }, "sha512-6vaNInhu+CHxtONf3zw3vq4SP2DOQhjBvIa3rNcG0+P7eKWlYH6Peu7rHizSloRU2EwMz6GraLieis9Ac9+p1w=="],
-
- "maplibre-gl/@mapbox/tiny-sdf": ["@mapbox/tiny-sdf@2.2.0", "", {}, "sha512-LVL4wgI9YAum5V+LNVQO6QgFBPw7/MIIY4XJPNsPDMrjEwcE+JfKk1LuIl8GnF197ejVdC9QdPaxrx5gfgdGXg=="],
-
- "maplibre-gl/@mapbox/unitbezier": ["@mapbox/unitbezier@0.0.1", "", {}, "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw=="],
-
- "maplibre-gl/earcut": ["earcut@3.0.2", "", {}, "sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ=="],
-
- "maplibre-gl/geojson-vt": ["geojson-vt@4.0.2", "", {}, "sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A=="],
-
- "maplibre-gl/potpack": ["potpack@2.1.0", "", {}, "sha512-pcaShQc1Shq0y+E7GqJqvZj8DTthWV1KeHGdi0Z6IAin2Oi3JnLCOfwnCo84qc+HAp52wT9nK9H7FAJp5a44GQ=="],
-
- "maplibre-gl/quickselect": ["quickselect@3.0.0", "", {}, "sha512-XdjUArbK4Bm5fLLvlm5KpTFOiOThgfWWI4axAZDWg4E/0mKdZyI9tNEfds27qCi1ze/vwTR16kvmmGhRra3c2g=="],
-
- "maplibre-gl/supercluster": ["supercluster@8.0.1", "", { "dependencies": { "kdbush": "^4.0.2" } }, "sha512-IiOea5kJ9iqzD2t7QJq/cREyLHTtSmUT6gQsweojg9WH2sYJqZK9SswTu6jrscO6D1G5v5vYZ9ru/eq85lXeZQ=="],
-
- "maplibre-gl/tinyqueue": ["tinyqueue@3.0.0", "", {}, "sha512-gRa9gwYU3ECmQYv3lslts5hxuIa90veaEcxDYuu3QGOIAEM2mOZkVHp48ANJuu1CURtRdHKUBY5Lm1tHV+sD4g=="],
-
"mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="],
- "needle/debug": ["debug@3.2.7", "", { "dependencies": { "ms": "^2.1.1" } }, "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ=="],
-
"parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
"pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
- "readable-stream/isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="],
-
- "readable-stream/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
-
- "redux-thunk/redux": ["redux@5.0.1", "", {}, "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w=="],
-
- "rehype-sanitize/@types/hast": ["@types/hast@2.3.10", "", { "dependencies": { "@types/unist": "^2" } }, "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw=="],
-
- "rehype-sanitize/unified": ["unified@10.1.2", "", { "dependencies": { "@types/unist": "^2.0.0", "bail": "^2.0.0", "extend": "^3.0.0", "is-buffer": "^2.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^5.0.0" } }, "sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q=="],
-
- "stream-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="],
-
- "string_decoder/safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="],
-
- "supercluster/kdbush": ["kdbush@3.0.0", "", {}, "sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew=="],
-
- "svg-path-bounds/normalize-svg-path": ["normalize-svg-path@1.1.0", "", { "dependencies": { "svg-arc-to-cubic-bezier": "^3.0.0" } }, "sha512-r9KHKG2UUeB5LoTouwDzBy2VxXlHsiM6fyLQvnJa0S5hrhzqElH/CH7TUGhT1fVvIYBIKf3OpY4YJ4CK+iaqHg=="],
-
- "unified/is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
-
- "victory-vendor/d3-array": ["d3-array@3.2.4", "", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="],
-
- "victory-vendor/d3-shape": ["d3-shape@3.2.0", "", { "dependencies": { "d3-path": "^3.1.0" } }, "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA=="],
-
- "victory-vendor/d3-time": ["d3-time@3.1.0", "", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="],
-
- "victory-vendor/d3-timer": ["d3-timer@3.0.1", "", {}, "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA=="],
-
"vite-node/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
"vite-node/vite": ["vite@7.3.3", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA=="],
@@ -2341,29 +1476,7 @@
"@inkeep/cxkit-primitives/@radix-ui/react-tooltip/@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.1.1", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-vVfA2IZ9q/J+gEamvj761Oq1FpWgCDaNOOIfbPVp2MVPLEomUr5+Vf7kJGwQ24YxZSlQVar7Bes8kyTo5Dshpg=="],
- "ag-grid-enterprise/ag-charts-community/ag-charts-core": ["ag-charts-core@12.3.1", "", { "dependencies": { "ag-charts-types": "12.3.1" } }, "sha512-711UJ0fXengb8+4PEW4nlzWDowmbYymPcjW2eJWHRzzvttUf14hnh+wP/l/s3EGVgYkEHe9vkXFwmeOJUlkC0Q=="],
-
- "ag-grid-enterprise/ag-charts-community/ag-charts-locale": ["ag-charts-locale@12.3.1", "", {}, "sha512-dCn7oHh3xLI576FT514aBedNQgtb5zwh/Gcj7jHvjOWYRnfH8kaekZzLzntITA6dF6E78okJfoI7CUCbYduQ4Q=="],
-
- "ag-grid-enterprise/ag-charts-enterprise/ag-charts-core": ["ag-charts-core@12.3.1", "", { "dependencies": { "ag-charts-types": "12.3.1" } }, "sha512-711UJ0fXengb8+4PEW4nlzWDowmbYymPcjW2eJWHRzzvttUf14hnh+wP/l/s3EGVgYkEHe9vkXFwmeOJUlkC0Q=="],
-
- "color-normalize/color-rgba/color-parse": ["color-parse@1.4.3", "", { "dependencies": { "color-name": "^1.0.0" } }, "sha512-BADfVl/FHkQkyo8sRBwMYBqemqsgnu7JZAwUgvBvuwwuNUZAhSvLTbsEErS5bQXzOjDR0dWzJ4vXN2Q+QoPx0A=="],
-
- "glsl-tokenizer/through2/readable-stream": ["readable-stream@1.0.34", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", "isarray": "0.0.1", "string_decoder": "~0.10.x" } }, "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg=="],
-
- "hast-util-sanitize/@types/hast/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
-
- "rehype-sanitize/@types/hast/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
-
- "rehype-sanitize/unified/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="],
-
- "rehype-sanitize/unified/is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
-
- "rehype-sanitize/unified/vfile": ["vfile@5.3.7", "", { "dependencies": { "@types/unist": "^2.0.0", "is-buffer": "^2.0.0", "unist-util-stringify-position": "^3.0.0", "vfile-message": "^3.0.0" } }, "sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g=="],
-
- "stream-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
-
- "victory-vendor/d3-shape/d3-path": ["d3-path@3.1.0", "", {}, "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ=="],
+ "vite-node/vite/postcss": ["postcss@8.5.10", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ=="],
"@inkeep/cxkit-primitives/@radix-ui/react-avatar/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.1", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g=="],
@@ -2427,14 +1540,6 @@
"@inkeep/cxkit-primitives/@radix-ui/react-tooltip/@radix-ui/react-use-controllable-state/@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw=="],
- "glsl-tokenizer/through2/readable-stream/isarray": ["isarray@0.0.1", "", {}, "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ=="],
-
- "glsl-tokenizer/through2/readable-stream/string_decoder": ["string_decoder@0.10.31", "", {}, "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ=="],
-
- "rehype-sanitize/unified/vfile/unist-util-stringify-position": ["unist-util-stringify-position@3.0.3", "", { "dependencies": { "@types/unist": "^2.0.0" } }, "sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg=="],
-
- "rehype-sanitize/unified/vfile/vfile-message": ["vfile-message@3.1.4", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-stringify-position": "^3.0.0" } }, "sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw=="],
-
"@inkeep/cxkit-primitives/@radix-ui/react-avatar/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
}
}
diff --git a/docs/app/reflex.lock/package.json b/docs/app/reflex.lock/package.json
index ffda398ce7f..b4ee0ca5f95 100644
--- a/docs/app/reflex.lock/package.json
+++ b/docs/app/reflex.lock/package.json
@@ -7,84 +7,53 @@
},
"dependencies": {
"@base-ui/react": "1.4.1",
- "@glideapps/glide-data-grid": "6.0.3",
"@hugeicons/core-free-icons": "4.1.1",
"@hugeicons/react": "1.1.6",
"@inkeep/cxkit-react": "0.5.115",
- "@mantine/core": "8.3.9",
- "@masenf/hello-react": "github:masenf/hello-react",
"@radix-ui/react-accordion": "1.2.12",
"@radix-ui/react-dialog": "1.1.15",
"@radix-ui/react-form": "0.1.8",
"@radix-ui/themes": "3.3.0",
- "@react-router/node": "7.14.1",
- "@splinetool/react-spline": "4.1.0",
- "@splinetool/runtime": "1.5.5",
- "@xyflow/react": "12.8.4",
- "ag-charts-enterprise": "11.2.4",
- "ag-charts-react": "11.2.4",
- "ag-grid-community": "34.3.1",
- "ag-grid-enterprise": "34.3.1",
- "ag-grid-react": "34.3.1",
+ "@react-router/node": "7.15.0",
"clsx-for-tailwind": "1.0.0",
- "gridjs": "6.2.0",
- "gridjs-react": "6.1.1",
"hast-util-to-string": "3.0.1",
- "isbot": "5.1.39",
- "leaflet": "1.9.4",
- "lodash": "4.18.1",
- "lucide-react": "1.8.0",
- "mergician": "v2.0.2",
+ "isbot": "5.1.40",
+ "lucide-react": "1.14.0",
"moment": "2.30.1",
- "moment-timezone": "^0.6.2",
- "plotly.js": "3.5.0",
- "react": "19.2.5",
- "react-colorful": "5.7.0",
- "react-debounce-input": "3.3.0",
- "react-dnd": "^16.0.1",
- "react-dnd-html5-backend": "^16.0.1",
- "react-dom": "19.2.5",
- "react-dropzone": "15.0.0",
+ "react": "19.2.6",
+ "react-dom": "19.2.6",
"react-fast-marquee": "1.6.5",
"react-helmet": "6.1.0",
- "react-leaflet": "5.0.0",
"react-markdown": "10.1.0",
"react-moment": "1.2.2",
"react-player": "3.4.0",
- "react-plotly.js": "2.6.0",
- "react-responsive-carousel": "3.2.23",
- "react-router": "7.14.1",
- "react-router-dom": "7.14.1",
+ "react-router": "7.15.0",
+ "react-router-dom": "7.15.0",
"react-syntax-highlighter": "16.1.1",
- "reactflow": "^11.11.4",
- "recharts": "3.8.1",
- "rehype-autolink-headings": "7.1.0",
"rehype-katex": "7.0.1",
"rehype-raw": "7.0.0",
- "rehype-sanitize": "5.0.1",
- "rehype-slug": "6.0.0",
"rehype-unwrap-images": "1.0.0",
- "remark-emoji": "5.0.2",
"remark-gfm": "4.0.1",
"remark-math": "6.0.0",
"shiki": "3.3.0",
"socket.io-client": "4.8.3",
"sonner": "2.0.7",
"tailwindcss-animated": "2.0.0",
+ "tailwindcss-scroll-mask": "0.0.3@2.0.0",
"universal-cookie": "7.2.2",
"vaul": "1.1.2"
},
"devDependencies": {
"@emotion/react": "11.14.0",
- "@react-router/dev": "7.14.1",
- "@react-router/fs-routes": "7.14.1",
- "@tailwindcss/postcss": "4.2.3",
+ "@react-router/dev": "7.15.0",
+ "@react-router/fs-routes": "7.15.0",
+ "@tailwindcss/postcss": "4.3.0",
"@tailwindcss/typography": "0.5.19",
"autoprefixer": "10.5.0",
- "postcss": "8.5.10",
+ "postcss": "8.5.14",
"postcss-import": "16.1.1",
- "tailwindcss": "4.2.3",
- "vite": "8.0.9"
+ "tailwindcss": "4.3.0",
+ "vite": "8.0.12"
},
"overrides": {
"cookie": "1.1.1"
diff --git a/docs/app/styles/tailwind.css b/docs/app/styles/tailwind.css
new file mode 100644
index 00000000000..1242c0597fc
--- /dev/null
+++ b/docs/app/styles/tailwind.css
@@ -0,0 +1,6 @@
+@layer theme, base, components, utilities;
+@import "tailwindcss/theme.css" layer(theme);
+@import "tailwindcss/preflight.css" layer(base);
+@import "@radix-ui/themes/styles.css" layer(components);
+@import "tailwindcss/utilities.css" layer(utilities);
+@config "../tailwind.config.js";
diff --git a/docs/app/tailwind.config.js b/docs/app/tailwind.config.js
new file mode 100644
index 00000000000..805e2330a45
--- /dev/null
+++ b/docs/app/tailwind.config.js
@@ -0,0 +1,18 @@
+
+
+
+import plugin1 from "@tailwindcss/typography";
+
+
+
+export default {
+ content: ["./app/**/*.{js,ts,jsx,tsx}", "./utils/**/*.{js,ts,jsx,tsx}"],
+ theme: {},
+
+
+
+
+
+
+ plugins: [plugin1]
+};
diff --git a/packages/reflex-compiler-rust/.gitignore b/packages/reflex-compiler-rust/.gitignore
new file mode 100644
index 00000000000..08a548c5a57
--- /dev/null
+++ b/packages/reflex-compiler-rust/.gitignore
@@ -0,0 +1,7 @@
+target/
+dist/
+wheels/
+*.so
+*.pyd
+__pycache__/
+.venv/
diff --git a/packages/reflex-compiler-rust/Cargo.lock b/packages/reflex-compiler-rust/Cargo.lock
new file mode 100644
index 00000000000..18a0b68c4ae
--- /dev/null
+++ b/packages/reflex-compiler-rust/Cargo.lock
@@ -0,0 +1,419 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "autocfg"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
+
+[[package]]
+name = "bumpalo"
+version = "3.20.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+
+[[package]]
+name = "either"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "indoc"
+version = "2.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
+dependencies = [
+ "rustversion",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
+
+[[package]]
+name = "libc"
+version = "0.2.186"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
+
+[[package]]
+name = "memoffset"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.21.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
+
+[[package]]
+name = "portable-atomic"
+version = "1.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.106"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "pyo3"
+version = "0.22.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f402062616ab18202ae8319da13fa4279883a2b8a9d9f83f20dbade813ce1884"
+dependencies = [
+ "cfg-if",
+ "indoc",
+ "libc",
+ "memoffset",
+ "once_cell",
+ "portable-atomic",
+ "pyo3-build-config",
+ "pyo3-ffi",
+ "pyo3-macros",
+ "unindent",
+]
+
+[[package]]
+name = "pyo3-build-config"
+version = "0.22.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b14b5775b5ff446dd1056212d778012cbe8a0fbffd368029fd9e25b514479c38"
+dependencies = [
+ "once_cell",
+ "target-lexicon",
+]
+
+[[package]]
+name = "pyo3-ffi"
+version = "0.22.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ab5bcf04a2cdcbb50c7d6105de943f543f9ed92af55818fd17b660390fc8636"
+dependencies = [
+ "libc",
+ "pyo3-build-config",
+]
+
+[[package]]
+name = "pyo3-macros"
+version = "0.22.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fd24d897903a9e6d80b968368a34e1525aeb719d568dba8b3d4bfa5dc67d453"
+dependencies = [
+ "proc-macro2",
+ "pyo3-macros-backend",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "pyo3-macros-backend"
+version = "0.22.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36c011a03ba1e50152b4b394b479826cad97e7a21eb52df179cd91ac411cbfbe"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "pyo3-build-config",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rayon"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "reflex_arena"
+version = "0.0.0"
+dependencies = [
+ "bumpalo",
+]
+
+[[package]]
+name = "reflex_bench"
+version = "0.0.0"
+
+[[package]]
+name = "reflex_codegen"
+version = "0.0.0"
+dependencies = [
+ "itoa",
+ "reflex_arena",
+ "reflex_intern",
+ "reflex_ir",
+ "reflex_semantic",
+ "ryu",
+]
+
+[[package]]
+name = "reflex_db"
+version = "0.0.0"
+dependencies = [
+ "rayon",
+ "reflex_arena",
+ "reflex_codegen",
+ "reflex_intern",
+ "reflex_ir",
+ "thiserror",
+ "xxhash-rust",
+]
+
+[[package]]
+name = "reflex_intern"
+version = "0.0.0"
+
+[[package]]
+name = "reflex_ir"
+version = "0.0.0"
+dependencies = [
+ "bumpalo",
+ "reflex_arena",
+ "reflex_intern",
+ "rmp",
+ "thiserror",
+]
+
+[[package]]
+name = "reflex_py"
+version = "0.0.0"
+dependencies = [
+ "bumpalo",
+ "pyo3",
+ "reflex_arena",
+ "reflex_codegen",
+ "reflex_db",
+ "reflex_intern",
+ "reflex_ir",
+ "reflex_pyread",
+ "rmp",
+ "rmp-serde",
+ "serde",
+ "thiserror",
+]
+
+[[package]]
+name = "reflex_pyread"
+version = "0.0.0"
+dependencies = [
+ "pyo3",
+ "reflex_arena",
+ "reflex_intern",
+ "reflex_ir",
+ "thiserror",
+]
+
+[[package]]
+name = "reflex_semantic"
+version = "0.0.0"
+dependencies = [
+ "reflex_arena",
+ "reflex_intern",
+ "reflex_ir",
+]
+
+[[package]]
+name = "reflex_vars"
+version = "0.0.0"
+
+[[package]]
+name = "rmp"
+version = "0.8.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ba8be72d372b2c9b35542551678538b562e7cf86c3315773cae48dfbfe7790c"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "rmp-serde"
+version = "1.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155"
+dependencies = [
+ "rmp",
+ "serde",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
+
+[[package]]
+name = "ryu"
+version = "1.0.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
+
+[[package]]
+name = "serde"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+dependencies = [
+ "serde_core",
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "syn"
+version = "2.0.117"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "target-lexicon"
+version = "0.12.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
+
+[[package]]
+name = "thiserror"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
+
+[[package]]
+name = "unindent"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3"
+
+[[package]]
+name = "xxhash-rust"
+version = "0.8.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3"
diff --git a/packages/reflex-compiler-rust/Cargo.toml b/packages/reflex-compiler-rust/Cargo.toml
new file mode 100644
index 00000000000..fd8f25eb992
--- /dev/null
+++ b/packages/reflex-compiler-rust/Cargo.toml
@@ -0,0 +1,55 @@
+[workspace]
+resolver = "2"
+members = [
+ "crates/reflex_ir",
+ "crates/reflex_arena",
+ "crates/reflex_intern",
+ "crates/reflex_db",
+ "crates/reflex_semantic",
+ "crates/reflex_codegen",
+ "crates/reflex_py",
+ "crates/reflex_bench",
+ "crates/reflex_vars",
+ "crates/reflex_pyread",
+]
+
+[workspace.package]
+edition = "2021"
+rust-version = "1.79"
+license = "Apache-2.0"
+publish = false
+
+[workspace.dependencies]
+# Local crates
+reflex_ir = { path = "crates/reflex_ir" }
+reflex_arena = { path = "crates/reflex_arena" }
+reflex_intern = { path = "crates/reflex_intern" }
+reflex_db = { path = "crates/reflex_db" }
+reflex_semantic = { path = "crates/reflex_semantic" }
+reflex_codegen = { path = "crates/reflex_codegen" }
+reflex_vars = { path = "crates/reflex_vars" }
+reflex_pyread = { path = "crates/reflex_pyread" }
+
+# Internal-but-shared
+xxhash-rust = { version = "0.8", features = ["xxh3"] }
+
+# External
+pyo3 = { version = "0.22", features = ["abi3-py310", "extension-module"] }
+bumpalo = { version = "3.16", features = ["collections"] }
+rayon = "1.10"
+rmp = "0.8"
+rmp-serde = "1.3"
+serde = { version = "1", features = ["derive"] }
+thiserror = "1.0"
+
+[profile.release]
+opt-level = 3
+lto = "fat"
+codegen-units = 1
+debug = false
+strip = "symbols"
+
+[profile.bench]
+inherits = "release"
+debug = true
+strip = "none"
diff --git a/packages/reflex-compiler-rust/README.md b/packages/reflex-compiler-rust/README.md
new file mode 100644
index 00000000000..3bcf460e8e8
--- /dev/null
+++ b/packages/reflex-compiler-rust/README.md
@@ -0,0 +1,66 @@
+# reflex-compiler-rust (spike phase)
+
+Workspace skeleton for the Rust rewrite of the Reflex compiler. See
+`ignore/RUST_REWRITE_PLAN.md` (§3 — crate skeleton) for the long version.
+
+**Status:** scaffold + napkin-math benchmarks only. No real compiler logic
+yet. All non-`reflex_py` crates are stub `lib.rs` files. Do NOT depend on
+this package; it is not published and the IR contract is in flux.
+
+## Why this exists right now
+
+The plan claims the round trip `Python → msgpack → Rust → emit → bytes`
+beats the current Python compiler. Before writing any real port we need
+numbers on the dominant costs:
+
+1. PyO3 crossing cost (per call)
+2. msgpack serialize on the Python side
+3. msgpack deserialize on the Rust side (rmp-serde vs hand-rolled)
+4. Tree walk + JSX-like emit (Python vs Rust)
+
+The spike code lives in `crates/reflex_py/src/lib.rs`; the driver in
+`scripts/spike_bench.py`.
+
+## Build + run
+
+```
+cd packages/reflex-compiler-rust
+uv venv .venv && . .venv/bin/activate
+uv pip install maturin msgpack
+maturin develop --release
+uv run python scripts/spike_bench.py --sizes 10,100,1000,10000
+```
+
+`maturin develop --release` is non-negotiable — the debug profile is 5–20×
+slower and would lie about the steady-state cost.
+
+## Layout
+
+```
+Cargo.toml # workspace root, all 8 crates
+pyproject.toml # maturin, abi3-py310, points to crates/reflex_py
+rust-toolchain.toml # pinned stable
+python/reflex_compiler_rust # thin python wrapper around the cdylib
+crates/
+ reflex_ir/ stub # IR enum types — §3.2
+ reflex_arena/ stub # bumpalo + thread-local stash — §3.1
+ reflex_intern/ stub # Symbol interning — §3
+ reflex_db/ stub # Salsa boundary — §3.3
+ reflex_semantic/ stub # six aggregator walks — D7
+ reflex_codegen/ stub # IR → JS/JSX/CSS — D8
+ reflex_py/ REAL # PyO3 entry + spike probes
+ reflex_bench/ stub # criterion benches (TODO)
+scripts/
+ spike_bench.py # the napkin-math driver
+```
+
+## What the spike measures vs the real plan
+
+| Spike probe | Plan section / claim it tests |
+|------------------------------------|-------------------------------|
+| `empty_call`, `bytes_passthrough` | §5 hot-reload budget — the per-call floor |
+| `pack_streaming` (Python) | §1 "no per-node dict" / §5 IR-emit <0.10ms/page |
+| `walk_serde` | Pitfall 12: "rmp-serde allocates strings to heap" |
+| `walk_manual` | §1 "custom Rust deserializer into bumpalo arena" |
+| `walk_manual_emit` / `walk_arena_emit` | §5 "Rust compile work 2.81s → <0.5s" |
+| `walk_emit_join` (Python) | The Python baseline for codegen |
diff --git a/packages/reflex-compiler-rust/crates/reflex_arena/Cargo.toml b/packages/reflex-compiler-rust/crates/reflex_arena/Cargo.toml
new file mode 100644
index 00000000000..c0f9838b41e
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_arena/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "reflex_arena"
+version = "0.0.0"
+edition.workspace = true
+rust-version.workspace = true
+license.workspace = true
+publish.workspace = true
+
+[dependencies]
+bumpalo = { workspace = true }
diff --git a/packages/reflex-compiler-rust/crates/reflex_arena/src/lib.rs b/packages/reflex-compiler-rust/crates/reflex_arena/src/lib.rs
new file mode 100644
index 00000000000..745f420666c
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_arena/src/lib.rs
@@ -0,0 +1,186 @@
+//! Bump arena + thread-local single-slot stash. See plan §3.1, §3.3, R1/R2/R8.
+//!
+//! R2 (no-Drop): `Arena::alloc` requires `T: Copy`, which Rust enforces is
+//! mutually exclusive with `Drop`. The const assertion is belt-and-braces —
+//! it catches `Copy` types that wrap a `ManuallyDrop`-disabled non-trivial
+//! payload before they ever reach a hot path.
+//!
+//! R8 deviation: the plan calls for `#[thread_local]` (nightly-only). We use
+//! the stable `thread_local!` macro. The teardown race R8 cites is specific
+//! to bun's mimalloc; `bumpalo::Bump` releases via the default allocator at
+//! Drop, so the destructor ordering issue does not apply.
+
+use std::cell::{Cell, RefCell};
+
+use bumpalo::Bump;
+
+pub struct Arena(Bump);
+
+impl Arena {
+ #[inline]
+ pub fn new() -> Self {
+ Self(Bump::new())
+ }
+
+ #[inline]
+ pub fn with_capacity(cap: usize) -> Self {
+ Self(Bump::with_capacity(cap))
+ }
+
+ /// Allocate one `T`. `T: Copy` is the R2 enforcement; the const assert is
+ /// a redundancy that catches future regressions.
+ #[inline(always)]
+ pub fn alloc(&self, value: T) -> &mut T {
+ const { assert!(!std::mem::needs_drop::()) }
+ self.0.alloc(value)
+ }
+
+ #[inline]
+ pub fn alloc_slice_copy(&self, src: &[T]) -> &mut [T] {
+ const { assert!(!std::mem::needs_drop::()) }
+ self.0.alloc_slice_copy(src)
+ }
+
+ /// Build a slice from an iterator, copying through the arena.
+ #[inline]
+ pub fn alloc_slice_fill_iter(&self, iter: I) -> &mut [T]
+ where
+ T: Copy,
+ I: IntoIterator- ,
+ I::IntoIter: ExactSizeIterator,
+ {
+ const { assert!(!std::mem::needs_drop::
()) }
+ self.0.alloc_slice_fill_iter(iter)
+ }
+
+ #[inline]
+ pub fn alloc_str(&self, s: &str) -> &str {
+ self.0.alloc_str(s)
+ }
+
+ #[inline]
+ pub fn bump(&self) -> &Bump {
+ &self.0
+ }
+
+ #[inline]
+ pub fn reset(&mut self) {
+ self.0.reset();
+ }
+
+ #[inline]
+ pub fn allocated_bytes(&self) -> usize {
+ self.0.allocated_bytes()
+ }
+}
+
+impl Default for Arena {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+thread_local! {
+ static ARENA_STASH: RefCell> = const { RefCell::new(None) };
+}
+
+/// An arena rented from the thread-local stash. Returned to the stash on Drop,
+/// or freed if the stash is occupied (bun's single-slot semantic — nested
+/// scopes are rare; the second arena drops normally).
+pub struct PooledArena {
+ arena: Option,
+ used: Cell,
+}
+
+impl PooledArena {
+ #[inline]
+ pub fn acquire() -> Self {
+ let arena = ARENA_STASH
+ .with(|cell| cell.borrow_mut().take())
+ .unwrap_or_else(Arena::new);
+ Self {
+ arena: Some(arena),
+ used: Cell::new(false),
+ }
+ }
+
+ /// Borrow the arena. First call marks the arena as used; Drop will reset
+ /// before returning to the stash. If never called, Drop skips the reset
+ /// (R8 dirty-bit).
+ #[inline]
+ pub fn arena(&self) -> &Arena {
+ self.used.set(true);
+ self.arena
+ .as_ref()
+ .expect("arena always present until Drop")
+ }
+}
+
+impl Drop for PooledArena {
+ fn drop(&mut self) {
+ let Some(mut arena) = self.arena.take() else {
+ return;
+ };
+ if self.used.get() {
+ arena.reset();
+ }
+ ARENA_STASH.with(|cell| {
+ let mut slot = cell.borrow_mut();
+ if slot.is_none() {
+ *slot = Some(arena);
+ }
+ });
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[derive(Clone, Copy)]
+ struct Point {
+ x: i32,
+ y: i32,
+ }
+
+ #[test]
+ fn alloc_returns_arena_ref() {
+ let arena = Arena::new();
+ let p = arena.alloc(Point { x: 1, y: 2 });
+ assert_eq!(p.x, 1);
+ assert_eq!(p.y, 2);
+ }
+
+ #[test]
+ fn alloc_str_copies_into_arena() {
+ let arena = Arena::new();
+ let s = String::from("hello");
+ let r = arena.alloc_str(&s);
+ drop(s);
+ assert_eq!(r, "hello");
+ }
+
+ #[test]
+ fn alloc_slice_copies() {
+ let arena = Arena::new();
+ let src = [1u32, 2, 3, 4];
+ let r = arena.alloc_slice_copy(&src);
+ assert_eq!(r, &[1, 2, 3, 4]);
+ }
+
+ #[test]
+ fn pooled_arena_round_trip() {
+ let a = PooledArena::acquire();
+ let _ = a.arena().alloc(42u32);
+ drop(a);
+ let b = PooledArena::acquire();
+ let _ = b.arena().alloc(7u32);
+ drop(b);
+ }
+
+ #[test]
+ fn unused_pooled_arena_skips_reset() {
+ let a = PooledArena::acquire();
+ drop(a);
+ }
+}
diff --git a/packages/reflex-compiler-rust/crates/reflex_bench/Cargo.toml b/packages/reflex-compiler-rust/crates/reflex_bench/Cargo.toml
new file mode 100644
index 00000000000..d9e9de3ca6e
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_bench/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "reflex_bench"
+version = "0.0.0"
+edition.workspace = true
+rust-version.workspace = true
+license.workspace = true
+publish.workspace = true
diff --git a/packages/reflex-compiler-rust/crates/reflex_bench/src/lib.rs b/packages/reflex-compiler-rust/crates/reflex_bench/src/lib.rs
new file mode 100644
index 00000000000..1b34ce55d77
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_bench/src/lib.rs
@@ -0,0 +1 @@
+//! Fixture-driven Rust benchmarks (criterion etc.). Stub during spike phase — spike benches live in `reflex_py`.
diff --git a/packages/reflex-compiler-rust/crates/reflex_codegen/Cargo.toml b/packages/reflex-compiler-rust/crates/reflex_codegen/Cargo.toml
new file mode 100644
index 00000000000..84d45e8b3cf
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_codegen/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "reflex_codegen"
+version = "0.0.0"
+edition.workspace = true
+rust-version.workspace = true
+license.workspace = true
+publish.workspace = true
+
+[dependencies]
+reflex_ir = { workspace = true }
+reflex_intern = { workspace = true }
+reflex_arena = { workspace = true }
+reflex_semantic = { workspace = true }
+itoa = "1"
+ryu = "1"
diff --git a/packages/reflex-compiler-rust/crates/reflex_codegen/src/app_root.rs b/packages/reflex-compiler-rust/crates/reflex_codegen/src/app_root.rs
new file mode 100644
index 00000000000..903179b7a01
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_codegen/src/app_root.rs
@@ -0,0 +1,72 @@
+//! App-root wrapper emission. See plan §4.5 (artifact #5), D10.
+//!
+//! The app root is the top-level React component that wraps every page with
+//! providers (state, theme, color-mode) plus error boundaries. The shape
+//! mirrors Reflex's existing template, with imports gathered from the
+//! `PluginManifest.stylesheet_imports` list.
+
+use reflex_intern::resolve_unchecked;
+use reflex_ir::PluginManifest;
+
+use crate::buffer::CodeBuffer;
+
+pub fn emit_app_root(buf: &mut CodeBuffer, manifest: &PluginManifest<'_>) {
+ buf.write_str("import { jsx, Fragment } from \"react/jsx-runtime\";\n");
+ buf.write_str("import { StateContext, ColorModeContext, initialState } from \"./context\";\n");
+ buf.write_str("import { useState } from \"react\";\n");
+
+ // Plugin stylesheets — `import "./some.css";` side-effect imports.
+ let mut emitted = std::collections::HashSet::new();
+ for plugin in manifest.plugins {
+ for ss in plugin.stylesheet_imports {
+ if !emitted.insert(*ss) {
+ continue;
+ }
+ buf.write_str("import ");
+ buf.write_js_string(resolve_unchecked(*ss));
+ buf.write_str(";\n");
+ }
+ }
+
+ buf.write_str("\nexport default function AppWrap({ children }) {\n");
+ buf.write_str(" const [state, setState] = useState(initialState);\n");
+ buf.write_str(" const [colorMode, setColorMode] = useState(\"system\");\n");
+ buf.write_str(" return jsx(StateContext.Provider, { value: state }, ");
+ buf.write_str("jsx(ColorModeContext.Provider, { value: colorMode }, children));\n");
+ buf.write_str("}\n");
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use reflex_ir::{PluginEntry, PluginManifest};
+
+ #[test]
+ fn emits_app_root_shell() {
+ let manifest = PluginManifest {
+ schema_version: 1,
+ plugins: &[],
+ };
+ let mut buf = CodeBuffer::new();
+ emit_app_root(&mut buf, &manifest);
+ let s = buf.as_str();
+ assert!(s.contains("export default function AppWrap"));
+ assert!(s.contains("StateContext.Provider"));
+ }
+
+ #[test]
+ fn emits_plugin_stylesheets() {
+ let entry = PluginEntry {
+ name: reflex_intern::intern("radix"),
+ static_assets: &[],
+ stylesheet_imports: &[reflex_intern::intern("@radix-ui/themes/styles.css")],
+ };
+ let manifest = PluginManifest {
+ schema_version: 1,
+ plugins: std::slice::from_ref(&entry),
+ };
+ let mut buf = CodeBuffer::new();
+ emit_app_root(&mut buf, &manifest);
+ assert!(buf.as_str().contains("import \"@radix-ui/themes/styles.css\""));
+ }
+}
diff --git a/packages/reflex-compiler-rust/crates/reflex_codegen/src/app_root_module.rs b/packages/reflex-compiler-rust/crates/reflex_codegen/src/app_root_module.rs
new file mode 100644
index 00000000000..23f8d8ced82
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_codegen/src/app_root_module.rs
@@ -0,0 +1,130 @@
+//! Port of `app_root_template` (the legacy `.web/app/root.jsx`
+//! template), distinct from the IR-based `emit_app_root` in `app_root.rs`.
+//!
+//! The legacy template lays out the React-Router root: providers
+//! (state, theme, event loop), an `AppWrap` shell that runs hooks +
+//! renders the app-wrap-composed Component tree, the document Layout
+//! wrap, and the EmbedLayout fallback used when `mount_target` is set.
+//!
+//! Phase-1 port: Python still does the dynamic rendering (component
+//! → JSX, hooks → JS, imports → import lines) — those depend on the
+//! legacy `_RenderUtils.render` walk. Rust assembles the static
+//! template + splices the pre-rendered strings. Phase 2 will move
+//! the renderers to Rust too.
+
+use std::io::{self, Write};
+
+/// Emit `.web/app/root.jsx`.
+///
+/// Mirrors `app_root_template` in
+/// `packages/reflex-base/src/reflex_base/compiler/templates.py`. The
+/// rendered output starts with a leading newline to match the legacy
+/// f-string's `\n{imports_str}...` opening.
+///
+/// Args:
+/// imports_str: rendered `import { … } from "…"` lines, joined
+/// with `\n` by the caller.
+/// dynamic_imports_str: rendered dynamic-import statements, joined
+/// with `\n` by the caller.
+/// custom_code_str: any user-contributed top-level code, joined
+/// with `\n`.
+/// hooks_str: rendered hook body (already includes its leading
+/// indentation), or empty.
+/// render_str: rendered JSX expression for the app-wrap chain — the
+/// expression spliced inside `return (…)` in `AppWrap`.
+/// import_window_libraries: rendered
+/// `import * as from "…"` lines for window libraries.
+/// window_imports_str: rendered `"": ,` entries that
+/// populate the global `window["__reflex"]` mapping.
+/// w: byte sink.
+pub fn emit_app_root_module(
+ imports_str: &str,
+ dynamic_imports_str: &str,
+ custom_code_str: &str,
+ hooks_str: &str,
+ render_str: &str,
+ import_window_libraries: &str,
+ window_imports_str: &str,
+ w: &mut W,
+) -> io::Result<()> {
+ w.write_all(b"\n")?;
+ w.write_all(imports_str.as_bytes())?;
+ w.write_all(b"\n")?;
+ w.write_all(dynamic_imports_str.as_bytes())?;
+ w.write_all(
+ b"\nimport { EventLoopProvider, StateProvider, defaultColorMode } from \"$/utils/context\";\n",
+ )?;
+ w.write_all(b"import { ThemeProvider } from '$/utils/react-theme';\n")?;
+ w.write_all(b"import { Layout as AppLayout } from './_document';\n")?;
+ w.write_all(b"import { Outlet } from 'react-router';\n")?;
+ w.write_all(import_window_libraries.as_bytes())?;
+ w.write_all(b"\n\n")?;
+ w.write_all(custom_code_str.as_bytes())?;
+ w.write_all(b"\n\nfunction ReflexProviders({children}) {\n")?;
+ w.write_all(b" useEffect(() => {\n")?;
+ w.write_all(b" // Make contexts and state objects available globally for dynamic eval'd components\n")?;
+ w.write_all(b" let windowImports = {\n ")?;
+ w.write_all(window_imports_str.as_bytes())?;
+ w.write_all(b"\n };\n window[\"__reflex\"] = windowImports;\n }, []);\n\n")?;
+ w.write_all(
+ b" return jsx(ThemeProvider, {defaultTheme: defaultColorMode, attribute: \"class\"},\n",
+ )?;
+ w.write_all(b" jsx(StateProvider, {},\n jsx(EventLoopProvider, {},\n jsx(AppWrap, {}, children)\n )\n )\n );\n}\n\n\n")?;
+ w.write_all(b"function AppWrap({children}) {\n")?;
+ w.write_all(hooks_str.as_bytes())?;
+ w.write_all(b"\nreturn (")?;
+ w.write_all(render_str.as_bytes())?;
+ w.write_all(b")\n}\n\n")?;
+ w.write_all(b"export function Layout({children}) {\n")?;
+ w.write_all(b" return jsx(AppLayout, {}, jsx(ReflexProviders, {}, children));\n")?;
+ w.write_all(b"}\n\n")?;
+ w.write_all(b"// Used by entry.client.js when mount_target is configured: skips the document\n")?;
+ w.write_all(b"// shell (which renders react-router's // and requires a\n")?;
+ w.write_all(b"// framework router context) but keeps the runtime providers.\n")?;
+ w.write_all(b"export function EmbedLayout({children}) {\n")?;
+ w.write_all(b" return jsx(ReflexProviders, {}, children);\n")?;
+ w.write_all(b"}\n\n")?;
+ w.write_all(b"export default function App() {\n return jsx(Outlet, {});\n}\n\n")?;
+ Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ fn render(f: F) -> String
+ where
+ F: FnOnce(&mut Vec) -> io::Result<()>,
+ {
+ let mut buf = Vec::new();
+ f(&mut buf).unwrap();
+ String::from_utf8(buf).unwrap()
+ }
+
+ #[test]
+ fn includes_react_router_outlet_default() {
+ let out = render(|w| emit_app_root_module("", "", "", "", "null", "", "", w));
+ assert!(out.contains("export default function App()"));
+ assert!(out.contains("return jsx(Outlet, {});"));
+ }
+
+ #[test]
+ fn splices_render_string_into_appwrap() {
+ let out = render(|w| {
+ emit_app_root_module(
+ "import {Foo} from \"bar\";",
+ "",
+ "// custom",
+ " // hooks",
+ "jsx(MyApp, {})",
+ "",
+ "",
+ w,
+ )
+ });
+ assert!(out.contains("import {Foo} from \"bar\";"));
+ assert!(out.contains("// custom"));
+ assert!(out.contains("// hooks"));
+ assert!(out.contains("return (jsx(MyApp, {}))"));
+ }
+}
diff --git a/packages/reflex-compiler-rust/crates/reflex_codegen/src/buffer.rs b/packages/reflex-compiler-rust/crates/reflex_codegen/src/buffer.rs
new file mode 100644
index 00000000000..794315f147e
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_codegen/src/buffer.rs
@@ -0,0 +1,168 @@
+//! Output buffer. Single growable `Vec` per emit (R6).
+//!
+//! The buffer is allocated upfront with a capacity hint from the IR size
+//! estimate (caller-supplied). Every `write_*` method appends raw bytes —
+//! there are no per-node `format!` or `String::new()` allocations.
+
+use std::fmt;
+
+pub struct CodeBuffer {
+ buf: Vec,
+}
+
+impl CodeBuffer {
+ #[inline]
+ pub fn new() -> Self {
+ Self { buf: Vec::new() }
+ }
+
+ #[inline]
+ pub fn with_capacity(cap: usize) -> Self {
+ Self {
+ buf: Vec::with_capacity(cap),
+ }
+ }
+
+ #[inline(always)]
+ pub fn write_byte(&mut self, b: u8) {
+ self.buf.push(b);
+ }
+
+ #[inline(always)]
+ pub fn write_bytes(&mut self, bytes: &[u8]) {
+ self.buf.extend_from_slice(bytes);
+ }
+
+ #[inline(always)]
+ pub fn write_str(&mut self, s: &str) {
+ self.buf.extend_from_slice(s.as_bytes());
+ }
+
+ /// Emit a JS string literal with double quotes. Escapes the few JS-special
+ /// characters; assumes input is otherwise already-encoded JS source.
+ pub fn write_js_string(&mut self, s: &str) {
+ self.buf.push(b'"');
+ for &b in s.as_bytes() {
+ match b {
+ b'"' => self.buf.extend_from_slice(b"\\\""),
+ b'\\' => self.buf.extend_from_slice(b"\\\\"),
+ b'\n' => self.buf.extend_from_slice(b"\\n"),
+ b'\r' => self.buf.extend_from_slice(b"\\r"),
+ b'\t' => self.buf.extend_from_slice(b"\\t"),
+ 0x00..=0x1F => {
+ self.buf.extend_from_slice(b"\\u00");
+ self.buf.push(hex_nibble(b >> 4));
+ self.buf.push(hex_nibble(b & 0x0F));
+ }
+ _ => self.buf.push(b),
+ }
+ }
+ self.buf.push(b'"');
+ }
+
+ /// Append a decimal integer.
+ pub fn write_u64(&mut self, n: u64) {
+ let mut tmp = itoa::Buffer::new();
+ self.buf.extend_from_slice(tmp.format(n).as_bytes());
+ }
+
+ pub fn write_i64(&mut self, n: i64) {
+ let mut tmp = itoa::Buffer::new();
+ self.buf.extend_from_slice(tmp.format(n).as_bytes());
+ }
+
+ pub fn write_f64(&mut self, n: f64) {
+ let mut tmp = ryu::Buffer::new();
+ self.buf.extend_from_slice(tmp.format(n).as_bytes());
+ }
+
+ #[inline]
+ pub fn len(&self) -> usize {
+ self.buf.len()
+ }
+
+ #[inline]
+ pub fn is_empty(&self) -> bool {
+ self.buf.is_empty()
+ }
+
+ #[inline]
+ pub fn as_bytes(&self) -> &[u8] {
+ &self.buf
+ }
+
+ pub fn into_bytes(self) -> Vec {
+ self.buf
+ }
+
+ pub fn as_str(&self) -> &str {
+ // All `write_*` paths emit UTF-8 bytes (we accept `&str` inputs and
+ // never split multi-byte sequences). If a caller writes raw bytes
+ // that aren't valid UTF-8, this will panic — that's a programmer
+ // error in codegen, not user input.
+ std::str::from_utf8(&self.buf).expect("CodeBuffer must contain UTF-8")
+ }
+}
+
+impl Default for CodeBuffer {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl fmt::Debug for CodeBuffer {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("CodeBuffer")
+ .field("len", &self.buf.len())
+ .finish()
+ }
+}
+
+#[inline(always)]
+fn hex_nibble(n: u8) -> u8 {
+ match n {
+ 0..=9 => b'0' + n,
+ 10..=15 => b'a' + (n - 10),
+ _ => unreachable!(),
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn writes_string_unescaped_when_safe() {
+ let mut b = CodeBuffer::new();
+ b.write_js_string("hello world");
+ assert_eq!(b.as_str(), "\"hello world\"");
+ }
+
+ #[test]
+ fn escapes_double_quotes() {
+ let mut b = CodeBuffer::new();
+ b.write_js_string(r#"say "hi""#);
+ assert_eq!(b.as_str(), r#""say \"hi\"""#);
+ }
+
+ #[test]
+ fn escapes_newlines() {
+ let mut b = CodeBuffer::new();
+ b.write_js_string("a\nb");
+ assert_eq!(b.as_str(), r#""a\nb""#);
+ }
+
+ #[test]
+ fn writes_decimal_int() {
+ let mut b = CodeBuffer::new();
+ b.write_u64(12345);
+ assert_eq!(b.as_str(), "12345");
+ }
+
+ #[test]
+ fn writes_negative_int() {
+ let mut b = CodeBuffer::new();
+ b.write_i64(-42);
+ assert_eq!(b.as_str(), "-42");
+ }
+}
diff --git a/packages/reflex-compiler-rust/crates/reflex_codegen/src/context.rs b/packages/reflex-compiler-rust/crates/reflex_codegen/src/context.rs
new file mode 100644
index 00000000000..9d2cfc56e07
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_codegen/src/context.rs
@@ -0,0 +1,119 @@
+//! Context file emission: the JS shell that wraps the Python-computed
+//! initial state JSON and client-storage config. See plan §4.3, D10.
+//!
+//! The shape (verbatim Reflex convention):
+//!
+//! ```js
+//! import { createContext } from "react";
+//!
+//! export const StateContext = createContext(null);
+//! export const ColorModeContext = createContext(null);
+//!
+//! export const initialState = ;
+//! export const clientStorage = ;
+//! export const computedVarDeps = {: [, ...], ...};
+//! ```
+//!
+//! State *values* come from Python (Pydantic + async resolution); Rust just
+//! emits the JS shell that splices them in. Rust never inspects the JSON.
+
+use reflex_intern::resolve_unchecked;
+use reflex_ir::GlobalState;
+
+use crate::buffer::CodeBuffer;
+
+pub fn emit_context(buf: &mut CodeBuffer, state: &GlobalState<'_>) {
+ buf.write_str("import { createContext } from \"react\";\n\n");
+ buf.write_str("export const StateContext = createContext(null);\n");
+ buf.write_str("export const ColorModeContext = createContext(null);\n\n");
+
+ buf.write_str("export const initialState = ");
+ write_json_blob(buf, state.initial_state_json);
+ buf.write_str(";\n");
+
+ buf.write_str("export const clientStorage = ");
+ write_json_blob(buf, state.client_storage_json);
+ buf.write_str(";\n");
+
+ buf.write_str("export const computedVarDeps = {");
+ for (i, dep) in state.computed_var_deps.iter().enumerate() {
+ if i > 0 {
+ buf.write_str(", ");
+ }
+ buf.write_js_string(resolve_unchecked(dep.var));
+ buf.write_str(": [");
+ for (j, depend) in dep.depends_on.iter().enumerate() {
+ if j > 0 {
+ buf.write_str(", ");
+ }
+ buf.write_js_string(resolve_unchecked(*depend));
+ }
+ buf.write_str("]");
+ }
+ buf.write_str("};\n");
+}
+
+/// Write a pre-encoded JSON blob. If the blob isn't valid UTF-8 we fall back
+/// to `null` — that means the Python side shipped garbage, which is a
+/// programmer error, but we don't want to crash the emit pipeline.
+fn write_json_blob(buf: &mut CodeBuffer, blob: &[u8]) {
+ match std::str::from_utf8(blob) {
+ Ok(s) if !s.is_empty() => buf.write_str(s),
+ _ => buf.write_str("null"),
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use reflex_ir::{ComputedVarDep, GlobalState};
+
+ #[test]
+ fn emits_basic_shell() {
+ let state = GlobalState {
+ schema_version: 1,
+ initial_state_json: b"{\"count\": 0}",
+ client_storage_json: b"{}",
+ computed_var_deps: &[],
+ };
+ let mut buf = CodeBuffer::new();
+ emit_context(&mut buf, &state);
+ let s = buf.as_str();
+ assert!(s.contains("import { createContext } from \"react\";"));
+ assert!(s.contains("export const initialState = {\"count\": 0};"));
+ assert!(s.contains("export const clientStorage = {};"));
+ assert!(s.contains("export const computedVarDeps = {};"));
+ }
+
+ #[test]
+ fn empty_json_emits_null() {
+ let state = GlobalState {
+ schema_version: 1,
+ initial_state_json: b"",
+ client_storage_json: b"",
+ computed_var_deps: &[],
+ };
+ let mut buf = CodeBuffer::new();
+ emit_context(&mut buf, &state);
+ let s = buf.as_str();
+ assert!(s.contains("export const initialState = null;"));
+ }
+
+ #[test]
+ fn emits_computed_var_deps() {
+ let dep = ComputedVarDep {
+ var: reflex_intern::intern("total"),
+ depends_on: &[reflex_intern::intern("a"), reflex_intern::intern("b")],
+ };
+ let state = GlobalState {
+ schema_version: 1,
+ initial_state_json: b"null",
+ client_storage_json: b"null",
+ computed_var_deps: std::slice::from_ref(&dep),
+ };
+ let mut buf = CodeBuffer::new();
+ emit_context(&mut buf, &state);
+ let s = buf.as_str();
+ assert!(s.contains("\"total\": [\"a\", \"b\"]"));
+ }
+}
diff --git a/packages/reflex-compiler-rust/crates/reflex_codegen/src/context_module.rs b/packages/reflex-compiler-rust/crates/reflex_codegen/src/context_module.rs
new file mode 100644
index 00000000000..5601152c734
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_codegen/src/context_module.rs
@@ -0,0 +1,324 @@
+//! Port of `context_template` —
+//! `packages/reflex-base/src/reflex_base/compiler/templates.py`.
+//!
+//! The legacy template takes the resolved state hierarchy + client-
+//! storage config and emits the full `utils/context.js` module. We
+//! port the static template assembly to Rust; the dynamic inputs
+//! (initial-state JSON, client-storage JSON, default-color-mode JS
+//! literal) are pre-serialized by the Python caller and spliced in
+//! verbatim.
+//!
+//! Streams output through a `Write` so the PyO3 binding can drive a
+//! `BufWriter` and skip any intermediate `String` allocation.
+
+use std::io::{self, Write};
+
+/// Hardcoded constants from
+/// `reflex_base.constants.compiler.CompileVars`. They're
+/// implementation-defined and never change at runtime, so we keep them
+/// in Rust instead of marshalling them across PyO3 every call.
+const FRONTEND_EXCEPTION_STATE_FULL: &str =
+ "reflex___state____state.reflex___state____frontend_event_exception_state";
+const UPDATE_VARS_INTERNAL: &str =
+ "reflex___state____update_vars_internal_state.update_vars_internal";
+const ON_LOAD_INTERNAL: &str = "reflex___state____on_load_internal_state.on_load_internal";
+const HYDRATE: &str = "hydrate";
+
+/// Emit `.web/utils/context.js`.
+///
+/// Args:
+/// is_dev_mode: emitted as `isDevMode = `.
+/// default_color_mode_js: JS expression for the default color mode
+/// — the legacy emitter passes either a quoted string like
+/// `"system"` or the literal `"system"`, depending on app
+/// config. Passed verbatim.
+/// state_name: the root state's full dotted name (e.g.
+/// `reflex___state____state`). `None` produces the no-state
+/// fallback (`onLoadInternalEvent = () => []`).
+/// state_keys: the state names whose contexts and stores we wire up.
+/// Each gets `replace('.', '__')` applied for its JS identifier.
+/// initial_state_json: the JSON-serialized initial state dict,
+/// already produced by the Python caller via `json_dumps`.
+/// If `state_keys` is empty this is normally `"{}"`.
+/// client_storage_json: the JSON-serialized client storage config,
+/// already produced by `json.dumps` (or `"{}"` if `None`).
+/// w: byte sink.
+pub fn emit_context_module(
+ is_dev_mode: bool,
+ default_color_mode_js: &str,
+ state_name: Option<&str>,
+ state_keys: &[&str],
+ initial_state_json: &str,
+ client_storage_json: &str,
+ w: &mut W,
+) -> io::Result<()> {
+ // Header — imports + top-level constants.
+ w.write_all(
+ b"import { createContext, useContext, useMemo, useReducer, useState, createElement, useEffect } from \"react\"\n",
+ )?;
+ w.write_all(
+ b"import { applyDelta, ReflexEvent, hydrateClientStorage, useEventLoop, refs } from \"$/utils/state\"\n",
+ )?;
+ w.write_all(b"import { jsx } from \"@emotion/react\";\n\n")?;
+
+ // `export const initialState = { … }` — pre-serialized by Python.
+ w.write_all(b"export const initialState = ")?;
+ w.write_all(initial_state_json.as_bytes())?;
+ w.write_all(b"\n\n")?;
+
+ w.write_all(b"export const defaultColorMode = ")?;
+ w.write_all(default_color_mode_js.as_bytes())?;
+ w.write_all(
+ b"\nexport const ColorModeContext = createContext({\n \
+ colorMode: defaultColorMode,\n \
+ resolvedColorMode: defaultColorMode === \"dark\" ? \"dark\" : \"light\",\n \
+ toggleColorMode: () => {},\n \
+ setColorMode: () => {},\n\
+ });\n",
+ )?;
+ w.write_all(b"export const UploadFilesContext = createContext(null);\n")?;
+ w.write_all(b"export const DispatchContext = createContext(null);\n")?;
+
+ // `StateContexts = { foo: createContext(null), bar: ... };` —
+ // concatenated with NO separator (matches the legacy template's
+ // ''.join with each entry ending in ',').
+ w.write_all(b"export const StateContexts = {")?;
+ for key in state_keys {
+ write_formatted_state_name(w, key)?;
+ w.write_all(b": createContext(null),")?;
+ }
+ w.write_all(b"};\n")?;
+
+ w.write_all(b"export const EventLoopContext = createContext(null);\n")?;
+ w.write_all(b"export const clientStorage = ")?;
+ w.write_all(client_storage_json.as_bytes())?;
+ w.write_all(b"\n\n")?;
+
+ // State block: either the rich version when a state root exists, or
+ // a no-op fallback. Mirrors the conditional in the legacy template.
+ match state_name {
+ Some(sn) => emit_state_block_with_state(w, sn)?,
+ None => emit_state_block_no_state(w)?,
+ }
+ // Legacy template puts an empty line between the state block (which
+ // already ends with `\n `) and the next `export`. That collapses
+ // to `\n\n` after substitution.
+ w.write_all(b"\n\n")?;
+
+ w.write_all(b"export const isDevMode = ")?;
+ w.write_all(if is_dev_mode { b"true" } else { b"false" })?;
+ w.write_all(b";\n\n")?;
+
+ // Trailing static helpers — providers + ClientSide wrapper.
+ w.write_all(STATIC_PROVIDERS_PRELUDE)?;
+
+ // `StateProvider` body — useReducer per state, dispatchers,
+ // nested createElement scaffolding. Empty state_keys produces the
+ // legacy empty-string output (the `\n`.join over an empty list).
+ w.write_all(b"export function StateProvider({ children }) {\n ")?;
+ let mut first_store = true;
+ for key in state_keys {
+ if !first_store {
+ w.write_all(b"\n")?;
+ }
+ first_store = false;
+ w.write_all(b"const [")?;
+ write_formatted_state_name(w, key)?;
+ w.write_all(b", dispatch_")?;
+ write_formatted_state_name(w, key)?;
+ w.write_all(b"] = useReducer(applyDelta, initialState[\"")?;
+ w.write_all(key.as_bytes())?;
+ w.write_all(b"\"])")?;
+ }
+ w.write_all(b"\n const dispatchers = useMemo(() => {\n return {\n ")?;
+ let mut first_disp = true;
+ for key in state_keys {
+ if !first_disp {
+ w.write_all(b"\n")?;
+ }
+ first_disp = false;
+ w.write_all(b"\"")?;
+ w.write_all(key.as_bytes())?;
+ w.write_all(b"\": dispatch_")?;
+ write_formatted_state_name(w, key)?;
+ w.write_all(b",")?;
+ }
+ w.write_all(b"\n }\n }, [])\n\n return (\n ")?;
+
+ let mut first_ctx = true;
+ for key in state_keys {
+ if !first_ctx {
+ w.write_all(b"\n")?;
+ }
+ first_ctx = false;
+ w.write_all(b"createElement(StateContexts.")?;
+ write_formatted_state_name(w, key)?;
+ w.write_all(b",{value: ")?;
+ write_formatted_state_name(w, key)?;
+ w.write_all(b"},")?;
+ }
+ w.write_all(b"\n createElement(DispatchContext, {value: dispatchers}, children)\n ")?;
+ for _ in 0..state_keys.len() {
+ w.write_all(b")")?;
+ }
+ w.write_all(b"\n )\n}")?;
+ Ok(())
+}
+
+/// Write a state name with dots replaced by double-underscore so it's
+/// a valid JS identifier (matching `format_state_name` in
+/// `reflex_base.utils.format`).
+fn write_formatted_state_name(w: &mut W, name: &str) -> io::Result<()> {
+ // Hot path: search for a dot before doing per-byte work.
+ if !name.contains('.') {
+ return w.write_all(name.as_bytes());
+ }
+ for b in name.bytes() {
+ if b == b'.' {
+ w.write_all(b"__")?;
+ } else {
+ w.write_all(std::slice::from_ref(&b))?;
+ }
+ }
+ Ok(())
+}
+
+/// Emit the rich state block — populated when the app has a state root.
+fn emit_state_block_with_state(w: &mut W, state_name: &str) -> io::Result<()> {
+ w.write_all(b"\nexport const state_name = \"")?;
+ w.write_all(state_name.as_bytes())?;
+ w.write_all(b"\"\n\nexport const exception_state_name = \"")?;
+ w.write_all(FRONTEND_EXCEPTION_STATE_FULL.as_bytes())?;
+ w.write_all(b"\"\n\n")?;
+ w.write_all(b"// These events are triggered on initial load and each page navigation.\n")?;
+ w.write_all(b"export const onLoadInternalEvent = () => {\n")?;
+ w.write_all(b" const internal_events = [];\n\n")?;
+ w.write_all(b" // Get tracked cookie and local storage vars to send to the backend.\n")?;
+ w.write_all(b" const client_storage_vars = hydrateClientStorage(clientStorage);\n")?;
+ w.write_all(b" // But only send the vars if any are actually set in the browser.\n")?;
+ w.write_all(b" if (client_storage_vars && Object.keys(client_storage_vars).length !== 0) {\n")?;
+ w.write_all(b" internal_events.push(\n")?;
+ w.write_all(b" ReflexEvent(\n")?;
+ w.write_all(b" '")?;
+ w.write_all(state_name.as_bytes())?;
+ w.write_all(b".")?;
+ w.write_all(UPDATE_VARS_INTERNAL.as_bytes())?;
+ w.write_all(b"',\n")?;
+ w.write_all(b" {vars: client_storage_vars},\n")?;
+ w.write_all(b" ),\n")?;
+ w.write_all(b" );\n }\n\n")?;
+ w.write_all(b" // `on_load_internal` triggers the correct on_load event(s) for the current page.\n")?;
+ w.write_all(b" // If the page does not define any on_load event, this will just set `is_hydrated = true`.\n")?;
+ w.write_all(b" internal_events.push(ReflexEvent('")?;
+ w.write_all(state_name.as_bytes())?;
+ w.write_all(b".")?;
+ w.write_all(ON_LOAD_INTERNAL.as_bytes())?;
+ w.write_all(b"'));\n\n return internal_events;\n}\n\n")?;
+ w.write_all(b"// The following events are sent when the websocket connects or reconnects.\n")?;
+ w.write_all(b"export const initialEvents = () => [\n ReflexEvent('")?;
+ w.write_all(state_name.as_bytes())?;
+ w.write_all(b".")?;
+ w.write_all(HYDRATE.as_bytes())?;
+ w.write_all(b"'),\n ...onLoadInternalEvent()\n]\n ")?;
+ Ok(())
+}
+
+/// Emit the no-state fallback block.
+fn emit_state_block_no_state(w: &mut W) -> io::Result<()> {
+ w.write_all(
+ b"\nexport const state_name = undefined\n\n\
+ export const exception_state_name = undefined\n\n\
+ export const onLoadInternalEvent = () => []\n\n\
+ export const initialEvents = () => []\n",
+ )
+}
+
+const STATIC_PROVIDERS_PRELUDE: &[u8] = b"export function UploadFilesProvider({ children }) {\n \
+const [filesById, setFilesById] = useState({})\n \
+refs[\"__clear_selected_files\"] = (id) => setFilesById(filesById => {\n \
+const newFilesById = {...filesById}\n \
+delete newFilesById[id]\n \
+return newFilesById\n })\n \
+return createElement(\n \
+UploadFilesContext.Provider,\n \
+{ value: [filesById, setFilesById] },\n \
+children\n );\n}\n\n\
+export function ClientSide(component) {\n \
+return ({ children, ...props }) => {\n \
+const [Component, setComponent] = useState(null);\n \
+useEffect(() => {\n \
+async function load() {\n \
+const comp = await component();\n \
+setComponent(() => comp);\n }\n \
+load();\n }, []);\n \
+return Component ? jsx(Component, props, children) : null;\n };\n}\n\n\
+export function EventLoopProvider({ children }) {\n \
+const dispatch = useContext(DispatchContext)\n \
+const [addEvents, connectErrors] = useEventLoop(\n \
+dispatch,\n \
+initialEvents,\n \
+clientStorage,\n )\n \
+return createElement(\n \
+EventLoopContext.Provider,\n \
+{ value: [addEvents, connectErrors] },\n \
+children\n );\n}\n\n";
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ fn render(f: F) -> String
+ where
+ F: FnOnce(&mut Vec) -> io::Result<()>,
+ {
+ let mut buf = Vec::new();
+ f(&mut buf).unwrap();
+ String::from_utf8(buf).unwrap()
+ }
+
+ #[test]
+ fn format_state_name_replaces_dots() {
+ let s = render(|w| write_formatted_state_name(w, "foo.bar.baz"));
+ assert_eq!(s, "foo__bar__baz");
+ }
+
+ #[test]
+ fn format_state_name_passthrough_when_no_dot() {
+ let s = render(|w| write_formatted_state_name(w, "foo"));
+ assert_eq!(s, "foo");
+ }
+
+ #[test]
+ fn no_state_block_emits_undefined_fallbacks() {
+ let s = render(|w| {
+ emit_context_module(false, "\"system\"", None, &[], "{}", "{}", w)
+ });
+ assert!(s.contains("export const state_name = undefined"));
+ assert!(s.contains("export const initialEvents = () => []"));
+ assert!(s.contains("export const StateContexts = {};"));
+ }
+
+ #[test]
+ fn with_state_emits_hydrate_event_path() {
+ let s = render(|w| {
+ emit_context_module(
+ true,
+ "\"system\"",
+ Some("reflex___state____state"),
+ &["reflex___state____state"],
+ "{}",
+ "{}",
+ w,
+ )
+ });
+ assert!(s.contains("export const isDevMode = true;"));
+ assert!(
+ s.contains("ReflexEvent('reflex___state____state.hydrate')"),
+ "hydrate event missing:\n{s}"
+ );
+ assert!(
+ s.contains("StateContexts.reflex___state____state"),
+ "state context missing:\n{s}"
+ );
+ }
+}
diff --git a/packages/reflex-compiler-rust/crates/reflex_codegen/src/diagnostic.rs b/packages/reflex-compiler-rust/crates/reflex_codegen/src/diagnostic.rs
new file mode 100644
index 00000000000..947cb597b57
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_codegen/src/diagnostic.rs
@@ -0,0 +1,77 @@
+//! User-facing diagnostics. See plan §4.6.
+//!
+//! A `Diagnostic` is the framework's structured error type: a severity,
+//! a short message, a (file, line, col) location pulled from the IR's
+//! `SourceLoc`, and optionally a longer help message. The struct mirrors
+//! the data `miette` expects when rendering — we keep it deliberately small
+//! and dependency-light because PyO3 has to ferry it back to Python where
+//! `miette` itself isn't available.
+//!
+//! Once the consumer is happy with the structured form we can wire
+//! `miette::Diagnostic` in a separate crate to render terminal-style spans.
+
+use reflex_ir::SourceLoc;
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum Severity {
+ Error,
+ Warning,
+ Note,
+}
+
+#[derive(Debug, Clone)]
+pub struct Diagnostic {
+ pub severity: Severity,
+ pub code: &'static str,
+ pub message: String,
+ pub source_loc: SourceLoc,
+ pub help: Option,
+}
+
+impl Diagnostic {
+ pub fn error(code: &'static str, message: impl Into, loc: SourceLoc) -> Self {
+ Self {
+ severity: Severity::Error,
+ code,
+ message: message.into(),
+ source_loc: loc,
+ help: None,
+ }
+ }
+
+ pub fn warning(code: &'static str, message: impl Into, loc: SourceLoc) -> Self {
+ Self {
+ severity: Severity::Warning,
+ code,
+ message: message.into(),
+ source_loc: loc,
+ help: None,
+ }
+ }
+
+ pub fn with_help(mut self, help: impl Into) -> Self {
+ self.help = Some(help.into());
+ self
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use reflex_ir::PyFileId;
+
+ #[test]
+ fn error_builder() {
+ let d = Diagnostic::error(
+ "E001",
+ "unknown tag",
+ SourceLoc { file: PyFileId(1), line: 10, col: 5 },
+ )
+ .with_help("try one of: div, span, p");
+ assert_eq!(d.severity, Severity::Error);
+ assert_eq!(d.code, "E001");
+ assert_eq!(d.message, "unknown tag");
+ assert_eq!(d.source_loc.line, 10);
+ assert_eq!(d.help.unwrap(), "try one of: div, span, p");
+ }
+}
diff --git a/packages/reflex-compiler-rust/crates/reflex_codegen/src/document_root_module.rs b/packages/reflex-compiler-rust/crates/reflex_codegen/src/document_root_module.rs
new file mode 100644
index 00000000000..b33f7505093
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_codegen/src/document_root_module.rs
@@ -0,0 +1,53 @@
+//! Port of `document_root_template` (the legacy `.web/app/_document.js`
+//! emitter).
+//!
+//! The document root provides React-Router's `` / `` /
+//! `` shell. The legacy template takes a pre-computed imports
+//! list and a render dict; Phase-1 port has Python render both to
+//! strings and Rust splice them into the layout fn.
+
+use std::io::{self, Write};
+
+/// Emit `.web/app/_document.js`.
+///
+/// Mirrors `document_root_template` in
+/// `packages/reflex-base/src/reflex_base/compiler/templates.py`.
+///
+/// Args:
+/// imports_str: rendered `import { … } from "…"` lines joined with
+/// `\n` by the caller.
+/// document_render_str: rendered JSX expression for the document
+/// tree — splices verbatim into `return (…)`.
+/// w: byte sink.
+pub fn emit_document_root_module(
+ imports_str: &str,
+ document_render_str: &str,
+ w: &mut W,
+) -> io::Result<()> {
+ w.write_all(imports_str.as_bytes())?;
+ w.write_all(b"\n\nexport function Layout({children}) {\n return (\n ")?;
+ w.write_all(document_render_str.as_bytes())?;
+ w.write_all(b"\n )\n}")?;
+ Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn assembles_layout_with_render() {
+ let mut buf = Vec::new();
+ emit_document_root_module(
+ "import { Foo } from \"bar\";",
+ "jsx(Foo, {}, children)",
+ &mut buf,
+ )
+ .unwrap();
+ let out = String::from_utf8(buf).unwrap();
+ assert_eq!(
+ out,
+ "import { Foo } from \"bar\";\n\nexport function Layout({children}) {\n return (\n jsx(Foo, {}, children)\n )\n}"
+ );
+ }
+}
diff --git a/packages/reflex-compiler-rust/crates/reflex_codegen/src/jsx.rs b/packages/reflex-compiler-rust/crates/reflex_codegen/src/jsx.rs
new file mode 100644
index 00000000000..aa8b5f9f672
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_codegen/src/jsx.rs
@@ -0,0 +1,324 @@
+//! JSX emission for the Component tree. See plan §4.8.
+//!
+//! `emit_component(buf, component)` writes one JS expression that, when
+//! evaluated, produces the React element described by the IR node.
+//!
+//! Source-map variant: every entry point has a `_with_map` sibling that
+//! records `(byte_offset → SourceLoc)` tuples for back-mapping diagnostics.
+
+use reflex_intern::resolve_unchecked;
+use reflex_ir::{Component, EventHandler, Literal, MatchArm, Value};
+
+use crate::buffer::CodeBuffer;
+use crate::sourcemap::SourceMap;
+
+/// Emit one Component as a JS expression (no source-map recording).
+#[inline]
+pub fn emit_component<'a>(buf: &mut CodeBuffer, component: &Component<'a>) {
+ emit_component_with_map(buf, component, None)
+}
+
+/// Emit one Component as a JS expression. If `map` is `Some`, records the
+/// byte offset of this node and threads the recorder through children.
+pub fn emit_component_with_map<'a>(
+ buf: &mut CodeBuffer,
+ component: &Component<'a>,
+ map: Option<&mut SourceMap>,
+) {
+ let offset = buf.len() as u32;
+ let loc = component.source_loc();
+ match map {
+ Some(map) => {
+ map.record(offset, loc);
+ emit_component_inner(buf, component, Some(map));
+ }
+ None => {
+ emit_component_inner(buf, component, None);
+ }
+ }
+}
+
+fn emit_component_inner<'a>(
+ buf: &mut CodeBuffer,
+ component: &Component<'a>,
+ map: Option<&mut SourceMap>,
+) {
+ match component {
+ Component::Element {
+ tag,
+ props,
+ children,
+ event_handlers,
+ ..
+ } => emit_element(buf, *tag, props, children, event_handlers, map),
+ Component::Text { value, .. } => buf.write_js_string(value),
+ Component::Foreach { iter, body, .. } => emit_foreach(buf, iter, body, map),
+ Component::Cond {
+ test, then, else_, ..
+ } => emit_cond(buf, test, then, *else_, map),
+ Component::Match {
+ value,
+ arms,
+ default,
+ ..
+ } => emit_match(buf, value, arms, *default, map),
+ Component::Memoize { inner, key, .. } => emit_memoize(buf, inner, key.0, map),
+ Component::Fragment { children, .. } => emit_fragment(buf, children, map),
+ Component::Expr { value, .. } => emit_value(buf, value),
+ }
+}
+
+fn emit_element<'a>(
+ buf: &mut CodeBuffer,
+ tag: reflex_intern::Symbol,
+ props: &'a [(reflex_intern::Symbol, Value<'a>)],
+ children: &'a [Component<'a>],
+ event_handlers: &'a [EventHandler<'a>],
+ mut map: Option<&mut SourceMap>,
+) {
+ buf.write_str("jsx(");
+ buf.write_str(resolve_unchecked(tag));
+ buf.write_str(", {");
+ let mut first = true;
+ for (name, value) in props {
+ if !first {
+ buf.write_str(", ");
+ }
+ first = false;
+ emit_prop_name(buf, *name);
+ buf.write_str(": ");
+ emit_value(buf, value);
+ }
+ for handler in event_handlers {
+ if !first {
+ buf.write_str(", ");
+ }
+ first = false;
+ emit_prop_name(buf, handler.trigger);
+ buf.write_str(": ");
+ buf.write_str(handler.expr);
+ }
+ buf.write_str("}");
+ for child in children {
+ buf.write_str(", ");
+ emit_component_with_map(buf, child, map.as_deref_mut());
+ }
+ buf.write_str(")");
+}
+
+fn emit_fragment<'a>(
+ buf: &mut CodeBuffer,
+ children: &'a [Component<'a>],
+ mut map: Option<&mut SourceMap>,
+) {
+ buf.write_str("jsx(Fragment, {}");
+ for child in children {
+ buf.write_str(", ");
+ emit_component_with_map(buf, child, map.as_deref_mut());
+ }
+ buf.write_str(")");
+}
+
+fn emit_foreach<'a>(
+ buf: &mut CodeBuffer,
+ iter: &Value<'a>,
+ body: &Component<'a>,
+ map: Option<&mut SourceMap>,
+) {
+ buf.write_str("(");
+ emit_value(buf, iter);
+ buf.write_str(").map((item, index) => ");
+ emit_component_with_map(buf, body, map);
+ buf.write_str(")");
+}
+
+fn emit_cond<'a>(
+ buf: &mut CodeBuffer,
+ test: &Value<'a>,
+ then: &Component<'a>,
+ else_: Option<&Component<'a>>,
+ mut map: Option<&mut SourceMap>,
+) {
+ buf.write_str("((");
+ emit_value(buf, test);
+ buf.write_str(") ? ");
+ emit_component_with_map(buf, then, map.as_deref_mut());
+ buf.write_str(" : ");
+ if let Some(e) = else_ {
+ emit_component_with_map(buf, e, map);
+ } else {
+ buf.write_str("null");
+ }
+ buf.write_str(")");
+}
+
+fn emit_match<'a>(
+ buf: &mut CodeBuffer,
+ value: &Value<'a>,
+ arms: &'a [MatchArm<'a>],
+ default: Option<&Component<'a>>,
+ mut map: Option<&mut SourceMap>,
+) {
+ buf.write_str("match_template((");
+ emit_value(buf, value);
+ buf.write_str("), [");
+ for (i, arm) in arms.iter().enumerate() {
+ if i > 0 {
+ buf.write_str(", ");
+ }
+ buf.write_str("[");
+ emit_value(buf, &arm.case);
+ buf.write_str(", ");
+ emit_component_with_map(buf, arm.body, map.as_deref_mut());
+ buf.write_str("]");
+ }
+ buf.write_str("], ");
+ if let Some(d) = default {
+ emit_component_with_map(buf, d, map);
+ } else {
+ buf.write_str("null");
+ }
+ buf.write_str(")");
+}
+
+fn emit_memoize<'a>(
+ buf: &mut CodeBuffer,
+ inner: &Component<'a>,
+ key: u64,
+ map: Option<&mut SourceMap>,
+) {
+ buf.write_str("jsx(MemoWrapper, {key: \"");
+ buf.write_u64(key);
+ buf.write_str("\"}, ");
+ emit_component_with_map(buf, inner, map);
+ buf.write_str(")");
+}
+
+pub fn emit_value<'a>(buf: &mut CodeBuffer, value: &Value<'a>) {
+ match value {
+ Value::JsExpr { expr, .. } => buf.write_str(expr),
+ Value::Literal(lit) => emit_literal(buf, lit),
+ Value::Ref(sym) => buf.write_str(resolve_unchecked(*sym)),
+ }
+}
+
+fn emit_literal<'a>(buf: &mut CodeBuffer, lit: &Literal<'a>) {
+ match lit {
+ Literal::Null => buf.write_str("null"),
+ Literal::Bool(true) => buf.write_str("true"),
+ Literal::Bool(false) => buf.write_str("false"),
+ Literal::Int(n) => buf.write_i64(*n),
+ Literal::Float(f) => buf.write_f64(*f),
+ Literal::Str(s) => buf.write_js_string(s),
+ }
+}
+
+/// Prop names emit unquoted when they're valid JS identifiers; otherwise
+/// they emit as quoted string keys. Known Python-side names (snake_case
+/// HTML attributes, event triggers) are converted to camelCase so the
+/// emitted JSX matches what React expects.
+fn emit_prop_name(buf: &mut CodeBuffer, sym: reflex_intern::Symbol) {
+ let name = resolve_unchecked(sym);
+ // React props are camelCase. Reflex authors declare them in
+ // snake_case (Python convention); the legacy emitter runs every
+ // prop name through `to_camel_case`. Match that behaviour so
+ // ``remark_plugins`` → ``remarkPlugins`` etc.
+ if name.contains('_') && is_js_identifier(name) {
+ write_camel_case(buf, name);
+ return;
+ }
+ if is_js_identifier(name) {
+ buf.write_str(name);
+ } else {
+ buf.write_js_string(name);
+ }
+}
+
+/// Convert ``snake_case`` to ``camelCase``: lowercase the first segment
+/// and Title-case the rest. Mirrors
+/// :func:`reflex_base.utils.format.to_camel_case`. The legacy helper
+/// treats hyphens as underscores by default — but JS identifiers can't
+/// contain hyphens anyway, so `is_js_identifier` already excludes that
+/// case and we only need to handle ``_``.
+fn write_camel_case(buf: &mut CodeBuffer, name: &str) {
+ let mut after_underscore = false;
+ for (i, ch) in name.char_indices() {
+ if ch == '_' {
+ if i > 0 {
+ after_underscore = true;
+ }
+ continue;
+ }
+ if after_underscore {
+ // ASCII uppercase covers every JS-identifier byte we care
+ // about; non-ASCII passes through unchanged.
+ for upper in ch.to_uppercase() {
+ let mut tmp = [0; 4];
+ buf.write_str(upper.encode_utf8(&mut tmp));
+ }
+ after_underscore = false;
+ } else {
+ let mut tmp = [0; 4];
+ buf.write_str(ch.encode_utf8(&mut tmp));
+ }
+ }
+}
+
+fn is_js_identifier(s: &str) -> bool {
+ let bytes = s.as_bytes();
+ if bytes.is_empty() {
+ return false;
+ }
+ let first = bytes[0];
+ if !(first.is_ascii_alphabetic() || first == b'_' || first == b'$') {
+ return false;
+ }
+ for &b in &bytes[1..] {
+ if !(b.is_ascii_alphanumeric() || b == b'_' || b == b'$') {
+ return false;
+ }
+ }
+ true
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use reflex_arena::Arena;
+ use reflex_ir::{parse::test_helpers::tiny_page_bytes, parse_page};
+
+ #[test]
+ fn emits_tiny_page() {
+ let arena = Arena::new();
+ let bytes = tiny_page_bytes();
+ let page = parse_page(&arena, &bytes).unwrap();
+ let mut buf = CodeBuffer::new();
+ emit_component(&mut buf, page.root);
+ assert_eq!(buf.as_str(), "jsx(div, {id: x}, \"hello\")");
+ }
+
+ #[test]
+ fn js_identifier_recognition() {
+ assert!(is_js_identifier("foo"));
+ assert!(is_js_identifier("_x"));
+ assert!(is_js_identifier("$id"));
+ assert!(is_js_identifier("aria1"));
+ assert!(!is_js_identifier(""));
+ assert!(!is_js_identifier("1foo"));
+ assert!(!is_js_identifier("aria-label"));
+ assert!(!is_js_identifier("foo bar"));
+ }
+
+ #[test]
+ fn map_recording_is_a_noop_for_synthetic_locs() {
+ // The tiny page's nodes all use SourceLoc::SYNTHETIC, so the map
+ // stays empty even with `Some(&mut map)`.
+ let arena = Arena::new();
+ let bytes = tiny_page_bytes();
+ let page = parse_page(&arena, &bytes).unwrap();
+ let mut buf = CodeBuffer::new();
+ let mut map = SourceMap::new();
+ emit_component_with_map(&mut buf, page.root, Some(&mut map));
+ assert!(map.is_empty());
+ }
+}
diff --git a/packages/reflex-compiler-rust/crates/reflex_codegen/src/lib.rs b/packages/reflex-compiler-rust/crates/reflex_codegen/src/lib.rs
new file mode 100644
index 00000000000..8061fb6ee75
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_codegen/src/lib.rs
@@ -0,0 +1,37 @@
+//! IR → JS/JSX/CSS codegen. See plan §3, D8/D10, R6.
+//!
+//! All output goes through a single growable `CodeBuffer` (`Vec`). No
+//! intermediate `String`s, no `format!` in per-node paths — use `write_*`
+//! methods that append bytes directly.
+
+#![forbid(unsafe_code)]
+
+pub mod app_root;
+pub mod app_root_module;
+pub mod buffer;
+pub mod context;
+pub mod context_module;
+pub mod diagnostic;
+pub mod document_root_module;
+pub mod jsx;
+pub mod memo;
+pub mod page;
+pub mod sourcemap;
+pub mod static_artifacts;
+pub mod theme;
+pub mod vite;
+
+pub use app_root::emit_app_root;
+pub use app_root_module::emit_app_root_module;
+pub use buffer::CodeBuffer;
+pub use context::emit_context;
+pub use context_module::emit_context_module;
+pub use diagnostic::{Diagnostic, Severity};
+pub use document_root_module::emit_document_root_module;
+pub use jsx::{emit_component, emit_component_with_map, emit_value};
+pub use memo::{emit_memo_index, emit_memo_module};
+pub use page::{emit_page, emit_page_with_extras, emit_page_with_map};
+pub use sourcemap::SourceMap;
+pub use static_artifacts::{emit_stateful_pages_json, emit_styles_root, emit_theme_module};
+pub use theme::emit_theme;
+pub use vite::emit_vite_config;
diff --git a/packages/reflex-compiler-rust/crates/reflex_codegen/src/memo.rs b/packages/reflex-compiler-rust/crates/reflex_codegen/src/memo.rs
new file mode 100644
index 00000000000..e0e879e4273
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_codegen/src/memo.rs
@@ -0,0 +1,222 @@
+//! Emit a per-memo module — `export const = memo(({children}) => …)`.
+//!
+//! Mirrors `packages/reflex-base/src/reflex_base/compiler/templates.py`'s
+//! `memo_single_component_template`. Most of the import + JSX-render
+//! plumbing is shared with `emit_page`; this module just swaps the page
+//! shell for the `memo(...)` shell.
+//!
+//! Used by phase 3 of the memoize port (plan §0b lever (b3)): once we've
+//! decided to wrap a Component, pyread builds an IR with the wrapped
+//! body (children replaced by a `{children}` placeholder expression),
+//! and this function emits the standalone memo module that the page
+//! module then imports.
+
+use reflex_intern::{intern, resolve_unchecked, Symbol};
+use reflex_ir::Page;
+
+use crate::buffer::CodeBuffer;
+use crate::jsx::emit_component_with_map;
+
+/// Baseline runtime aliases every memo module needs. `memo` from React
+/// plus `jsx` from Emotion — anything else the body refers to gets
+/// pulled in via the harvested `component_imports`.
+const MEMO_RUNTIME_IMPORTS: &[(&str, &str)] = &[
+ ("react", "memo"),
+ ("react", "useContext"),
+ ("react", "useRef"),
+ ("$/utils/context", "EventLoopContext"),
+ ("$/utils/context", "StateContexts"),
+ ("$/utils/state", "refs"),
+ ("$/utils/state", "ReflexEvent"),
+ ("@emotion/react", "jsx"),
+];
+
+/// Emit a memo wrapper module.
+///
+/// The `Page` IR is reused as the input shape because the body of a memo
+/// is structurally a single JSX tree — same as a page. Title / meta on
+/// `page` are ignored (memos don't carry document metadata); everything
+/// else flows through.
+///
+/// Args:
+/// buf: target buffer.
+/// page: the wrapper body's IR. Pyread builds this from the
+/// hole-substituted Component (`children = [Bare(Var("children"))]`).
+/// name: exported memo identifier (`export const = …`).
+/// signature: parameter list spliced after `memo(`. Use `"{ children }"`
+/// for passthrough wrappers, `"()"` for snapshot bodies that don't
+/// receive children.
+/// pre_hooks: pre-rendered hook block (e.g. `const { resolvedColorMode }
+/// = useContext(ColorModeContext)`) the Python orchestrator harvested
+/// via `body._get_all_hooks()`. Spliced between the state-context
+/// hooks and `return` so memo bodies that reference hook-defined
+/// identifiers (color mode, custom `_get_hooks_internal`, etc.) get
+/// their declarations. Pass `""` if there are none.
+pub fn emit_memo_module(
+ buf: &mut CodeBuffer,
+ page: &Page<'_>,
+ name: &str,
+ signature: &str,
+ pre_hooks: &str,
+) {
+ emit_combined_imports(buf, page.component_imports);
+
+ buf.write_str("\nexport const ");
+ buf.write_str(name);
+ buf.write_str(" = memo(");
+ buf.write_str(signature);
+ buf.write_str(" => {\n");
+
+ if page.needs_ref {
+ buf.write_str(
+ " const ref_root = useRef(null); refs[\"ref_root\"] = ref_root;\n",
+ );
+ }
+ for binding in page.state_bindings {
+ let s = resolve_unchecked(*binding);
+ buf.write_str(" const ");
+ buf.write_str(s);
+ buf.write_str(" = useContext(StateContexts.");
+ buf.write_str(s);
+ buf.write_str(");\n");
+ }
+ buf.write_str(
+ " const [addEvents, connectErrors] = useContext(EventLoopContext);\n",
+ );
+ if !pre_hooks.is_empty() {
+ buf.write_str(" ");
+ buf.write_str(pre_hooks);
+ if !pre_hooks.ends_with('\n') {
+ buf.write_str("\n");
+ }
+ }
+
+ buf.write_str(" return ");
+ emit_component_with_map(buf, page.root, None);
+ buf.write_str(";\n});\n");
+}
+
+/// Emit the memo index module — the small barrel file at
+/// `.web/utils/components.jsx` that re-exports every `@rx.memo` custom
+/// component so `root.jsx` can pull them in via `$/utils/components`.
+///
+/// Mirrors `memo_index_template` in
+/// `packages/reflex-base/src/reflex_base/compiler/templates.py`.
+///
+/// Streams output directly to `w` — no intermediate `String` is
+/// allocated. Tests pass a `&mut Vec`; the PyO3 binding passes a
+/// `BufWriter` so bytes flow straight to disk.
+///
+/// Args:
+/// reexports: `(export_name, relative_module_specifier)` pairs. The
+/// specifier is the path component after `$/utils/` —
+/// e.g. `("Foo", "components/Foo")` produces
+/// `export { Foo } from "components/Foo";`.
+/// w: a writer the rendered module source is streamed into.
+pub fn emit_memo_index(
+ reexports: &[(&str, &str)],
+ w: &mut W,
+) -> std::io::Result<()> {
+ if reexports.is_empty() {
+ // Match the legacy template exactly — an empty reexport list
+ // produces a single trailing newline (joining `[]` with `"\n"`
+ // yields `""`, then the template appends `"\n"`).
+ return w.write_all(b"\n");
+ }
+ for (name, specifier) in reexports {
+ w.write_all(b"export { ")?;
+ w.write_all(name.as_bytes())?;
+ w.write_all(b" } from \"")?;
+ w.write_all(specifier.as_bytes())?;
+ w.write_all(b"\";\n")?;
+ }
+ Ok(())
+}
+
+fn emit_combined_imports(buf: &mut CodeBuffer, component_imports: &[(Symbol, Symbol)]) {
+ // Same dedup-by-module logic as `emit_page`'s `emit_combined_imports`,
+ // but seeded with the memo-specific runtime header (`memo` instead of
+ // `Fragment`). We split runtime imports into `react` (top) + rest
+ // (bottom) so the layout matches the legacy `memo_single_component_template`
+ // output.
+ let mut combined: Vec<(Symbol, Symbol)> =
+ Vec::with_capacity(MEMO_RUNTIME_IMPORTS.len() + component_imports.len());
+
+ let runtime_react: Vec<(Symbol, Symbol)> = MEMO_RUNTIME_IMPORTS
+ .iter()
+ .filter(|(m, _)| *m == "react")
+ .map(|(m, a)| (intern(m), intern(a)))
+ .collect();
+ let runtime_rest: Vec<(Symbol, Symbol)> = MEMO_RUNTIME_IMPORTS
+ .iter()
+ .filter(|(m, _)| *m != "react")
+ .map(|(m, a)| (intern(m), intern(a)))
+ .collect();
+
+ combined.extend(runtime_react);
+ combined.extend_from_slice(component_imports);
+ combined.extend(runtime_rest);
+
+ emit_imports_grouped_by_module(buf, &combined);
+}
+
+fn emit_imports_grouped_by_module(buf: &mut CodeBuffer, imports: &[(Symbol, Symbol)]) {
+ let mut modules: Vec = Vec::new();
+ for (m, _) in imports {
+ if !modules.contains(m) {
+ modules.push(*m);
+ }
+ }
+ for module in modules {
+ let mut emitted: Vec = Vec::new();
+ buf.write_str("import { ");
+ let mut first = true;
+ for (m, alias) in imports {
+ if *m != module {
+ continue;
+ }
+ if emitted.contains(alias) {
+ continue;
+ }
+ if !first {
+ buf.write_str(", ");
+ }
+ first = false;
+ buf.write_str(resolve_unchecked(*alias));
+ emitted.push(*alias);
+ }
+ buf.write_str(" } from \"");
+ buf.write_str(resolve_unchecked(module));
+ buf.write_str("\";\n");
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::emit_memo_index;
+
+ fn render(reexports: &[(&str, &str)]) -> String {
+ let mut buf = Vec::new();
+ emit_memo_index(reexports, &mut buf).unwrap();
+ String::from_utf8(buf).unwrap()
+ }
+
+ #[test]
+ fn emits_one_reexport_per_entry() {
+ let reexports = [
+ ("Foo", "components/Foo"),
+ ("BarBaz", "components/BarBaz"),
+ ];
+ assert_eq!(
+ render(&reexports),
+ "export { Foo } from \"components/Foo\";\nexport { BarBaz } from \"components/BarBaz\";\n"
+ );
+ }
+
+ #[test]
+ fn empty_reexports_match_legacy_template() {
+ // Legacy `memo_index_template([])` returns `"\n"` because
+ // `"\n".join([]) + "\n" == "\n"`. The Rust port matches.
+ assert_eq!(render(&[]), "\n");
+ }
+}
diff --git a/packages/reflex-compiler-rust/crates/reflex_codegen/src/page.rs b/packages/reflex-compiler-rust/crates/reflex_codegen/src/page.rs
new file mode 100644
index 00000000000..a330c2af52e
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_codegen/src/page.rs
@@ -0,0 +1,480 @@
+//! Page-level emission: render a `Page` as a JS source string ready for the
+//! Vite frontend to consume. See plan §4.1, §4.8.
+//!
+//! Output shape — the contract the running React frontend expects:
+//!
+//! ```js
+//! import { Fragment, useContext, useRef } from "react";
+//! import { Box as RadixThemesBox } from "@radix-ui/themes"; // from page.component_imports
+//! import { EventLoopContext, StateContexts } from "$/utils/context";
+//! import { refs, ReflexEvent } from "$/utils/state";
+//! import { jsx } from "@emotion/react";
+//!
+//! export default function Component() {
+//! const ref_root = useRef(null); refs["ref_root"] = ref_root; // when page.needs_ref
+//! const = useContext(StateContexts.); // per state_bindings entry
+//! const [addEvents, connectErrors] = useContext(EventLoopContext);
+//! return ( jsx(...) );
+//! }
+//!
+//! export const __reflex_route = "";
+//! export const __reflex_title = "";
+//! ```
+//!
+//! `route_ident` is only used for the `__reflex_route_ident` constant —
+//! the React runtime always imports the page via the default export named
+//! `Component`. Caller can override with [`emit_page_named`].
+
+use reflex_intern::{intern, resolve_unchecked, Symbol};
+use reflex_ir::Page;
+
+use crate::buffer::CodeBuffer;
+use crate::jsx::emit_component_with_map;
+use crate::sourcemap::SourceMap;
+
+/// Default JS function name the React runtime imports from each page module.
+pub const DEFAULT_ROUTE_FN: &str = "Component";
+
+/// Emit a full page module (no source-map recording, default `Component` name).
+#[inline]
+pub fn emit_page(buf: &mut CodeBuffer, page: &Page<'_>, route_ident: &str) {
+ emit_page_inner(buf, page, route_ident, DEFAULT_ROUTE_FN, None, &[], "")
+}
+
+/// Emit a full page module with a custom JS function name.
+#[inline]
+pub fn emit_page_named(
+ buf: &mut CodeBuffer,
+ page: &Page<'_>,
+ route_ident: &str,
+ fn_name: &str,
+) {
+ emit_page_inner(buf, page, route_ident, fn_name, None, &[], "")
+}
+
+/// Emit a full page module and record an optional `SourceMap`.
+pub fn emit_page_with_map(
+ buf: &mut CodeBuffer,
+ page: &Page<'_>,
+ route_ident: &str,
+ map: &mut SourceMap,
+) {
+ emit_page_inner(buf, page, route_ident, DEFAULT_ROUTE_FN, Some(map), &[], "")
+}
+
+/// Emit a full page module with caller-provided `custom_code` blocks
+/// (Component `_get_all_custom_code()` strings spliced between imports
+/// and the `export default function` line) and a `hooks_body` (pre-
+/// rendered hooks block inserted inside the component function before
+/// the `useContext(EventLoopContext)` line).
+///
+/// Mirrors the legacy `page_template` which renders custom code +
+/// hooks alongside the JSX. Used by `compile_page_from_component` so
+/// pages emitted via pyread include the markdown `ComponentMap_*`
+/// closures (and similar helpers) the legacy template produced.
+pub fn emit_page_with_extras(
+ buf: &mut CodeBuffer,
+ page: &Page<'_>,
+ route_ident: &str,
+ custom_code: &[&str],
+ hooks_body: &str,
+) {
+ emit_page_inner(
+ buf,
+ page,
+ route_ident,
+ DEFAULT_ROUTE_FN,
+ None,
+ custom_code,
+ hooks_body,
+ )
+}
+
+fn emit_page_inner(
+ buf: &mut CodeBuffer,
+ page: &Page<'_>,
+ route_ident: &str,
+ fn_name: &str,
+ map: Option<&mut SourceMap>,
+ custom_code: &[&str],
+ hooks_body: &str,
+) {
+ // ---- Imports ------------------------------------------------------
+ //
+ // React + Reflex runtime imports are always present. Component-class
+ // imports come from page.component_imports (harvested by the bridge).
+ //
+ // We merge baseline runtime imports with component_imports through a
+ // single grouped emitter so that aliases bringing in extras from the
+ // same module (e.g. `ColorModeContext` alongside `EventLoopContext`)
+ // collapse onto one import line instead of producing duplicates.
+
+ emit_combined_imports(buf, page.component_imports);
+
+ // ---- Custom code blocks ------------------------------------------
+ // Component `_get_all_custom_code()` strings (markdown component
+ // maps, JIT helpers, etc.) get spliced between imports and the
+ // `export default function` line — matching the legacy `page_template`.
+ for block in custom_code {
+ buf.write_byte(b'\n');
+ buf.write_str(block);
+ if !block.ends_with('\n') {
+ buf.write_byte(b'\n');
+ }
+ }
+
+ // ---- Render function ---------------------------------------------
+ buf.write_str("\nexport default function ");
+ buf.write_str(fn_name);
+ buf.write_str("() {\n");
+
+ if page.needs_ref {
+ // Single shared ref for now; once the bridge tracks per-`id` refs
+ // we'll emit one line per id. The legacy renderer uses
+ // `ref_`.
+ buf.write_str(
+ " const ref_root = useRef(null); refs[\"ref_root\"] = ref_root;\n",
+ );
+ }
+ for binding in page.state_bindings {
+ let s = resolve_unchecked(*binding);
+ buf.write_str(" const ");
+ buf.write_str(s);
+ buf.write_str(" = useContext(StateContexts.");
+ buf.write_str(s);
+ buf.write_str(");\n");
+ }
+ buf.write_str(
+ " const [addEvents, connectErrors] = useContext(EventLoopContext);\n",
+ );
+
+ // ---- Hooks block --------------------------------------------------
+ // Pre-rendered hook body from the caller — declarations the
+ // legacy `_render_hooks` would have produced. We splice the
+ // string verbatim; if it's non-empty and doesn't end with a
+ // newline, add one to keep the trailing `return` separate.
+ if !hooks_body.is_empty() {
+ buf.write_str(hooks_body);
+ if !hooks_body.ends_with('\n') {
+ buf.write_byte(b'\n');
+ }
+ }
+
+ // If the page declares a title or any meta tags, wrap the root in a
+ // Fragment alongside the synthetic / JSX children so the
+ // browser tab + social-card metadata work the same way the legacy
+ // compiler emits them.
+ let wrap_in_fragment = page.title.is_some() || !page.meta.is_empty();
+
+ buf.write_str(" return ");
+ if wrap_in_fragment {
+ buf.write_str("jsx(Fragment, {}, ");
+ emit_component_with_map(buf, page.root, map);
+ if let Some(title) = page.title {
+ buf.write_str(", jsx(\"title\", {}, ");
+ buf.write_js_string(title);
+ buf.write_str(")");
+ }
+ for meta in page.meta {
+ buf.write_str(", jsx(\"meta\", {");
+ let name = resolve_unchecked(meta.name);
+ // Treat well-known social/OG keys as `property=…`; everything
+ // else uses `name=…`. Matches the legacy emitter's convention.
+ let key = if name.starts_with("og:") || name.starts_with("twitter:") {
+ "property"
+ } else {
+ "name"
+ };
+ buf.write_str(key);
+ buf.write_str(": ");
+ buf.write_js_string(name);
+ buf.write_str(", content: ");
+ buf.write_js_string(meta.content);
+ buf.write_str("})");
+ }
+ buf.write_str(")");
+ } else {
+ emit_component_with_map(buf, page.root, map);
+ }
+ buf.write_str(";\n}\n");
+
+ // ---- Page metadata exports ---------------------------------------
+ buf.write_str("\nexport const __reflex_route = ");
+ buf.write_js_string(page.route);
+ buf.write_str(";\n");
+
+ buf.write_str("export const __reflex_route_ident = ");
+ buf.write_js_string(route_ident);
+ buf.write_str(";\n");
+
+ if let Some(title) = page.title {
+ buf.write_str("export const __reflex_title = ");
+ buf.write_js_string(title);
+ buf.write_str(";\n");
+ }
+}
+
+/// Baseline runtime aliases that every page module needs, paired with the
+/// module they come from. Emitted alongside `component_imports` so a single
+/// grouped pass dedupes overlapping symbols (e.g. `EventLoopContext`).
+const RUNTIME_IMPORTS: &[(&str, &str)] = &[
+ ("react", "Fragment"),
+ ("react", "useContext"),
+ ("react", "useRef"),
+ ("$/utils/context", "EventLoopContext"),
+ ("$/utils/context", "StateContexts"),
+ ("$/utils/state", "refs"),
+ ("$/utils/state", "ReflexEvent"),
+ ("@emotion/react", "jsx"),
+];
+
+/// Emit imports for a page module by combining baseline runtime imports with
+/// the component-harvested `component_imports` from the IR, in that order.
+///
+/// `react` is anchored at the top and `@emotion/react` at the bottom to match
+/// the layout the legacy emitter produced; modules harvested from
+/// `component_imports` slot in between in first-seen order. Aliases that
+/// appear in both sources collapse via the per-module dedup inside
+/// `emit_imports_grouped_by_module`.
+fn emit_combined_imports(buf: &mut CodeBuffer, component_imports: &[(Symbol, Symbol)]) {
+ let mut combined: Vec<(Symbol, Symbol)> =
+ Vec::with_capacity(RUNTIME_IMPORTS.len() + component_imports.len());
+
+ let runtime_react: Vec<(Symbol, Symbol)> = RUNTIME_IMPORTS
+ .iter()
+ .filter(|(m, _)| *m == "react")
+ .map(|(m, a)| (intern(m), intern(a)))
+ .collect();
+ let runtime_rest: Vec<(Symbol, Symbol)> = RUNTIME_IMPORTS
+ .iter()
+ .filter(|(m, _)| *m != "react")
+ .map(|(m, a)| (intern(m), intern(a)))
+ .collect();
+
+ combined.extend(runtime_react);
+ combined.extend_from_slice(component_imports);
+ combined.extend(runtime_rest);
+
+ emit_imports_grouped_by_module(buf, &combined);
+}
+
+fn emit_imports_grouped_by_module(
+ buf: &mut CodeBuffer,
+ imports: &[(Symbol, Symbol)],
+) {
+ // Imports are `(module, alias_spec)` pairs where alias_spec is the
+ // string spliced inside `import { … }` — either a bare identifier
+ // (`"useState"`) or a renamed one (`"Box as RadixThemesBox"`). We
+ // group by module preserving first-seen order, and within each module
+ // dedup the alias_spec list so the bridge can pass redundant entries
+ // without producing `{ useState, useState }`.
+ let mut modules: Vec = Vec::new();
+ for (m, _) in imports {
+ if !modules.contains(m) {
+ modules.push(*m);
+ }
+ }
+ for module in modules {
+ let mut emitted: Vec = Vec::new();
+ buf.write_str("import { ");
+ let mut first = true;
+ for (m, alias) in imports {
+ if *m != module {
+ continue;
+ }
+ if emitted.contains(alias) {
+ continue;
+ }
+ emitted.push(*alias);
+ if !first {
+ buf.write_str(", ");
+ }
+ first = false;
+ buf.write_str(resolve_unchecked(*alias));
+ }
+ buf.write_str(" } from ");
+ buf.write_js_string(resolve_unchecked(module));
+ buf.write_str(";\n");
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use reflex_arena::Arena;
+ use reflex_ir::parse::test_helpers::tiny_page_bytes;
+ use reflex_ir::parse_page;
+
+ #[test]
+ fn emits_page_module() {
+ let arena = Arena::new();
+ let bytes = tiny_page_bytes();
+ let page = parse_page(&arena, &bytes).unwrap();
+ let mut buf = CodeBuffer::new();
+ emit_page(&mut buf, &page, "Index");
+ let s = buf.as_str();
+ assert!(s.contains("import { Fragment, useContext, useRef } from \"react\""));
+ assert!(s.contains(
+ "import { EventLoopContext, StateContexts } from \"$/utils/context\""
+ ));
+ assert!(s.contains("export default function Component()"));
+ assert!(s.contains("const [addEvents, connectErrors] = useContext(EventLoopContext);"));
+ assert!(s.contains("jsx(div, {id: x}, \"hello\")"));
+ assert!(s.contains("export const __reflex_route = \"/index\""));
+ assert!(s.contains("export const __reflex_route_ident = \"Index\""));
+ }
+
+ #[test]
+ fn page_with_component_imports() {
+ use reflex_ir::{Component, NodeId, Page as PageIR, SourceLoc};
+ let arena = Arena::new();
+ let text = arena.alloc(Component::Text {
+ value: "x",
+ id: NodeId(1),
+ source_loc: SourceLoc::SYNTHETIC,
+ });
+ let imports = [(
+ reflex_intern::intern("@radix-ui/themes"),
+ reflex_intern::intern("Box as RadixThemesBox"),
+ )];
+ let bindings = [reflex_intern::intern(
+ "reflex___state____state__demo____my_state",
+ )];
+ let page = PageIR {
+ schema_version: 2,
+ route: "/x",
+ root: &*text,
+ title: None,
+ meta: &[],
+ source_files: &[],
+ component_imports: arena.bump().alloc_slice_copy(&imports),
+ state_bindings: arena.bump().alloc_slice_copy(&bindings),
+ needs_ref: true,
+ };
+ let mut buf = CodeBuffer::new();
+ emit_page(&mut buf, &page, "X");
+ let s = buf.as_str();
+ assert!(
+ s.contains("import { Box as RadixThemesBox } from \"@radix-ui/themes\""),
+ "got:\n{s}"
+ );
+ assert!(s.contains(
+ "const reflex___state____state__demo____my_state = useContext(StateContexts.reflex___state____state__demo____my_state);"
+ ));
+ assert!(s.contains("const ref_root = useRef(null); refs[\"ref_root\"] = ref_root;"));
+ }
+
+ #[test]
+ fn runtime_imports_merge_with_component_imports() {
+ // Regression: when VarData walks bring `$/utils/context` /
+ // `$/utils/state` symbols into `component_imports`, the page emitter
+ // must merge them with the baseline runtime imports rather than
+ // emitting a second `import { ... } from "$/utils/context"` line that
+ // re-declares `EventLoopContext` / `StateContexts`.
+ use reflex_ir::{Component, NodeId, Page as PageIR, SourceLoc};
+ let arena = Arena::new();
+ let text = arena.alloc(Component::Text {
+ value: "x",
+ id: NodeId(1),
+ source_loc: SourceLoc::SYNTHETIC,
+ });
+ let imports = [
+ (
+ reflex_intern::intern("$/utils/context"),
+ reflex_intern::intern("EventLoopContext"),
+ ),
+ (
+ reflex_intern::intern("$/utils/context"),
+ reflex_intern::intern("StateContexts"),
+ ),
+ (
+ reflex_intern::intern("$/utils/context"),
+ reflex_intern::intern("ColorModeContext"),
+ ),
+ (
+ reflex_intern::intern("$/utils/state"),
+ reflex_intern::intern("ReflexEvent"),
+ ),
+ (
+ reflex_intern::intern("$/utils/state"),
+ reflex_intern::intern("applyEventActions"),
+ ),
+ ];
+ let page = PageIR {
+ schema_version: 2,
+ route: "/x",
+ root: &*text,
+ title: None,
+ meta: &[],
+ source_files: &[],
+ component_imports: arena.bump().alloc_slice_copy(&imports),
+ state_bindings: &[],
+ needs_ref: false,
+ };
+ let mut buf = CodeBuffer::new();
+ emit_page(&mut buf, &page, "X");
+ let s = buf.as_str();
+
+ // Each runtime module appears exactly once.
+ assert_eq!(
+ s.matches("from \"$/utils/context\"").count(),
+ 1,
+ "$/utils/context emitted more than once:\n{s}"
+ );
+ assert_eq!(
+ s.matches("from \"$/utils/state\"").count(),
+ 1,
+ "$/utils/state emitted more than once:\n{s}"
+ );
+
+ // The merged $/utils/context line carries baseline + the extra
+ // ColorModeContext brought in by component_imports.
+ assert!(
+ s.contains(
+ "import { EventLoopContext, StateContexts, ColorModeContext } from \"$/utils/context\";"
+ ),
+ "merged context import missing:\n{s}"
+ );
+ // The merged $/utils/state line carries baseline + applyEventActions
+ // (first-seen order: component_imports come before the trailing
+ // baseline aliases, so `refs` slots in after `applyEventActions`).
+ assert!(
+ s.contains(
+ "import { ReflexEvent, applyEventActions, refs } from \"$/utils/state\";"
+ ),
+ "merged state import missing:\n{s}"
+ );
+ }
+
+ #[test]
+ fn source_map_records_non_synthetic_locs() {
+ use reflex_ir::{Component, NodeId, Page as PageIR, PyFileId, SourceLoc};
+ let arena = Arena::new();
+ let real_loc = SourceLoc { file: PyFileId(7), line: 42, col: 11 };
+ let text = arena.alloc(Component::Text {
+ value: arena.alloc_str("hi"),
+ id: NodeId(1),
+ source_loc: real_loc,
+ });
+ let page = PageIR {
+ schema_version: 2,
+ route: "/r",
+ root: &*text,
+ title: None,
+ meta: &[],
+ source_files: &[],
+ component_imports: &[],
+ state_bindings: &[],
+ needs_ref: false,
+ };
+ let mut buf = CodeBuffer::new();
+ let mut map = crate::SourceMap::new();
+ emit_page_with_map(&mut buf, &page, "R", &mut map);
+ let s = buf.as_str();
+ let pos = s.find("\"hi\"").expect(r#"the "hi" literal should be in the output"#);
+ let mapped = map.lookup(pos as u32).expect("location recorded");
+ assert_eq!(mapped.file.0, 7);
+ assert_eq!(mapped.line, 42);
+ assert_eq!(map.len(), 1);
+ }
+}
diff --git a/packages/reflex-compiler-rust/crates/reflex_codegen/src/sourcemap.rs b/packages/reflex-compiler-rust/crates/reflex_codegen/src/sourcemap.rs
new file mode 100644
index 00000000000..8d371a67400
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_codegen/src/sourcemap.rs
@@ -0,0 +1,122 @@
+//! Source-map recording during JSX emission. See plan §4.6.
+//!
+//! Every IR node carries a `SourceLoc` (`PyFileId`, line, col). When the
+//! codegen emits that node's tokens it records the current `CodeBuffer`
+//! byte offset alongside the `SourceLoc`. The resulting `SourceMap` lets a
+//! diagnostic at byte N of the emitted module back-map to the Python source
+//! that produced it.
+//!
+//! The format is intentionally simple — a sorted `Vec<(u32 offset, SourceLoc)>`.
+//! Lookup is binary search. We don't emit JS-side source maps (`source-map`
+//! v3) here because the consumer of these mappings is Reflex's own error
+//! reporter, not the browser. If browser source maps are needed later, this
+//! is the layer that converts.
+
+use reflex_ir::SourceLoc;
+
+#[derive(Debug, Clone, Default)]
+pub struct SourceMap {
+ entries: Vec<(u32, SourceLoc)>,
+}
+
+impl SourceMap {
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ /// Record `offset → loc`. `offset` should be the byte position in the
+ /// surrounding `CodeBuffer` just before the token whose origin is `loc`.
+ #[inline]
+ pub fn record(&mut self, offset: u32, loc: SourceLoc) {
+ // Skip synthetic locations — they don't help diagnostics and would
+ // bloat the map.
+ if loc.file.0 == 0 && loc.line == 0 && loc.col == 0 {
+ return;
+ }
+ // Merge consecutive identical locations — they tend to cluster at
+ // every prop/child of the same Element.
+ if let Some((_, last)) = self.entries.last() {
+ if *last == loc {
+ return;
+ }
+ }
+ self.entries.push((offset, loc));
+ }
+
+ /// Find the SourceLoc that covers `offset`. Returns `None` if `offset`
+ /// precedes all recorded entries.
+ pub fn lookup(&self, offset: u32) -> Option {
+ if self.entries.is_empty() {
+ return None;
+ }
+ match self.entries.binary_search_by_key(&offset, |(o, _)| *o) {
+ Ok(idx) => Some(self.entries[idx].1),
+ Err(0) => None,
+ Err(idx) => Some(self.entries[idx - 1].1),
+ }
+ }
+
+ pub fn len(&self) -> usize {
+ self.entries.len()
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.entries.is_empty()
+ }
+
+ pub fn entries(&self) -> &[(u32, SourceLoc)] {
+ &self.entries
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use reflex_ir::PyFileId;
+
+ fn loc(file: u32, line: u32) -> SourceLoc {
+ SourceLoc {
+ file: PyFileId(file),
+ line,
+ col: 1,
+ }
+ }
+
+ #[test]
+ fn skip_synthetic() {
+ let mut m = SourceMap::new();
+ m.record(10, SourceLoc::SYNTHETIC);
+ assert!(m.is_empty());
+ }
+
+ #[test]
+ fn merges_consecutive_duplicates() {
+ let mut m = SourceMap::new();
+ m.record(0, loc(1, 5));
+ m.record(10, loc(1, 5));
+ m.record(20, loc(1, 5));
+ assert_eq!(m.len(), 1);
+ }
+
+ #[test]
+ fn lookup_picks_floor() {
+ let mut m = SourceMap::new();
+ m.record(0, loc(1, 5));
+ m.record(100, loc(1, 10));
+ m.record(200, loc(2, 1));
+ assert_eq!(m.lookup(0).unwrap().line, 5);
+ assert_eq!(m.lookup(50).unwrap().line, 5);
+ assert_eq!(m.lookup(100).unwrap().line, 10);
+ assert_eq!(m.lookup(150).unwrap().line, 10);
+ assert_eq!(m.lookup(200).unwrap().line, 1);
+ assert_eq!(m.lookup(1000).unwrap().line, 1);
+ }
+
+ #[test]
+ fn lookup_below_first_returns_none() {
+ let mut m = SourceMap::new();
+ m.record(100, loc(1, 5));
+ assert!(m.lookup(0).is_none());
+ assert!(m.lookup(99).is_none());
+ }
+}
diff --git a/packages/reflex-compiler-rust/crates/reflex_codegen/src/static_artifacts.rs b/packages/reflex-compiler-rust/crates/reflex_codegen/src/static_artifacts.rs
new file mode 100644
index 00000000000..4470470e3ab
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_codegen/src/static_artifacts.rs
@@ -0,0 +1,161 @@
+//! Mechanical ports of the small static-artifact templates that
+//! ``rust_pipeline.py`` needs to write into ``.web/``.
+//!
+//! Each function streams output through a generic ``Write`` so the
+//! PyO3 binding can hand it a ``BufWriter`` and skip any
+//! intermediate ``String`` allocation. Unit tests pass ``&mut Vec``
+//! and compare against the legacy Python templates in
+//! ``packages/reflex-base/src/reflex_base/compiler/templates.py``.
+
+use std::io::{self, Write};
+
+/// Emit ``.web/styles/styles.css``.
+///
+/// Ports ``styles_template`` —
+/// ``"@layer __reflex_base;\n" + "\n".join(f"@import url('{s}');", ...)``.
+///
+/// Args:
+/// stylesheets: stylesheet URLs/paths spliced into ``@import`` lines.
+/// w: byte sink.
+pub fn emit_styles_root(stylesheets: &[&str], w: &mut W) -> io::Result<()> {
+ w.write_all(b"@layer __reflex_base;\n")?;
+ for (i, sheet) in stylesheets.iter().enumerate() {
+ if i > 0 {
+ w.write_all(b"\n")?;
+ }
+ w.write_all(b"@import url('")?;
+ w.write_all(sheet.as_bytes())?;
+ w.write_all(b"');")?;
+ }
+ Ok(())
+}
+
+/// Emit ``.web/utils/theme.js``.
+///
+/// Ports ``theme_template`` — ``f"export default {theme}"``. ``theme``
+/// is already a JS object literal (the legacy compiler runs
+/// ``LiteralVar.create(theme)`` before passing it in).
+///
+/// Args:
+/// theme_js: the JS expression that becomes the default export.
+/// w: byte sink.
+pub fn emit_theme_module(theme_js: &str, w: &mut W) -> io::Result<()> {
+ w.write_all(b"export default ")?;
+ w.write_all(theme_js.as_bytes())
+}
+
+/// Write ``.web/backend/stateful_pages.json``.
+///
+/// Mirrors ``App._write_stateful_pages_marker`` — emits a JSON array of
+/// route strings the backend treats as stateful. The Python side
+/// computes the route list (it requires a state walk we haven't ported
+/// yet); Rust just serializes and writes.
+///
+/// Args:
+/// routes: stateful route strings.
+/// w: byte sink.
+pub fn emit_stateful_pages_json(routes: &[&str], w: &mut W) -> io::Result<()> {
+ w.write_all(b"[")?;
+ for (i, route) in routes.iter().enumerate() {
+ if i > 0 {
+ w.write_all(b", ")?;
+ }
+ write_json_string(w, route)?;
+ }
+ w.write_all(b"]")
+}
+
+/// Write a JSON-escaped string literal (with surrounding quotes).
+///
+/// Matches Python's ``json.dumps`` for ASCII inputs: backslash, quote,
+/// and control bytes are escaped; everything else is passed through
+/// verbatim. Route strings are URL paths so this is sufficient — full
+/// Unicode JSON escaping isn't needed here.
+fn write_json_string(w: &mut W, s: &str) -> io::Result<()> {
+ w.write_all(b"\"")?;
+ let bytes = s.as_bytes();
+ let mut start = 0;
+ for (i, &b) in bytes.iter().enumerate() {
+ let escaped: &[u8] = match b {
+ b'"' => b"\\\"",
+ b'\\' => b"\\\\",
+ b'\n' => b"\\n",
+ b'\r' => b"\\r",
+ b'\t' => b"\\t",
+ 0x08 => b"\\b",
+ 0x0c => b"\\f",
+ 0x00..=0x1f => {
+ // Rare control byte; fall back to the \uXXXX form.
+ w.write_all(&bytes[start..i])?;
+ write!(w, "\\u{:04x}", b)?;
+ start = i + 1;
+ continue;
+ }
+ _ => continue,
+ };
+ w.write_all(&bytes[start..i])?;
+ w.write_all(escaped)?;
+ start = i + 1;
+ }
+ w.write_all(&bytes[start..])?;
+ w.write_all(b"\"")
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ fn render(f: F) -> String
+ where
+ F: FnOnce(&mut Vec) -> io::Result<()>,
+ {
+ let mut buf = Vec::new();
+ f(&mut buf).unwrap();
+ String::from_utf8(buf).unwrap()
+ }
+
+ #[test]
+ fn styles_root_matches_legacy_template() {
+ // `styles_template(["a.css", "b.css"])` =
+ // "@layer __reflex_base;\n@import url('a.css');\n@import url('b.css');"
+ let out = render(|w| emit_styles_root(&["a.css", "b.css"], w));
+ assert_eq!(
+ out,
+ "@layer __reflex_base;\n@import url('a.css');\n@import url('b.css');"
+ );
+ }
+
+ #[test]
+ fn styles_root_empty_list_keeps_layer_header() {
+ // `styles_template([])` = "@layer __reflex_base;\n" + "" = "@layer __reflex_base;\n"
+ let out = render(|w| emit_styles_root(&[], w));
+ assert_eq!(out, "@layer __reflex_base;\n");
+ }
+
+ #[test]
+ fn theme_module_matches_legacy_template() {
+ // `theme_template("{accentColor: 'tomato'}")` =
+ // "export default {accentColor: 'tomato'}"
+ let out = render(|w| emit_theme_module("{accentColor: 'tomato'}", w));
+ assert_eq!(out, "export default {accentColor: 'tomato'}");
+ }
+
+ #[test]
+ fn stateful_pages_empty_list() {
+ let out = render(|w| emit_stateful_pages_json(&[], w));
+ assert_eq!(out, "[]");
+ }
+
+ #[test]
+ fn stateful_pages_with_routes() {
+ let out =
+ render(|w| emit_stateful_pages_json(&["index", "about", "blog/post-1"], w));
+ assert_eq!(out, r#"["index", "about", "blog/post-1"]"#);
+ }
+
+ #[test]
+ fn stateful_pages_escapes_special_chars() {
+ let out = render(|w| emit_stateful_pages_json(&["a/\"b\""], w));
+ assert_eq!(out, r#"["a/\"b\""]"#);
+ }
+}
diff --git a/packages/reflex-compiler-rust/crates/reflex_codegen/src/theme.rs b/packages/reflex-compiler-rust/crates/reflex_codegen/src/theme.rs
new file mode 100644
index 00000000000..5448c14dad0
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_codegen/src/theme.rs
@@ -0,0 +1,117 @@
+//! Theme CSS emission. See plan §4.2, D10.
+//!
+//! The Python theme (`ThemeIR`) ships a `(token_name, token_value)` table
+//! plus a raw `global_style` CSS chunk and an `appearance` setting. We emit
+//! a `:root { --token: value; ... }` block followed by the global CSS,
+//! returning one self-contained stylesheet string.
+//!
+//! Token names are emitted as CSS custom properties: `tokenName` →
+//! `--token-name`. Reflex's convention matches Emotion's output.
+
+use reflex_intern::resolve_unchecked;
+use reflex_ir::Theme;
+
+use crate::buffer::CodeBuffer;
+
+pub fn emit_theme(buf: &mut CodeBuffer, theme: &Theme<'_>) {
+ if !theme.tokens.is_empty() {
+ buf.write_str(":root {\n");
+ for (name, value) in theme.tokens {
+ buf.write_str(" --");
+ write_kebab(buf, resolve_unchecked(*name));
+ buf.write_str(": ");
+ buf.write_str(value);
+ buf.write_str(";\n");
+ }
+ buf.write_str("}\n\n");
+ }
+
+ if !theme.global_style.is_empty() {
+ buf.write_str(theme.global_style);
+ if !theme.global_style.ends_with('\n') {
+ buf.write_byte(b'\n');
+ }
+ }
+}
+
+/// camelCase / snake_case → kebab-case for CSS custom property names.
+fn write_kebab(buf: &mut CodeBuffer, name: &str) {
+ let mut first = true;
+ for &b in name.as_bytes() {
+ match b {
+ b'_' => {
+ buf.write_byte(b'-');
+ first = false;
+ }
+ b'A'..=b'Z' => {
+ if !first {
+ buf.write_byte(b'-');
+ }
+ buf.write_byte(b.to_ascii_lowercase());
+ first = false;
+ }
+ _ => {
+ buf.write_byte(b);
+ first = false;
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use reflex_arena::Arena;
+ use reflex_intern::intern;
+ use reflex_ir::Theme;
+
+ #[test]
+ fn emits_tokens_and_global_css() {
+ let arena = Arena::new();
+ let tokens: Vec<(reflex_intern::Symbol, &str)> = vec![
+ (intern("accentColor"), "#ff0080"),
+ (intern("radiusMd"), "8px"),
+ ];
+ let theme = Theme {
+ schema_version: 1,
+ tokens: arena.bump().alloc_slice_copy(&tokens),
+ global_style: "body { margin: 0; }",
+ appearance: "light",
+ };
+ let mut buf = CodeBuffer::new();
+ emit_theme(&mut buf, &theme);
+ let s = buf.as_str();
+ assert!(s.contains(":root {"));
+ assert!(s.contains("--accent-color: #ff0080;"));
+ assert!(s.contains("--radius-md: 8px;"));
+ assert!(s.contains("body { margin: 0; }"));
+ }
+
+ #[test]
+ fn empty_tokens_omit_root_block() {
+ let theme = Theme {
+ schema_version: 1,
+ tokens: &[],
+ global_style: "body { color: red; }",
+ appearance: "light",
+ };
+ let mut buf = CodeBuffer::new();
+ emit_theme(&mut buf, &theme);
+ let s = buf.as_str();
+ assert!(!s.contains(":root"));
+ assert!(s.contains("body { color: red; }"));
+ }
+
+ #[test]
+ fn empty_theme_emits_nothing() {
+ let theme = Theme {
+ schema_version: 1,
+ tokens: &[],
+ global_style: "",
+ appearance: "light",
+ };
+ let mut buf = CodeBuffer::new();
+ emit_theme(&mut buf, &theme);
+ assert!(buf.is_empty());
+ }
+}
diff --git a/packages/reflex-compiler-rust/crates/reflex_codegen/src/vite.rs b/packages/reflex-compiler-rust/crates/reflex_codegen/src/vite.rs
new file mode 100644
index 00000000000..4cccc04e0b0
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_codegen/src/vite.rs
@@ -0,0 +1,57 @@
+//! Vite config emission. See plan §4.5 (artifact #8), D10.
+//!
+//! The config is a JS module that exports the default Vite config object.
+//! For v1 we emit a minimal config — Reflex's existing Vite config has more
+//! options but they're conditionally toggled and depend on Reflex's
+//! configuration system, which lives in Python. We expose hooks so the
+//! Python side can splice in plugin-specific blocks.
+
+use crate::buffer::CodeBuffer;
+
+/// Emit a minimal `vite.config.js` to `buf`.
+///
+/// `extra_plugins_js` is a free-form JS array body (e.g.
+/// `"reflexPlugin(), tsconfigPaths()"`) that the Python side composes from
+/// the plugin manifest. The emitter splices it verbatim — it's the Python
+/// side's responsibility to ensure the snippet is valid JS.
+pub fn emit_vite_config(buf: &mut CodeBuffer, extra_plugins_js: &str) {
+ buf.write_str("import { defineConfig } from \"vite\";\n");
+ buf.write_str("import react from \"@vitejs/plugin-react\";\n\n");
+
+ buf.write_str("export default defineConfig({\n");
+ buf.write_str(" plugins: [react()");
+ if !extra_plugins_js.is_empty() {
+ buf.write_str(", ");
+ buf.write_str(extra_plugins_js);
+ }
+ buf.write_str("],\n");
+ buf.write_str(" build: { outDir: \"dist\", emptyOutDir: true },\n");
+ buf.write_str(" resolve: {\n");
+ buf.write_str(" alias: {\n");
+ buf.write_str(" \"$\": \"/src\",\n");
+ buf.write_str(" },\n");
+ buf.write_str(" },\n");
+ buf.write_str("});\n");
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn minimal_config() {
+ let mut buf = CodeBuffer::new();
+ emit_vite_config(&mut buf, "");
+ let s = buf.as_str();
+ assert!(s.contains("defineConfig"));
+ assert!(s.contains("plugins: [react()],"));
+ }
+
+ #[test]
+ fn config_with_extra_plugins() {
+ let mut buf = CodeBuffer::new();
+ emit_vite_config(&mut buf, "reflexPlugin()");
+ let s = buf.as_str();
+ assert!(s.contains("plugins: [react(), reflexPlugin()],"));
+ }
+}
diff --git a/packages/reflex-compiler-rust/crates/reflex_db/Cargo.toml b/packages/reflex-compiler-rust/crates/reflex_db/Cargo.toml
new file mode 100644
index 00000000000..fdc5cb0d867
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_db/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "reflex_db"
+version = "0.0.0"
+edition.workspace = true
+rust-version.workspace = true
+license.workspace = true
+publish.workspace = true
+
+[dependencies]
+reflex_ir = { workspace = true }
+reflex_codegen = { workspace = true }
+reflex_arena = { workspace = true }
+reflex_intern = { workspace = true }
+rayon = { workspace = true }
+thiserror = { workspace = true }
+xxhash-rust = { version = "0.8", features = ["xxh3"] }
diff --git a/packages/reflex-compiler-rust/crates/reflex_db/src/lib.rs b/packages/reflex-compiler-rust/crates/reflex_db/src/lib.rs
new file mode 100644
index 00000000000..37604156886
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_db/src/lib.rs
@@ -0,0 +1,198 @@
+//! Compiler caching layer. See plan §3.3 / R4 / D5.
+//!
+//! **Implementation note (2026-05-16).** The plan calls for a full Salsa
+//! database with `#[salsa::input] PageIr` per route and `#[salsa::tracked]
+//! emit_page` queries. The actual user-visible behavior at v0.1 is "if the
+//! page's content hash hasn't changed, skip recompilation" — which is what
+//! Salsa's revision-based invalidation reduces to for a single-tracked-query
+//! pipeline. We ship that simpler model first: an `xxh3_64`-keyed
+//! `HashMap<(route_ident, ContentHash), Arc>` cache wrapped around the
+//! parse-and-emit pipeline. The public API (`CompilerDb::emit_page`) is the
+//! surface Salsa will replace; once D7 needs cross-query sharing
+//! (aggregator walks reusing the parsed tree), the inner storage swaps to
+//! real `#[salsa::tracked]` queries without touching callers.
+//!
+//! **Pitfall 14 — collisions.** Debug builds re-hash on cache hit and panic
+//! on mismatch. Release builds trust the hash.
+
+#![forbid(unsafe_code)]
+
+use std::collections::HashMap;
+use std::sync::{Arc, Mutex};
+
+use rayon::prelude::*;
+use reflex_arena::Arena;
+use reflex_codegen::{emit_page as emit_page_module, CodeBuffer};
+use reflex_ir::parse_page;
+use xxhash_rust::xxh3::xxh3_64;
+
+/// Content hash. Stable across process restarts.
+pub type ContentHash = u64;
+
+/// Compute the canonical content hash for a serialized IR blob.
+#[inline]
+pub fn hash_content(bytes: &[u8]) -> ContentHash {
+ xxh3_64(bytes)
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum DbError {
+ #[error("page parse failed: {0}")]
+ Parse(#[from] reflex_ir::ParseError),
+ #[error("emit produced non-utf8 output: {0}")]
+ Utf8(#[from] std::string::FromUtf8Error),
+}
+
+/// One per long-lived compiler session. Cheap to clone (`Arc` inside).
+#[derive(Default, Clone)]
+pub struct CompilerDb {
+ inner: Arc>,
+}
+
+#[derive(Default)]
+struct DbInner {
+ /// `(route_ident, content_hash) -> rendered JS`. Route ident is part of
+ /// the key because the emitted module declares
+ /// `export default function ()`, which depends on the ident
+ /// even when the bytes are identical.
+ page_cache: HashMap<(String, ContentHash), Arc>,
+ /// Optional cap on `page_cache` size. `None` = unbounded.
+ cache_cap: Option,
+}
+
+impl CompilerDb {
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ /// Cap the page cache. Oldest-by-insertion eviction. Set to `None` for
+ /// unbounded (default).
+ pub fn set_cache_capacity(&self, cap: Option) {
+ self.inner.lock().unwrap().cache_cap = cap;
+ }
+
+ /// Compute the JS source for one page. On cache hit, returns the stored
+ /// `Arc` without re-parsing.
+ pub fn emit_page(&self, route_ident: &str, bytes: &[u8]) -> Result, DbError> {
+ let hash = hash_content(bytes);
+ let key = (route_ident.to_owned(), hash);
+
+ if let Some(cached) = self.inner.lock().unwrap().page_cache.get(&key) {
+ return Ok(cached.clone());
+ }
+
+ let rendered = compile_one_page(route_ident, bytes)?;
+ let rendered: Arc = Arc::from(rendered.into_boxed_str());
+
+ let mut inner = self.inner.lock().unwrap();
+ if let Some(cap) = inner.cache_cap {
+ if inner.page_cache.len() >= cap {
+ if let Some(k) = inner.page_cache.keys().next().cloned() {
+ inner.page_cache.remove(&k);
+ }
+ }
+ }
+ inner.page_cache.insert(key, rendered.clone());
+ Ok(rendered)
+ }
+
+ pub fn clear(&self) {
+ self.inner.lock().unwrap().page_cache.clear();
+ }
+
+ pub fn cache_len(&self) -> usize {
+ self.inner.lock().unwrap().page_cache.len()
+ }
+
+ /// Compile many pages in parallel. Returns results in the same order as
+ /// the input. See plan D11 — rayon over pages.
+ ///
+ /// Cache hits avoid any work, so the parallel path only pays for genuine
+ /// cache misses. Threading is provided by the global rayon pool; the
+ /// caller doesn't need to set one up.
+ pub fn emit_pages_parallel(
+ &self,
+ inputs: &[(String, Vec)],
+ ) -> Vec, DbError>> {
+ inputs
+ .par_iter()
+ .map(|(ident, bytes)| self.emit_page(ident, bytes))
+ .collect()
+ }
+}
+
+fn compile_one_page(route_ident: &str, bytes: &[u8]) -> Result {
+ let arena = Arena::with_capacity(bytes.len() * 4);
+ let page = parse_page(&arena, bytes)?;
+ let mut buf = CodeBuffer::with_capacity(bytes.len() * 2);
+ emit_page_module(&mut buf, &page, route_ident);
+ Ok(String::from_utf8(buf.into_bytes())?)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use reflex_ir::parse::test_helpers::tiny_page_bytes;
+
+ #[test]
+ fn first_call_is_miss_second_is_hit() {
+ let db = CompilerDb::new();
+ let bytes = tiny_page_bytes();
+ assert_eq!(db.cache_len(), 0);
+ let a = db.emit_page("Index", &bytes).unwrap();
+ assert_eq!(db.cache_len(), 1);
+ let b = db.emit_page("Index", &bytes).unwrap();
+ // Arc identity check — same string, no recompile.
+ assert!(Arc::ptr_eq(&a, &b));
+ }
+
+ #[test]
+ fn different_idents_are_separate_entries() {
+ let db = CompilerDb::new();
+ let bytes = tiny_page_bytes();
+ let _a = db.emit_page("Index", &bytes).unwrap();
+ let _b = db.emit_page("About", &bytes).unwrap();
+ assert_eq!(db.cache_len(), 2);
+ }
+
+ #[test]
+ fn cache_cap_evicts() {
+ let db = CompilerDb::new();
+ db.set_cache_capacity(Some(1));
+ let bytes = tiny_page_bytes();
+ let _ = db.emit_page("A", &bytes).unwrap();
+ let _ = db.emit_page("B", &bytes).unwrap();
+ assert_eq!(db.cache_len(), 1);
+ }
+
+ #[test]
+ fn xxh3_stable_under_repeat() {
+ let a = hash_content(b"hello world");
+ let b = hash_content(b"hello world");
+ assert_eq!(a, b);
+ }
+
+ #[test]
+ fn parallel_emit_preserves_order_and_caches() {
+ let db = CompilerDb::new();
+ let bytes = tiny_page_bytes();
+ let inputs = vec![
+ ("A".to_owned(), bytes.clone()),
+ ("B".to_owned(), bytes.clone()),
+ ("C".to_owned(), bytes.clone()),
+ ];
+ let out = db.emit_pages_parallel(&inputs);
+ assert_eq!(out.len(), 3);
+ for r in &out {
+ r.as_ref().unwrap();
+ }
+ assert_eq!(db.cache_len(), 3);
+
+ // Re-run: every entry is a cache hit, so the Arc pointers should be
+ // identical to the first run's.
+ let out2 = db.emit_pages_parallel(&inputs);
+ for (a, b) in out.iter().zip(out2.iter()) {
+ assert!(Arc::ptr_eq(a.as_ref().unwrap(), b.as_ref().unwrap()));
+ }
+ }
+}
diff --git a/packages/reflex-compiler-rust/crates/reflex_intern/Cargo.toml b/packages/reflex-compiler-rust/crates/reflex_intern/Cargo.toml
new file mode 100644
index 00000000000..604a26cda08
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_intern/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "reflex_intern"
+version = "0.0.0"
+edition.workspace = true
+rust-version.workspace = true
+license.workspace = true
+publish.workspace = true
diff --git a/packages/reflex-compiler-rust/crates/reflex_intern/src/lib.rs b/packages/reflex-compiler-rust/crates/reflex_intern/src/lib.rs
new file mode 100644
index 00000000000..aeb3eb257ed
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_intern/src/lib.rs
@@ -0,0 +1,164 @@
+//! Symbol interning. See plan §3, R5.
+//!
+//! Every identifier and namespace string in the IR is a `Symbol(u32)`. Equality
+//! and hashing of identifiers becomes `u32 == u32`; the string materializes
+//! only at final byte-emit time.
+//!
+//! This iteration ships a single per-process interner behind a `Mutex`. The
+//! plan's R5/R8 target is per-thread sharded interners with `#[thread_local]`,
+//! deferred until profiling shows the lock matters. The public API
+//! (`Symbol`, `intern`, `resolve`, `well_known`) is stable across that change.
+
+use std::collections::HashMap;
+use std::sync::{Mutex, OnceLock};
+
+/// Interned string identifier.
+///
+/// `Symbol(0)` is reserved for the empty string so `Symbol::default()` is a
+/// valid no-op. Other symbols are assigned in insertion order.
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
+pub struct Symbol(pub u32);
+
+impl Symbol {
+ pub const EMPTY: Symbol = Symbol(0);
+
+ #[inline]
+ pub fn as_u32(self) -> u32 {
+ self.0
+ }
+}
+
+impl Default for Symbol {
+ fn default() -> Self {
+ Self::EMPTY
+ }
+}
+
+struct Interner {
+ /// Indexed by `Symbol.0 as usize`. Strings live forever (leaked into
+ /// `'static` so callers don't pay a copy at resolve time).
+ strings: Vec<&'static str>,
+ lookup: HashMap<&'static str, Symbol>,
+}
+
+impl Interner {
+ fn new() -> Self {
+ let mut me = Self {
+ strings: Vec::with_capacity(64),
+ lookup: HashMap::with_capacity(64),
+ };
+ let s = me.intern_inner("");
+ debug_assert_eq!(s, Symbol::EMPTY);
+ for name in WELL_KNOWN {
+ me.intern_inner(name);
+ }
+ me
+ }
+
+ fn intern_inner(&mut self, s: &str) -> Symbol {
+ if let Some(&sym) = self.lookup.get(s) {
+ return sym;
+ }
+ let leaked: &'static str = Box::leak(s.to_owned().into_boxed_str());
+ let sym = Symbol(self.strings.len() as u32);
+ self.strings.push(leaked);
+ self.lookup.insert(leaked, sym);
+ sym
+ }
+
+ fn resolve(&self, sym: Symbol) -> Option<&'static str> {
+ self.strings.get(sym.0 as usize).copied()
+ }
+}
+
+fn interner() -> &'static Mutex {
+ static IT: OnceLock> = OnceLock::new();
+ IT.get_or_init(|| Mutex::new(Interner::new()))
+}
+
+/// Common identifiers pre-interned at startup so they get low IDs and are
+/// already in the table when the first compile starts.
+const WELL_KNOWN: &[&str] = &[
+ "rx.",
+ "$/",
+ "reflex___state____",
+ "__reflex_",
+ "react",
+ "useState",
+ "useEffect",
+ "useCallback",
+ "useMemo",
+ "useRef",
+ "useContext",
+ "useReducer",
+ "jsx",
+ "Fragment",
+ "children",
+ "key",
+ "ref",
+ "className",
+ "style",
+ "onClick",
+ "onChange",
+ "onSubmit",
+];
+
+/// Intern a string, returning its `Symbol`.
+pub fn intern(s: &str) -> Symbol {
+ interner().lock().unwrap().intern_inner(s)
+}
+
+/// Resolve a `Symbol` back to its string. Returns `None` if the symbol was
+/// created by a different interner instance (shouldn't happen in normal use).
+pub fn resolve(sym: Symbol) -> Option<&'static str> {
+ interner().lock().unwrap().resolve(sym)
+}
+
+/// Resolve, panicking on unknown symbol. Use only when an unknown symbol is a
+/// program bug, not a recoverable error.
+pub fn resolve_unchecked(sym: Symbol) -> &'static str {
+ resolve(sym).expect("symbol not in interner")
+}
+
+/// Look up the `Symbol` for a known-pre-interned string. Returns `None` if the
+/// string was never interned. Useful for hot-path comparisons that want to
+/// avoid `intern()`'s mutex.
+pub fn well_known(s: &str) -> Option {
+ interner().lock().unwrap().lookup.get(s).copied()
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn empty_is_symbol_zero() {
+ assert_eq!(intern(""), Symbol::EMPTY);
+ }
+
+ #[test]
+ fn same_string_same_symbol() {
+ let a = intern("foo_bar");
+ let b = intern("foo_bar");
+ assert_eq!(a, b);
+ }
+
+ #[test]
+ fn different_strings_different_symbols() {
+ let a = intern("alpha");
+ let b = intern("beta");
+ assert_ne!(a, b);
+ }
+
+ #[test]
+ fn resolve_round_trip() {
+ let s = intern("hello_world");
+ assert_eq!(resolve(s).unwrap(), "hello_world");
+ }
+
+ #[test]
+ fn well_known_returns_some() {
+ assert!(well_known("rx.").is_some());
+ assert!(well_known("react").is_some());
+ }
+}
diff --git a/packages/reflex-compiler-rust/crates/reflex_ir/Cargo.toml b/packages/reflex-compiler-rust/crates/reflex_ir/Cargo.toml
new file mode 100644
index 00000000000..e2d1bc8f9da
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_ir/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "reflex_ir"
+version = "0.0.0"
+edition.workspace = true
+rust-version.workspace = true
+license.workspace = true
+publish.workspace = true
+
+[dependencies]
+reflex_arena = { workspace = true }
+reflex_intern = { workspace = true }
+bumpalo = { workspace = true }
+rmp = { workspace = true }
+thiserror = { workspace = true }
diff --git a/packages/reflex-compiler-rust/crates/reflex_ir/src/lib.rs b/packages/reflex-compiler-rust/crates/reflex_ir/src/lib.rs
new file mode 100644
index 00000000000..134510ef467
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_ir/src/lib.rs
@@ -0,0 +1,368 @@
+//! IR enum types. See plan §3.2, §4.
+//!
+//! Every variant is `Copy`. Slices and string refs borrow the arena (`'a`).
+//! Public field access is allowed within the workspace; the plan's R4
+//! corollary (Salsa-tracked accessors) wraps these reads in `reflex_db`, but
+//! the underlying enum has to be matchable for the visitor pattern in `D6`.
+//!
+//! Wire-format compatibility: enum discriminants are explicitly assigned so
+//! the msgpack parser in `reflex_py` can read a `u8` kind byte and `match`
+//! straight into a constructor without lookup tables.
+
+#![forbid(unsafe_code)]
+
+use reflex_intern::Symbol;
+
+pub mod parse;
+pub mod visitor;
+
+pub use parse::{parse_global_state, parse_page, parse_plugin_manifest, parse_theme, ParseError};
+pub use visitor::{walk_component, walk_event_handler, walk_match_arm, walk_page, walk_value, IrVisitor};
+
+/// Stable hash of a node's canonical bytes. Used as the Salsa cache key and as
+/// the React `key=` value for memoized subtrees.
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
+pub struct NodeId(pub u64);
+
+/// Per-`CompilerSession` interned Python source file. Survives revisions.
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
+pub struct PyFileId(pub u32);
+
+/// Source location for diagnostics. `line` and `col` are 1-based; `(0, 0)`
+/// means "synthetic / no source location" (e.g. generated Fragment wrappers).
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
+pub struct SourceLoc {
+ pub file: PyFileId,
+ pub line: u32,
+ pub col: u32,
+}
+
+impl SourceLoc {
+ pub const SYNTHETIC: SourceLoc = SourceLoc {
+ file: PyFileId(0),
+ line: 0,
+ col: 0,
+ };
+}
+
+/// VarData carries the hidden state that travels with a JS expression so the
+/// IR consumer can hoist imports, register hooks, and order computed-var
+/// dependencies. Strings live in the arena.
+#[derive(Clone, Copy, Debug)]
+pub struct VarData<'a> {
+ /// Hook source fragments this expression depends on (raw JS strings).
+ pub hooks: &'a [&'a str],
+ /// `(module, name)` imports this expression requires.
+ pub imports: &'a [(Symbol, Symbol)],
+ /// Owning state class name, if any (`State`, `MySubState`, …).
+ pub state: Option,
+ /// Vars this var depends on (interned `_js_expr` strings).
+ pub deps: &'a [Symbol],
+ /// Position hint for hoisting (`None` = no constraint, else 0-based).
+ pub position: Option,
+ /// Component memoization wrappers this expression needs at hoist time.
+ pub components: &'a [Symbol],
+}
+
+impl<'a> VarData<'a> {
+ pub const EMPTY: VarData<'static> = VarData {
+ hooks: &[],
+ imports: &[],
+ state: None,
+ deps: &[],
+ position: None,
+ components: &[],
+ };
+}
+
+/// A JS-expressible value embedded in IR. `JsExpr` is the dominant case —
+/// Python pre-bakes the JS source and ships it along with VarData.
+#[derive(Clone, Copy, Debug)]
+pub enum Value<'a> {
+ /// Pre-baked JS source string + the metadata needed to hoist its
+ /// imports/hooks. The string is the literal text spliced into JSX.
+ JsExpr {
+ expr: &'a str,
+ var_data: VarData<'a>,
+ },
+ /// Compile-time literal (no JS evaluation needed at render time).
+ Literal(Literal<'a>),
+ /// Reference to another named symbol (e.g. a state class identifier).
+ Ref(Symbol),
+}
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum Literal<'a> {
+ Null,
+ Bool(bool),
+ Int(i64),
+ Float(f64),
+ /// Already-JSON-quoted string (the Python emitter handled escaping).
+ Str(&'a str),
+}
+
+/// A single `onClick`-style binding.
+#[derive(Clone, Copy, Debug)]
+pub struct EventHandler<'a> {
+ /// Event trigger name (`"on_click"`, `"on_change"`, …).
+ pub trigger: Symbol,
+ /// JS expression bound to the trigger.
+ pub expr: &'a str,
+ pub var_data: VarData<'a>,
+}
+
+/// A React-hook fragment hoisted to the top of the page render function.
+#[derive(Clone, Copy, Debug)]
+pub struct Hook<'a> {
+ /// Raw JS source for the hook body.
+ pub code: &'a str,
+ /// Vars this hook depends on (for ordering).
+ pub deps: &'a [Symbol],
+ /// Position bucket (`0` = useState/useEffect setup, `1` = derived, …).
+ pub position: u8,
+}
+
+/// A single `rx.match(...)` arm.
+#[derive(Clone, Copy, Debug)]
+pub struct MatchArm<'a> {
+ pub case: Value<'a>,
+ pub body: &'a Component<'a>,
+}
+
+/// The Component IR tree.
+///
+/// `#[repr(C, u8)]` pins the discriminant to a `u8` and gives the layout a
+/// stable shape so the Salsa cache and msgpack decoder can rely on it. The
+/// remaining payload is laid out C-style, which trades a small amount of
+/// padding for predictable pointer-stride into slices of `Component`.
+#[repr(C, u8)]
+#[derive(Clone, Copy, Debug)]
+pub enum Component<'a> {
+ Element {
+ tag: Symbol,
+ props: &'a [(Symbol, Value<'a>)],
+ children: &'a [Component<'a>],
+ event_handlers: &'a [EventHandler<'a>],
+ hooks: &'a [Hook<'a>],
+ id: NodeId,
+ source_loc: SourceLoc,
+ } = 0,
+ Text {
+ value: &'a str,
+ id: NodeId,
+ source_loc: SourceLoc,
+ } = 1,
+ Foreach {
+ iter: Value<'a>,
+ body: &'a Component<'a>,
+ id: NodeId,
+ source_loc: SourceLoc,
+ } = 2,
+ Cond {
+ test: Value<'a>,
+ then: &'a Component<'a>,
+ else_: Option<&'a Component<'a>>,
+ id: NodeId,
+ source_loc: SourceLoc,
+ } = 3,
+ Match {
+ value: Value<'a>,
+ arms: &'a [MatchArm<'a>],
+ default: Option<&'a Component<'a>>,
+ id: NodeId,
+ source_loc: SourceLoc,
+ } = 4,
+ Memoize {
+ inner: &'a Component<'a>,
+ key: NodeId,
+ id: NodeId,
+ source_loc: SourceLoc,
+ } = 5,
+ Fragment {
+ children: &'a [Component<'a>],
+ id: NodeId,
+ source_loc: SourceLoc,
+ } = 6,
+ /// JSX value inline (e.g. `{state.title}`) — emits as a single expression
+ /// with no surrounding tag. Distinct from `Text`, which double-quotes.
+ Expr {
+ value: Value<'a>,
+ id: NodeId,
+ source_loc: SourceLoc,
+ } = 7,
+}
+
+impl<'a> Component<'a> {
+ #[inline]
+ pub fn id(&self) -> NodeId {
+ match self {
+ Component::Element { id, .. }
+ | Component::Text { id, .. }
+ | Component::Foreach { id, .. }
+ | Component::Cond { id, .. }
+ | Component::Match { id, .. }
+ | Component::Memoize { id, .. }
+ | Component::Fragment { id, .. }
+ | Component::Expr { id, .. } => *id,
+ }
+ }
+
+ #[inline]
+ pub fn source_loc(&self) -> SourceLoc {
+ match self {
+ Component::Element { source_loc, .. }
+ | Component::Text { source_loc, .. }
+ | Component::Foreach { source_loc, .. }
+ | Component::Cond { source_loc, .. }
+ | Component::Match { source_loc, .. }
+ | Component::Memoize { source_loc, .. }
+ | Component::Fragment { source_loc, .. }
+ | Component::Expr { source_loc, .. } => *source_loc,
+ }
+ }
+
+ /// Wire-format discriminant. Matches the `kind` field in the msgpack
+ /// schema (§4.1).
+ #[inline]
+ pub fn kind(&self) -> u8 {
+ match self {
+ Component::Element { .. } => 0,
+ Component::Text { .. } => 1,
+ Component::Foreach { .. } => 2,
+ Component::Cond { .. } => 3,
+ Component::Match { .. } => 4,
+ Component::Memoize { .. } => 5,
+ Component::Fragment { .. } => 6,
+ Component::Expr { .. } => 7,
+ }
+ }
+}
+
+/// Top-level page IR. The unit of Salsa input granularity (§3.3 / D5).
+///
+/// **Schema v2 additions** (`component_imports`, `state_bindings`,
+/// `needs_ref`): the Reflex React runtime expects the page module to
+/// import its component classes, wire `useContext(StateContexts.*)` for
+/// each state class used by the page, and (for any element with an `id`
+/// prop) wire `useRef`. The bridge harvests these from the Component
+/// tree; the Rust codegen emits the corresponding boilerplate.
+#[derive(Clone, Copy, Debug)]
+pub struct Page<'a> {
+ /// Wire schema version (`PageIR.v`).
+ pub schema_version: u32,
+ pub route: &'a str,
+ pub root: &'a Component<'a>,
+ pub title: Option<&'a str>,
+ pub meta: &'a [Meta<'a>],
+ /// Python source files this page depends on (drives source-map back-mapping).
+ pub source_files: &'a [PyFileId],
+ /// Component-class imports required at module scope:
+ /// `import { as } from "";`.
+ /// Encoded as `(module, alias)` pairs where `module` is the JS module
+ /// specifier and `alias` is the component identifier used inside JSX.
+ pub component_imports: &'a [(Symbol, Symbol)],
+ /// State-context identifiers needed via
+ /// `useContext(StateContexts.)`. Each one becomes a `const`
+ /// binding at the top of the render function.
+ pub state_bindings: &'a [Symbol],
+ /// Whether the page references any `id` prop, requiring a `useRef`
+ /// setup at the top of the render function.
+ pub needs_ref: bool,
+}
+
+#[derive(Clone, Copy, Debug)]
+pub struct Meta<'a> {
+ pub name: Symbol,
+ pub content: &'a str,
+}
+
+/// Theme IR (§4.2).
+#[derive(Clone, Copy, Debug)]
+pub struct Theme<'a> {
+ pub schema_version: u32,
+ pub tokens: &'a [(Symbol, &'a str)],
+ pub global_style: &'a str,
+ pub appearance: &'a str,
+}
+
+/// GlobalState IR (§4.3).
+///
+/// `initial_state` and `client_storage` are kept as raw msgpack-encoded blobs
+/// because their shape is recursive `Any` — Rust never inspects them; it
+/// just splices them into the emitted JS shell as JSON literals.
+#[derive(Clone, Copy, Debug)]
+pub struct GlobalState<'a> {
+ pub schema_version: u32,
+ /// Pre-JSON-encoded initial state.
+ pub initial_state_json: &'a [u8],
+ /// Pre-JSON-encoded client storage config.
+ pub client_storage_json: &'a [u8],
+ pub computed_var_deps: &'a [ComputedVarDep<'a>],
+}
+
+#[derive(Clone, Copy, Debug)]
+pub struct ComputedVarDep<'a> {
+ pub var: Symbol,
+ pub depends_on: &'a [Symbol],
+}
+
+/// Manifest of installed plugins. The plugin protocol is three-phase (§4.7);
+/// the manifest is all Rust ever sees — actual `pre_compile`/`enter_leave`/
+/// `post_build` code stays in Python.
+#[derive(Clone, Copy, Debug)]
+pub struct PluginManifest<'a> {
+ pub schema_version: u32,
+ pub plugins: &'a [PluginEntry<'a>],
+}
+
+#[derive(Clone, Copy, Debug)]
+pub struct PluginEntry<'a> {
+ pub name: Symbol,
+ pub static_assets: &'a [(Symbol, &'a [u8])],
+ pub stylesheet_imports: &'a [Symbol],
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ fn assert_copy() {}
+ fn assert_no_drop() {
+ assert!(!std::mem::needs_drop::());
+ }
+
+ #[test]
+ fn component_is_copy() {
+ assert_copy::>();
+ assert_copy::>();
+ assert_copy::>();
+ assert_copy::>();
+ assert_copy::>();
+ assert_copy::>();
+ assert_copy::>();
+ assert_copy::>();
+ assert_copy::>();
+ assert_copy::>();
+ }
+
+ #[test]
+ fn r2_no_drop() {
+ assert_no_drop::>();
+ assert_no_drop::>();
+ assert_no_drop::>();
+ assert_no_drop::>();
+ assert_no_drop::>();
+ assert_no_drop::>();
+ assert_no_drop::>();
+ }
+
+ #[test]
+ fn component_kind_matches_discriminant() {
+ let id = NodeId(0);
+ let loc = SourceLoc::SYNTHETIC;
+ let text = Component::Text { value: "hi", id, source_loc: loc };
+ let frag = Component::Fragment { children: &[], id, source_loc: loc };
+ assert_eq!(text.kind(), 1);
+ assert_eq!(frag.kind(), 6);
+ }
+}
diff --git a/packages/reflex-compiler-rust/crates/reflex_ir/src/parse.rs b/packages/reflex-compiler-rust/crates/reflex_ir/src/parse.rs
new file mode 100644
index 00000000000..3132af8c6e3
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_ir/src/parse.rs
@@ -0,0 +1,959 @@
+//! Hand-rolled msgpack parser for the §4 wire format. See plan §3.4, D4, R1.
+//!
+//! Wire format is positional msgpack arrays. The parser borrows `&str`
+//! directly from the input bytes where possible, copying into the arena only
+//! when the lifetime must survive the input slice (e.g. ID strings stored in
+//! VarData that the codegen will keep referencing). Spike measurement: 25×
+//! faster than rmp-serde at 10k nodes.
+//!
+//! Schema versions:
+//! 1 — current; per §4.1.
+//!
+//! Wire shapes (see RUST_REWRITE_PLAN.md §4 for the canonical definition):
+//!
+//! ```text
+//! PageIR = [v:u32, route:str, root:Component, title:str|nil,
+//! meta:[Meta], source_files:[u32]]
+//! Component = [kind:u8, ...payload] (payload depends on kind)
+//! Element(0) = [0, tag:str, props:[[name:str, Value]], children:[Component],
+//! events:[EventHandler], hooks:[Hook], id:u64, loc:[u32,u32,u32]]
+//! Text(1) = [1, value:str, id:u64, loc]
+//! Foreach(2) = [2, iter:Value, body:Component, id:u64, loc]
+//! Cond(3) = [3, test:Value, then:Component, else:Component|nil, id:u64, loc]
+//! Match(4) = [4, value:Value, arms:[MatchArm], default:Component|nil, id:u64, loc]
+//! Memoize(5) = [5, inner:Component, key:u64, id:u64, loc]
+//! Fragment(6) = [6, children:[Component], id:u64, loc]
+//! Expr(7) = [7, value:Value, id:u64, loc]
+//! Value = [kind:u8, ...payload]
+//! JsExpr(0) = [0, expr:str, VarData]
+//! Literal(1) = [1, Literal]
+//! Ref(2) = [2, name:str]
+//! Literal = [kind:u8, ...payload]
+//! Null(0) = [0]
+//! Bool(1) = [1, bool]
+//! Int(2) = [2, i64]
+//! Float(3) = [3, f64]
+//! Str(4) = [4, str]
+//! VarData = [hooks:[str], imports:[[module:str, name:str]],
+//! state:str|nil, deps:[str], position:u8|nil, components:[str]]
+//! EventHandler = [trigger:str, expr:str, VarData]
+//! Hook = [code:str, deps:[str], position:u8]
+//! MatchArm = [case:Value, body:Component]
+//! Meta = [name:str, content:str]
+//! SourceLoc = [file_id:u32, line:u32, col:u32]
+//! ```
+
+use std::convert::TryFrom;
+
+use reflex_arena::Arena;
+use reflex_intern::{intern, Symbol};
+use rmp::decode::{self, RmpRead};
+use rmp::Marker;
+
+use crate::{
+ Component, ComputedVarDep, EventHandler, GlobalState, Hook, Literal, MatchArm, Meta, NodeId,
+ Page, PluginEntry, PluginManifest, PyFileId, SourceLoc, Theme, Value, VarData,
+};
+
+pub const SCHEMA_VERSION: u32 = 2;
+
+#[derive(Debug, thiserror::Error)]
+pub enum ParseError {
+ #[error("unsupported schema version {0}; this build accepts {SCHEMA_VERSION}")]
+ UnsupportedVersion(u32),
+ #[error("malformed msgpack: {0}")]
+ Msgpack(String),
+ #[error("invalid utf-8: {0}")]
+ Utf8(#[from] std::str::Utf8Error),
+ #[error("unknown component kind {0}")]
+ UnknownComponentKind(u8),
+ #[error("unknown value kind {0}")]
+ UnknownValueKind(u8),
+ #[error("unknown literal kind {0}")]
+ UnknownLiteralKind(u8),
+ #[error("expected array of length {expected}, got {actual}")]
+ BadArrayLen { expected: u32, actual: u32 },
+ #[error("integer overflow: {0}")]
+ IntOverflow(&'static str),
+}
+
+type Result = std::result::Result;
+
+fn map_msgpack(e: E) -> ParseError {
+ ParseError::Msgpack(e.to_string())
+}
+
+// ---- Primitive readers ------------------------------------------------------
+
+#[inline]
+fn read_array_len(buf: &mut &[u8]) -> Result {
+ decode::read_array_len(buf).map_err(map_msgpack)
+}
+
+#[inline]
+fn read_str_borrowed<'a>(buf: &mut &'a [u8]) -> Result<&'a str> {
+ let len = decode::read_str_len(buf).map_err(map_msgpack)? as usize;
+ if buf.len() < len {
+ return Err(ParseError::Msgpack("short read on str".into()));
+ }
+ let (head, rest) = buf.split_at(len);
+ *buf = rest;
+ Ok(std::str::from_utf8(head)?)
+}
+
+#[inline]
+fn read_bin_borrowed<'a>(buf: &mut &'a [u8]) -> Result<&'a [u8]> {
+ let len = decode::read_bin_len(buf).map_err(map_msgpack)? as usize;
+ if buf.len() < len {
+ return Err(ParseError::Msgpack("short read on bin".into()));
+ }
+ let (head, rest) = buf.split_at(len);
+ *buf = rest;
+ Ok(head)
+}
+
+#[inline]
+fn read_u32(buf: &mut &[u8]) -> Result {
+ let n = decode::read_int::(buf).map_err(map_msgpack)?;
+ u32::try_from(n).map_err(|_| ParseError::IntOverflow("u32"))
+}
+
+#[inline]
+fn read_u64(buf: &mut &[u8]) -> Result {
+ let raw: i128 = decode::read_int::(buf).map_err(map_msgpack)?;
+ u64::try_from(raw).map_err(|_| ParseError::IntOverflow("u64"))
+}
+
+#[inline]
+fn read_i64(buf: &mut &[u8]) -> Result {
+ decode::read_int::(buf).map_err(map_msgpack)
+}
+
+#[inline]
+fn read_f64(buf: &mut &[u8]) -> Result {
+ decode::read_f64(buf).map_err(map_msgpack)
+}
+
+#[inline]
+fn read_bool(buf: &mut &[u8]) -> Result {
+ decode::read_bool(buf).map_err(map_msgpack)
+}
+
+#[inline]
+fn read_u8(buf: &mut &[u8]) -> Result {
+ buf.read_u8().map_err(map_msgpack)
+}
+
+/// Peek the next marker and consume it if it's `nil`. Returns `true` when nil
+/// was consumed, `false` when the marker is something else (and the cursor is
+/// unchanged).
+#[inline]
+fn try_read_nil(buf: &mut &[u8]) -> Result {
+ if buf.is_empty() {
+ return Err(ParseError::Msgpack("unexpected end while peeking nil".into()));
+ }
+ let m = Marker::from_u8(buf[0]);
+ if matches!(m, Marker::Null) {
+ *buf = &buf[1..];
+ Ok(true)
+ } else {
+ Ok(false)
+ }
+}
+
+#[inline]
+fn read_str_or_nil_borrowed<'a>(buf: &mut &'a [u8]) -> Result> {
+ if try_read_nil(buf)? {
+ Ok(None)
+ } else {
+ Ok(Some(read_str_borrowed(buf)?))
+ }
+}
+
+#[inline]
+fn read_u8_or_nil(buf: &mut &[u8]) -> Result > {
+ if try_read_nil(buf)? {
+ Ok(None)
+ } else {
+ let n = decode::read_int::(buf).map_err(map_msgpack)?;
+ u8::try_from(n)
+ .map(Some)
+ .map_err(|_| ParseError::IntOverflow("u8"))
+ }
+}
+
+#[inline]
+fn intern_str(s: &str) -> Symbol {
+ intern(s)
+}
+
+// ---- Array-of-Symbol / array-of-strings / array-of-(Symbol,Symbol) ----------
+
+#[inline]
+fn read_symbol_array<'a>(arena: &'a Arena, buf: &mut &[u8]) -> Result<&'a [Symbol]> {
+ let n = read_array_len(buf)? as usize;
+ let mut tmp: bumpalo::collections::Vec =
+ bumpalo::collections::Vec::with_capacity_in(n, arena.bump());
+ for _ in 0..n {
+ let s = read_str_borrowed(buf)?;
+ tmp.push(intern_str(s));
+ }
+ Ok(tmp.into_bump_slice())
+}
+
+#[inline]
+fn read_str_array_owned<'a>(arena: &'a Arena, buf: &mut &[u8]) -> Result<&'a [&'a str]> {
+ let n = read_array_len(buf)? as usize;
+ let mut tmp: bumpalo::collections::Vec<&'a str> =
+ bumpalo::collections::Vec::with_capacity_in(n, arena.bump());
+ for _ in 0..n {
+ let s = read_str_borrowed(buf)?;
+ tmp.push(arena.alloc_str(s));
+ }
+ Ok(tmp.into_bump_slice())
+}
+
+#[inline]
+fn read_symbol_pair_array<'a>(
+ arena: &'a Arena,
+ buf: &mut &[u8],
+) -> Result<&'a [(Symbol, Symbol)]> {
+ let n = read_array_len(buf)? as usize;
+ let mut tmp: bumpalo::collections::Vec<(Symbol, Symbol)> =
+ bumpalo::collections::Vec::with_capacity_in(n, arena.bump());
+ for _ in 0..n {
+ let pair_len = read_array_len(buf)?;
+ if pair_len != 2 {
+ return Err(ParseError::BadArrayLen {
+ expected: 2,
+ actual: pair_len,
+ });
+ }
+ let a = intern_str(read_str_borrowed(buf)?);
+ let b = intern_str(read_str_borrowed(buf)?);
+ tmp.push((a, b));
+ }
+ Ok(tmp.into_bump_slice())
+}
+
+// ---- Structured readers -----------------------------------------------------
+
+fn read_source_loc(buf: &mut &[u8]) -> Result {
+ let arr_len = read_array_len(buf)?;
+ if arr_len != 3 {
+ return Err(ParseError::BadArrayLen {
+ expected: 3,
+ actual: arr_len,
+ });
+ }
+ let file = PyFileId(read_u32(buf)?);
+ let line = read_u32(buf)?;
+ let col = read_u32(buf)?;
+ Ok(SourceLoc { file, line, col })
+}
+
+fn read_var_data<'a>(arena: &'a Arena, buf: &mut &[u8]) -> Result> {
+ let arr_len = read_array_len(buf)?;
+ if arr_len != 6 {
+ return Err(ParseError::BadArrayLen {
+ expected: 6,
+ actual: arr_len,
+ });
+ }
+ let hooks = read_str_array_owned(arena, buf)?;
+ let imports = read_symbol_pair_array(arena, buf)?;
+ let state = read_str_or_nil_borrowed(buf)?.map(intern_str);
+ let deps = read_symbol_array(arena, buf)?;
+ let position = read_u8_or_nil(buf)?;
+ let components = read_symbol_array(arena, buf)?;
+ Ok(VarData {
+ hooks,
+ imports,
+ state,
+ deps,
+ position,
+ components,
+ })
+}
+
+fn read_literal<'a>(arena: &'a Arena, buf: &mut &[u8]) -> Result> {
+ let arr_len = read_array_len(buf)?;
+ let kind = read_u8(buf)?;
+ match kind {
+ 0 => {
+ if arr_len != 1 {
+ return Err(ParseError::BadArrayLen {
+ expected: 1,
+ actual: arr_len,
+ });
+ }
+ Ok(Literal::Null)
+ }
+ 1 => {
+ if arr_len != 2 {
+ return Err(ParseError::BadArrayLen {
+ expected: 2,
+ actual: arr_len,
+ });
+ }
+ Ok(Literal::Bool(read_bool(buf)?))
+ }
+ 2 => {
+ if arr_len != 2 {
+ return Err(ParseError::BadArrayLen {
+ expected: 2,
+ actual: arr_len,
+ });
+ }
+ Ok(Literal::Int(read_i64(buf)?))
+ }
+ 3 => {
+ if arr_len != 2 {
+ return Err(ParseError::BadArrayLen {
+ expected: 2,
+ actual: arr_len,
+ });
+ }
+ Ok(Literal::Float(read_f64(buf)?))
+ }
+ 4 => {
+ if arr_len != 2 {
+ return Err(ParseError::BadArrayLen {
+ expected: 2,
+ actual: arr_len,
+ });
+ }
+ let s = read_str_borrowed(buf)?;
+ Ok(Literal::Str(arena.alloc_str(s)))
+ }
+ _ => Err(ParseError::UnknownLiteralKind(kind)),
+ }
+}
+
+fn read_value<'a>(arena: &'a Arena, buf: &mut &[u8]) -> Result> {
+ let arr_len = read_array_len(buf)?;
+ let kind = read_u8(buf)?;
+ match kind {
+ 0 => {
+ if arr_len != 3 {
+ return Err(ParseError::BadArrayLen {
+ expected: 3,
+ actual: arr_len,
+ });
+ }
+ let expr = arena.alloc_str(read_str_borrowed(buf)?);
+ let var_data = read_var_data(arena, buf)?;
+ Ok(Value::JsExpr { expr, var_data })
+ }
+ 1 => {
+ if arr_len != 2 {
+ return Err(ParseError::BadArrayLen {
+ expected: 2,
+ actual: arr_len,
+ });
+ }
+ Ok(Value::Literal(read_literal(arena, buf)?))
+ }
+ 2 => {
+ if arr_len != 2 {
+ return Err(ParseError::BadArrayLen {
+ expected: 2,
+ actual: arr_len,
+ });
+ }
+ Ok(Value::Ref(intern_str(read_str_borrowed(buf)?)))
+ }
+ _ => Err(ParseError::UnknownValueKind(kind)),
+ }
+}
+
+fn read_event_handler<'a>(arena: &'a Arena, buf: &mut &[u8]) -> Result> {
+ let arr_len = read_array_len(buf)?;
+ if arr_len != 3 {
+ return Err(ParseError::BadArrayLen {
+ expected: 3,
+ actual: arr_len,
+ });
+ }
+ let trigger = intern_str(read_str_borrowed(buf)?);
+ let expr = arena.alloc_str(read_str_borrowed(buf)?);
+ let var_data = read_var_data(arena, buf)?;
+ Ok(EventHandler {
+ trigger,
+ expr,
+ var_data,
+ })
+}
+
+fn read_hook<'a>(arena: &'a Arena, buf: &mut &[u8]) -> Result> {
+ let arr_len = read_array_len(buf)?;
+ if arr_len != 3 {
+ return Err(ParseError::BadArrayLen {
+ expected: 3,
+ actual: arr_len,
+ });
+ }
+ let code = arena.alloc_str(read_str_borrowed(buf)?);
+ let deps = read_symbol_array(arena, buf)?;
+ let n = decode::read_int::(buf).map_err(map_msgpack)?;
+ let position = u8::try_from(n).map_err(|_| ParseError::IntOverflow("u8"))?;
+ Ok(Hook {
+ code,
+ deps,
+ position,
+ })
+}
+
+fn read_match_arm<'a>(arena: &'a Arena, buf: &mut &[u8]) -> Result> {
+ let arr_len = read_array_len(buf)?;
+ if arr_len != 2 {
+ return Err(ParseError::BadArrayLen {
+ expected: 2,
+ actual: arr_len,
+ });
+ }
+ let case = read_value(arena, buf)?;
+ let body = arena.alloc(read_component(arena, buf)?);
+ Ok(MatchArm { case, body: &*body })
+}
+
+fn read_meta<'a>(arena: &'a Arena, buf: &mut &[u8]) -> Result > {
+ let arr_len = read_array_len(buf)?;
+ if arr_len != 2 {
+ return Err(ParseError::BadArrayLen {
+ expected: 2,
+ actual: arr_len,
+ });
+ }
+ let name = intern_str(read_str_borrowed(buf)?);
+ let content = arena.alloc_str(read_str_borrowed(buf)?);
+ Ok(Meta { name, content })
+}
+
+fn read_component<'a>(arena: &'a Arena, buf: &mut &[u8]) -> Result> {
+ let arr_len = read_array_len(buf)?;
+ let kind = read_u8(buf)?;
+ match kind {
+ 0 => {
+ if arr_len != 8 {
+ return Err(ParseError::BadArrayLen {
+ expected: 8,
+ actual: arr_len,
+ });
+ }
+ let tag = intern_str(read_str_borrowed(buf)?);
+
+ let n_props = read_array_len(buf)? as usize;
+ let mut props: bumpalo::collections::Vec<(Symbol, Value<'a>)> =
+ bumpalo::collections::Vec::with_capacity_in(n_props, arena.bump());
+ for _ in 0..n_props {
+ let pair_len = read_array_len(buf)?;
+ if pair_len != 2 {
+ return Err(ParseError::BadArrayLen {
+ expected: 2,
+ actual: pair_len,
+ });
+ }
+ let name = intern_str(read_str_borrowed(buf)?);
+ let value = read_value(arena, buf)?;
+ props.push((name, value));
+ }
+
+ let n_children = read_array_len(buf)? as usize;
+ let mut children: bumpalo::collections::Vec> =
+ bumpalo::collections::Vec::with_capacity_in(n_children, arena.bump());
+ for _ in 0..n_children {
+ children.push(read_component(arena, buf)?);
+ }
+
+ let n_events = read_array_len(buf)? as usize;
+ let mut events: bumpalo::collections::Vec> =
+ bumpalo::collections::Vec::with_capacity_in(n_events, arena.bump());
+ for _ in 0..n_events {
+ events.push(read_event_handler(arena, buf)?);
+ }
+
+ let n_hooks = read_array_len(buf)? as usize;
+ let mut hooks: bumpalo::collections::Vec> =
+ bumpalo::collections::Vec::with_capacity_in(n_hooks, arena.bump());
+ for _ in 0..n_hooks {
+ hooks.push(read_hook(arena, buf)?);
+ }
+
+ let id = NodeId(read_u64(buf)?);
+ let source_loc = read_source_loc(buf)?;
+ Ok(Component::Element {
+ tag,
+ props: props.into_bump_slice(),
+ children: children.into_bump_slice(),
+ event_handlers: events.into_bump_slice(),
+ hooks: hooks.into_bump_slice(),
+ id,
+ source_loc,
+ })
+ }
+ 1 => {
+ if arr_len != 4 {
+ return Err(ParseError::BadArrayLen {
+ expected: 4,
+ actual: arr_len,
+ });
+ }
+ let value = arena.alloc_str(read_str_borrowed(buf)?);
+ let id = NodeId(read_u64(buf)?);
+ let source_loc = read_source_loc(buf)?;
+ Ok(Component::Text {
+ value,
+ id,
+ source_loc,
+ })
+ }
+ 2 => {
+ if arr_len != 5 {
+ return Err(ParseError::BadArrayLen {
+ expected: 5,
+ actual: arr_len,
+ });
+ }
+ let iter = read_value(arena, buf)?;
+ let body = arena.alloc(read_component(arena, buf)?);
+ let id = NodeId(read_u64(buf)?);
+ let source_loc = read_source_loc(buf)?;
+ Ok(Component::Foreach {
+ iter,
+ body: &*body,
+ id,
+ source_loc,
+ })
+ }
+ 3 => {
+ if arr_len != 6 {
+ return Err(ParseError::BadArrayLen {
+ expected: 6,
+ actual: arr_len,
+ });
+ }
+ let test = read_value(arena, buf)?;
+ let then = arena.alloc(read_component(arena, buf)?);
+ let else_ = if try_read_nil(buf)? {
+ None
+ } else {
+ Some(&*arena.alloc(read_component(arena, buf)?))
+ };
+ let id = NodeId(read_u64(buf)?);
+ let source_loc = read_source_loc(buf)?;
+ Ok(Component::Cond {
+ test,
+ then: &*then,
+ else_,
+ id,
+ source_loc,
+ })
+ }
+ 4 => {
+ if arr_len != 6 {
+ return Err(ParseError::BadArrayLen {
+ expected: 6,
+ actual: arr_len,
+ });
+ }
+ let value = read_value(arena, buf)?;
+ let n_arms = read_array_len(buf)? as usize;
+ let mut arms: bumpalo::collections::Vec> =
+ bumpalo::collections::Vec::with_capacity_in(n_arms, arena.bump());
+ for _ in 0..n_arms {
+ arms.push(read_match_arm(arena, buf)?);
+ }
+ let default = if try_read_nil(buf)? {
+ None
+ } else {
+ Some(&*arena.alloc(read_component(arena, buf)?))
+ };
+ let id = NodeId(read_u64(buf)?);
+ let source_loc = read_source_loc(buf)?;
+ Ok(Component::Match {
+ value,
+ arms: arms.into_bump_slice(),
+ default,
+ id,
+ source_loc,
+ })
+ }
+ 5 => {
+ if arr_len != 5 {
+ return Err(ParseError::BadArrayLen {
+ expected: 5,
+ actual: arr_len,
+ });
+ }
+ let inner = arena.alloc(read_component(arena, buf)?);
+ let key = NodeId(read_u64(buf)?);
+ let id = NodeId(read_u64(buf)?);
+ let source_loc = read_source_loc(buf)?;
+ Ok(Component::Memoize {
+ inner: &*inner,
+ key,
+ id,
+ source_loc,
+ })
+ }
+ 6 => {
+ if arr_len != 4 {
+ return Err(ParseError::BadArrayLen {
+ expected: 4,
+ actual: arr_len,
+ });
+ }
+ let n_children = read_array_len(buf)? as usize;
+ let mut children: bumpalo::collections::Vec> =
+ bumpalo::collections::Vec::with_capacity_in(n_children, arena.bump());
+ for _ in 0..n_children {
+ children.push(read_component(arena, buf)?);
+ }
+ let id = NodeId(read_u64(buf)?);
+ let source_loc = read_source_loc(buf)?;
+ Ok(Component::Fragment {
+ children: children.into_bump_slice(),
+ id,
+ source_loc,
+ })
+ }
+ 7 => {
+ if arr_len != 4 {
+ return Err(ParseError::BadArrayLen {
+ expected: 4,
+ actual: arr_len,
+ });
+ }
+ let value = read_value(arena, buf)?;
+ let id = NodeId(read_u64(buf)?);
+ let source_loc = read_source_loc(buf)?;
+ Ok(Component::Expr {
+ value,
+ id,
+ source_loc,
+ })
+ }
+ _ => Err(ParseError::UnknownComponentKind(kind)),
+ }
+}
+
+// ---- Public entry points ----------------------------------------------------
+
+/// Parse a `PageIR` msgpack blob into an arena-allocated `Page<'a>`.
+///
+/// Wire format (v2 positional array):
+///
+/// ```text
+/// [v=2, route, root, title|nil, meta, source_files,
+/// component_imports, state_bindings, needs_ref]
+/// ```
+pub fn parse_page<'a>(arena: &'a Arena, bytes: &[u8]) -> Result> {
+ let mut buf: &[u8] = bytes;
+ let arr_len = read_array_len(&mut buf)?;
+ if arr_len != 9 {
+ return Err(ParseError::BadArrayLen {
+ expected: 9,
+ actual: arr_len,
+ });
+ }
+ let version = read_u32(&mut buf)?;
+ if version != SCHEMA_VERSION {
+ return Err(ParseError::UnsupportedVersion(version));
+ }
+ let route = arena.alloc_str(read_str_borrowed(&mut buf)?);
+ let root = arena.alloc(read_component(arena, &mut buf)?);
+ let title = match read_str_or_nil_borrowed(&mut buf)? {
+ None => None,
+ Some(s) => Some(&*arena.alloc_str(s)),
+ };
+
+ let n_meta = read_array_len(&mut buf)? as usize;
+ let mut meta: bumpalo::collections::Vec > =
+ bumpalo::collections::Vec::with_capacity_in(n_meta, arena.bump());
+ for _ in 0..n_meta {
+ meta.push(read_meta(arena, &mut buf)?);
+ }
+
+ let n_files = read_array_len(&mut buf)? as usize;
+ let mut files: bumpalo::collections::Vec =
+ bumpalo::collections::Vec::with_capacity_in(n_files, arena.bump());
+ for _ in 0..n_files {
+ files.push(PyFileId(read_u32(&mut buf)?));
+ }
+
+ let component_imports = read_symbol_pair_array(arena, &mut buf)?;
+ let state_bindings = read_symbol_array(arena, &mut buf)?;
+ let needs_ref = read_bool(&mut buf)?;
+
+ Ok(Page {
+ schema_version: version,
+ route,
+ root: &*root,
+ title,
+ meta: meta.into_bump_slice(),
+ source_files: files.into_bump_slice(),
+ component_imports,
+ state_bindings,
+ needs_ref,
+ })
+}
+
+// ---- Theme / GlobalState / PluginManifest parsers ---------------------------
+
+pub fn parse_theme<'a>(arena: &'a Arena, bytes: &[u8]) -> Result> {
+ let mut buf: &[u8] = bytes;
+ let arr_len = read_array_len(&mut buf)?;
+ if arr_len != 4 {
+ return Err(ParseError::BadArrayLen {
+ expected: 4,
+ actual: arr_len,
+ });
+ }
+ let version = read_u32(&mut buf)?;
+ if version != SCHEMA_VERSION {
+ return Err(ParseError::UnsupportedVersion(version));
+ }
+ let n_tokens = read_array_len(&mut buf)? as usize;
+ let mut tokens: bumpalo::collections::Vec<(Symbol, &'a str)> =
+ bumpalo::collections::Vec::with_capacity_in(n_tokens, arena.bump());
+ for _ in 0..n_tokens {
+ let pair_len = read_array_len(&mut buf)?;
+ if pair_len != 2 {
+ return Err(ParseError::BadArrayLen {
+ expected: 2,
+ actual: pair_len,
+ });
+ }
+ let name = intern_str(read_str_borrowed(&mut buf)?);
+ let value = arena.alloc_str(read_str_borrowed(&mut buf)?);
+ tokens.push((name, value));
+ }
+ let global_style = arena.alloc_str(read_str_borrowed(&mut buf)?);
+ let appearance = arena.alloc_str(read_str_borrowed(&mut buf)?);
+ Ok(Theme {
+ schema_version: version,
+ tokens: tokens.into_bump_slice(),
+ global_style,
+ appearance,
+ })
+}
+
+pub fn parse_global_state<'a>(arena: &'a Arena, bytes: &[u8]) -> Result> {
+ let mut buf: &[u8] = bytes;
+ let arr_len = read_array_len(&mut buf)?;
+ if arr_len != 4 {
+ return Err(ParseError::BadArrayLen {
+ expected: 4,
+ actual: arr_len,
+ });
+ }
+ let version = read_u32(&mut buf)?;
+ if version != SCHEMA_VERSION {
+ return Err(ParseError::UnsupportedVersion(version));
+ }
+ let initial_state_json = arena.alloc_slice_copy(read_bin_borrowed(&mut buf)?);
+ let client_storage_json = arena.alloc_slice_copy(read_bin_borrowed(&mut buf)?);
+ let n_deps = read_array_len(&mut buf)? as usize;
+ let mut deps: bumpalo::collections::Vec> =
+ bumpalo::collections::Vec::with_capacity_in(n_deps, arena.bump());
+ for _ in 0..n_deps {
+ let pair_len = read_array_len(&mut buf)?;
+ if pair_len != 2 {
+ return Err(ParseError::BadArrayLen {
+ expected: 2,
+ actual: pair_len,
+ });
+ }
+ let var = intern_str(read_str_borrowed(&mut buf)?);
+ let depends_on = read_symbol_array(arena, &mut buf)?;
+ deps.push(ComputedVarDep { var, depends_on });
+ }
+ Ok(GlobalState {
+ schema_version: version,
+ initial_state_json: &*initial_state_json,
+ client_storage_json: &*client_storage_json,
+ computed_var_deps: deps.into_bump_slice(),
+ })
+}
+
+pub fn parse_plugin_manifest<'a>(arena: &'a Arena, bytes: &[u8]) -> Result> {
+ let mut buf: &[u8] = bytes;
+ let arr_len = read_array_len(&mut buf)?;
+ if arr_len != 2 {
+ return Err(ParseError::BadArrayLen {
+ expected: 2,
+ actual: arr_len,
+ });
+ }
+ let version = read_u32(&mut buf)?;
+ if version != SCHEMA_VERSION {
+ return Err(ParseError::UnsupportedVersion(version));
+ }
+ let n_plugins = read_array_len(&mut buf)? as usize;
+ let mut plugins: bumpalo::collections::Vec> =
+ bumpalo::collections::Vec::with_capacity_in(n_plugins, arena.bump());
+ for _ in 0..n_plugins {
+ let plug_len = read_array_len(&mut buf)?;
+ if plug_len != 3 {
+ return Err(ParseError::BadArrayLen {
+ expected: 3,
+ actual: plug_len,
+ });
+ }
+ let name = intern_str(read_str_borrowed(&mut buf)?);
+ let n_assets = read_array_len(&mut buf)? as usize;
+ let mut assets: bumpalo::collections::Vec<(Symbol, &'a [u8])> =
+ bumpalo::collections::Vec::with_capacity_in(n_assets, arena.bump());
+ for _ in 0..n_assets {
+ let asset_len = read_array_len(&mut buf)?;
+ if asset_len != 2 {
+ return Err(ParseError::BadArrayLen {
+ expected: 2,
+ actual: asset_len,
+ });
+ }
+ let path = intern_str(read_str_borrowed(&mut buf)?);
+ let body = arena.alloc_slice_copy(read_bin_borrowed(&mut buf)?);
+ assets.push((path, &*body));
+ }
+ let stylesheet_imports = read_symbol_array(arena, &mut buf)?;
+ plugins.push(PluginEntry {
+ name,
+ static_assets: assets.into_bump_slice(),
+ stylesheet_imports,
+ });
+ }
+ Ok(PluginManifest {
+ schema_version: version,
+ plugins: plugins.into_bump_slice(),
+ })
+}
+
+// ---- Helpers for tests ------------------------------------------------------
+
+#[doc(hidden)]
+pub mod test_helpers {
+ /// Build a minimal Page msgpack blob (v2): a single Element with one
+ /// prop, one Text child, no component imports, no state bindings.
+ pub fn tiny_page_bytes() -> Vec {
+ use rmp::encode::*;
+ let mut out = Vec::new();
+ // PageIR v2 = [v, route, root, title|nil, meta, source_files,
+ // component_imports, state_bindings, needs_ref]
+ write_array_len(&mut out, 9).unwrap();
+ write_u32(&mut out, 2).unwrap();
+ write_str(&mut out, "/index").unwrap();
+ // Root: Element(0) = [0, tag, props, children, events, hooks, id, loc]
+ write_array_len(&mut out, 8).unwrap();
+ write_uint(&mut out, 0).unwrap();
+ write_str(&mut out, "div").unwrap();
+ // props: one prop ("id", JsExpr "x")
+ write_array_len(&mut out, 1).unwrap();
+ write_array_len(&mut out, 2).unwrap();
+ write_str(&mut out, "id").unwrap();
+ // Value JsExpr(0) = [0, expr, var_data]
+ write_array_len(&mut out, 3).unwrap();
+ write_uint(&mut out, 0).unwrap();
+ write_str(&mut out, "x").unwrap();
+ // VarData empty
+ write_array_len(&mut out, 6).unwrap();
+ write_array_len(&mut out, 0).unwrap(); // hooks
+ write_array_len(&mut out, 0).unwrap(); // imports
+ write_nil(&mut out).unwrap(); // state
+ write_array_len(&mut out, 0).unwrap(); // deps
+ write_nil(&mut out).unwrap(); // position
+ write_array_len(&mut out, 0).unwrap(); // components
+ // children: one Text
+ write_array_len(&mut out, 1).unwrap();
+ write_array_len(&mut out, 4).unwrap();
+ write_uint(&mut out, 1).unwrap(); // kind=Text
+ write_str(&mut out, "hello").unwrap();
+ write_uint(&mut out, 42).unwrap(); // id
+ write_array_len(&mut out, 3).unwrap();
+ write_uint(&mut out, 0).unwrap();
+ write_uint(&mut out, 0).unwrap();
+ write_uint(&mut out, 0).unwrap();
+ // events: empty
+ write_array_len(&mut out, 0).unwrap();
+ // hooks: empty
+ write_array_len(&mut out, 0).unwrap();
+ // id
+ write_uint(&mut out, 7).unwrap();
+ // loc
+ write_array_len(&mut out, 3).unwrap();
+ write_uint(&mut out, 0).unwrap();
+ write_uint(&mut out, 0).unwrap();
+ write_uint(&mut out, 0).unwrap();
+ // title: nil
+ write_nil(&mut out).unwrap();
+ // meta: empty
+ write_array_len(&mut out, 0).unwrap();
+ // source_files: empty
+ write_array_len(&mut out, 0).unwrap();
+ // component_imports: empty
+ write_array_len(&mut out, 0).unwrap();
+ // state_bindings: empty
+ write_array_len(&mut out, 0).unwrap();
+ // needs_ref: false
+ write_bool(&mut out, false).unwrap();
+ out
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::IrVisitor;
+
+ #[test]
+ fn parses_tiny_page() {
+ let arena = Arena::new();
+ let bytes = test_helpers::tiny_page_bytes();
+ let page = parse_page(&arena, &bytes).expect("parse ok");
+ assert_eq!(page.schema_version, SCHEMA_VERSION);
+ assert_eq!(page.route, "/index");
+ match page.root {
+ Component::Element { tag, children, .. } => {
+ assert_eq!(reflex_intern::resolve(*tag).unwrap(), "div");
+ assert_eq!(children.len(), 1);
+ match &children[0] {
+ Component::Text { value, id, .. } => {
+ assert_eq!(*value, "hello");
+ assert_eq!(id.0, 42);
+ }
+ _ => panic!("expected Text"),
+ }
+ }
+ _ => panic!("expected Element"),
+ }
+ }
+
+ #[test]
+ fn rejects_wrong_version() {
+ use rmp::encode::*;
+ let arena = Arena::new();
+ let mut bytes = Vec::new();
+ write_array_len(&mut bytes, 9).unwrap();
+ write_u32(&mut bytes, 9999).unwrap();
+ let err = parse_page(&arena, &bytes).unwrap_err();
+ assert!(matches!(err, ParseError::UnsupportedVersion(9999)));
+ }
+
+ #[test]
+ fn visitor_walks_parsed_tree() {
+ struct Counter(usize);
+ impl<'a> IrVisitor<'a> for Counter {
+ fn visit_component(&mut self, c: &Component<'a>) {
+ self.0 += 1;
+ crate::walk_component(self, c);
+ }
+ }
+ let arena = Arena::new();
+ let bytes = test_helpers::tiny_page_bytes();
+ let page = parse_page(&arena, &bytes).unwrap();
+ let mut c = Counter(0);
+ c.visit_component(page.root);
+ assert_eq!(c.0, 2);
+ }
+}
diff --git a/packages/reflex-compiler-rust/crates/reflex_ir/src/visitor.rs b/packages/reflex-compiler-rust/crates/reflex_ir/src/visitor.rs
new file mode 100644
index 00000000000..27163b98d3c
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_ir/src/visitor.rs
@@ -0,0 +1,178 @@
+//! Monomorphic IR visitor. See plan §3 / D6, R3.
+//!
+//! Every `visit_*` method has a default that calls the corresponding free
+//! `walk_*` function. Visitors override only the nodes they care about. The
+//! traversal is generic (no `&dyn Trait`) so each visitor monomorphizes its
+//! own copy of the walk functions — match arms inline and the compiler can
+//! kill the dead branches.
+
+use crate::{Component, EventHandler, Hook, MatchArm, Meta, Page, Value, VarData};
+
+pub trait IrVisitor<'a> {
+ #[inline]
+ fn visit_page(&mut self, page: &Page<'a>) {
+ walk_page(self, page);
+ }
+
+ #[inline]
+ fn visit_component(&mut self, component: &Component<'a>) {
+ walk_component(self, component);
+ }
+
+ #[inline]
+ fn visit_value(&mut self, value: &Value<'a>) {
+ walk_value(self, value);
+ }
+
+ #[inline]
+ fn visit_var_data(&mut self, _var_data: &VarData<'a>) {}
+
+ #[inline]
+ fn visit_event_handler(&mut self, handler: &EventHandler<'a>) {
+ walk_event_handler(self, handler);
+ }
+
+ #[inline]
+ fn visit_hook(&mut self, _hook: &Hook<'a>) {}
+
+ #[inline]
+ fn visit_match_arm(&mut self, arm: &MatchArm<'a>) {
+ walk_match_arm(self, arm);
+ }
+
+ #[inline]
+ fn visit_meta(&mut self, _meta: &Meta<'a>) {}
+}
+
+#[inline]
+pub fn walk_page<'a, V: IrVisitor<'a> + ?Sized>(v: &mut V, page: &Page<'a>) {
+ for meta in page.meta {
+ v.visit_meta(meta);
+ }
+ v.visit_component(page.root);
+}
+
+#[inline]
+pub fn walk_component<'a, V: IrVisitor<'a> + ?Sized>(v: &mut V, component: &Component<'a>) {
+ match component {
+ Component::Element {
+ props,
+ children,
+ event_handlers,
+ hooks,
+ ..
+ } => {
+ for (_, value) in *props {
+ v.visit_value(value);
+ }
+ for child in *children {
+ v.visit_component(child);
+ }
+ for handler in *event_handlers {
+ v.visit_event_handler(handler);
+ }
+ for hook in *hooks {
+ v.visit_hook(hook);
+ }
+ }
+ Component::Text { .. } => {}
+ Component::Foreach { iter, body, .. } => {
+ v.visit_value(iter);
+ v.visit_component(body);
+ }
+ Component::Cond {
+ test, then, else_, ..
+ } => {
+ v.visit_value(test);
+ v.visit_component(then);
+ if let Some(e) = else_ {
+ v.visit_component(e);
+ }
+ }
+ Component::Match {
+ value,
+ arms,
+ default,
+ ..
+ } => {
+ v.visit_value(value);
+ for arm in *arms {
+ v.visit_match_arm(arm);
+ }
+ if let Some(d) = default {
+ v.visit_component(d);
+ }
+ }
+ Component::Memoize { inner, .. } => {
+ v.visit_component(inner);
+ }
+ Component::Fragment { children, .. } => {
+ for child in *children {
+ v.visit_component(child);
+ }
+ }
+ Component::Expr { value, .. } => {
+ v.visit_value(value);
+ }
+ }
+}
+
+#[inline]
+pub fn walk_value<'a, V: IrVisitor<'a> + ?Sized>(v: &mut V, value: &Value<'a>) {
+ if let Value::JsExpr { var_data, .. } = value {
+ v.visit_var_data(var_data);
+ }
+}
+
+#[inline]
+pub fn walk_event_handler<'a, V: IrVisitor<'a> + ?Sized>(v: &mut V, handler: &EventHandler<'a>) {
+ v.visit_var_data(&handler.var_data);
+}
+
+#[inline]
+pub fn walk_match_arm<'a, V: IrVisitor<'a> + ?Sized>(v: &mut V, arm: &MatchArm<'a>) {
+ v.visit_value(&arm.case);
+ v.visit_component(arm.body);
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::{NodeId, SourceLoc};
+
+ struct Counter {
+ components: usize,
+ values: usize,
+ }
+
+ impl<'a> IrVisitor<'a> for Counter {
+ fn visit_component(&mut self, c: &Component<'a>) {
+ self.components += 1;
+ walk_component(self, c);
+ }
+
+ fn visit_value(&mut self, v: &Value<'a>) {
+ self.values += 1;
+ walk_value(self, v);
+ }
+ }
+
+ #[test]
+ fn counts_nested_components() {
+ let loc = SourceLoc::SYNTHETIC;
+ let inner_text = Component::Text {
+ value: "hi",
+ id: NodeId(1),
+ source_loc: loc,
+ };
+ let root = Component::Fragment {
+ children: std::slice::from_ref(&inner_text),
+ id: NodeId(2),
+ source_loc: loc,
+ };
+ let mut c = Counter { components: 0, values: 0 };
+ c.visit_component(&root);
+ assert_eq!(c.components, 2);
+ assert_eq!(c.values, 0);
+ }
+}
diff --git a/packages/reflex-compiler-rust/crates/reflex_py/Cargo.toml b/packages/reflex-compiler-rust/crates/reflex_py/Cargo.toml
new file mode 100644
index 00000000000..47603e2edc5
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_py/Cargo.toml
@@ -0,0 +1,25 @@
+[package]
+name = "reflex_py"
+version = "0.0.0"
+edition.workspace = true
+rust-version.workspace = true
+license.workspace = true
+publish.workspace = true
+
+[lib]
+name = "_native"
+crate-type = ["cdylib"]
+
+[dependencies]
+pyo3 = { workspace = true }
+rmp = { workspace = true }
+rmp-serde = { workspace = true }
+serde = { workspace = true }
+bumpalo = { workspace = true }
+thiserror = { workspace = true }
+reflex_ir = { workspace = true }
+reflex_codegen = { workspace = true }
+reflex_arena = { workspace = true }
+reflex_intern = { workspace = true }
+reflex_db = { workspace = true }
+reflex_pyread = { workspace = true }
diff --git a/packages/reflex-compiler-rust/crates/reflex_py/src/lib.rs b/packages/reflex-compiler-rust/crates/reflex_py/src/lib.rs
new file mode 100644
index 00000000000..a9fd6220068
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_py/src/lib.rs
@@ -0,0 +1,357 @@
+//! PyO3 entry for the reflex-compiler-rust wheel.
+//!
+//! Two surfaces live here:
+//!
+//! 1. The spike microbenchmark probes (`empty_call`, `walk_*`) — preserved
+//! because `scripts/spike_bench.py` and `SPIKE_RESULTS.md` reference them.
+//! Spike wire format: positional msgpack arrays, simple shape documented
+//! inline below.
+//!
+//! 2. `CompilerSession` — the real entry point. Takes a §4 PageIR msgpack
+//! blob, runs the parse → JSX-emit pipeline, returns a JS source string.
+//! Salsa caching lands in D5 — for now every call rebuilds.
+
+mod session;
+
+use std::io::Cursor;
+
+use bumpalo::Bump;
+use pyo3::prelude::*;
+use pyo3::types::PyBytes;
+use rmp::decode::{self, RmpRead};
+use serde::Deserialize;
+
+// ---- 1. Pure crossing probes -------------------------------------------------
+
+#[pyfunction]
+fn empty_call() {}
+
+#[pyfunction]
+fn add_one(x: i64) -> i64 {
+ x + 1
+}
+
+#[pyfunction]
+fn bytes_passthrough(b: &Bound<'_, PyBytes>) -> usize {
+ b.as_bytes().len()
+}
+
+// ---- 2. rmp-serde path -------------------------------------------------------
+
+#[derive(Deserialize)]
+#[serde(untagged)]
+enum SerdeNode {
+ Element(u8, String, Vec<(String, String)>, Vec),
+ Text(u8, String),
+}
+
+fn serde_walk(n: &SerdeNode, nodes: &mut u64, bytes: &mut u64) {
+ match n {
+ SerdeNode::Element(_, tag, props, children) => {
+ *nodes += 1;
+ *bytes += tag.len() as u64;
+ for (k, v) in props {
+ *bytes += (k.len() + v.len()) as u64;
+ }
+ for c in children {
+ serde_walk(c, nodes, bytes);
+ }
+ }
+ SerdeNode::Text(_, s) => {
+ *nodes += 1;
+ *bytes += s.len() as u64;
+ }
+ }
+}
+
+#[pyfunction]
+fn walk_serde(b: &Bound<'_, PyBytes>) -> PyResult<(u64, u64)> {
+ let bytes = b.as_bytes();
+ let root: SerdeNode = rmp_serde::from_slice(bytes)
+ .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("rmp-serde: {e}")))?;
+ let mut n = 0u64;
+ let mut s = 0u64;
+ serde_walk(&root, &mut n, &mut s);
+ Ok((n, s))
+}
+
+// ---- 3. Hand-rolled msgpack reader, zero-copy borrows -----------------------
+//
+// rmp::decode lets us read length-prefixed scalars and use the remaining cursor
+// to take string bytes as &[u8] without copying. We then `str::from_utf8` (which
+// is a check, not a copy). No heap allocations on the parse side at all.
+//
+// Walking happens inline with parsing — we never materialize the tree. That's
+// exactly what real Rust codegen would do; this is the realistic lower bound.
+
+fn manual_read_str<'a>(buf: &mut &'a [u8]) -> std::io::Result<&'a str> {
+ let len = decode::read_str_len(buf).map_err(|e| std::io::Error::other(e.to_string()))? as usize;
+ if buf.len() < len {
+ return Err(std::io::Error::other("short read"));
+ }
+ let (s, rest) = buf.split_at(len);
+ *buf = rest;
+ std::str::from_utf8(s).map_err(|e| std::io::Error::other(e.to_string()))
+}
+
+fn manual_walk<'a>(buf: &mut &'a [u8], nodes: &mut u64, bytes: &mut u64) -> std::io::Result<()> {
+ let arr_len = decode::read_array_len(buf).map_err(|e| std::io::Error::other(e.to_string()))?;
+ let kind = buf.read_u8()?;
+ *nodes += 1;
+ if kind == 0 {
+ // [0, tag, props, children] -> arr_len == 4
+ debug_assert_eq!(arr_len, 4);
+ let tag = manual_read_str(buf)?;
+ *bytes += tag.len() as u64;
+ let n_props =
+ decode::read_array_len(buf).map_err(|e| std::io::Error::other(e.to_string()))?;
+ for _ in 0..n_props {
+ let pair_len =
+ decode::read_array_len(buf).map_err(|e| std::io::Error::other(e.to_string()))?;
+ debug_assert_eq!(pair_len, 2);
+ let k = manual_read_str(buf)?;
+ let v = manual_read_str(buf)?;
+ *bytes += (k.len() + v.len()) as u64;
+ }
+ let n_children =
+ decode::read_array_len(buf).map_err(|e| std::io::Error::other(e.to_string()))?;
+ for _ in 0..n_children {
+ manual_walk(buf, nodes, bytes)?;
+ }
+ } else {
+ // [1, value]
+ debug_assert_eq!(arr_len, 2);
+ let v = manual_read_str(buf)?;
+ *bytes += v.len() as u64;
+ }
+ Ok(())
+}
+
+#[pyfunction]
+fn walk_manual(b: &Bound<'_, PyBytes>) -> PyResult<(u64, u64)> {
+ let mut cur: &[u8] = b.as_bytes();
+ let mut n = 0u64;
+ let mut s = 0u64;
+ manual_walk(&mut cur, &mut n, &mut s)
+ .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("manual walk: {e}")))?;
+ Ok((n, s))
+}
+
+// ---- 4. Hand-rolled walk + JSX-like emit into a buffer ----------------------
+//
+// This is what a real Rust codegen pass would do: parse and emit in a single
+// streaming pass, writing into one growable byte buffer. No arena needed for
+// this shape because the output buffer IS the only allocation.
+
+fn emit_str(out: &mut Vec, s: &str) {
+ out.push(b'"');
+ out.extend_from_slice(s.as_bytes());
+ out.push(b'"');
+}
+
+fn manual_walk_emit<'a>(buf: &mut &'a [u8], out: &mut Vec) -> std::io::Result<()> {
+ let _ = decode::read_array_len(buf).map_err(|e| std::io::Error::other(e.to_string()))?;
+ let kind = buf.read_u8()?;
+ if kind == 0 {
+ let tag = manual_read_str(buf)?;
+ out.extend_from_slice(b"jsx(");
+ out.extend_from_slice(tag.as_bytes());
+ out.extend_from_slice(b",{");
+ let n_props =
+ decode::read_array_len(buf).map_err(|e| std::io::Error::other(e.to_string()))?;
+ for i in 0..n_props {
+ if i > 0 {
+ out.push(b',');
+ }
+ let _ =
+ decode::read_array_len(buf).map_err(|e| std::io::Error::other(e.to_string()))?;
+ let k = manual_read_str(buf)?;
+ let v = manual_read_str(buf)?;
+ out.extend_from_slice(k.as_bytes());
+ out.push(b':');
+ out.extend_from_slice(v.as_bytes());
+ }
+ out.push(b'}');
+ let n_children =
+ decode::read_array_len(buf).map_err(|e| std::io::Error::other(e.to_string()))?;
+ for _ in 0..n_children {
+ out.push(b',');
+ manual_walk_emit(buf, out)?;
+ }
+ out.push(b')');
+ } else {
+ let v = manual_read_str(buf)?;
+ emit_str(out, v);
+ }
+ Ok(())
+}
+
+#[pyfunction]
+fn walk_manual_emit<'py>(py: Python<'py>, b: &Bound<'py, PyBytes>) -> PyResult> {
+ let input = b.as_bytes().to_vec(); // copy out before releasing GIL
+ let out = py
+ .allow_threads(|| -> std::io::Result> {
+ let mut cur: &[u8] = &input;
+ let mut out = Vec::with_capacity(input.len() * 2);
+ manual_walk_emit(&mut cur, &mut out)?;
+ Ok(out)
+ })
+ .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("emit: {e}")))?;
+ Ok(PyBytes::new_bound(py, &out))
+}
+
+// ---- 5. Bumpalo arena demo --------------------------------------------------
+//
+// Parse the tree into a bumpalo-backed AST (Copy nodes, &str borrows into a
+// owned String copy in the arena), then walk it. Tests R1/R2: arena holds the
+// tree; nothing leaks; drop is a single bump reset.
+
+#[derive(Clone, Copy)]
+enum ArenaNode<'a> {
+ Element {
+ tag: &'a str,
+ props: &'a [(&'a str, &'a str)],
+ children: &'a [ArenaNode<'a>],
+ },
+ Text(&'a str),
+}
+
+fn parse_arena<'a, 'b>(buf: &mut &'b [u8], arena: &'a Bump) -> std::io::Result>
+where
+ 'b: 'a,
+{
+ let _ = decode::read_array_len(buf).map_err(|e| std::io::Error::other(e.to_string()))?;
+ let kind = buf.read_u8()?;
+ if kind == 0 {
+ let tag = arena.alloc_str(manual_read_str(buf)?);
+ let n_props =
+ decode::read_array_len(buf).map_err(|e| std::io::Error::other(e.to_string()))? as usize;
+ let mut props = bumpalo::collections::Vec::with_capacity_in(n_props, arena);
+ for _ in 0..n_props {
+ let _ =
+ decode::read_array_len(buf).map_err(|e| std::io::Error::other(e.to_string()))?;
+ let k = arena.alloc_str(manual_read_str(buf)?);
+ let v = arena.alloc_str(manual_read_str(buf)?);
+ props.push((&*k, &*v));
+ }
+ let n_children =
+ decode::read_array_len(buf).map_err(|e| std::io::Error::other(e.to_string()))? as usize;
+ let mut children = bumpalo::collections::Vec::with_capacity_in(n_children, arena);
+ for _ in 0..n_children {
+ children.push(parse_arena(buf, arena)?);
+ }
+ Ok(ArenaNode::Element {
+ tag,
+ props: props.into_bump_slice(),
+ children: children.into_bump_slice(),
+ })
+ } else {
+ Ok(ArenaNode::Text(arena.alloc_str(manual_read_str(buf)?)))
+ }
+}
+
+fn arena_emit(node: &ArenaNode<'_>, out: &mut Vec) {
+ match node {
+ ArenaNode::Element { tag, props, children } => {
+ out.extend_from_slice(b"jsx(");
+ out.extend_from_slice(tag.as_bytes());
+ out.extend_from_slice(b",{");
+ for (i, (k, v)) in props.iter().enumerate() {
+ if i > 0 {
+ out.push(b',');
+ }
+ out.extend_from_slice(k.as_bytes());
+ out.push(b':');
+ out.extend_from_slice(v.as_bytes());
+ }
+ out.push(b'}');
+ for c in *children {
+ out.push(b',');
+ arena_emit(c, out);
+ }
+ out.push(b')');
+ }
+ ArenaNode::Text(s) => emit_str(out, s),
+ }
+}
+
+#[pyfunction]
+fn walk_arena_emit<'py>(py: Python<'py>, b: &Bound<'py, PyBytes>) -> PyResult> {
+ let input = b.as_bytes().to_vec();
+ let out = py
+ .allow_threads(|| -> std::io::Result> {
+ let arena = Bump::with_capacity(input.len() * 2);
+ let mut cur: &[u8] = &input;
+ let root = parse_arena(&mut cur, &arena)?;
+ let mut out = Vec::with_capacity(input.len() * 2);
+ arena_emit(&root, &mut out);
+ Ok(out)
+ })
+ .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("arena emit: {e}")))?;
+ Ok(PyBytes::new_bound(py, &out))
+}
+
+// ---- 6. rmp-serde + emit (for apples-to-apples vs hand-rolled) --------------
+
+fn serde_emit(n: &SerdeNode, out: &mut Vec) {
+ match n {
+ SerdeNode::Element(_, tag, props, children) => {
+ out.extend_from_slice(b"jsx(");
+ out.extend_from_slice(tag.as_bytes());
+ out.extend_from_slice(b",{");
+ for (i, (k, v)) in props.iter().enumerate() {
+ if i > 0 {
+ out.push(b',');
+ }
+ out.extend_from_slice(k.as_bytes());
+ out.push(b':');
+ out.extend_from_slice(v.as_bytes());
+ }
+ out.push(b'}');
+ for c in children {
+ out.push(b',');
+ serde_emit(c, out);
+ }
+ out.push(b')');
+ }
+ SerdeNode::Text(_, s) => emit_str(out, s),
+ }
+}
+
+#[pyfunction]
+fn walk_serde_emit<'py>(py: Python<'py>, b: &Bound<'py, PyBytes>) -> PyResult> {
+ let input = b.as_bytes().to_vec();
+ let out = py
+ .allow_threads(|| -> PyResult> {
+ let mut de = rmp_serde::Deserializer::new(Cursor::new(&input));
+ let root: SerdeNode = SerdeNode::deserialize(&mut de)
+ .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("rmp-serde: {e}")))?;
+ let mut out = Vec::with_capacity(input.len() * 2);
+ serde_emit(&root, &mut out);
+ Ok(out)
+ })?;
+ Ok(PyBytes::new_bound(py, &out))
+}
+
+// ---- Module export ----------------------------------------------------------
+
+#[pymodule]
+fn _native(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> {
+ // Spike probes.
+ m.add_function(wrap_pyfunction!(empty_call, m)?)?;
+ m.add_function(wrap_pyfunction!(add_one, m)?)?;
+ m.add_function(wrap_pyfunction!(bytes_passthrough, m)?)?;
+ m.add_function(wrap_pyfunction!(walk_serde, m)?)?;
+ m.add_function(wrap_pyfunction!(walk_manual, m)?)?;
+ m.add_function(wrap_pyfunction!(walk_manual_emit, m)?)?;
+ m.add_function(wrap_pyfunction!(walk_arena_emit, m)?)?;
+ m.add_function(wrap_pyfunction!(walk_serde_emit, m)?)?;
+
+ // Schema version sanity (parsers agree with the wire format).
+ m.add("SCHEMA_VERSION", reflex_ir::parse::SCHEMA_VERSION)?;
+
+ // The real compiler session.
+ m.add_class::()?;
+ m.add_class::()?;
+ Ok(())
+}
diff --git a/packages/reflex-compiler-rust/crates/reflex_py/src/session.rs b/packages/reflex-compiler-rust/crates/reflex_py/src/session.rs
new file mode 100644
index 00000000000..7110169f2a6
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_py/src/session.rs
@@ -0,0 +1,687 @@
+//! `CompilerSession` — the real PyO3 entry point. See plan §3.4, D4.
+//!
+//! The Python side keeps a long-lived `CompilerSession` instance across
+//! reloads. Each `compile_app(...)` call ships per-page msgpack blobs plus
+//! the theme/global-state/plugin-manifest blobs; Rust parses, emits, and
+//! returns a `CompiledOutput` carrying `{path: bytes}` for the Python side
+//! to write.
+//!
+//! Salsa caching lands in D5. Until then every call rebuilds — the
+//! `content_hash` ferried over the wire is recorded but unused.
+
+use std::collections::HashMap;
+
+use pyo3::prelude::*;
+use pyo3::types::{PyBytes, PyDict, PyList, PyTuple};
+
+use reflex_arena::Arena;
+use reflex_codegen::{
+ emit_app_root, emit_app_root_module, emit_context, emit_context_module,
+ emit_document_root_module, emit_memo_index, emit_memo_module, emit_page,
+ emit_page_with_extras, emit_page_with_map, emit_stateful_pages_json, emit_styles_root,
+ emit_theme, emit_theme_module, emit_vite_config, CodeBuffer, SourceMap,
+};
+use reflex_db::CompilerDb;
+use reflex_ir::{parse_global_state, parse_page, parse_plugin_manifest, parse_theme};
+use reflex_pyread::{
+ collect_all_imports as pyread_collect_all_imports,
+ collect_all_imports_into as pyread_collect_all_imports_into,
+ merge_imports_into as pyread_merge_imports_into, read_page,
+ should_memoize as memoize_should_memoize, MemoRefs, PyRefs,
+};
+
+/// One per Python `reflex.compiler.session.CompilerSession`. Holds the
+/// content-hash-keyed compile cache (D5) so hot-reload is incremental.
+#[pyclass]
+pub struct CompilerSession {
+ db: CompilerDb,
+}
+
+#[pymethods]
+impl CompilerSession {
+ #[new]
+ fn new() -> Self {
+ Self {
+ db: CompilerDb::new(),
+ }
+ }
+
+ /// Cap the page cache. Pass `None` for unbounded.
+ fn set_cache_capacity(&self, cap: Option) {
+ self.db.set_cache_capacity(cap);
+ }
+
+ /// Drop all cached page renders. Equivalent to creating a new session.
+ fn clear_cache(&self) {
+ self.db.clear();
+ }
+
+ /// Number of cached page renders. Useful for tests + diagnostics.
+ fn cache_len(&self) -> usize {
+ self.db.cache_len()
+ }
+
+ /// Compile a single page's IR bytes to a JS source string. The Python side
+ /// passes `(route_ident, page_ir_bytes)`; we return the rendered module
+ /// source.
+ ///
+ /// This is the single-page fast path used by hot-reload. `compile_app`
+ /// below batches over many pages and produces auxiliary artifacts.
+ fn compile_page(
+ &self,
+ py: Python<'_>,
+ route_ident: &str,
+ page_bytes: &Bound<'_, PyBytes>,
+ ) -> PyResult {
+ let bytes = page_bytes.as_bytes().to_vec();
+ let route_ident = route_ident.to_owned();
+ let db = self.db.clone();
+ py.allow_threads(move || {
+ db.emit_page(&route_ident, &bytes)
+ .map(|s| s.as_ref().to_owned())
+ .map_err(|e| pyo3::exceptions::PyValueError::new_err(e.to_string()))
+ })
+ }
+
+ /// Compile one page and also return its source map. The cache is bypassed
+ /// (it stores only the rendered string, not the map). Use when you need
+ /// diagnostic back-mapping; use `compile_page` for hot-reload.
+ ///
+ /// Returns `(rendered_js, [(offset, file_id, line, col), ...])`.
+ fn compile_page_with_sourcemap(
+ &self,
+ py: Python<'_>,
+ route_ident: &str,
+ page_bytes: &Bound<'_, PyBytes>,
+ ) -> PyResult<(String, Vec<(u32, u32, u32, u32)>)> {
+ let bytes = page_bytes.as_bytes().to_vec();
+ let route_ident = route_ident.to_owned();
+ py.allow_threads(move || compile_page_with_sourcemap_inner(&route_ident, &bytes))
+ }
+
+ /// Compile a memo wrapper module from a Component PyObject.
+ ///
+ /// Mirrors `templates.memo_single_component_template` (plan §0b
+ /// lever (b3)). The Component should already have its `{children}`
+ /// hole substituted at the Python level (passthrough wrappers carry
+ /// a single `Bare(Var(_js_expr="children"))` child; snapshot bodies
+ /// own their full subtree). Pyread walks the tree; `emit_memo_module`
+ /// emits the `export const = memo(...)` shell.
+ ///
+ /// Args:
+ /// name: exported memo identifier.
+ /// signature: parameter list spliced after `memo(`. Typical
+ /// values are `"{ children }"` for passthroughs and `"()"`
+ /// for snapshot bodies.
+ /// component: the wrapper-component PyObject (already
+ /// hole-substituted).
+ #[pyo3(signature = (name, signature, component, route="/__memo__", pre_hooks=""))]
+ fn compile_memo_from_component<'py>(
+ &self,
+ py: Python<'py>,
+ name: &str,
+ signature: &str,
+ component: &Bound<'py, PyAny>,
+ route: &str,
+ pre_hooks: &str,
+ ) -> PyResult {
+ let refs = PyRefs::new(py)?;
+ let arena = Arena::new();
+ let page = read_page(py, component, route, None, &[], &arena, &refs)?;
+ let mut buf = CodeBuffer::with_capacity(1024);
+ emit_memo_module(&mut buf, &page, name, signature, pre_hooks);
+ String::from_utf8(buf.into_bytes()).map_err(|e| {
+ pyo3::exceptions::PyValueError::new_err(format!(
+ "rust memo emit produced non-utf8: {e}"
+ ))
+ })
+ }
+
+ /// Emit the memo index module to `out_path`.
+ ///
+ /// Streams the rendered bytes straight to disk via a
+ /// `BufWriter` — no intermediate `String` allocation. Mirrors
+ /// `memo_index_template` in
+ /// `packages/reflex-base/src/reflex_base/compiler/templates.py`.
+ ///
+ /// Args:
+ /// reexports: list of `(export_name, relative_module_specifier)`
+ /// tuples.
+ /// out_path: absolute filesystem path the index gets written to.
+ /// Parent directory must already exist.
+ fn compile_memo_index(
+ &self,
+ reexports: Vec<(String, String)>,
+ out_path: &str,
+ ) -> PyResult<()> {
+ let pairs: Vec<(&str, &str)> = reexports
+ .iter()
+ .map(|(n, s)| (n.as_str(), s.as_str()))
+ .collect();
+ write_to_file(out_path, |w| emit_memo_index(&pairs, w))
+ }
+
+ /// Emit `.web/styles/styles.css`.
+ ///
+ /// Ports `styles_template` — wraps every stylesheet in an
+ /// `@import url('…');` line under a single
+ /// `@layer __reflex_base;` header.
+ fn compile_styles_root(
+ &self,
+ stylesheets: Vec,
+ out_path: &str,
+ ) -> PyResult<()> {
+ let refs: Vec<&str> = stylesheets.iter().map(String::as_str).collect();
+ write_to_file(out_path, |w| emit_styles_root(&refs, w))
+ }
+
+ /// Emit `.web/utils/theme.js`.
+ ///
+ /// Ports `theme_template` — a single
+ /// `export default ` line where `theme_js` is the JS
+ /// object literal Python builds via `LiteralVar.create(theme)`.
+ fn compile_theme_module(&self, theme_js: &str, out_path: &str) -> PyResult<()> {
+ write_to_file(out_path, |w| emit_theme_module(theme_js, w))
+ }
+
+ /// Emit `.web/backend/stateful_pages.json`.
+ ///
+ /// Mirrors `App._write_stateful_pages_marker`. Python decides which
+ /// routes are stateful (it requires a state walk we haven't ported);
+ /// Rust just serializes the list as JSON and writes the file.
+ fn compile_stateful_pages_marker(
+ &self,
+ routes: Vec,
+ out_path: &str,
+ ) -> PyResult<()> {
+ let refs: Vec<&str> = routes.iter().map(String::as_str).collect();
+ write_to_file(out_path, |w| emit_stateful_pages_json(&refs, w))
+ }
+
+ /// Emit `.web/utils/context.js`.
+ ///
+ /// Ports `context_template` from
+ /// `packages/reflex-base/src/reflex_base/compiler/templates.py`.
+ ///
+ /// Args:
+ /// is_dev_mode: emitted as `export const isDevMode = …`.
+ /// default_color_mode_js: the JS expression assigned to
+ /// `defaultColorMode` (a quoted string or a lookup expr).
+ /// state_name: full dotted name of the state root, or `None`
+ /// for the no-state fallback.
+ /// state_keys: full dotted names of every state context.
+ /// initial_state_json: pre-serialized initial-state dict.
+ /// client_storage_json: pre-serialized client-storage config.
+ /// Emit `.web/app/root.jsx`.
+ ///
+ /// Ports `app_root_template`. Python pre-renders the dynamic
+ /// strings (imports, hooks, render_str, …) using
+ /// `_RenderUtils.render` + friends — those depend on the legacy
+ /// Python JSX renderer. Rust splices them into the static layout.
+ fn compile_app_root_module(
+ &self,
+ imports_str: &str,
+ dynamic_imports_str: &str,
+ custom_code_str: &str,
+ hooks_str: &str,
+ render_str: &str,
+ import_window_libraries: &str,
+ window_imports_str: &str,
+ out_path: &str,
+ ) -> PyResult<()> {
+ write_to_file(out_path, |w| {
+ emit_app_root_module(
+ imports_str,
+ dynamic_imports_str,
+ custom_code_str,
+ hooks_str,
+ render_str,
+ import_window_libraries,
+ window_imports_str,
+ w,
+ )
+ })
+ }
+
+ /// Emit `.web/app/_document.js`.
+ ///
+ /// Ports `document_root_template`. Python pre-renders the imports
+ /// list and the document JSX expression; Rust splices both into
+ /// the layout function.
+ fn compile_document_root_module(
+ &self,
+ imports_str: &str,
+ document_render_str: &str,
+ out_path: &str,
+ ) -> PyResult<()> {
+ write_to_file(out_path, |w| {
+ emit_document_root_module(imports_str, document_render_str, w)
+ })
+ }
+
+ #[pyo3(signature = (is_dev_mode, default_color_mode_js, state_name, state_keys, initial_state_json, client_storage_json, out_path))]
+ fn compile_context_module(
+ &self,
+ is_dev_mode: bool,
+ default_color_mode_js: &str,
+ state_name: Option<&str>,
+ state_keys: Vec,
+ initial_state_json: &str,
+ client_storage_json: &str,
+ out_path: &str,
+ ) -> PyResult<()> {
+ let keys: Vec<&str> = state_keys.iter().map(String::as_str).collect();
+ write_to_file(out_path, |w| {
+ emit_context_module(
+ is_dev_mode,
+ default_color_mode_js,
+ state_name,
+ &keys,
+ initial_state_json,
+ client_storage_json,
+ w,
+ )
+ })
+ }
+
+ /// Run the memoize-decision walk on a Component PyObject.
+ ///
+ /// Ports `reflex.compiler.plugins.memoize._should_memoize` to Rust
+ /// (plan §0a phase 2 / §0b lever (b2)). Behavior-identical with the
+ /// legacy predicate: for any Component the legacy plugin would
+ /// memoize, this returns True; for any it would skip, False.
+ ///
+ /// Used by:
+ ///
+ /// * Parity tests (`tests/units/compiler/test_memoize_plugin.py`).
+ /// * Phase 3 (wrapper construction in Rust) — once the decision is
+ /// produced here, the same walk can drive `Component::Memoize`
+ /// wrapping during pyread.
+ fn should_memoize<'py>(
+ &self,
+ py: Python<'py>,
+ component: &Bound<'py, PyAny>,
+ ) -> PyResult {
+ let pyrefs = PyRefs::new(py)?;
+ let refs = MemoRefs::from_pyrefs(py, &pyrefs)?;
+ Ok(memoize_should_memoize(py, component, &refs)?)
+ }
+
+ /// Mirror `Component._get_all_imports()` with the merge happening in
+ /// Rust. Walks `component`'s children + `_get_components_in_props()`,
+ /// calls each node's cached `_get_imports()`, and merges into a
+ /// `HashMap` rather than the Python `merge_parsed_imports` recursion.
+ ///
+ /// Returns the same shape `_get_all_imports` returns:
+ /// `dict[str, list[ImportVar]]` with no library-prefix transform —
+ /// callers wrap in `merge_imports(...)` for the `$/utils/...`
+ /// rewrite.
+ fn collect_all_imports<'py>(
+ &self,
+ py: Python<'py>,
+ component: &Bound<'py, PyAny>,
+ ) -> PyResult> {
+ pyread_collect_all_imports(py, component)
+ }
+
+ /// In-place variant of [`collect_all_imports`]: walks `component`'s
+ /// tree and merges every entry into `target` with the
+ /// `merge_imports` library-prefix transform (`$/utils/...`)
+ /// applied. The caller-owned `target` dict is mutated.
+ ///
+ /// Replaces the
+ /// `merge_imports(target, component._get_all_imports())` pattern in
+ /// `rust_pipeline.compile_pages` — accumulating across pages
+ /// into one dict cuts the O(N²) outer-loop iteration the Python
+ /// pattern incurs.
+ fn collect_all_imports_into<'py>(
+ &self,
+ py: Python<'py>,
+ target: &Bound<'py, PyDict>,
+ component: &Bound<'py, PyAny>,
+ ) -> PyResult<()> {
+ pyread_collect_all_imports_into(py, target, component)
+ }
+
+ /// Apply the `merge_imports` library-prefix transform to every
+ /// entry of `source` and append into `target` in place. Use this
+ /// for dicts that aren't paired with a Component tree walk (custom
+ /// component imports, hand-built import dicts), where the tree-
+ /// walking [`collect_all_imports_into`] doesn't apply.
+ fn merge_imports_into<'py>(
+ &self,
+ py: Python<'py>,
+ target: &Bound<'py, PyDict>,
+ source: &Bound<'py, PyDict>,
+ ) -> PyResult<()> {
+ pyread_merge_imports_into(py, target, source)
+ }
+
+ /// Snapshot the Rust-side per-phase timings from the most recent
+ /// `compile_page_from_component` (or `read_page` directly). Returns
+ /// a `dict[str, int]` keyed by phase name with nanosecond totals.
+ ///
+ /// Counters reset at the start of every `read_page`. Phase names
+ /// mirror the call sites in `pyo3_reader.rs`:
+ ///
+ /// * `class_name_ns` — `type(c).__name__` dispatch
+ /// * `resolve_tag_ns` — alias/tag/library reads in `resolve_tag_symbol`
+ /// * `import_alias_ns` — same attrs re-read in `import_alias_for`
+ /// * `needs_ref_ns` — `getattr("id")` check
+ /// * `read_props_ns`, `read_children_ns`, `read_event_handlers_ns`
+ /// * `read_var_data_ns` — Var._get_all_var_data + decode
+ /// * `harvest_register_ns` — RefCell mutations
+ /// * `emit_ns` — pure-Rust IR → JSX string build
+ /// * `read_page_total_ns` — end-to-end `read_page`
+ fn last_phase_timings_ns<'py>(&self, py: Python<'py>) -> PyResult> {
+ let t = reflex_pyread::timing::snapshot();
+ let d = PyDict::new_bound(py);
+ d.set_item("read_page_total_ns", t.read_page_total_ns)?;
+ d.set_item("emit_ns", t.emit_ns)?;
+ d.set_item("class_name_ns", t.class_name_ns)?;
+ d.set_item("resolve_tag_ns", t.resolve_tag_ns)?;
+ d.set_item("import_alias_ns", t.import_alias_ns)?;
+ d.set_item("needs_ref_ns", t.needs_ref_ns)?;
+ d.set_item("read_var_data_ns", t.read_var_data_ns)?;
+ d.set_item("harvest_register_ns", t.harvest_register_ns)?;
+ d.set_item("get_props_call_ns", t.get_props_call_ns)?;
+ d.set_item("prop_value_getattr_ns", t.prop_value_getattr_ns)?;
+ d.set_item("children_attr_ns", t.children_attr_ns)?;
+ d.set_item("event_triggers_attr_ns", t.event_triggers_attr_ns)?;
+ d.set_item("isinstance_var_ns", t.isinstance_var_ns)?;
+ d.set_item("value_literal_dispatch_ns", t.value_literal_dispatch_ns)?;
+ d.set_item("var_js_expr_attr_ns", t.var_js_expr_attr_ns)?;
+ // Counts.
+ d.set_item("node_count", t.node_count)?;
+ d.set_item("element_count", t.element_count)?;
+ d.set_item("var_count", t.var_count)?;
+ d.set_item("prop_count", t.prop_count)?;
+ d.set_item("event_handler_count", t.event_handler_count)?;
+ Ok(d)
+ }
+
+ /// Compile a single page directly from a Python `Component` PyObject —
+ /// the new lever-(a) entry point that bypasses `bridge.py` + msgpack.
+ ///
+ /// The reader walks `component` via PyO3 `getattr` (see plan §0b and
+ /// `reflex_pyread::read_page`), builds the IR in-arena, and emits JSX.
+ /// The cache is not consulted; D5's Salsa storage keys on msgpack
+ /// content hashes and the pyread path doesn't produce one. Hot reload
+ /// gains incremental caching later.
+ ///
+ /// Args:
+ /// route_ident: JS identifier used for the route export.
+ /// component: the page's root `BaseComponent` instance.
+ /// route: URL path (e.g. `/about`).
+ /// title: optional document title.
+ /// meta_tags: optional `[(name_or_property, content), …]` list.
+ #[pyo3(signature = (route_ident, component, route, title=None, meta_tags=None, custom_code=None, hooks_body=None))]
+ fn compile_page_from_component<'py>(
+ &self,
+ py: Python<'py>,
+ route_ident: &str,
+ component: &Bound<'py, PyAny>,
+ route: &str,
+ title: Option<&str>,
+ meta_tags: Option<&Bound<'py, PyList>>,
+ custom_code: Option>,
+ hooks_body: Option<&str>,
+ ) -> PyResult {
+ let meta_pairs: Vec<(String, String)> = match meta_tags {
+ Some(list) => {
+ let mut out = Vec::with_capacity(list.len());
+ for item in list.iter() {
+ let tup: Bound = item.downcast_into()?;
+ if tup.len() != 2 {
+ return Err(pyo3::exceptions::PyValueError::new_err(
+ "meta_tags entries must be (name, content) tuples",
+ ));
+ }
+ let n: String = tup.get_item(0)?.extract()?;
+ let c: String = tup.get_item(1)?.extract()?;
+ out.push((n, c));
+ }
+ out
+ }
+ None => Vec::new(),
+ };
+
+ let custom_code_owned: Vec = custom_code.unwrap_or_default();
+ let custom_code_refs: Vec<&str> = custom_code_owned.iter().map(String::as_str).collect();
+ let hooks = hooks_body.unwrap_or("");
+
+ let refs = PyRefs::new(py)?;
+ let arena = Arena::new();
+ let page = read_page(py, component, route, title, &meta_pairs, &arena, &refs)?;
+ let mut buf = CodeBuffer::with_capacity(1024);
+ {
+ let _emit = reflex_pyread::TimingSpan::new(reflex_pyread::TimingField::Emit);
+ emit_page_with_extras(&mut buf, &page, route_ident, &custom_code_refs, hooks);
+ }
+ String::from_utf8(buf.into_bytes()).map_err(|e| {
+ pyo3::exceptions::PyValueError::new_err(format!(
+ "rust pyread emit produced non-utf8: {e}"
+ ))
+ })
+ }
+
+ /// Compile a whole app. Returns a `CompiledOutput` whose `files` dict maps
+ /// output paths (relative to the Vite project root) to UTF-8 byte blobs.
+ ///
+ /// `pages`: list of `(route_ident, route_path, page_ir_bytes)` triples.
+ /// `theme`: optional theme IR bytes.
+ /// `global_state`: optional global-state IR bytes.
+ /// `plugin_manifest`: optional plugin-manifest IR bytes.
+ #[pyo3(signature = (pages, theme=None, global_state=None, plugin_manifest=None))]
+ fn compile_app(
+ &self,
+ py: Python<'_>,
+ pages: &Bound<'_, PyList>,
+ theme: Option<&Bound<'_, PyBytes>>,
+ global_state: Option<&Bound<'_, PyBytes>>,
+ plugin_manifest: Option<&Bound<'_, PyBytes>>,
+ ) -> PyResult {
+ let mut page_inputs: Vec<(String, String, Vec)> = Vec::with_capacity(pages.len());
+ for item in pages.iter() {
+ let tup: Bound = item.downcast_into()?;
+ if tup.len() != 3 {
+ return Err(pyo3::exceptions::PyValueError::new_err(
+ "each page entry must be (route_ident, route_path, ir_bytes)",
+ ));
+ }
+ let ident: String = tup.get_item(0)?.extract()?;
+ let route: String = tup.get_item(1)?.extract()?;
+ let bytes: Vec = tup.get_item(2)?.extract::<&[u8]>()?.to_vec();
+ page_inputs.push((ident, route, bytes));
+ }
+ let theme_bytes = theme.map(|b| b.as_bytes().to_vec());
+ let state_bytes = global_state.map(|b| b.as_bytes().to_vec());
+ let plugin_bytes = plugin_manifest.map(|b| b.as_bytes().to_vec());
+
+ let db = self.db.clone();
+ let result = py
+ .allow_threads(move || compile_app_inner(&db, page_inputs, theme_bytes, state_bytes, plugin_bytes))
+ .map_err(map_err)?;
+ Ok(result)
+ }
+}
+
+fn map_err(e: CompileError) -> PyErr {
+ pyo3::exceptions::PyValueError::new_err(e.to_string())
+}
+
+#[derive(Debug, thiserror::Error)]
+enum CompileError {
+ #[error("page compile failed for {route_ident}: {source}")]
+ Page {
+ route_ident: String,
+ #[source]
+ source: reflex_db::DbError,
+ },
+ #[error("theme parse failed: {0}")]
+ Theme(reflex_ir::ParseError),
+ #[error("global-state parse failed: {0}")]
+ GlobalState(reflex_ir::ParseError),
+ #[error("plugin manifest parse failed: {0}")]
+ PluginManifest(reflex_ir::ParseError),
+}
+
+fn compile_page_with_sourcemap_inner(
+ route_ident: &str,
+ page_bytes: &[u8],
+) -> PyResult<(String, Vec<(u32, u32, u32, u32)>)> {
+ let arena = Arena::with_capacity(page_bytes.len() * 4);
+ let page = parse_page(&arena, page_bytes)
+ .map_err(|e| pyo3::exceptions::PyValueError::new_err(format!("page parse: {e}")))?;
+ let mut buf = CodeBuffer::with_capacity(page_bytes.len() * 2);
+ let mut map = SourceMap::new();
+ emit_page_with_map(&mut buf, &page, route_ident, &mut map);
+ let js = String::from_utf8(buf.into_bytes()).map_err(|e| {
+ pyo3::exceptions::PyValueError::new_err(format!("emit produced non-utf8: {e}"))
+ })?;
+ let mappings: Vec<(u32, u32, u32, u32)> = map
+ .entries()
+ .iter()
+ .map(|(offset, loc)| (*offset, loc.file.0, loc.line, loc.col))
+ .collect();
+ Ok((js, mappings))
+}
+
+fn compile_app_inner(
+ db: &CompilerDb,
+ pages: Vec<(String, String, Vec)>,
+ theme: Option>,
+ global_state: Option>,
+ plugin_manifest: Option>,
+) -> Result {
+ let arena = Arena::new();
+ let mut files: HashMap> = HashMap::with_capacity(pages.len() + 4);
+
+ // D11: parallel page emit via rayon. Cache hits still avoid work.
+ let parallel_inputs: Vec<(String, Vec)> = pages
+ .iter()
+ .map(|(ident, _, bytes)| (ident.clone(), bytes.clone()))
+ .collect();
+ let results = db.emit_pages_parallel(¶llel_inputs);
+ for ((ident, route, _), result) in pages.iter().zip(results.into_iter()) {
+ let rendered = result.map_err(|source| CompileError::Page {
+ route_ident: ident.clone(),
+ source,
+ })?;
+ let out_path = page_output_path(route);
+ files.insert(out_path, rendered.as_bytes().to_vec());
+ }
+
+ if let Some(bytes) = theme {
+ let theme = parse_theme(&arena, &bytes).map_err(CompileError::Theme)?;
+ let mut buf = CodeBuffer::with_capacity(1024);
+ emit_theme(&mut buf, &theme);
+ files.insert("src/styles/theme.css".to_owned(), buf.into_bytes());
+ }
+ if let Some(bytes) = global_state {
+ let state = parse_global_state(&arena, &bytes).map_err(CompileError::GlobalState)?;
+ let mut buf = CodeBuffer::with_capacity(2048);
+ emit_context(&mut buf, &state);
+ files.insert("src/context.js".to_owned(), buf.into_bytes());
+ }
+ if let Some(bytes) = plugin_manifest {
+ let manifest = parse_plugin_manifest(&arena, &bytes).map_err(CompileError::PluginManifest)?;
+ let mut buf = CodeBuffer::with_capacity(2048);
+ emit_app_root(&mut buf, &manifest);
+ files.insert("src/AppWrap.jsx".to_owned(), buf.into_bytes());
+
+ // Plugin static assets pass-through (artifact #7 in §4.5 — Python
+ // ships path+bytes, Rust copies into the output map verbatim).
+ for plugin in manifest.plugins {
+ for (path, body) in plugin.static_assets {
+ files.insert(
+ reflex_intern::resolve_unchecked(*path).to_owned(),
+ body.to_vec(),
+ );
+ }
+ }
+ }
+
+ // Always emit a Vite config — Python doesn't need to opt in.
+ let mut vite_buf = CodeBuffer::with_capacity(512);
+ emit_vite_config(&mut vite_buf, "");
+ files.insert("vite.config.js".to_owned(), vite_buf.into_bytes());
+
+ Ok(CompiledOutput {
+ files,
+ orphans: Vec::new(),
+ diagnostics: Vec::new(),
+ })
+}
+
+/// Convert a route path to the emitted JS module path. `/` → `pages/index.jsx`,
+/// `/about` → `pages/about.jsx`, `/foo/bar` → `pages/foo/bar.jsx`. The Python
+/// side will respect this layout when writing files.
+fn page_output_path(route: &str) -> String {
+ let trimmed = route.trim_start_matches('/');
+ if trimmed.is_empty() {
+ return "pages/index.jsx".to_owned();
+ }
+ let mut out = String::with_capacity(8 + trimmed.len());
+ out.push_str("pages/");
+ out.push_str(trimmed);
+ out.push_str(".jsx");
+ out
+}
+
+#[pyclass]
+pub struct CompiledOutput {
+ files: HashMap>,
+ orphans: Vec,
+ diagnostics: Vec,
+}
+
+#[pymethods]
+impl CompiledOutput {
+ /// Return the rendered files as a `dict[str, bytes]`.
+ fn files<'py>(&self, py: Python<'py>) -> PyResult> {
+ let dict = PyDict::new_bound(py);
+ for (path, contents) in &self.files {
+ dict.set_item(path, PyBytes::new_bound(py, contents))?;
+ }
+ Ok(dict)
+ }
+
+ /// Paths the Python side should delete (no longer emitted by Rust).
+ fn orphans(&self) -> Vec {
+ self.orphans.clone()
+ }
+
+ /// Compiler diagnostics. List of pre-formatted strings for now; D11 will
+ /// convert to structured `miette`-style records.
+ fn diagnostics(&self) -> Vec {
+ self.diagnostics.clone()
+ }
+
+ fn __repr__(&self) -> String {
+ format!(
+ "CompiledOutput(files={}, orphans={}, diagnostics={})",
+ self.files.len(),
+ self.orphans.len(),
+ self.diagnostics.len()
+ )
+ }
+}
+
+/// Open `out_path` for buffered write and run `f` on the writer, mapping
+/// any `io::Error` into a Python `OSError`.
+///
+/// Used by every "build content + write to disk" PyO3 method on
+/// `CompilerSession` so the buffering, error mapping, and flush
+/// behaviour stay consistent across the whole static-artifact surface.
+fn write_to_file(out_path: &str, f: F) -> PyResult<()>
+where
+ F: FnOnce(&mut std::io::BufWriter) -> std::io::Result<()>,
+{
+ let file = std::fs::File::create(out_path)
+ .map_err(|e| pyo3::exceptions::PyOSError::new_err(format!("{out_path}: {e}")))?;
+ let mut w = std::io::BufWriter::new(file);
+ f(&mut w).map_err(|e| pyo3::exceptions::PyOSError::new_err(e.to_string()))?;
+ std::io::Write::flush(&mut w)
+ .map_err(|e| pyo3::exceptions::PyOSError::new_err(e.to_string()))
+}
diff --git a/packages/reflex-compiler-rust/crates/reflex_pyread/Cargo.toml b/packages/reflex-compiler-rust/crates/reflex_pyread/Cargo.toml
new file mode 100644
index 00000000000..7e9b75e926d
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_pyread/Cargo.toml
@@ -0,0 +1,27 @@
+[package]
+name = "reflex_pyread"
+version = "0.0.0"
+edition.workspace = true
+rust-version.workspace = true
+license.workspace = true
+publish.workspace = true
+
+[dependencies]
+# `pyo3` is optional so the pure-Rust helpers (string decoders, etc.) can
+# be unit-tested with `--no-default-features` — uv's snap-managed Python
+# hides libpython from the system linker, so anything that drags pyo3 in
+# fails to link a test binary.
+#
+# `reflex_py` (the cdylib) re-enables this feature plus `extension-module`
+# via its own dep declaration; in normal `maturin develop` builds the
+# `pyo3` feature here stays on.
+pyo3 = { version = "0.22", default-features = false, features = ["abi3-py310"], optional = true }
+
+thiserror = { workspace = true }
+reflex_ir = { workspace = true }
+reflex_arena = { workspace = true }
+reflex_intern = { workspace = true }
+
+[features]
+default = ["pyo3"]
+pyo3 = ["dep:pyo3"]
diff --git a/packages/reflex-compiler-rust/crates/reflex_pyread/src/imports.rs b/packages/reflex-compiler-rust/crates/reflex_pyread/src/imports.rs
new file mode 100644
index 00000000000..fb598887e2f
--- /dev/null
+++ b/packages/reflex-compiler-rust/crates/reflex_pyread/src/imports.rs
@@ -0,0 +1,196 @@
+//! Rust mirror of `reflex_base.components.component.Component._get_all_imports`
+//! and `reflex_base.utils.imports.merge_imports`.
+//!
+//! Why this exists: profiling `reflex run-rust` on the docs app showed
+//! `_get_all_imports` + `merge_imports` at ~1.3 s cumulative for 7 pages
+//! — 5.2M iterations through a Python generator. Pushing the walk and
+//! merge into Rust drops the inner recursion and lets `compile_pages`
+//! accumulate into a single Python dict in-place instead of rebuilding
+//! it once per page / memo body.
+//!
+//! Three entry points:
+//!
+//! * [`collect_all_imports`] — non-mutating: returns a fresh dict
+//! shaped like `_get_all_imports` returns (no `$` prefix).
+//! * [`collect_all_imports_into`] — walks the tree and **merges into a
+//! caller-owned target dict** with the `$/utils/...` prefix transform
+//! applied per key. Replaces the legacy
+//! `merge_imports(target, component._get_all_imports())` pattern.
+//! * [`merge_imports_into`] — same prefix transform applied to an
+//! already-built `ParsedImportDict`, no tree walk.
+//!
+//! The per-Component `_get_imports` work stays on the Python side because
+//! user subclasses override it via `add_imports()`; the walker dispatches
+//! through PyO3 callbacks rather than reimplementing the override chain.
+//!
+//! See `RUST_REWRITE_PLAN.md` lever (a) — page-level harvest walks.
+
+use std::collections::HashMap;
+
+use pyo3::prelude::*;
+use pyo3::types::{PyAnyMethods, PyDict, PyList, PyString, PyStringMethods};
+
+/// Library-name prefixes that get rewritten to `$` so Vite's
+/// `$` alias resolves them to `.web//...`. Mirrors
+/// `reflex_base.utils.imports.merge_imports`.
+const ALIAS_PREFIXES: &[&str] = &["/utils/", "/components/", "/styles/", "/public/"];
+
+fn apply_alias_prefix(lib: &str) -> String {
+ if ALIAS_PREFIXES.iter().any(|p| lib.starts_with(p)) {
+ let mut out = String::with_capacity(lib.len() + 1);
+ out.push('$');
+ out.push_str(lib);
+ out
+ } else {
+ lib.to_owned()
+ }
+}
+
+/// Walk `root`'s component tree (children + `_get_components_in_props()`)
+/// and return a merged `dict[str, list[ImportVar]]` — no `$/utils/...`
+/// rewrite. Use this when the caller wants the raw shape Python's
+/// `_get_all_imports` returns.
+pub fn collect_all_imports<'py>(
+ py: Python<'py>,
+ root: &Bound<'py, PyAny>,
+) -> PyResult> {
+ let mut acc: HashMap> = HashMap::new();
+ walk_raw(py, root, &mut acc)?;
+
+ let out = PyDict::new_bound(py);
+ for (lib, items) in acc {
+ let list = PyList::new_bound(py, items);
+ out.set_item(lib, list)?;
+ }
+ Ok(out)
+}
+
+/// Walk `root`'s tree like [`collect_all_imports`], applying the
+/// `$/utils/...` prefix transform per key, and append every entry into
+/// `target` in place. The caller-owned target dict is mutated so the
+/// page loop can accumulate across pages without rebuilding the dict on
+/// each iteration — the costly pattern profiling exposed.
+///
+/// `target` is expected to be `dict[str, list[ImportVar]]`. For
+/// libraries already present in `target`, items are appended to the
+/// existing list; otherwise a fresh list is created.
+pub fn collect_all_imports_into<'py>(
+ py: Python<'py>,
+ target: &Bound<'py, PyDict>,
+ root: &Bound<'py, PyAny>,
+) -> PyResult<()> {
+ walk_into(py, root, target)
+}
+
+/// Apply the `$/utils/...` prefix transform to every entry of `source`
+/// and append into `target` in place. Equivalent to
+/// `merge_imports(target, source)` from
+/// `reflex_base.utils.imports` for the `ParsedImportDict` case (no
+/// `str`/`set`/`tuple` field dispatch — callers must pre-normalize via
+/// `parse_imports` if needed).
+pub fn merge_imports_into<'py>(
+ py: Python<'py>,
+ target: &Bound<'py, PyDict>,
+ source: &Bound<'py, PyDict>,
+) -> PyResult<()> {
+ for (lib_obj, items_obj) in source.iter() {
+ let lib_str = lib_obj.downcast::()?.to_str()?;
+ let new_lib = apply_alias_prefix(lib_str);
+ append_items(py, target, &new_lib, &items_obj)?;
+ }
+ Ok(())
+}
+
+fn walk_raw<'py>(
+ py: Python<'py>,
+ node: &Bound<'py, PyAny>,
+ acc: &mut HashMap>,
+) -> PyResult<()> {
+ let imports_obj = node.call_method0("_get_imports")?;
+ let imports_dict = imports_obj.downcast::()?;
+ for (lib_obj, items_obj) in imports_dict.iter() {
+ let lib_py = lib_obj.downcast::()?;
+ let lib = lib_py.to_str()?.to_owned();
+ let items_list = items_obj.downcast::()?;
+ let bucket = acc.entry(lib).or_default();
+ bucket.reserve(items_list.len());
+ for item in items_list.iter() {
+ bucket.push(item.unbind());
+ }
+ }
+
+ recurse_children_and_prop_components(py, node, &mut |child| walk_raw(py, child, acc))
+}
+
+fn walk_into<'py>(
+ py: Python<'py>,
+ node: &Bound<'py, PyAny>,
+ target: &Bound<'py, PyDict>,
+) -> PyResult<()> {
+ let imports_obj = node.call_method0("_get_imports")?;
+ let imports_dict = imports_obj.downcast::()?;
+ for (lib_obj, items_obj) in imports_dict.iter() {
+ let lib_str = lib_obj.downcast::