import {Signal} from "../signal.js"
import {accessed} from "./accessed_symbol.js"
import {Collector} from "../../reactor/types.js"
import {debounce} from "../../tools/debounce/debounce.js"
export type LeanTrack = {
lean: true
actor: () => void
}
export type NormalTrack
= {
collector: () => P
responder: ((payload: P) => void) | void
}
export type Track
= LeanTrack | NormalTrack
export class SignalTracker {
#active = true
#all_signals: Set>
#waiters: Set>
#relevant_signals = new Set>()
#stoppers = new Set<() => void>()
constructor({
all_signals,
waiters,
}: {
all_signals: Set>
waiters: Set>
}) {
this.#all_signals = all_signals
this.#waiters = waiters
}
#actuate = debounce(0, (track: Track) => {
if (this.#active) {
if ("lean" in track)
track.actor()
else {
const {payload, recording} = this.observe(track.collector)
this.add_listeners(track, recording)
if (track.responder)
track.responder(payload)
}
}
})
#reset_all_signals_accessed_indicator() {
for (const signal of this.#all_signals)
signal[accessed] = false
}
get #signals_that_should_be_tracked() {
return [...this.#all_signals].filter(signal => (
signal[accessed] &&
!this.#relevant_signals.has(signal)
))
}
observe(collector: Collector
) {
this.#reset_all_signals_accessed_indicator()
const payload = collector()
return {
payload,
recording: this.#signals_that_should_be_tracked,
}
}
add_listeners
(track: Track
, recording: Signal[]) {
for (const signal of recording) {
this.#relevant_signals.add(signal)
this.#stoppers.add(
signal.on(
() => this.#waiters.add(this.#actuate(track))
)
)
}
}
shutdown() {
this.#active = false
this.#stoppers.forEach(stop => stop())
}
}