react-state-basis 0.6.2 → 0.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,9 +5,10 @@
5
5
  <div align="center">
6
6
 
7
7
  # react-state-basis
8
- ### Runtime Architectural Auditor for React
9
8
 
10
- **Basis tracks when state updates (never what) to catch architectural debt that standard tools miss, while keeping your data private.**
9
+ ### Runtime diagnostics for React state
10
+
11
+ **Basis observes when state updates happen and uses those patterns to highlight state-management issues that can be difficult to spot in a code review or profiler. It does not inspect state values.**
11
12
 
12
13
  [![npm version](https://img.shields.io/npm/v/react-state-basis.svg?style=flat-square)](https://www.npmjs.com/package/react-state-basis)
13
14
  [![GitHub stars](https://img.shields.io/github/stars/liovic/react-state-basis.svg?style=flat-square)](https://github.com/liovic/react-state-basis/stargazers)
@@ -20,12 +21,16 @@
20
21
  ## Quick Start
21
22
 
22
23
  ### 1. Install
24
+
23
25
  ```bash
24
26
  npm i react-state-basis
25
27
  ```
26
28
 
27
- ### 2. Setup (Vite)
28
- Add the plugin to your `vite.config.ts`. The Babel plugin auto-labels your hooks—you continue importing from `react` as normal.
29
+ ### 2. Setup with Vite
30
+
31
+ Add the plugin to your `vite.config.ts`.
32
+
33
+ The Babel plugin labels React hooks automatically, so you can continue importing from `react` as usual.
29
34
 
30
35
  ```ts
31
36
  import { defineConfig } from 'vite';
@@ -34,43 +39,55 @@ import { basis } from 'react-state-basis/vite';
34
39
 
35
40
  export default defineConfig({
36
41
  plugins: [
37
- react({
38
- babel: { plugins: [['react-state-basis/plugin']] }
42
+ react({
43
+ babel: {
44
+ plugins: [['react-state-basis/plugin']],
45
+ },
39
46
  }),
40
- basis()
41
- ]
47
+ basis(),
48
+ ],
42
49
  });
43
50
  ```
51
+ This is the supported setup today. Next.js / SWC is not instrumented yet.
44
52
 
45
53
  ### 3. Initialize
54
+
46
55
  ```tsx
47
56
  import { BasisProvider } from 'react-state-basis';
48
57
 
49
58
  root.render(
50
- <BasisProvider
59
+ <BasisProvider
51
60
  debug={true}
52
- showHUD={true} // Set to false for console-only forensics
61
+ showHUD={true}
53
62
  >
54
63
  <App />
55
64
  </BasisProvider>
56
65
  );
57
66
  ```
58
67
 
59
- ### 4. Verify the Signal
60
- Drop this pattern into any component. Basis will identify the rhythm of the debt within ~100ms.
68
+ Set `showHUD={false}` to keep the diagnostics in the console without showing the overlay.
69
+
70
+ ### 4. Try it
71
+
72
+ For example:
61
73
 
62
74
  ```tsx
63
75
  const [a, setA] = useState(0);
64
76
  const [b, setB] = useState(0);
65
77
 
66
78
  useEffect(() => {
67
- setB(a + 1); // ⚡ BASIS: "Double Render Detected"
79
+ setB(a + 1);
68
80
  }, [a]);
69
81
 
70
- return <button onClick={() => setA(a + 1)}>Pulse Basis</button>;
82
+ return (
83
+ <button onClick={() => setA(a + 1)}>
84
+ Update
85
+ </button>
86
+ );
71
87
  ```
72
88
 
73
- Click the button. You should see this in your console:
89
+ When the button is clicked, Basis can identify the effect-driven update pattern and report where it originated. You should see in your console:
90
+
74
91
  ```
75
92
  ⚡ BASIS | DOUBLE RENDER
76
93
  📍 Location: YourComponent.tsx
@@ -78,64 +95,141 @@ Issue: effect_L5 triggers b in a separate frame.
78
95
  Fix: Derive b during the render phase (remove effect) or wrap in useMemo.
79
96
  ```
80
97
 
81
- ---
82
-
83
- ### 5. Control & Scope
84
- * **Ghost Mode:** Disable the Matrix UI while keeping console-based forensics active by setting `showHUD={false}` on the provider.
85
- * **Selective Auditing:** Add `// @basis-ignore` at the top of any file to disable instrumentation. Recommended for:
86
- * High-frequency animation logic (>60fps)
87
- * Third-party library wrappers
88
- * Intentional synchronization (e.g., local mirrors of external caches)
98
+ Detection happens at runtime and timing varies by pattern.
89
99
 
90
100
  ---
91
101
 
92
102
  ## HUD
93
103
 
94
- The optional HUD shows your **State Basis Matrix** in real-time. Purple pulses ($\Omega$) are Context anchors; Red pulses (!) are redundant shadows.
104
+ The optional HUD shows state updates as they happen.
95
105
 
96
106
  <p align="center">
97
- <img src="./assets/050Basis.gif" width="800" alt="Basis Demo" />
107
+ <img src="./assets/050Basis.gif" width="800" alt="Basis Demo">
98
108
  </p>
99
109
 
100
- > **Note:** While the HUD visualizes real-time updates, the **Architectural Health Report** (Console) provides the deep topological analysis.
110
+ The HUD is useful for seeing individual updates. The console report looks at the observed update graph over time, which can reveal patterns that are harder to see from a single interaction.
111
+
112
+ ---
113
+
114
+ ## What Basis Looks For
115
+
116
+ Basis does not try to determine whether your state is "correct." Instead, it looks for update patterns that are often worth investigating.
117
+
118
+ ### Effect-driven updates
119
+
120
+ A `useEffect` causes another state update immediately after rendering.
121
+
122
+ This can be a sign that some state could be derived during render instead.
123
+
124
+ ### Correlated state
125
+
126
+ Two pieces of state repeatedly update within the same time window.
127
+
128
+ For example:
129
+
130
+ ```tsx
131
+ const [isLoading, setIsLoading] = useState(false);
132
+ const [isSuccess, setIsSuccess] = useState(false);
133
+ ```
134
+
135
+ If these values consistently change together, it may be worth checking whether they could be represented by a single state value.
136
+
137
+ Basis reports the correlation; it does not assume that the states should be merged.
138
+
139
+ ### Fragmented updates
140
+
141
+ A single interaction causes updates across multiple components, files, contexts, or stores.
142
+
143
+ Sometimes this is intentional. In other cases, it can indicate that state ownership is spread across several places.
144
+
145
+ ### Context mirroring
146
+
147
+ Local state is repeatedly updated from Context state.
148
+
149
+ This can create two representations of the same information and is worth reviewing when the local copy does not have an independent purpose.
150
+
151
+ ### Update origins
152
+
153
+ When several updates occur together, Basis can use the observed update graph to identify which updates appear upstream of others.
154
+
155
+ This is intended to help investigate a chain of updates rather than simply reporting every downstream symptom.
156
+
157
+ ### Infinite update protection
158
+
159
+ Basis includes safeguards to stop its own instrumentation from continuing indefinitely when an application enters a recursive update loop.
101
160
 
102
161
  ---
103
162
 
104
- ## What Basis Detects
163
+ ### Important: these are signals, not proofs
164
+
165
+ Basis uses runtime timing and correlation heuristics.
105
166
 
106
- Basis draws on ideas from graph theory, signal processing, and linear algebra to flag architectural issues static linters miss:
167
+ A detected pattern is **not automatically a bug**, and Basis does not know the intent behind your application architecture.
107
168
 
108
- - **⚡ Double Renders (Sync Leaks)** - Detects when a `useEffect` triggers a state update immediately after a render, forcing the browser to paint twice.
109
- - **⚡ Prime Movers (Root Causes)** - Ignores downstream symptoms and points you to the exact hook or event that started the chain reaction.
110
- - **⚡ Fragmented Updates** - Detects when a single click forces updates in multiple different files/contexts simultaneously (Tearing risk).
111
- - **Ω Context Mirroring** - Detects when you redundantly copy Global Context data into Local State (creating two sources of truth).
112
- - **♊ Duplicate State** - Identifies variables that always update at the exact same time and should be merged (e.g. `isLoading` + `isSuccess`).
113
- - **🛑 Infinite Loops** - A safety circuit-breaker that kills the auditor before a recursive update freezes your browser.
169
+ Use the results as prompts for investigation rather than as rules for how React code should be written.
114
170
 
115
- [**See examples & fixes →**](https://github.com/liovic/react-state-basis/wiki/The-Forensic-Catalog)
171
+ [See examples and possible fixes ](https://github.com/liovic/react-state-basis/wiki/The-Forensic-Catalog)
116
172
 
117
173
  ---
118
174
 
119
- ## Reports & Telemetry
175
+ ## Reports
176
+
177
+ Run:
178
+
179
+ ```js
180
+ window.printBasisReport()
181
+ ```
182
+
183
+ to print a summary of the observed update graph.
184
+
185
+ The report can include:
186
+
187
+ * **Update sources** - where observed update chains appear to originate.
188
+ * **Fan-out** - which updates are followed by the largest number of downstream updates.
189
+ * **Correlated state** - state variables that repeatedly update together.
190
+ * **Effect-driven updates** - updates that occur as a consequence of effects.
191
+ * **Engine metrics** - runtime measurements collected by the Basis engine.
192
+
193
+ These metrics are diagnostic rather than a score for the quality of your application.
120
194
 
121
- ### Architectural Health Report
122
- Check your entire app's state architecture by running `window.printBasisReport()` in the console.
195
+ ### Runtime metrics
123
196
 
124
- * **Refactor Priorities:** Ranks issues with eigenvector centrality on the update graph so you can see which hook or event fans out the most.
125
- * **Efficiency Score:** A rough ratio of independent update sources vs effect-driven follow-up updates. Diagnostic, not a grade.
126
- * **Sync Issues:** Groups entangled variables into clusters (e.g., Boolean Explosions).
197
+ You can inspect engine metrics with:
127
198
 
128
- ### Hardware Telemetry
129
- Verify engine efficiency and heap stability in real-time via `window.getBasisMetrics()`.
199
+ ```js
200
+ window.getBasisMetrics()
201
+ ```
130
202
 
131
203
  ---
132
204
 
133
- ## Real-World Evidence
205
+ ## Controlling the Instrumentation
206
+
207
+ ### Console-only mode
208
+
209
+ Disable the HUD while keeping diagnostics enabled:
210
+
211
+ ```tsx
212
+ <BasisProvider showHUD={false}>
213
+ <App />
214
+ </BasisProvider>
215
+ ```
216
+
217
+ ### Ignoring files
218
+
219
+ Add:
220
+
221
+ ```ts
222
+ // @basis-ignore
223
+ ```
224
+
225
+ to a file to disable Basis instrumentation for that file.
134
226
 
135
- Basis is verified against industry-standard codebases to ensure high-fidelity detection:
227
+ This can be useful for:
136
228
 
137
- * **Excalidraw (114k⭐)** - Proposed a theme-sync fix [**PR #10637**](https://github.com/excalidraw/excalidraw/pull/10637) (not merged)
138
- * **shadcn-admin (10k⭐)** - Detected redundant state pattern in viewport detection hooks. [**PR #274**](https://github.com/satnaing/shadcn-admin/pull/274) (merged)
229
+ * high-frequency animation code
230
+ * third-party library wrappers
231
+ * intentionally synchronized state
232
+ * code where instrumentation is not useful
139
233
 
140
234
  ---
141
235
 
@@ -143,62 +237,89 @@ Basis is verified against industry-standard codebases to ensure high-fidelity de
143
237
 
144
238
  ### Zustand
145
239
 
146
- Wrap your store with `basisLogger` to give Basis visibility into external
147
- store updates. Store signals appear as Σ in the HUD and health report.
240
+ Basis can observe Zustand store updates alongside React state.
148
241
 
149
242
  ```typescript
150
243
  import { create } from 'zustand';
151
244
  import { basisLogger } from 'react-state-basis/zustand';
152
245
 
153
246
  export const useStore = create(
154
- basisLogger((set) => ({
155
- theme: 'light',
156
- toggleTheme: () => set((state) => ({
157
- theme: state.theme === 'light' ? 'dark' : 'light'
158
- })),
159
- }), 'MyStore')
247
+ basisLogger(
248
+ (set) => ({
249
+ theme: 'light',
250
+
251
+ toggleTheme: () =>
252
+ set((state) => ({
253
+ theme: state.theme === 'light' ? 'dark' : 'light',
254
+ })),
255
+ }),
256
+ 'MyStore'
257
+ )
160
258
  );
161
259
  ```
162
260
 
163
- This enables detection of **Store Mirroring**, **Store Sync Leaks**, and
164
- **Global Event Fragmentation** across React and Zustand state simultaneously.
261
+ This allows React and Zustand updates to appear in the same runtime graph.
262
+
263
+ [See the Zustand example →](./examples/basis-zustand/)
165
264
 
166
- [See full Zustand example →](./examples/basis-zustand/)
265
+ ### Planned integrations
167
266
 
168
- ### More integrations coming
267
+ XState, React Query, and Redux Toolkit are planned.
169
268
 
170
- Planned: XState, React Query, Redux Toolkit. Community PRs welcome.
269
+ Community contributions are welcome.
171
270
 
172
271
  ---
173
272
 
174
273
  ## Performance & Privacy
175
274
 
176
- **Development:** <1ms overhead per update cycle, zero heap growth
177
- **Production:** ~0.01ms per hook (monitoring disabled, ~2-3KB bundle)
178
- **Privacy:** Only tracks update timing, never state values
275
+ Basis is designed primarily as a development-time diagnostic tool.
179
276
 
180
- [**See benchmarks →**](https://github.com/liovic/react-state-basis/wiki/Performance-Forensics)
277
+ * **Development:** instrumentation overhead is designed to remain small; current benchmarks show less than 1ms per update cycle in tested scenarios.
278
+ * **Production:** monitoring is disabled, with a small production footprint.
279
+ * **Privacy:** Basis records update timing and relationships, not application state values.
280
+
281
+ Actual overhead depends on the application and instrumentation configuration.
282
+
283
+ [See benchmarks →](https://github.com/liovic/react-state-basis/wiki/Performance-Forensics)
181
284
 
182
285
  ---
183
286
 
184
- ## Documentation & Theory
287
+ ## Real-World Examples
288
+
289
+ Basis has also been tested against existing open-source applications.
290
+
291
+ * **Excalidraw** - Basis identified a theme synchronization pattern and a possible simplification. [PR #10637](https://github.com/excalidraw/excalidraw/pull/10637) was proposed but not merged.
292
+ * **shadcn-admin** - Basis identified a redundant state pattern in viewport detection hooks. [PR #274](https://github.com/satnaing/shadcn-admin/pull/274) was merged.
185
293
 
186
- Basis is built on heuristics inspired by **Signal Processing**, **Linear Algebra**, and **Graph Theory**. [**The wiki**](https://github.com/liovic/react-state-basis/wiki) explains the mental model and the engine. It is a heuristic, not a proof.
294
+ These examples are intended as demonstrations of the tool's output, not as claims that every detected pattern represents a defect.
187
295
 
188
296
  ---
189
297
 
190
- ## Roadmap
298
+ ## How It Works
299
+
300
+ Basis observes the timing and relationships between state updates while your application runs.
301
+
302
+ It builds an in-memory representation of those updates and applies heuristics to identify recurring patterns.
303
+
304
+ It does **not** need to inspect the values stored in your state to perform these checks.
305
+
306
+ The analysis is intentionally heuristic. Runtime behavior can show that two things consistently happen together, but it cannot by itself prove why they happen together or whether the relationship is intentional.
191
307
 
192
- Each era of Basis answers a different architectural question:
308
+ For a deeper look at the implementation and underlying model:
193
309
 
194
- **v0.4.x** - The Correlation Era - *Are these states moving together?*
195
- ✓ **v0.5.x** - The Decomposition Era - *Is this local state just a copy of Context?*
196
- → **v0.6.x** - The Graph Era - *Which bug should I fix first for maximum impact?*
197
- **v0.7.x** - The Information Era - *Does this state carry real information, or is it derivative?*
198
- **v0.8.x** - The Manifold Era - *How many hooks does your component actually need?*
310
+ [Read the documentation and theory →](https://github.com/liovic/react-state-basis/wiki)
311
+
312
+ ---
313
+
314
+ ## Roadmap
199
315
 
316
+ * ✓ **v0.4.x** - Identify state that repeatedly updates together
317
+ * ✓ **v0.5.x** - Identify local state synchronized from Context
318
+ * → **v0.6.x** - Analyze update fan-out and likely upstream sources
319
+ * **v0.7.x** - Improve detection of derived vs. independent state
320
+ * **v0.8.x** - Explore how much local state components actually use
200
321
 
201
- [**More info**](https://github.com/liovic/react-state-basis/wiki/Roadmap)
322
+ [See the full roadmap →](https://github.com/liovic/react-state-basis/wiki/Roadmap)
202
323
 
203
324
  ---
204
325
 
@@ -837,6 +837,13 @@ var unregisterVariable = (l) => {
837
837
  instance.loopCounters.delete(l);
838
838
  instance.pausedVariables.delete(l);
839
839
  instance.redundantLabels.delete(l);
840
+ instance.graph.delete(l);
841
+ instance.graph.forEach((targets) => targets.delete(l));
842
+ instance.violationMap.delete(l);
843
+ instance.violationMap.forEach((list) => {
844
+ const idx = list.findIndex((v) => v.target === l);
845
+ if (idx !== -1) list.splice(idx, 1);
846
+ });
840
847
  };
841
848
  var beginEffectTracking = (l) => {
842
849
  if (instance.config.debug) instance.currentEffectSource = l;
@@ -874,4 +881,4 @@ export {
874
881
  printBasisHealthReport,
875
882
  getBasisMetrics
876
883
  };
877
- //# sourceMappingURL=chunk-E3D6YEKB.mjs.map
884
+ //# sourceMappingURL=chunk-6GBZSOB3.mjs.map