mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-08 16:39:42 +02:00
refactor: improve code quality with stricter TypeScript and memory leak prevention
- Add stricter TypeScript compiler flags (noUnusedLocals, noUnusedParameters, noImplicitReturns, noImplicitOverride, noFallthroughCasesInSwitch, allowUnreachableCode, allowUnusedLabels) - Remove unused variables caught by stricter flags: - Remove unused `renameSession` destructuring in App.tsx - Remove unused `BG_GRAY` constant in DirectAttach.ts - Remove unused `INPUT_BATCH_INTERVAL` constant in useSessionManager.ts - Add proper EventEmitter cleanup to RalphLoop: - Store bound event handlers for cleanup - Add cleanupEventHandlers() method - Add destroy() method for complete cleanup - Add destroyRalphLoop() singleton cleanup function - Update ralph-loop tests to use destroy() instead of stop() to prevent MaxListenersExceededWarning Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
+49
-8
@@ -72,6 +72,13 @@ export class RalphLoop extends EventEmitter {
|
|||||||
private tasksCompleted: number = 0;
|
private tasksCompleted: number = 0;
|
||||||
private tasksGenerated: number = 0;
|
private tasksGenerated: number = 0;
|
||||||
|
|
||||||
|
/** Bound event handlers for cleanup (prevents memory leaks) */
|
||||||
|
private sessionEventHandlers: {
|
||||||
|
completion: (sessionId: string, phrase: string) => void;
|
||||||
|
error: (sessionId: string, error: string) => void;
|
||||||
|
stopped: (sessionId: string) => void;
|
||||||
|
} | null = null;
|
||||||
|
|
||||||
constructor(options: RalphLoopOptions = {}) {
|
constructor(options: RalphLoopOptions = {}) {
|
||||||
super();
|
super();
|
||||||
this.sessionManager = getSessionManager();
|
this.sessionManager = getSessionManager();
|
||||||
@@ -95,17 +102,32 @@ export class RalphLoop extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private setupEventHandlers(): void {
|
private setupEventHandlers(): void {
|
||||||
this.sessionManager.on('sessionCompletion', (sessionId: string, phrase: string) => {
|
// Store bound handlers for later cleanup
|
||||||
|
this.sessionEventHandlers = {
|
||||||
|
completion: (sessionId: string, phrase: string) => {
|
||||||
this.handleSessionCompletion(sessionId, phrase);
|
this.handleSessionCompletion(sessionId, phrase);
|
||||||
});
|
},
|
||||||
|
error: (sessionId: string, error: string) => {
|
||||||
this.sessionManager.on('sessionError', (sessionId: string, error: string) => {
|
|
||||||
this.handleSessionError(sessionId, error);
|
this.handleSessionError(sessionId, error);
|
||||||
});
|
},
|
||||||
|
stopped: (sessionId: string) => {
|
||||||
this.sessionManager.on('sessionStopped', (sessionId: string) => {
|
|
||||||
this.handleSessionStopped(sessionId);
|
this.handleSessionStopped(sessionId);
|
||||||
});
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
this.sessionManager.on('sessionCompletion', this.sessionEventHandlers.completion);
|
||||||
|
this.sessionManager.on('sessionError', this.sessionEventHandlers.error);
|
||||||
|
this.sessionManager.on('sessionStopped', this.sessionEventHandlers.stopped);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Remove event listeners to prevent memory leaks */
|
||||||
|
private cleanupEventHandlers(): void {
|
||||||
|
if (this.sessionEventHandlers) {
|
||||||
|
this.sessionManager.off('sessionCompletion', this.sessionEventHandlers.completion);
|
||||||
|
this.sessionManager.off('sessionError', this.sessionEventHandlers.error);
|
||||||
|
this.sessionManager.off('sessionStopped', this.sessionEventHandlers.stopped);
|
||||||
|
this.sessionEventHandlers = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get status(): RalphLoopStatus {
|
get status(): RalphLoopStatus {
|
||||||
@@ -427,6 +449,17 @@ export class RalphLoop extends EventEmitter {
|
|||||||
this.minDurationMs = hours * 60 * 60 * 1000;
|
this.minDurationMs = hours * 60 * 60 * 1000;
|
||||||
this.store.setRalphLoopState({ minDurationMs: this.minDurationMs });
|
this.store.setRalphLoopState({ minDurationMs: this.minDurationMs });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys the loop and cleans up all resources.
|
||||||
|
* Use this for complete cleanup (e.g., in tests or before creating a new instance).
|
||||||
|
* After calling destroy(), the instance should not be reused.
|
||||||
|
*/
|
||||||
|
destroy(): void {
|
||||||
|
this.stop();
|
||||||
|
this.cleanupEventHandlers();
|
||||||
|
this.removeAllListeners();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Singleton instance
|
// Singleton instance
|
||||||
@@ -439,3 +472,11 @@ export function getRalphLoop(options?: RalphLoopOptions): RalphLoop {
|
|||||||
}
|
}
|
||||||
return loopInstance;
|
return loopInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Destroys the singleton instance. Use in tests or for cleanup. */
|
||||||
|
export function destroyRalphLoop(): void {
|
||||||
|
if (loopInstance) {
|
||||||
|
loopInstance.destroy();
|
||||||
|
loopInstance = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -122,7 +122,6 @@ export function App(): React.ReactElement {
|
|||||||
cases,
|
cases,
|
||||||
lastUsedCase,
|
lastUsedCase,
|
||||||
toggleRespawn,
|
toggleRespawn,
|
||||||
renameSession,
|
|
||||||
} = useSessionManager();
|
} = useSessionManager();
|
||||||
|
|
||||||
// Calculate terminal height based on stdout dimensions
|
// Calculate terminal height based on stdout dimensions
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ const RESET = `${CSI}0m`;
|
|||||||
const BOLD = `${CSI}1m`;
|
const BOLD = `${CSI}1m`;
|
||||||
const DIM = `${CSI}2m`;
|
const DIM = `${CSI}2m`;
|
||||||
const BG_BLUE = `${CSI}44m`;
|
const BG_BLUE = `${CSI}44m`;
|
||||||
const BG_GRAY = `${CSI}48;5;238m`;
|
|
||||||
const FG_WHITE = `${CSI}37m`;
|
const FG_WHITE = `${CSI}37m`;
|
||||||
const FG_CYAN = `${CSI}36m`;
|
const FG_CYAN = `${CSI}36m`;
|
||||||
const FG_YELLOW = `${CSI}33m`;
|
const FG_YELLOW = `${CSI}33m`;
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ const SCREENS_FILE = join(homedir(), '.claudeman', 'screens.json');
|
|||||||
const INNER_STATE_FILE = join(homedir(), '.claudeman', 'state-inner.json');
|
const INNER_STATE_FILE = join(homedir(), '.claudeman', 'state-inner.json');
|
||||||
const SETTINGS_FILE = join(homedir(), '.claudeman', 'settings.json');
|
const SETTINGS_FILE = join(homedir(), '.claudeman', 'settings.json');
|
||||||
const OUTPUT_POLL_INTERVAL = 300; // Poll terminal output every 300ms (faster refresh)
|
const OUTPUT_POLL_INTERVAL = 300; // Poll terminal output every 300ms (faster refresh)
|
||||||
const INPUT_BATCH_INTERVAL = 16; // Batch input every 16ms (60fps)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emoji to ASCII replacement map for screen hardcopy output.
|
* Emoji to ASCII replacement map for screen hardcopy output.
|
||||||
|
|||||||
@@ -124,7 +124,8 @@ describe('RalphLoop', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
loop.stop();
|
// Use destroy() instead of stop() to clean up event listeners
|
||||||
|
loop.destroy();
|
||||||
vi.useRealTimers();
|
vi.useRealTimers();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+8
-1
@@ -15,7 +15,14 @@
|
|||||||
"declarationMap": true,
|
"declarationMap": true,
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
"jsxImportSource": "react"
|
"jsxImportSource": "react",
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"allowUnreachableCode": false,
|
||||||
|
"allowUnusedLabels": false
|
||||||
},
|
},
|
||||||
"include": ["src/**/*"],
|
"include": ["src/**/*"],
|
||||||
"exclude": ["node_modules", "dist"]
|
"exclude": ["node_modules", "dist"]
|
||||||
|
|||||||
Reference in New Issue
Block a user