import { subscribe } from '../vanilla'
type Cleanup = () => voidtype WatchGet = <T extends object>(proxyObject: T) => Ttype WatchCallback = (get: WatchGet) => Cleanup | void | undefinedtype WatchOptions = { sync?: boolean}
let currentCleanups: Set<Cleanup> | undefined
export function watch( callback: WatchCallback, options?: WatchOptions): Cleanup { let alive = true const cleanups = new Set<Cleanup>()
type ProxyObject = object type Unsubscribe = () => void const subscriptions = new Map<ProxyObject, Unsubscribe>()
const cleanup = () => { if (alive) { alive = false cleanups.forEach((clean) => clean()) cleanups.clear() subscriptions.forEach((unsubscribe) => unsubscribe()) subscriptions.clear() } }
const revalidate = () => { if (!alive) { return } cleanups.forEach((clean) => clean()) cleanups.clear()
const proxiesToSubscribe = new Set<ProxyObject>()
const parent = currentCleanups currentCleanups = cleanups
try { const cleanupReturn = callback((proxyObject) => { proxiesToSubscribe.add(proxyObject) return proxyObject })
if (cleanupReturn) { cleanups.add(cleanupReturn) } } finally { currentCleanups = parent }
subscriptions.forEach((unsubscribe, proxyObject) => { if (proxiesToSubscribe.has(proxyObject)) { proxiesToSubscribe.delete(proxyObject) } else { subscriptions.delete(proxyObject) unsubscribe() } })
proxiesToSubscribe.forEach((proxyObject) => { const unsubscribe = subscribe(proxyObject, revalidate, options?.sync) subscriptions.set(proxyObject, unsubscribe) }) }
if (currentCleanups) { currentCleanups.add(cleanup) }
revalidate()
return cleanup}