feat(watchers): Improve write stabilization and ignore temporary editor files

This commit is contained in:
2025-12-08 15:09:16 +00:00
parent 0dc4eaba52
commit 7a7ee041a3
8 changed files with 94 additions and 36 deletions

View File

@@ -20,10 +20,24 @@ export class NodeWatcher implements IWatcher {
constructor(private options: IWatcherOptions) {
this.writeStabilizer = new WriteStabilizer(
options.stabilityThreshold,
options.pollInterval
options.pollInterval,
options.maxWaitTime
);
}
/**
* Check if a file is a temporary file created by editors
*/
private isTemporaryFile(filePath: string): boolean {
const basename = path.basename(filePath);
// Editor temp files: *.tmp.*, *.swp, *.swx, *~, .#*
if (basename.includes('.tmp.')) return true;
if (basename.endsWith('.swp') || basename.endsWith('.swx')) return true;
if (basename.endsWith('~')) return true;
if (basename.startsWith('.#')) return true;
return false;
}
get isWatching(): boolean {
return this._isWatching;
}
@@ -114,6 +128,11 @@ export class NodeWatcher implements IWatcher {
): Promise<void> {
const fullPath = path.join(basePath, filename);
// Skip temporary files created by editors (atomic saves)
if (this.isTemporaryFile(fullPath)) {
return;
}
// Throttle duplicate events
if (!this.shouldEmit(fullPath, eventType)) {
return;
@@ -132,23 +151,14 @@ export class NodeWatcher implements IWatcher {
this.events$.next({ type: 'addDir', path: fullPath, stats });
}
} else {
// Wait for write to stabilize before emitting
try {
const stableStats = await this.writeStabilizer.waitForWriteFinish(fullPath);
const wasWatched = this.watchedFiles.has(fullPath);
this.watchedFiles.add(fullPath);
this.events$.next({
type: wasWatched ? 'change' : 'add',
path: fullPath,
stats: stableStats
});
} catch {
// File was deleted during stabilization
if (this.watchedFiles.has(fullPath)) {
this.watchedFiles.delete(fullPath);
this.events$.next({ type: 'unlink', path: fullPath });
}
}
// Rename events (atomic saves) don't need stabilization - file is already complete
const wasWatched = this.watchedFiles.has(fullPath);
this.watchedFiles.add(fullPath);
this.events$.next({
type: wasWatched ? 'change' : 'add',
path: fullPath,
stats
});
}
} else {
// File doesn't exist - it was deleted
@@ -162,7 +172,7 @@ export class NodeWatcher implements IWatcher {
}
}
} else if (eventType === 'change') {
// File was modified
// File was modified in-place - use stabilization for streaming writes
if (stats && !stats.isDirectory()) {
try {
const stableStats = await this.writeStabilizer.waitForWriteFinish(fullPath);