| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647 |
- import { watch } from "node:fs"
- import type { WorkflowDef } from "./types"
- export class FileWatcher {
- private watchers: Map<string, ReturnType<typeof watch>> = new Map()
- private timers: Map<string, ReturnType<typeof setTimeout>> = new Map()
- watch(def: WorkflowDef, worktree: string, onTrigger: () => void): void {
- if (def.trigger !== "file_change" || !def.paths) return
- const key = def.name
- this.unwatch(key)
- const patterns = def.paths.map((p) => {
- let escaped = p
- .replace(/[.+^${}()|[\]\\]/g, "\\$&") // escape regex metachars
- .replace(/\*\*/g, "\x00") // globstar placeholder
- .replace(/\*/g, "[^/]*") // * → any non-slash
- .replace(/\x00/g, ".*") // globstar placeholder → .*
- .replace(/\?/g, "[^/]") // ? → single char
- return new RegExp("^" + escaped + "$")
- })
- const debounceMs = (def.debounce ?? 60) * 1000
- const watcher = watch(worktree, { recursive: true, persistent: false }, (_event, filename) => {
- if (!filename) return
- if (patterns.some((r) => r.test(filename))) {
- const existing = this.timers.get(key)
- if (existing) clearTimeout(existing)
- this.timers.set(key, setTimeout(onTrigger, debounceMs))
- }
- })
- this.watchers.set(key, watcher)
- }
- unwatch(key: string): void {
- const w = this.watchers.get(key)
- if (w) { w.close(); this.watchers.delete(key) }
- const t = this.timers.get(key)
- if (t) { clearTimeout(t); this.timers.delete(key) }
- }
- stopAll(): void {
- for (const [k] of this.watchers) this.unwatch(k)
- }
- }
|