MCPcopy
hub / github.com/colbymchenry/codegraph / FileWatcher

Class FileWatcher

src/sync/watcher.ts:236–856  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

234 * without blocking on a sync (issue #403)
235 */
236export class FileWatcher {
237 /** macOS/Windows: the single recursive watcher. Null on Linux. */
238 private recursiveWatcher: fs.FSWatcher | null = null;
239 /** Linux: one watcher per watched directory (keyed by absolute path). */
240 private dirWatchers = new Map<string, fs.FSWatcher>();
241 /** Set once the per-directory watch cap is hit, so we log only once. */
242 private dirCapWarned = false;
243 /**
244 * Set once the Linux inotify watch limit (ENOSPC) is hit. Double duty: we
245 * warn only once, AND we stop attempting new directory watches for the rest
246 * of the session — once the kernel budget is exhausted every further
247 * `inotify_add_watch` fails too, so trying the rest of the tree is pure
248 * waste. NON-fatal (does not degrade): installed watches keep working.
249 */
250 private inotifyLimitWarned = false;
251 /**
252 * One-way latch: the reason live watching was permanently disabled at runtime
253 * (watch-resource exhaustion, or lock contention past the retry budget), or
254 * null while healthy. Set by {@link degrade}; cleared only by a fresh start().
255 */
256 private degradedReason: string | null = null;
257 /** Consecutive lock-contention retries for watcher-triggered syncs. */
258 private lockRetryCount = 0;
259 /** Test-only inert mode: started, but with no OS watcher installed. */
260 private inert = false;
261 private debounceTimer: ReturnType<typeof setTimeout> | null = null;
262 /**
263 * Files seen by the watcher since the last successful sync — populated on
264 * every change event, cleared at the start of a sync, and re-populated by
265 * events that arrive mid-sync (or restored on sync failure). Keyed by the
266 * same project-relative POSIX path the rest of the codebase uses, so a
267 * caller can intersect tool-response file paths against this map cheaply.
268 */
269 private pendingFiles = new Map<string, { firstSeenMs: number; lastSeenMs: number }>();
270 /**
271 * Wall-clock ms at which the in-flight sync began. Combined with
272 * {@link pendingFiles}'s `lastSeenMs`, this distinguishes "still in the
273 * debounce window" (lastSeen > syncStarted, sync hasn't started yet for
274 * this edit) from "currently being indexed" (lastSeen <= syncStarted).
275 */
276 private syncStartedMs = 0;
277 private syncing = false;
278 private stopped = false;
279 /**
280 * True once the initial watch set is established. Unlike the previous
281 * chokidar implementation there is no asynchronous initial "crawl" emitting
282 * an `add` per existing file — `fs.watch` only reports changes from the
283 * moment it's installed — so this flips to true synchronously at the end of
284 * `start()`. The startup reconcile against on-disk state is handled
285 * separately by the engine's catch-up sync, not by the watcher.
286 */
287 private ready = false;
288 /**
289 * Callbacks that resolve when the watch set is established. Used by tests
290 * (and any production caller that cares about a clean baseline) to
291 * deterministically gate on watcher readiness.
292 */
293 private readyWaiters: Array<() => void> = [];

Callers

nothing calls this directly

Calls

no outgoing calls

Tested by

no test coverage detected