import { createSignal, type Operation, resource } from "effection"; import type { ValueSignal } from "./types.ts"; import { is, Set } from "immutable"; /** * A signal that represents a Set. */ export interface SetSignal extends ValueSignal> { /** * Adds an item to the Set. * @param item - The item to add to the Set. * @returns The Set. */ add(item: T): Set; /** * Removes an item from the Set. * @param item - The item to remove from the Set. * @returns `true` if the item was removed, `false` otherwise. */ delete(item: T): boolean; /** * Returns a new Set with the items that are in the current Set but not in the given iterable. * @param items - The items to remove from the Set. * @returns A new Set with the items that are in the current Set but not in the given iterable. */ difference(items: Iterable): Set; /** * Returns the Set value * @returns The Set. */ valueOf(): Set; } /** * Creates a signal that represents a set. Adding and removing items from the set will * push a new value through the stream. * @param initial - The initial value of the set. * @returns A signal that represents a set. */ export function createSetSignal( initial: Array = [], ): Operation> { return resource(function* (provide) { const signal = createSignal, void>(); const ref = { current: Set.of(...initial) }; function set(value: Iterable) { if (is(ref.current, value)) { return ref.current; } ref.current = Set.of(...value); signal.send(ref.current); return ref.current; } try { yield* provide({ [Symbol.iterator]: signal[Symbol.iterator], set, update(updater) { return set(updater(ref.current)); }, add(item) { ref.current = ref.current.add(item); signal.send(ref.current); return ref.current; }, difference(items) { ref.current = ref.current.subtract(items); signal.send(ref.current); return ref.current; }, delete(item) { if (ref.current.has(item)) { ref.current = ref.current.delete(item); signal.send(ref.current); return true; } return false; }, valueOf() { return ref.current.toSet(); }, }); } finally { signal.close(); } }); }