/** * A minimal, type-safe reactive signal with zero dependencies. * * This is the seed of the framework's reactivity. Today it powers nothing on * its own, but it is shaped so client islands (and later the `.wrn` compiler's * `state` blocks) can build reactive bindings on top of it. * * const count = signal(0) * count.get() // 0 * count.set(1) // notifies subscribers * const off = count.subscribe(v => console.log(v)) * off() // unsubscribe */ export type Subscriber = (value: T) => void; export type Unsubscribe = () => void; export interface Signal { /** Read the current value. */ get(): T; /** Write a new value; subscribers run only when the value actually changes. */ set(next: T): void; /** Apply a function to the current value. */ update(fn: (current: T) => T): void; /** Subscribe to changes; returns an unsubscribe function. */ subscribe(fn: Subscriber): Unsubscribe; } export function signal(initial: T): Signal { let value = initial; const subscribers = new Set>(); return { get(): T { return value; }, set(next: T): void { if (Object.is(next, value)) return; // skip no-op updates value = next; // Iterate a copy so a subscriber may unsubscribe during notification. for (const fn of [...subscribers]) fn(value); }, update(fn: (current: T) => T): void { this.set(fn(value)); }, subscribe(fn: Subscriber): Unsubscribe { subscribers.add(fn); return () => { subscribers.delete(fn); }; }, }; }