'use strict'; class RepositoryMonitor { constructor({ store, git, onChange, diagnostics = null }) { this.store = store; this.git = git; this.onChange = onChange; this.diagnostics = diagnostics; this.paths = []; this.fingerprints = new Map(); this.timer = null; this.running = false; this.paused = new Set(); } setPaths(paths) { this.paths = [...new Set((paths || []).filter(Boolean))]; for (const existing of [...this.fingerprints.keys()]) { if (!this.paths.includes(existing)) this.fingerprints.delete(existing); } } pause(localPath) { if (localPath) this.paused.add(localPath); } resume(localPath) { if (localPath) this.paused.delete(localPath); } restart() { this.stop(); if (!this.store.data.preferences.autoRefresh) return; const seconds = Math.min(Math.max(Number(this.store.data.preferences.repositoryPollSeconds) || 4, 2), 60); this.timer = setInterval(() => this.tick().catch((error) => this.diagnostics?.warning('repository-monitor.tick.failed', error)), seconds * 1000); this.timer.unref?.(); } stop() { if (this.timer) clearInterval(this.timer); this.timer = null; } async tick() { if (this.running || !this.paths.length) return; this.running = true; try { const queue = [...this.paths]; const workers = Array.from({ length: Math.min(4, queue.length) }, async () => { while (queue.length) { const localPath = queue.shift(); if (this.paused.has(localPath)) continue; try { const status = await this.git.status(localPath); const next = this.git.statusFingerprint(status); const previous = this.fingerprints.get(localPath); this.fingerprints.set(localPath, next); if (previous && previous !== next) { await this.diagnostics?.debug('repository-monitor.changed', { localPath, head: status.head, branch: status.branch?.head, counts: status.counts }); this.onChange?.({ localPath, status, reason: 'working-tree-changed' }); } } catch (error) { const next = `error:${error.message}`; const previous = this.fingerprints.get(localPath); this.fingerprints.set(localPath, next); if (previous && previous !== next) { await this.diagnostics?.warning('repository-monitor.unavailable', { localPath, message: error.message }); this.onChange?.({ localPath, error: error.message, reason: 'repository-unavailable' }); } } } }); await Promise.all(workers); } finally { this.running = false; } } } module.exports = { RepositoryMonitor };