(eventType: string)
| 472 | }; |
| 473 | |
| 474 | const onEvent = (eventType: string): void => { |
| 475 | if (entry.debounce) clearTimeout(entry.debounce); |
| 476 | entry.debounce = setTimeout(() => { |
| 477 | entry.debounce = null; |
| 478 | let s: fs.Stats; |
| 479 | try { |
| 480 | s = fs.statSync(resolved); |
| 481 | } catch { |
| 482 | return; // gone mid-atomic-write; next rename will re-attach |
| 483 | } |
| 484 | if (s.mtimeMs === entry.lastMtimeMs) return; // spurious / already sent |
| 485 | entry.lastMtimeMs = s.mtimeMs; |
| 486 | enqueueCommand(viewUUID, { type: "file_changed", mtimeMs: s.mtimeMs }); |
| 487 | }, FILE_WATCH_DEBOUNCE_MS); |
| 488 | |
| 489 | // Atomic saves replace the inode — old watcher stops firing. Re-attach. |
| 490 | if (eventType === "rename") { |
| 491 | try { |
| 492 | entry.watcher.close(); |
| 493 | } catch { |
| 494 | /* already closed */ |
| 495 | } |
| 496 | try { |
| 497 | entry.watcher = fs.watch(resolved, onEvent); |
| 498 | } catch { |
| 499 | // File removed, not replaced. Leave closed; pruneStaleQueues cleans up. |
| 500 | } |
| 501 | } |
| 502 | }; |
| 503 | |
| 504 | try { |
| 505 | entry.watcher = fs.watch(resolved, onEvent); |
nothing calls this directly
no test coverage detected
searching dependent graphs…