Internals: Linked-List Subscriber System
SoulState manages subscribers using a Doubly Linked List to achieve $O(1)$ addition and removal complexity. This is critical for high-churn user interfaces where components frequently mount and unmount.
The Subscription Node
Every subscription (granular or global) is represented by a node in the list.
// src/core/subscriptions.ts (extends ReactiveNode from graph.ts)
export interface SubscriptionNode<S> extends ReactiveNode {
selector: (state: any) => S;
listener: (curr: S, prev: S) => void;
equalityFn: (a: S, b: S) => boolean;
lastSelectedState: S;
fastKey: DependencyKey | null;
next: SubscriptionNode<any> | null;
prev: SubscriptionNode<any> | null;
}
O(1) Teardown Optimization
Unlike libraries that use arrays (which require $O(N)$ scanning to remove an item), SoulState's teardown is a direct pointer manipulation.
// Simplified teardown logic
const { prev, next } = node;
if (prev) prev.next = next;
else this.head = next;
if (next) next.prev = prev;
else this.tail = prev;
This ensures that even with 100,000 subscribers, unmounting a component is an O(1) pointer operation.
Listener Iteration Strategy
SoulState maintains two separate lists:
- Global Subscribers: Listeners that subscribe to the entire state.
- Granular Subscribers: Listeners that are managed by the Invalidation Graph.
Surgical Dispatch
When an update occurs:
- The Invalidation Graph directly identifies only the granular nodes affected by the change.
- The SubscriptionManager iterates through the global list and notifies them.
This separation ensures that granular updates don't pay the cost of iterating over unrelated listeners, maintaining the $O(M)$ propagation complexity that defines SoulState.
High-Churn Performance
The combination of linked-list management and graph-based dispatch makes SoulState the ideal runtime for dynamic, data-heavy applications where component lifecycles are frequent and unpredictable.