state-surgeon
v2.0.1
Published
Time-travel debugging platform that records every state mutation across full-stack JavaScript applications and provides interactive forensic tools to investigate bugs
Maintainers
Readme
🔬 State Surgeon
Time-travel debugging platform for JavaScript applications
State Surgeon records every state mutation across your React, Redux, and Zustand applications, providing interactive forensic tools to investigate bugs.
"Deterministic state timeline reconstruction with surgical debugging capabilities"
Features
- 🎯 Automatic State Capture - Instruments React hooks, Redux stores, and Zustand without code changes
- 🕰️ Time Travel - Scrub through your state history with a visual timeline
- 🔍 State Diff Viewer - Side-by-side comparison of before/after states
- 📊 Mutation Inspector - Detailed view of each state change with call stacks
- 🔗 Causal Chain Analysis - Find the root cause of state corruption
- 🧪 Test Case Generation - Generate test cases from bug timelines (coming soon)
Quick Start
1. Install
npm install state-surgeon2. Start the Recorder Server
import { createRecorderServer } from 'state-surgeon/recorder';
const server = createRecorderServer({
port: 8080,
wsPort: 8081,
debug: true,
});
await server.start();
console.log('State Surgeon Recorder running on http://localhost:8080');3. Instrument Your App
import React from 'react';
import { StateSurgeonClient, instrumentReact } from 'state-surgeon/instrument';
// Create client (connects to recorder)
const client = new StateSurgeonClient({
serverUrl: 'ws://localhost:8081',
appId: 'my-app',
debug: true,
});
// Instrument React hooks
instrumentReact(React);
// Now all useState and useReducer calls are automatically tracked!4. View the Dashboard
Open http://localhost:8080/_surgeon to see your state mutations in real-time.
Usage
React Instrumentation
import React from 'react';
import { instrumentReact } from 'state-surgeon/instrument';
// Instrument React (call once at app startup)
const cleanup = instrumentReact(React);
// Your components work normally
function Counter() {
const [count, setCount] = React.useState(0); // ✅ Automatically tracked!
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
// To stop instrumentation
cleanup();Redux Instrumentation
import { createStore, applyMiddleware } from 'redux';
import { createReduxMiddleware } from 'state-surgeon/instrument';
const stateSurgeonMiddleware = createReduxMiddleware({
storeName: 'main',
ignoreActions: ['@@INIT'], // Optional: ignore certain actions
});
const store = createStore(
rootReducer,
applyMiddleware(stateSurgeonMiddleware)
);Zustand Instrumentation
import { create } from 'zustand';
import { instrumentZustand } from 'state-surgeon/instrument';
const useStore = create(
instrumentZustand(
(set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}),
{ storeName: 'counter' }
)
);API Call Tracking
import { instrumentFetch, instrumentXHR } from 'state-surgeon/instrument';
// Track all fetch calls
const cleanupFetch = instrumentFetch({
captureResponseBody: true,
ignoreUrls: ['/health', '/metrics'],
});
// Track XMLHttpRequest
const cleanupXHR = instrumentXHR();API Reference
Instrument Module
StateSurgeonClient
Main client for connecting to the recorder server.
const client = new StateSurgeonClient({
serverUrl?: string; // WebSocket URL (default: 'ws://localhost:8081')
appId?: string; // Application identifier
sessionId?: string; // Session ID (auto-generated if not provided)
autoConnect?: boolean; // Connect on creation (default: true)
batchSize?: number; // Mutations to batch before sending (default: 50)
flushInterval?: number;// Flush interval in ms (default: 100)
debug?: boolean; // Enable console logging
});instrumentReact(React, options?)
Patches React's useState and useReducer to capture mutations.
const cleanup = instrumentReact(React, {
client?: StateSurgeonClient; // Custom client instance
captureComponentName?: boolean; // Detect component from stack trace
});createReduxMiddleware(options?)
Creates Redux middleware for capturing dispatched actions.
const middleware = createReduxMiddleware({
client?: StateSurgeonClient;
storeName?: string;
ignoreActions?: string[]; // Action types to skip
});Recorder Module
createRecorderServer(options?)
Creates the mutation recording server.
const server = createRecorderServer({
port?: number; // HTTP port (default: 8080)
wsPort?: number; // WebSocket port (default: 8081)
cors?: boolean; // Enable CORS (default: true)
debug?: boolean; // Enable logging
});
await server.start();Dashboard Module
StateSurgeonDashboard
React component for the forensic debugging UI.
import { StateSurgeonDashboard } from 'state-surgeon/dashboard';
function App() {
return <StateSurgeonDashboard serverUrl="http://localhost:8080" />;
}Advanced Usage
Finding State Corruption
import { TimelineReconstructor } from 'state-surgeon/recorder';
const reconstructor = new TimelineReconstructor(store);
// Find exactly when a value became invalid
const corruptedMutation = await reconstructor.findStateCorruption(
sessionId,
(state) => !isNaN(state.cart?.total) // Validation function
);
console.log('State became corrupted at:', corruptedMutation);Comparing Sessions
const { divergencePoint, session1Only, session2Only } =
await reconstructor.compareSessions(workingSessionId, brokenSessionId);
console.log('Sessions diverged at mutation index:', divergencePoint);Finding Mutations for a Path
const mutations = await reconstructor.findMutationsForPath(
sessionId,
'cart.total' // State path
);
console.log('These mutations affected cart.total:', mutations);Architecture
┌─────────────────────────────────────────────────────────────┐
│ Your Application │
├─────────────────────────────────────────────────────────────┤
│ instrumentReact() │ createReduxMiddleware() │ etc. │
│ ↓ ↓ │
│ StateSurgeonClient │
│ ↓ │
│ WebSocket Transport │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Recorder Server │
├─────────────────────────────────────────────────────────────┤
│ WebSocket Handler │ MutationStore │ API Routes │
│ │ │ │
│ ↓ ↓ │
│ TimelineReconstructor │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ Dashboard UI │
├─────────────────────────────────────────────────────────────┤
│ TimelineScrubber │ StateDiffViewer │ MutationInspector │
└─────────────────────────────────────────────────────────────┘Development
# Install dependencies
npm install
# Build
npm run build
# Run tests
npm test
# Run demo
npm run demoLicense
MIT © 2024
