Files
Raúl 53650f3f6b fix(annotate): watch open source files exactly (#1089)
* fix(annotate): watch open source files exactly

* test(annotate): cover atomic watcher saves

* fix(watch): survive atomic file replacement

* fix(watch): disable exact-file coalescing

* fix(watch): track exact file signatures

* fix(annotate): tolerate undefined watcher filenames and harden watch callbacks

The exact-file watcher only treated a `null` filename as "name unavailable".
On Linux, Bun's fs.watch delivers `filename === undefined` for events on the
watched directory itself (chmod/utimes/rename of the parent, as produced by
`tar -x`, `rsync -a`, `cp -a`), so `filename.toString()` threw an uncaught
TypeError and killed the annotate server for every Linux user with a watched
file open.

Widen the guard to `filename == null` (null and undefined) and move the
listener into `createExactFileWatchListener`, whose body is wrapped in
try/catch so no watcher event can ever take the server down. Mirrored in the
Pi runtime, with regression tests in both.

Claude-Session: https://claude.ai/code/session_01H5KQWqXqjrPxyxUNso1QHS

---------

Co-authored-by: Michael Ramos <mdramos8@gmail.com>
2026-07-26 20:28:45 -07:00

29 lines
769 B
TypeScript

import {
dirnameBrowserPath,
normalizeBrowserPath,
} from '@plannotator/shared/browser-paths';
export {
dirnameBrowserPath,
normalizeBrowserPath,
pathIsInsideDir,
} from '@plannotator/shared/browser-paths';
export interface SourceWatchSubscription {
query: string;
dirs: string[];
key: string;
}
export function buildSourceWatchSubscription(paths: string[]): SourceWatchSubscription {
const normalizedPaths = [...new Set(paths.map(normalizeBrowserPath).filter(Boolean))].sort();
const params = new URLSearchParams();
for (const path of normalizedPaths) params.append('filePath', path);
return {
query: params.toString(),
dirs: [...new Set(normalizedPaths.map(dirnameBrowserPath))].sort(),
key: normalizedPaths.join('\n'),
};
}