/ utils / signal.ts
signal.ts
 1  /**
 2   * Tiny listener-set primitive for pure event signals (no stored state).
 3   *
 4   * Collapses the ~8-line `const listeners = new Set(); function subscribe(){…};
 5   * function notify(){for(const l of listeners) l()}` boilerplate that was
 6   * duplicated ~15× across the codebase into a one-liner.
 7   *
 8   * Distinct from a store (AppState, createStore) — there is no snapshot, no
 9   * getState. Use this when subscribers only need to know "something happened",
10   * optionally with event args, not "what is the current value".
11   *
12   * Usage:
13   *   const changed = createSignal<[SettingSource]>()
14   *   export const subscribe = changed.subscribe
15   *   // later: changed.emit('userSettings')
16   */
17  
18  export type Signal<Args extends unknown[] = []> = {
19    /** Subscribe a listener. Returns an unsubscribe function. */
20    subscribe: (listener: (...args: Args) => void) => () => void
21    /** Call all subscribed listeners with the given arguments. */
22    emit: (...args: Args) => void
23    /** Remove all listeners. Useful in dispose/reset paths. */
24    clear: () => void
25  }
26  
27  export function createSignal<Args extends unknown[] = []>(): Signal<Args> {
28    const listeners = new Set<(...args: Args) => void>()
29    return {
30      subscribe(listener) {
31        listeners.add(listener)
32        return () => {
33          listeners.delete(listener)
34        }
35      },
36      emit(...args) {
37        for (const listener of listeners) listener(...args)
38      },
39      clear() {
40        listeners.clear()
41      },
42    }
43  }