Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | 3x 163x 163x 163x 163x | import chokidar from 'chokidar';
import { relative } from 'path';
import { addFileChange } from './db';
// Session-scoped watchers and deduplication
interface WatcherSession {
watcher: chokidar.FSWatcher;
changedFiles: Set<string>;
cwd: string;
timeouts: Set<NodeJS.Timeout>;
}
const sessions = new Map<number, WatcherSession>();
export function startWatcher(sessionId: number, cwd: string): void {
// Don't start duplicate watcher for same session
if (sessions.has(sessionId)) return;
const watcher = chokidar.watch(cwd, {
ignored: /(^|[\/\\])\..|(node_modules|dist|build|\.git)/,
persistent: true,
ignoreInitial: true,
});
const session: WatcherSession = {
watcher,
changedFiles: new Set<string>(),
cwd,
timeouts: new Set<NodeJS.Timeout>(),
};
sessions.set(sessionId, session);
watcher
.on('add', (path) => handleChange(sessionId, path, cwd, 'created'))
.on('change', (path) => handleChange(sessionId, path, cwd, 'modified'))
.on('unlink', (path) => handleChange(sessionId, path, cwd, 'deleted'));
}
export function stopWatcher(sessionId?: number): void {
if (sessionId !== undefined) {
// Stop specific session watcher
const session = sessions.get(sessionId);
Iif (session) {
session.watcher.close();
session.changedFiles.clear();
// Clear all pending timeouts to prevent leaks
for (const timeout of session.timeouts) {
clearTimeout(timeout);
}
session.timeouts.clear();
sessions.delete(sessionId);
}
} else E{
// Legacy: stop all watchers (for backwards compatibility)
for (const [id, session] of sessions.entries()) {
session.watcher.close();
session.changedFiles.clear();
// Clear all pending timeouts to prevent leaks
for (const timeout of session.timeouts) {
clearTimeout(timeout);
}
session.timeouts.clear();
sessions.delete(id);
}
}
}
export function cleanupWatcher(sessionId: number): void {
stopWatcher(sessionId);
}
function handleChange(
sessionId: number,
path: string,
cwd: string,
changeType: 'created' | 'modified' | 'deleted'
): void {
const session = sessions.get(sessionId);
if (!session) return;
const relativePath = relative(cwd, path);
// Deduplicate rapid changes to same file
const key = `${relativePath}-${changeType}`;
if (session.changedFiles.has(key)) return;
session.changedFiles.add(key);
const timeout = setTimeout(() => {
session.changedFiles.delete(key);
session.timeouts.delete(timeout);
}, 3000);
session.timeouts.add(timeout);
addFileChange({
sessionId,
filePath: relativePath,
changeType,
timestamp: new Date().toISOString(),
});
}
|