import type { MaybePromise } from '../types/utils.js' type Callback = ((...args: any[]) => any) | undefined type Callbacks = Record /** @internal */ export const listenersCache: Map = /*#__PURE__*/ new Map() /** @internal */ export const cleanupCache: Map void> = /*#__PURE__*/ new Map() type EmitFunction = ( emit: callbacks ) => MaybePromise void)> let callbackCount = 0 /** * @description Sets up an observer for a given function. If another function * is set up under the same observer id, the function will only be called once * for both instances of the observer. */ export function observe( observerId: string, callbacks: callbacks, fn: EmitFunction ): () => void { const callbackId = ++callbackCount const getListeners = () => listenersCache.get(observerId) || [] const unsubscribe = () => { const listeners = getListeners() listenersCache.set( observerId, listeners.filter((cb: any) => cb.id !== callbackId) ) } const unwatch = () => { const cleanup = cleanupCache.get(observerId) if (getListeners().length === 1 && cleanup) { cleanup() } unsubscribe() } const listeners = getListeners() listenersCache.set(observerId, [ ...listeners, { id: callbackId, fns: callbacks }, ]) if (listeners && listeners.length > 0) { return unwatch } const emit: callbacks = {} as callbacks for (const key in callbacks) { emit[key] = (( ...args: Parameters> ) => { const listeners = getListeners() if (listeners.length === 0) { return } for (const listener of listeners) { listener.fns[key]?.(...args) } }) as callbacks[Extract] } const cleanup = fn(emit) if (typeof cleanup === 'function') { cleanupCache.set(observerId, cleanup) } return unwatch }