MCPcopy Create free account
hub / github.com/colbymchenry/codegraph / watchTree

Method watchTree

src/sync/watcher.ts:474–538  ·  view source on GitHub ↗

* Add an inotify watch for `dir` and recurse into its non-ignored * subdirectories. When `markExisting` is true (a directory that appeared * AFTER startup), the source files already inside it are recorded as pending * — this closes the `mkdir + write` race where files created before the new

(dir: string, markExisting: boolean)

Source from the content-addressed store, hash-verified

472 * sync owns the baseline).
473 */
474 private watchTree(dir: string, markExisting: boolean): void {
475 // A degrade() mid-walk (exhaustion on an earlier directory) calls stop(),
476 // which sets `stopped`; bail so the recursion unwinds without adding more
477 // watches to a watcher that is shutting down. `inotifyLimitWarned` does the
478 // same after ENOSPC — the kernel budget is gone, so stop trying the rest of
479 // the tree (every add would fail) while keeping the watches already set.
480 if (this.stopped || this.degradedReason || this.inotifyLimitWarned) return;
481 if (this.dirWatchers.has(dir)) return;
482 if (this.dirWatchers.size >= maxDirWatches()) {
483 if (!this.dirCapWarned) {
484 this.dirCapWarned = true;
485 logWarn('File watcher hit directory-watch cap; remaining subtrees rely on manual/periodic sync', {
486 cap: maxDirWatches(),
487 });
488 }
489 return;
490 }
491
492 let w: fs.FSWatcher;
493 try {
494 w = watchImpl(dir, { persistent: true }, (_event, filename) =>
495 this.handleDirEvent(dir, filename)
496 );
497 } catch (err) {
498 // EMFILE/ENFILE means the PROCESS is out of descriptors — every further
499 // directory would fail too, so degrade the whole watcher rather than
500 // limping along with a partial watch set.
501 if (isWatchResourceExhaustion(err)) {
502 this.degrade(EXHAUSTION_REASON, { error: String(err), dir });
503 } else if (isInotifyWatchExhaustion(err)) {
504 // ENOSPC = inotify watch budget exhausted. NON-fatal: keep the watches
505 // we have and tell the user the knob to raise (warn once).
506 this.warnInotifyLimit({ error: String(err), dir });
507 }
508 // ENOENT / EACCES on a single directory stays non-fatal: skip it quietly.
509 return;
510 }
511 w.on('error', (err: unknown) => {
512 if (isWatchResourceExhaustion(err)) {
513 this.degrade(EXHAUSTION_REASON, { error: String(err), dir });
514 return;
515 }
516 if (isInotifyWatchExhaustion(err)) {
517 this.warnInotifyLimit({ error: String(err), dir });
518 }
519 this.unwatchDir(dir);
520 });
521 this.dirWatchers.set(dir, w);
522
523 let entries: fs.Dirent[];
524 try {
525 entries = fs.readdirSync(dir, { withFileTypes: true });
526 } catch {
527 return;
528 }
529 for (const entry of entries) {
530 const child = path.join(dir, entry.name);
531 if (entry.isDirectory()) {

Callers 2

startPerDirectoryMethod · 0.95
handleDirEventMethod · 0.95

Calls 15

handleDirEventMethod · 0.95
degradeMethod · 0.95
warnInotifyLimitMethod · 0.95
unwatchDirMethod · 0.95
shouldIgnoreDirMethod · 0.95
handleChangeMethod · 0.95
logWarnFunction · 0.90
normalizePathFunction · 0.90
maxDirWatchesFunction · 0.85
isInotifyWatchExhaustionFunction · 0.85
hasMethod · 0.80

Tested by

no test coverage detected