react-state-basis 0.4.2 → 0.5.1
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 +73 -321
- package/dist/index.d.mts +31 -23
- package/dist/index.d.ts +31 -23
- package/dist/index.js +353 -327
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +355 -332
- package/dist/index.mjs.map +1 -1
- package/dist/plugin.js +48 -22
- package/dist/production.d.mts +15 -6
- package/dist/production.d.ts +15 -6
- package/dist/production.js +21 -6
- package/dist/production.js.map +1 -1
- package/dist/production.mjs +19 -6
- package/dist/production.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,416 +4,168 @@
|
|
|
4
4
|
|
|
5
5
|
<div align="center">
|
|
6
6
|
|
|
7
|
-
#
|
|
8
|
-
### Runtime Architectural Auditor
|
|
7
|
+
# react-state-basis
|
|
8
|
+
### Runtime Architectural Auditor for React
|
|
9
|
+
|
|
10
|
+
**Basis tracks when state updates (never what) to catch architectural debt that standard tools miss, while keeping your data private.**
|
|
9
11
|
|
|
10
12
|
[](https://www.npmjs.com/package/react-state-basis)
|
|
11
13
|
[](https://github.com/liovic/react-state-basis/stargazers)
|
|
12
14
|
[](https://opensource.org/licenses/MIT)
|
|
13
15
|
|
|
14
|
-
**Basis tracks the rhythm of state updates, not the data. It identifies redundant patterns and sync-leaks that standard tools miss.**
|
|
15
|
-
|
|
16
|
-
> Stop relying on architectural intuition. Start measuring architectural debt.
|
|
17
|
-
|
|
18
16
|
</div>
|
|
19
17
|
|
|
20
18
|
---
|
|
21
19
|
|
|
22
|
-
##
|
|
23
|
-
|
|
24
|
-
**react-state-basis** watches your React app in development and flags common architectural issues:
|
|
25
|
-
|
|
26
|
-
- **Redundant state** - Two states that always update together
|
|
27
|
-
- **Update chains** - Effects that trigger more state updates (double renders)
|
|
28
|
-
- **Infinite loops** - Circular dependencies that freeze your browser
|
|
29
|
-
- **Tight coupling** - State variables that should be independent but aren't
|
|
30
|
-
|
|
31
|
-
It works by tracking *when* state updates happen (temporal patterns), not *what* the values are.
|
|
32
|
-
|
|
33
|
-
---
|
|
34
|
-
|
|
35
|
-
## Quick Example
|
|
36
|
-
```tsx
|
|
37
|
-
// ❌ Basis will flag this
|
|
38
|
-
const [user, setUser] = useState(null);
|
|
39
|
-
const [isLoggedIn, setIsLoggedIn] = useState(false);
|
|
40
|
-
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
setIsLoggedIn(!!user); // Double render - flagged as sync leak
|
|
43
|
-
}, [user]);
|
|
44
|
-
|
|
45
|
-
// ✅ Better
|
|
46
|
-
const [user, setUser] = useState(null);
|
|
47
|
-
const isLoggedIn = !!user; // Computed, no second render
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
---
|
|
51
|
-
|
|
52
|
-
## See It Work
|
|
53
|
-
|
|
54
|
-
The optional HUD shows your state basis matrix in real-time:
|
|
55
|
-
|
|
56
|
-
<p align="center">
|
|
57
|
-
<img src="./assets/react-state-basis-demo.gif" width="800" alt="React State Basis Demo" />
|
|
58
|
-
</p>
|
|
59
|
-
|
|
60
|
-
---
|
|
61
|
-
|
|
62
|
-
## Real-World Audits
|
|
63
|
-
|
|
64
|
-
Basis has been tested on major open-source projects to validate detection accuracy:
|
|
65
|
-
|
|
66
|
-
### Excalidraw (114k+ ⭐)
|
|
67
|
-
**Detected:** Causal Sync Leak in theme state synchronization
|
|
68
|
-
**Issue:** A `useEffect` was manually syncing theme state, causing an unnecessary double render on every theme toggle
|
|
69
|
-
**Fix:** [PR #10637](https://github.com/excalidraw/excalidraw/pull/10637) - Replaced with a computed value
|
|
70
|
-
|
|
71
|
-
<p align="center">
|
|
72
|
-
<img src="./assets/excalidraw-audit.png" width="800" alt="Excalidraw Audit" />
|
|
73
|
-
</p>
|
|
74
|
-
|
|
75
|
-
### shadcn-admin (10k+ ⭐)
|
|
76
|
-
**Detected:** Redundant state pattern in mobile detection hooks
|
|
77
|
-
**Issue:** Viewport resize events were being synchronized via effects rather than direct subscriptions
|
|
78
|
-
**Fix:** [PR #274](https://github.com/satnaing/shadcn-admin/pull/274) - Optimized subscription pattern
|
|
79
|
-
|
|
80
|
-
<p align="center">
|
|
81
|
-
<img src="./assets/shadcn-admin.png" width="800" alt="shadcn Admin Audit" />
|
|
82
|
-
</p>
|
|
83
|
-
|
|
84
|
-
> **Note:** These are proposed architectural improvements. Basis points out patterns worth investigating - the final decision rests with the maintainer.
|
|
85
|
-
|
|
86
|
-
---
|
|
87
|
-
|
|
88
|
-
## Setup (Vite)
|
|
20
|
+
## Quick Start
|
|
89
21
|
|
|
90
22
|
### 1. Install
|
|
91
23
|
```bash
|
|
92
24
|
npm i react-state-basis
|
|
93
25
|
```
|
|
94
26
|
|
|
95
|
-
### 2.
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
+
|
|
30
|
+
```ts
|
|
99
31
|
import { basis } from 'react-state-basis/vite';
|
|
100
32
|
|
|
101
33
|
export default defineConfig({
|
|
102
34
|
plugins: [
|
|
103
|
-
react({
|
|
104
|
-
babel: { plugins: [['react-state-basis/plugin']] }
|
|
35
|
+
react({
|
|
36
|
+
babel: { plugins: [['react-state-basis/plugin']] }
|
|
105
37
|
}),
|
|
106
38
|
basis()
|
|
107
39
|
]
|
|
108
40
|
});
|
|
109
41
|
```
|
|
110
42
|
|
|
111
|
-
### 3.
|
|
43
|
+
### 3. Initialize
|
|
112
44
|
```tsx
|
|
113
45
|
import { BasisProvider } from 'react-state-basis';
|
|
114
46
|
|
|
115
47
|
root.render(
|
|
116
|
-
<BasisProvider
|
|
48
|
+
<BasisProvider
|
|
49
|
+
debug={true}
|
|
50
|
+
showHUD={true} // Set to false for console-only forensics
|
|
51
|
+
>
|
|
117
52
|
<App />
|
|
118
53
|
</BasisProvider>
|
|
119
54
|
);
|
|
120
55
|
```
|
|
121
56
|
|
|
122
|
-
### 4. Verify
|
|
57
|
+
### 4. Verify the Signal
|
|
58
|
+
Drop this pattern into any component. Basis will identify the rhythm of the debt within ~100ms.
|
|
123
59
|
|
|
124
|
-
Add this test pattern to any component:
|
|
125
60
|
```tsx
|
|
126
61
|
const [a, setA] = useState(0);
|
|
127
62
|
const [b, setB] = useState(0);
|
|
128
63
|
|
|
129
64
|
useEffect(() => {
|
|
130
|
-
setB(a); //
|
|
65
|
+
setB(a); // ⚡ BASIS: "Double Render Detected"
|
|
131
66
|
}, [a]);
|
|
132
|
-
```
|
|
133
67
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
---
|
|
137
|
-
|
|
138
|
-
## What You'll See
|
|
139
|
-
|
|
140
|
-
### Console Alerts
|
|
141
|
-
|
|
142
|
-
**Redundant Pattern:**
|
|
143
|
-
Detected when two variables move in perfect unison.
|
|
144
|
-
```
|
|
145
|
-
📐 BASIS | REDUNDANT PATTERN
|
|
146
|
-
📍 Location: TodoList.tsx
|
|
147
|
-
Observation: "todos" and "count" move together.
|
|
148
|
-
One is likely a direct mirror of the other. Confidence: 94%
|
|
149
|
-
Action: Refactor "count" to useMemo.
|
|
68
|
+
return <button onClick={() => setA(a + 1)}>Pulse Basis</button>;
|
|
150
69
|
```
|
|
151
70
|
|
|
152
|
-
|
|
153
|
-
Detected when one variable consistently triggers another with a temporal lag.
|
|
71
|
+
Click the button. You should see this in your console within ~100ms:
|
|
154
72
|
```
|
|
155
|
-
|
|
156
|
-
📍 Location:
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
Result: This creates a Double Render Cycle.
|
|
73
|
+
⚡ BASIS | DOUBLE RENDER
|
|
74
|
+
📍 Location: YourComponent.tsx
|
|
75
|
+
Issue: a triggers b in a separate frame.
|
|
76
|
+
Fix: Derive b during the first render.
|
|
160
77
|
```
|
|
161
78
|
|
|
162
|
-
**Infinite Loop:**
|
|
163
|
-
Detected when a variable updates too rapidly (circuit breaker).
|
|
164
|
-
```
|
|
165
|
-
🛑 BASIS CRITICAL | CIRCUIT BREAKER
|
|
166
|
-
Infinite oscillation detected on: "counter"
|
|
167
|
-
Execution halted to prevent browser thread lock.
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
### Health Report
|
|
171
|
-
|
|
172
|
-
Check your entire app's state architecture:
|
|
173
|
-
```tsx
|
|
174
|
-
window.printBasisReport();
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
Shows:
|
|
178
|
-
- **Health Score** - Percentage of independent vs. synchronized state
|
|
179
|
-
- **Synchronized Clusters** - Groups of variables that move together
|
|
180
|
-
- **Correlation Matrix** - Full pairwise similarity analysis (for <15 variables)
|
|
181
|
-
|
|
182
|
-
### Hardware Telemetry
|
|
183
|
-
|
|
184
|
-
Verify engine efficiency and heap stability in real-time:
|
|
185
|
-
|
|
186
|
-
```tsx
|
|
187
|
-
window.getBasisMetrics();
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
Returns: Engine execution time, active hook count, and current memory allocation strategy.
|
|
191
|
-
|
|
192
79
|
---
|
|
193
80
|
|
|
194
|
-
|
|
81
|
+
### 5. Control & Scope
|
|
82
|
+
* **Ghost Mode:** Disable the Matrix UI while keeping console-based forensics active by setting `showHUD={false}` on the provider.
|
|
83
|
+
* **Selective Auditing:** Add `// @basis-ignore` at the top of any file to disable instrumentation. Recommended for:
|
|
84
|
+
* High-frequency animation logic (>60fps)
|
|
85
|
+
* Third-party library wrappers
|
|
86
|
+
* Intentional synchronization (e.g., local mirrors of external caches)
|
|
195
87
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
Basis tracks **when** state updates occur, creating a 50-tick timeline for each variable:
|
|
199
|
-
```
|
|
200
|
-
useState("count"): [0,0,1,0,0,1,1,0,...] (updates at ticks 2, 5, 6)
|
|
201
|
-
useState("total"): [0,0,1,0,0,1,1,0,...] (same pattern)
|
|
202
|
-
↑ Redundant: identical temporal signature
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
For every pair of variables, Basis checks three temporal relationships:
|
|
206
|
-
|
|
207
|
-
1. **Synchronous (Redundancy):** Do they update in the same tick?
|
|
208
|
-
```
|
|
209
|
-
A: [0,1,0,1,0,...]
|
|
210
|
-
B: [0,1,0,1,0,...] → Flagged as redundant
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
2. **Lead-Lag (A → B):** Does B consistently follow A in the next tick?
|
|
214
|
-
```
|
|
215
|
-
A: [0,1,0,1,0,...]
|
|
216
|
-
B: [0,0,1,0,1,...] → B follows A (sync leak detected)
|
|
217
|
-
```
|
|
218
|
-
|
|
219
|
-
3. **Lead-Lag (B → A):** Does A consistently follow B?
|
|
220
|
-
```
|
|
221
|
-
A: [0,0,1,0,1,...]
|
|
222
|
-
B: [0,1,0,1,0,...] → A follows B (reverse causality)
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
The engine uses **offset-based comparison** to check these patterns without allocating temporary arrays, ensuring minimal overhead even at high frame rates.
|
|
226
|
-
|
|
227
|
-
### Performance Optimizations
|
|
88
|
+
---
|
|
228
89
|
|
|
229
|
-
|
|
230
|
-
- **Batched Analysis:** Runs every 5 ticks (~100ms) to avoid impacting frame budget
|
|
231
|
-
- **Console Throttling:** Same alert won't repeat within 5 seconds
|
|
232
|
-
- **Zero Production Overhead:** Entire library is replaced with no-op shims in production builds
|
|
90
|
+
## Visual Proof
|
|
233
91
|
|
|
234
|
-
|
|
92
|
+
The optional HUD shows your **State Basis Matrix** in real-time. Purple pulses ($\Omega$) are Context anchors; Red pulses (!) are redundant shadows.
|
|
235
93
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
const [count, setCount] = useState(0);
|
|
240
|
-
const [doubled, setDoubled] = useState(0);
|
|
94
|
+
<p align="center">
|
|
95
|
+
<img src="./assets/050Basis.gif" width="800" alt="Basis v0.5.0 Demo" />
|
|
96
|
+
</p>
|
|
241
97
|
|
|
242
|
-
|
|
243
|
-
setDoubled(count * 2);
|
|
244
|
-
}, [count]);
|
|
98
|
+
---
|
|
245
99
|
|
|
246
|
-
|
|
247
|
-
const [count, setCount] = useState(0);
|
|
248
|
-
const doubled = useMemo(() => count * 2, [count]);
|
|
249
|
-
```
|
|
100
|
+
## What Basis Detects
|
|
250
101
|
|
|
251
|
-
|
|
252
|
-
```tsx
|
|
253
|
-
// ❌ Before (causes double render)
|
|
254
|
-
const [user, setUser] = useState(null);
|
|
255
|
-
const [isLoggedIn, setIsLoggedIn] = useState(false);
|
|
102
|
+
Basis treats every hook as a signal to catch these architectural violations:
|
|
256
103
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
104
|
+
- **Ω Context Mirroring** - Local state shadowing global context
|
|
105
|
+
- **♊ Duplicate State** - Independent variables that always update together
|
|
106
|
+
- **⚡ Sync Leaks** - 1-frame delays forcing double renders
|
|
107
|
+
- **🛑 Recursive Oscillation** - Infinite loops (with circuit breaker)
|
|
260
108
|
|
|
261
|
-
|
|
262
|
-
const [user, setUser] = useState(null);
|
|
263
|
-
const isLoggedIn = !!user; // Derived, no effect needed
|
|
264
|
-
```
|
|
109
|
+
[**See examples & fixes →**](https://github.com/liovic/react-state-basis/wiki/The-Forensic-Catalog)
|
|
265
110
|
|
|
266
111
|
---
|
|
267
112
|
|
|
268
|
-
##
|
|
269
|
-
|
|
270
|
-
In production builds, the entire tool is replaced with zero-op shims. **Zero runtime overhead. Zero bundle size increase.**
|
|
271
|
-
```json
|
|
272
|
-
// package.json - automatic based on NODE_ENV
|
|
273
|
-
"exports": {
|
|
274
|
-
".": {
|
|
275
|
-
"development": "./dist/index.mjs",
|
|
276
|
-
"production": "./dist/production.mjs", // No-op shims
|
|
277
|
-
"default": "./dist/production.mjs"
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
```
|
|
113
|
+
## Reports & Telemetry
|
|
281
114
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
## When to Skip Files
|
|
115
|
+
### Architectural Health Report
|
|
116
|
+
Check your entire app's state architecture by running `window.printBasisReport()` in the console.
|
|
285
117
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
// This file contains external protocols or hardware-bound synchronization outside the scope of architectural auditing.
|
|
290
|
-
```
|
|
118
|
+
* **Efficiency Score:** Ratio of independent signals to total hooks.
|
|
119
|
+
* **Entangled Clusters:** Groups of variables that move in sync (Boolean Explosion).
|
|
120
|
+
* **Correlation Matrix:** Raw pairwise similarity data for deep-dive forensics.
|
|
291
121
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
- **WebSocket message handlers** (rapid, intentional updates)
|
|
295
|
-
- **Canvas/WebGL render loops** (performance-critical paths)
|
|
296
|
-
- **Intentional synchronization** (e.g., React Query → local cache mirrors)
|
|
297
|
-
- **Third-party library wrappers** (where you don't control the architecture)
|
|
122
|
+
### Hardware Telemetry
|
|
123
|
+
Verify engine efficiency and heap stability in real-time via `window.getBasisMetrics()`.
|
|
298
124
|
|
|
299
125
|
---
|
|
300
126
|
|
|
301
|
-
##
|
|
127
|
+
## Real-World Evidence
|
|
302
128
|
|
|
303
|
-
|
|
304
|
-
|------|-------|-------------|
|
|
305
|
-
| **React DevTools** | Component hierarchy & values | Debugging specific components |
|
|
306
|
-
| **Why Did You Render** | Re-render optimization | Performance tuning renders |
|
|
307
|
-
| **ESLint exhaustive-deps** | Static dependency analysis | Preventing missing deps |
|
|
308
|
-
| **react-state-basis** | **Temporal state relationships** | **Finding redundant state & effect chains** |
|
|
129
|
+
Basis is verified against industry-standard codebases to ensure high-fidelity detection:
|
|
309
130
|
|
|
310
|
-
|
|
131
|
+
* **Excalidraw (114k⭐)** - Caught a theme-sync leak forcing a double-render on every toggle. [**PR #10637**](https://github.com/excalidraw/excalidraw/pull/10637)
|
|
132
|
+
* **shadcn-admin (10k⭐)** - Detected redundant state pattern in viewport detection hooks. [**PR #274**](https://github.com/satnaing/shadcn-admin/pull/274)
|
|
311
133
|
|
|
312
134
|
---
|
|
313
135
|
|
|
314
|
-
## Performance
|
|
315
|
-
|
|
316
|
-
**Development Mode**
|
|
317
|
-
|
|
318
|
-
**Basis is designed to be statistically invisible to the main thread.**
|
|
136
|
+
## Performance & Privacy
|
|
319
137
|
|
|
320
|
-
|
|
138
|
+
**Development:** <1ms overhead per update cycle, zero heap growth
|
|
139
|
+
**Production:** ~0.01ms per hook (monitoring disabled, ~2-3KB bundle)
|
|
140
|
+
**Privacy:** Only tracks update timing, never state values
|
|
321
141
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
These metrics were recorded during a **20-minute high-frequency endurance audit** (1.2M state pulses) using the built-in **Stress Lab**.
|
|
325
|
-
|
|
326
|
-
* **Logic Execution Overhead:** < 1.0ms per 100-hook update cycle.
|
|
327
|
-
* **Memory Profile:** **0 Delta heap growth.** (Static allocation via Ring Buffers).
|
|
328
|
-
* **Interaction Latency (INP):** ~56ms during continuous 50-hook concurrency tests (Green Zone).
|
|
329
|
-
* **Drawing Efficiency:** ~15ms drawing cost via Path2D GPU-batching.
|
|
330
|
-
|
|
331
|
-
> 🔍 **Forensic Proof:** Detailed heap snapshots, modulo-tax analysis, and linearized math benchmarks are documented in the [**v0.4.2 Performance RFC (#33)**](https://github.com/liovic/react-state-basis/issues/33).
|
|
332
|
-
|
|
333
|
-
<p align="center">
|
|
334
|
-
<img src="./assets/perf.gif" width="800" alt="Basis Stress Lab" />
|
|
335
|
-
</p>
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
**Production Mode:**
|
|
340
|
-
- Overhead: ~0.01ms per hook call (negligible wrapper overhead)
|
|
341
|
-
- Bundle size: ~2-3 KB minified (no-op wrappers only, no analysis engine)
|
|
342
|
-
- Monitoring logic: 100% removed
|
|
343
|
-
- HUD component: 100% removed
|
|
142
|
+
[**See benchmarks →**](https://github.com/liovic/react-state-basis/wiki/Performance-Forensics)
|
|
344
143
|
|
|
345
144
|
---
|
|
346
145
|
|
|
347
|
-
##
|
|
146
|
+
## Documentation & Theory
|
|
348
147
|
|
|
349
|
-
**
|
|
350
|
-
- ✅ Detecting synchronous redundant state
|
|
351
|
-
- ✅ Flagging effect-driven update chains (A → Effect → B)
|
|
352
|
-
- ✅ Catching infinite loops before browser freeze
|
|
353
|
-
- ✅ Distinguishing causality from redundancy
|
|
354
|
-
|
|
355
|
-
**Known edge cases:**
|
|
356
|
-
- ⚠️ **Async gaps:** Updates delayed by >40ms (e.g., slow API responses) may appear independent
|
|
357
|
-
- ⚠️ **Intentional sync:** Sometimes synchronization is required for library compatibility
|
|
358
|
-
- ⚠️ **Complex multi-way dependencies:** Three or more interconnected states might not show full relationship graph
|
|
359
|
-
- ⚠️ **Requires judgment:** Tool points out patterns worth investigating - you decide if they're issues
|
|
360
|
-
|
|
361
|
-
**Heuristic interpretation requires context. Always verify architectural intent before refactoring.**.
|
|
148
|
+
Basis is built on heuristics inspired by **Linear Algebra** and **Signal Processing**. To understand the underlying math, visit the [**Full Wiki**](https://github.com/liovic/react-state-basis/wiki).
|
|
362
149
|
|
|
363
150
|
---
|
|
364
151
|
|
|
365
152
|
## Roadmap
|
|
366
153
|
|
|
367
|
-
|
|
368
|
-
- [x] **v0.4.0**: Temporal Cross-Correlation Engine (Lead-Lag Analysis)
|
|
369
|
-
- [x] **v0.4.1:** Density Filtering (Eliminate false positives from animations/sliders)
|
|
370
|
-
- [x] v0.4.2: Ring Buffer (Zero-jank memory management for 500+ hooks) [**v0.4.2 Performance RFC (#33)**](https://github.com/liovic/react-state-basis/issues/33)
|
|
154
|
+
Each era of Basis answers a different architectural question:
|
|
371
155
|
|
|
156
|
+
✓ **v0.4.x** - The Correlation Era - *Are these states moving together?*
|
|
157
|
+
→ **v0.5.x** - The Decomposition Era - *Is this local state just a copy of Context?*
|
|
158
|
+
**v0.6.x** - The Graph Era - *Which bug should I fix first for maximum impact?*
|
|
159
|
+
**v0.7.x** - The Information Era - *Does this state carry real information, or is it derivative?*
|
|
160
|
+
**v0.8.x** - The Manifold Era - *How many hooks does your component actually need?*
|
|
372
161
|
|
|
373
|
-
### v0.5.0 (Planned)
|
|
374
|
-
- [ ] Zustand & Redux middleware integration
|
|
375
|
-
- [ ] Visual dependency graph in HUD
|
|
376
|
-
- [ ] Historical trend tracking across sessions
|
|
377
162
|
|
|
378
|
-
|
|
379
|
-
- [ ] Domain isolation analysis (detect feature boundaries)
|
|
380
|
-
- [ ] Export audit reports for team reviews
|
|
381
|
-
- [ ] CI integration for architectural regressions
|
|
382
|
-
|
|
383
|
-
---
|
|
384
|
-
|
|
385
|
-
## FAQ
|
|
386
|
-
|
|
387
|
-
**Q: Will this slow down my app?**
|
|
388
|
-
A: Only in development (~0.3ms per update). Production builds have zero overhead.
|
|
389
|
-
|
|
390
|
-
**Q: Do I have to change my code?**
|
|
391
|
-
A: No. The Babel plugin instruments hooks automatically.
|
|
392
|
-
|
|
393
|
-
**Q: What if it flags something that's not a problem?**
|
|
394
|
-
A: Use your judgment. Basis is a diagnostic tool that points out patterns worth investigating - not all flagged patterns are bugs.
|
|
395
|
-
|
|
396
|
-
**Q: How is this different from Redux DevTools?**
|
|
397
|
-
A: Redux DevTools shows state values and action history. Basis shows temporal relationships between state variables, regardless of what state management library you use.
|
|
398
|
-
|
|
399
|
-
**Q: Why "basis"?**
|
|
400
|
-
A: In linear algebra, a "basis" is a minimal set of independent vectors. The name reflects the goal of finding your app's minimal independent state - removing redundancy.
|
|
401
|
-
|
|
402
|
-
**Q: How is this different from Redux DevTools?**
|
|
403
|
-
A: Redux DevTools is a **Journal** - it logs specific values and actions within a Redux store. Basis is an **Architectural Auditor** - it instruments React's core primitives (useState, useReducer, useEffect) to detect hidden relationships between entirely separate components and hooks, even when they don't share a store. Redux DevTools answers "what changed and why?" while Basis answers "is this state architecture clean?"
|
|
404
|
-
|
|
405
|
-
---
|
|
406
|
-
|
|
407
|
-
## Contributing
|
|
408
|
-
|
|
409
|
-
Found a bug? Have an idea? Open an issue or PR.
|
|
410
|
-
|
|
411
|
-
For technical details on how the detection works, see the [Wiki](https://github.com/liovic/react-state-basis/wiki).
|
|
163
|
+
[**More info**](https://github.com/liovic/react-state-basis/wiki/Roadmap)
|
|
412
164
|
|
|
413
165
|
---
|
|
414
166
|
|
|
415
167
|
<div align="center">
|
|
416
168
|
|
|
417
|
-
Built by [LP](https://github.com/liovic) • MIT License
|
|
169
|
+
Built by [LP](https://github.com/liovic) • [MIT License](https://opensource.org/licenses/MIT)
|
|
418
170
|
|
|
419
|
-
</div>
|
|
171
|
+
</div>
|
package/dist/index.d.mts
CHANGED
|
@@ -3,47 +3,55 @@ import React__default, { ReactNode } from 'react';
|
|
|
3
3
|
export { basis } from './vite-plugin.mjs';
|
|
4
4
|
import 'vite';
|
|
5
5
|
|
|
6
|
-
declare const configureBasis: (c: any) => void;
|
|
7
6
|
/**
|
|
8
|
-
*
|
|
7
|
+
* Standard React Reducer type inference helpers.
|
|
9
8
|
*/
|
|
10
|
-
declare const printBasisHealthReport: (threshold?: number) => void;
|
|
11
|
-
declare const getBasisMetrics: () => {
|
|
12
|
-
engine: string;
|
|
13
|
-
hooks: number;
|
|
14
|
-
load: number;
|
|
15
|
-
analysis_ms: string;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
9
|
type GetReducerState<R extends React.Reducer<any, any>> = R extends React.Reducer<infer S, any> ? S : never;
|
|
19
10
|
type GetReducerAction<R extends React.Reducer<any, any>> = R extends React.Reducer<any, infer A> ? A : never;
|
|
20
11
|
declare function useState<S>(initialState: S | (() => S), label?: string): [S, React.Dispatch<React.SetStateAction<S>>];
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
declare function useReducer<R extends React.Reducer<any, any>, I>(reducer: R,
|
|
12
|
+
/**
|
|
13
|
+
* PUBLIC OVERLOAD: Lazy initialization
|
|
14
|
+
*/
|
|
15
|
+
declare function useReducer<R extends React.Reducer<any, any>, I>(reducer: R, initializerArg: I, initializer: (arg: I) => GetReducerState<R>, label?: string): [GetReducerState<R>, React.Dispatch<GetReducerAction<R>>];
|
|
16
|
+
/**
|
|
17
|
+
* PUBLIC OVERLOAD: Direct initialization
|
|
18
|
+
*/
|
|
19
|
+
declare function useReducer<R extends React.Reducer<any, any>>(reducer: R, initialState: GetReducerState<R>, initializer?: undefined, label?: string): [GetReducerState<R>, React.Dispatch<GetReducerAction<R>>];
|
|
20
|
+
declare function createContext<T>(defaultValue: T, label?: string): React.Context<T>;
|
|
21
|
+
declare function useContext<T>(context: React.Context<T>): T;
|
|
25
22
|
declare function useMemo<T>(factory: () => T, deps: React.DependencyList | undefined, label?: string): T;
|
|
26
|
-
declare function useCallback<T extends (...args:
|
|
23
|
+
declare function useCallback<T extends (...args: unknown[]) => unknown>(callback: T, deps: React.DependencyList, label?: string): T;
|
|
27
24
|
declare function useEffect(effect: React.EffectCallback, deps?: React.DependencyList, label?: string): void;
|
|
28
25
|
declare function useLayoutEffect(effect: React.EffectCallback, deps?: React.DependencyList, label?: string): void;
|
|
29
|
-
declare function
|
|
30
|
-
declare function
|
|
31
|
-
declare const
|
|
26
|
+
declare function useOptimistic<S, P>(passthrough: S, reducer?: (state: S, payload: P) => S, label?: string): [S, (payload: P) => void];
|
|
27
|
+
declare function useActionState<State, Payload>(action: (state: State, payload: Payload) => Promise<State> | State, initialState: State, permalink?: string, label?: string): [state: State, dispatch: (payload: Payload) => void, isPending: boolean];
|
|
28
|
+
declare const useRef: typeof React.useRef;
|
|
29
|
+
declare const useId: typeof React.useId;
|
|
32
30
|
declare const useDebugValue: typeof React.useDebugValue;
|
|
33
31
|
declare const useImperativeHandle: typeof React.useImperativeHandle;
|
|
34
32
|
declare const useInsertionEffect: typeof React.useInsertionEffect;
|
|
35
|
-
declare const useSyncExternalStore:
|
|
36
|
-
declare
|
|
37
|
-
declare
|
|
38
|
-
declare
|
|
33
|
+
declare const useSyncExternalStore: typeof React.useSyncExternalStore;
|
|
34
|
+
declare const useTransition: typeof React.useTransition;
|
|
35
|
+
declare const useDeferredValue: typeof React.useDeferredValue;
|
|
36
|
+
declare const use: <T>(usable: React.Usable<T>) => T;
|
|
39
37
|
|
|
40
38
|
interface BasisProviderProps {
|
|
41
39
|
children: ReactNode;
|
|
42
40
|
debug?: boolean;
|
|
41
|
+
showHUD?: boolean;
|
|
43
42
|
}
|
|
44
43
|
declare const BasisProvider: React__default.FC<BasisProviderProps>;
|
|
45
44
|
declare const useBasisConfig: () => {
|
|
46
45
|
debug: boolean;
|
|
47
46
|
};
|
|
48
47
|
|
|
49
|
-
|
|
48
|
+
declare const configureBasis: (c: any) => void;
|
|
49
|
+
declare const printBasisHealthReport: (threshold?: number) => void;
|
|
50
|
+
declare const getBasisMetrics: () => {
|
|
51
|
+
engine: string;
|
|
52
|
+
hooks: number;
|
|
53
|
+
analysis_ms: string;
|
|
54
|
+
entropy: string;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export { BasisProvider, configureBasis, createContext, getBasisMetrics, printBasisHealthReport, use, useActionState, useBasisConfig, useCallback, useContext, useDebugValue, useDeferredValue, useEffect, useId, useImperativeHandle, useInsertionEffect, useLayoutEffect, useMemo, useOptimistic, useReducer, useRef, useState, useSyncExternalStore, useTransition };
|
package/dist/index.d.ts
CHANGED
|
@@ -3,47 +3,55 @@ import React__default, { ReactNode } from 'react';
|
|
|
3
3
|
export { basis } from './vite-plugin.js';
|
|
4
4
|
import 'vite';
|
|
5
5
|
|
|
6
|
-
declare const configureBasis: (c: any) => void;
|
|
7
6
|
/**
|
|
8
|
-
*
|
|
7
|
+
* Standard React Reducer type inference helpers.
|
|
9
8
|
*/
|
|
10
|
-
declare const printBasisHealthReport: (threshold?: number) => void;
|
|
11
|
-
declare const getBasisMetrics: () => {
|
|
12
|
-
engine: string;
|
|
13
|
-
hooks: number;
|
|
14
|
-
load: number;
|
|
15
|
-
analysis_ms: string;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
9
|
type GetReducerState<R extends React.Reducer<any, any>> = R extends React.Reducer<infer S, any> ? S : never;
|
|
19
10
|
type GetReducerAction<R extends React.Reducer<any, any>> = R extends React.Reducer<any, infer A> ? A : never;
|
|
20
11
|
declare function useState<S>(initialState: S | (() => S), label?: string): [S, React.Dispatch<React.SetStateAction<S>>];
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
declare function useReducer<R extends React.Reducer<any, any>, I>(reducer: R,
|
|
12
|
+
/**
|
|
13
|
+
* PUBLIC OVERLOAD: Lazy initialization
|
|
14
|
+
*/
|
|
15
|
+
declare function useReducer<R extends React.Reducer<any, any>, I>(reducer: R, initializerArg: I, initializer: (arg: I) => GetReducerState<R>, label?: string): [GetReducerState<R>, React.Dispatch<GetReducerAction<R>>];
|
|
16
|
+
/**
|
|
17
|
+
* PUBLIC OVERLOAD: Direct initialization
|
|
18
|
+
*/
|
|
19
|
+
declare function useReducer<R extends React.Reducer<any, any>>(reducer: R, initialState: GetReducerState<R>, initializer?: undefined, label?: string): [GetReducerState<R>, React.Dispatch<GetReducerAction<R>>];
|
|
20
|
+
declare function createContext<T>(defaultValue: T, label?: string): React.Context<T>;
|
|
21
|
+
declare function useContext<T>(context: React.Context<T>): T;
|
|
25
22
|
declare function useMemo<T>(factory: () => T, deps: React.DependencyList | undefined, label?: string): T;
|
|
26
|
-
declare function useCallback<T extends (...args:
|
|
23
|
+
declare function useCallback<T extends (...args: unknown[]) => unknown>(callback: T, deps: React.DependencyList, label?: string): T;
|
|
27
24
|
declare function useEffect(effect: React.EffectCallback, deps?: React.DependencyList, label?: string): void;
|
|
28
25
|
declare function useLayoutEffect(effect: React.EffectCallback, deps?: React.DependencyList, label?: string): void;
|
|
29
|
-
declare function
|
|
30
|
-
declare function
|
|
31
|
-
declare const
|
|
26
|
+
declare function useOptimistic<S, P>(passthrough: S, reducer?: (state: S, payload: P) => S, label?: string): [S, (payload: P) => void];
|
|
27
|
+
declare function useActionState<State, Payload>(action: (state: State, payload: Payload) => Promise<State> | State, initialState: State, permalink?: string, label?: string): [state: State, dispatch: (payload: Payload) => void, isPending: boolean];
|
|
28
|
+
declare const useRef: typeof React.useRef;
|
|
29
|
+
declare const useId: typeof React.useId;
|
|
32
30
|
declare const useDebugValue: typeof React.useDebugValue;
|
|
33
31
|
declare const useImperativeHandle: typeof React.useImperativeHandle;
|
|
34
32
|
declare const useInsertionEffect: typeof React.useInsertionEffect;
|
|
35
|
-
declare const useSyncExternalStore:
|
|
36
|
-
declare
|
|
37
|
-
declare
|
|
38
|
-
declare
|
|
33
|
+
declare const useSyncExternalStore: typeof React.useSyncExternalStore;
|
|
34
|
+
declare const useTransition: typeof React.useTransition;
|
|
35
|
+
declare const useDeferredValue: typeof React.useDeferredValue;
|
|
36
|
+
declare const use: <T>(usable: React.Usable<T>) => T;
|
|
39
37
|
|
|
40
38
|
interface BasisProviderProps {
|
|
41
39
|
children: ReactNode;
|
|
42
40
|
debug?: boolean;
|
|
41
|
+
showHUD?: boolean;
|
|
43
42
|
}
|
|
44
43
|
declare const BasisProvider: React__default.FC<BasisProviderProps>;
|
|
45
44
|
declare const useBasisConfig: () => {
|
|
46
45
|
debug: boolean;
|
|
47
46
|
};
|
|
48
47
|
|
|
49
|
-
|
|
48
|
+
declare const configureBasis: (c: any) => void;
|
|
49
|
+
declare const printBasisHealthReport: (threshold?: number) => void;
|
|
50
|
+
declare const getBasisMetrics: () => {
|
|
51
|
+
engine: string;
|
|
52
|
+
hooks: number;
|
|
53
|
+
analysis_ms: string;
|
|
54
|
+
entropy: string;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export { BasisProvider, configureBasis, createContext, getBasisMetrics, printBasisHealthReport, use, useActionState, useBasisConfig, useCallback, useContext, useDebugValue, useDeferredValue, useEffect, useId, useImperativeHandle, useInsertionEffect, useLayoutEffect, useMemo, useOptimistic, useReducer, useRef, useState, useSyncExternalStore, useTransition };
|