@xaendar/signals 0.1.0 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xaendar/signals",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "A library for managing signals in web applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./xaendar-signals.es.js",
|
|
@@ -8,12 +8,14 @@
|
|
|
8
8
|
"types": "./xaendar-signals.es.d.ts",
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
11
|
-
"import":
|
|
12
|
-
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./xaendar-signals.es.d.ts",
|
|
13
|
+
"default": "./xaendar-signals.es.js"
|
|
14
|
+
}
|
|
13
15
|
}
|
|
14
16
|
},
|
|
15
17
|
"peerDependencies": {},
|
|
16
18
|
"dependencies": {
|
|
17
|
-
"@xaendar/common": "
|
|
19
|
+
"@xaendar/common": "0.1.6"
|
|
18
20
|
}
|
|
19
|
-
}
|
|
21
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"xaendar-signals.es.js","names":["#sources","#sinks","#callback","#equals","#state","#computeValue","#findDeepestStale","#value","#isValidTransition","#setValue","#propagateClean","#isErrorValue","#sinks","#value","#equals","#watched","#unwatched","#signals","#state","#notifyCallback","#isValidTransition"],"sources":["../../../packages/signals/src/lib/globals.ts","../../../packages/signals/src/lib/private-symbol.ts","../../../packages/signals/src/lib/models/computed.ts","../../../packages/signals/src/lib/models/state.ts","../../../packages/signals/src/lib/models/watcher.ts","../../../packages/signals/src/lib/subtle.ts","../../../packages/signals/src/lib/load-signals.ts"],"sourcesContent":["import { Stack } from '@xaendar/common';\r\nimport { Computed } from './models/computed';\r\nimport { GlobalState } from './types/global-state.type';\r\n\r\n/**\r\n * Global state for the signals runtime.\r\n * Tracks the currently computing `Computed` instance, whether the state is frozen,\r\n * and the current generation counter.\r\n *\r\n * @internal\r\n */\r\nexport const GLOBAL_STATE: GlobalState = {\r\n computing: null,\r\n frozen: false\r\n};\r\n\r\nconst computingStack = new Stack<Computed>;\r\n\r\n/**\r\n * Pushes a `Computed` instance onto the computing stack and sets it as the\r\n * currently active computation in `GLOBAL_STATE`.\r\n *\r\n * Should be called before executing a computed function to register\r\n * dependency tracking.\r\n *\r\n * @param computed - The `Computed` instance entering the computation phase.\r\n * @internal\r\n */\r\nexport function pushComputed(computed: Computed): void {\r\n computingStack.push(computed);\r\n GLOBAL_STATE.computing = computed;\r\n}\r\n\r\n/**\r\n * Pops the most recent `Computed` instance from the computing stack and\r\n * restores the previous one as the active computation in `GLOBAL_STATE`.\r\n *\r\n * Should be called after a computed function finishes executing.\r\n *\r\n * @internal\r\n */\r\nexport function popComputed(): void {\r\n computingStack.pop();\r\n GLOBAL_STATE.computing = computingStack[computingStack.length - 1] ?? null;\r\n}","/**\r\n * Symbol not available in public API,\r\n * used to call internal methods of `State` and `Computed` from `Watcher` without exposing them in the public API.\r\n *\r\n * @internal\r\n */\r\nexport const PRIVATE = Symbol('signals-private');\r\n\r\n/**\r\n * Asserts that the provided symbol matches the internal {@link PRIVATE} symbol,\r\n * ensuring the caller has access to internal APIs.\r\n *\r\n * Throws if the symbol does not match, preventing external code from\r\n * invoking methods intended for internal use only.\r\n *\r\n * @param symbol - The symbol to validate against {@link PRIVATE}.\r\n * @throws {Error} If `symbol` does not match {@link PRIVATE}.\r\n * @internal\r\n */\r\nexport function assertPrivateContext(symbol: symbol): void {\r\n if (symbol !== PRIVATE) {\r\n throw new Error('Invalid symbol');\r\n }\r\n}","\r\nimport { GLOBAL_STATE, pushComputed, popComputed } from '../globals';\r\nimport { PRIVATE, assertPrivateContext } from '../private-symbol';\r\nimport { ComputedState } from '../types/computed-state.type';\r\nimport { SignalEqual } from '../types/signal-equal.type';\r\nimport { SignalOptions } from '../types/signal-options.type';\r\nimport { State } from './state';\r\nimport { Watcher } from './watcher';\r\n\r\n/**\r\n * A read-only Signal whose value is derived lazily from other Signals.\r\n *\r\n * The value is recomputed only when explicitly read and only if one or more\r\n * of its (recursive) dependencies have changed since the last evaluation.\r\n * The result is cached and reused until the Signal becomes stale again.\r\n *\r\n * @template T The type of the computed value.\r\n *\r\n * @see Signal algorithms — \"The Signal.Computed class\"\r\n */\r\nexport class Computed<T = any> {\r\n /**\r\n * The current value of the signal.\r\n *\r\n * Uninitialised (`!`) until the first evaluation. After that, holds either\r\n * the return value of `#callback` or a boxed error\r\n * `{ isError: true; value: Error }` if the last evaluation threw.\r\n *\r\n * @internalSlot\r\n * @see Signal algorithms — \"Signal.Computed internal slots\"\r\n */\r\n #value!: T | { isError: true; value: Error };\r\n /**\r\n * The current evaluation state of this Signal.\r\n *\r\n * - `~dirty~` — value is known to be stale or has never been evaluated.\r\n * - `~checked~` — an indirect source changed; may or may not be stale.\r\n * - `~computing~` — `#callback` is currently executing; guards against cycles.\r\n * - `~clean~` — cached value is up-to-date.\r\n *\r\n * @internalSlot\r\n * @see Signal algorithms — \"Signal.Computed State machine\"\r\n */\r\n #state: ComputedState;\r\n /**\r\n * The ordered set of Signals read during the last evaluation of\r\n * `#callback`. Cleared and rebuilt on every re-evaluation so that\r\n * conditional branches that are no longer taken stop being tracked.\r\n *\r\n * May contain both `State` and `Computed` instances.\r\n *\r\n * @internalSlot\r\n * @see Signal algorithms — \"Signal.Computed internal slots\"\r\n */\r\n #sources: Set<State<unknown> | Computed<unknown>>;\r\n /**\r\n * Returns a snapshot of the current sources set for introspection.\r\n *\r\n * @param symbol - Private access symbol; rejects calls from outside the library.\r\n * @returns An array of `State` and `Computed` instances that this Signal depends on.\r\n * @internal\r\n */\r\n public getSources(symbol: symbol): (State<unknown> | Computed<unknown>)[] {\r\n assertPrivateContext(symbol);\r\n return [...this.#sources];\r\n }\r\n /**\r\n * The set of Signals and Watchers that directly depend on this Signal.\r\n *\r\n * Populated only when this Signal is reachable from at least one active\r\n * `Watcher`. An un-watched `Computed` has an empty sinks set, which allows\r\n * it to be garbage-collected independently from the rest of the graph.\r\n *\r\n * @internalSlot\r\n * @see Signal algorithms — \"Signal.Computed internal slots\"\r\n * @see Method — `Signal.Computed.prototype.get` (NOTE on sinks)\r\n */\r\n #sinks: Set<Computed<unknown> | Watcher>;\r\n /**\r\n * Returns a snapshot of the current sinks set for introspection.\r\n *\r\n * @param symbol - Private access symbol; rejects calls from outside the library.\r\n * @returns An array of `Computed` and `Watcher` instances that depend on this Signal.\r\n * @internal\r\n */\r\n public getSinks(symbol: symbol): (Computed<unknown> | Watcher)[] {\r\n assertPrivateContext(symbol);\r\n return [...this.#sinks];\r\n }\r\n /**\r\n * The equality function used to determine whether a newly computed value\r\n * is meaningfully different from the previously cached one.\r\n *\r\n * Called as `equals.call(computed, oldValue, newValue)`. Returns `true` if\r\n * the values are considered equal, in which case no downstream propagation\r\n * occurs. Defaults to `Object.is` when not provided via options.\r\n *\r\n * If this function throws, the exception is cached as the Signal's value\r\n * and the outcome is treated as `~dirty~`.\r\n *\r\n * @internalSlot\r\n * @see Signal algorithms — \"Signal.Computed internal slots\"\r\n * @see Algorithm — \"Set Signal value\"\r\n */\r\n #equals: SignalEqual<T>;\r\n /**\r\n * The pure function that produces this Signal's value. Evaluated lazily\r\n * whenever the Signal is read while in a `~dirty~` or `~checked~` state.\r\n *\r\n * Called with `this` bound to the `Computed` instance itself so that\r\n * internal methods (e.g. `addSource`) are accessible if needed.\r\n * Any exception thrown by this function is caught and cached.\r\n *\r\n * @internalSlot\r\n * @see Signal algorithms — \"Signal.Computed internal slots\"\r\n */\r\n #callback: (this: Computed<T>) => T;\r\n\r\n /**\r\n * Creates a new `Computed` signal.\r\n *\r\n * The Signal starts in the `~dirty~` state with an uninitialised value, so\r\n * `#callback` will be invoked on the first `get()`.\r\n *\r\n * @param cb - Pure function evaluated lazily to produce the value.\r\n * Receives the `Computed` instance as `this`.\r\n * @param options - Optional configuration:\r\n * - `equals` — custom equality function; defaults to `Object.is`.\r\n *\r\n * @see Signal algorithms — \"Signal.Computed Constructor\"\r\n */\r\n constructor(cb: (this: Computed<T>) => T, options?: SignalOptions<T>) {\r\n this.#callback = cb;\r\n this.#equals = options?.equals ?? Object.is;\r\n this.#sources = new Set;\r\n this.#sinks = new Set;\r\n this.#state = 'dirty';\r\n }\r\n\r\n /**\r\n * Returns the current value of this Signal, re-evaluating `#callback` if\r\n * the cached value may be stale.\r\n *\r\n * Registers this Signal as a source of any outer `Computed` currently\r\n * being evaluated (automatic dependency tracking).\r\n *\r\n * If the state is `~dirty~` or `~checked~`, walks the source graph\r\n * depth-first to find and recalculate the deepest stale `Computed` first,\r\n * then re-checks upward until this Signal is `~clean~`.\r\n *\r\n * @returns The current computed value, or a boxed error object if the last\r\n * evaluation threw.\r\n * @throws If `frozen` is `true`.\r\n * @throws If the Signal is in the `~computing~` state (cyclic dependency).\r\n *\r\n * @see Signal algorithms — \"Method: Signal.Computed.prototype.get\"\r\n */\r\n public get(): T | { isError: true; value: Error } {\r\n if (GLOBAL_STATE.frozen) {\r\n throw new Error('Cannot get value of a Computed signal while the global state is frozen');\r\n }\r\n\r\n if (this.#state === 'computing') {\r\n throw new Error('Circular dependency detected while computing a Computed signal');\r\n }\r\n\r\n GLOBAL_STATE.computing?.addSource(this, PRIVATE);\r\n\r\n if (this.#sinks.size === 0) {\r\n this.#computeValue();\r\n } else if (this.#state === 'dirty' || this.#state === 'checked') {\r\n while (this.#state === 'dirty' || this.#state === 'checked') {\r\n const deepest = this.#findDeepestStale();\r\n deepest.#computeValue();\r\n }\r\n }\r\n\r\n return this.#value;\r\n }\r\n\r\n /**\r\n * Registers a Signal as a source of this `Computed`, discovered during the\r\n * execution of `#callback`.\r\n *\r\n * If this `Computed` is currently being watched (has at least one sink),\r\n * the source is also informed of this Signal as a new sink, building the\r\n * live push-notification chain upward.\r\n *\r\n * @param source - The Signal read during evaluation.\r\n * @param symbol - Private access symbol; rejects calls from outside the library.\r\n * @internal\r\n */\r\n public addSource(source: State | Computed, symbol: symbol) {\r\n assertPrivateContext(symbol);\r\n this.#sources.add(source);\r\n source.addSink(this, PRIVATE)\r\n }\r\n\r\n /**\r\n * Returns the current evaluation state of this Signal.\r\n *\r\n * @param symbol - Private access symbol; rejects calls from outside the library.\r\n * @internal\r\n * @see Signal algorithms — \"Signal.Computed State machine\"\r\n */\r\n public getState(symbol: symbol): ComputedState {\r\n assertPrivateContext(symbol);\r\n return this.#state;\r\n }\r\n\r\n /**\r\n * Transitions this Signal to a new evaluation state.\r\n *\r\n * Only valid transitions (as defined by the state machine) are allowed.\r\n * Invalid transitions throw an error.\r\n *\r\n * @param newState - The target state.\r\n * @param symbol - Private access symbol; rejects calls from outside the library.\r\n * @throws If the transition from the current state to `newState` is not allowed.\r\n * @internal\r\n * @see Signal algorithms — \"Signal.Computed State machine\"\r\n */\r\n public setState(newState: ComputedState, symbol: symbol): void {\r\n assertPrivateContext(symbol);\r\n\r\n if (this.#state !== newState && this.#isValidTransition(this.#state, newState)) {\r\n this.#state = newState;\r\n }\r\n }\r\n\r\n /**\r\n * Registers a new sink (a `Computed` or `Watcher` that directly depends on\r\n * this Signal) in the internal sinks set.\r\n *\r\n * If this is the first sink, propagates the sink registration recursively\r\n * up through `#sources`, building the live dependency chain that enables\r\n * push-based invalidation.\r\n *\r\n * @param sink - The dependent node to register.\r\n * @param symbol - Private access symbol; rejects calls from outside the library.\r\n * @internal\r\n */\r\n public addSink(sink: Computed<unknown> | Watcher, symbol: symbol) {\r\n assertPrivateContext(symbol);\r\n if (this.#sinks.size === 0) {\r\n this.#sources.forEach(source => source.addSink(this, PRIVATE));\r\n }\r\n this.#sinks.add(sink);\r\n }\r\n\r\n /**\r\n * Removes a sink from the internal sinks set.\r\n *\r\n * If the sinks set becomes empty after removal, propagates the removal\r\n * recursively up through `#sources`, tearing down the live dependency\r\n * chain and allowing garbage collection of un-watched nodes.\r\n *\r\n * @param sink - The dependent node to remove.\r\n * @param symbol - Private access symbol; rejects calls from outside the library.\r\n * @internal\r\n */\r\n public removeSink(sink: Computed | Watcher, symbol: symbol) {\r\n assertPrivateContext(symbol);\r\n this.#sinks.delete(sink);\r\n\r\n if (this.#sinks.size === 0) {\r\n this.#sources.forEach(source => source.removeSink(this, PRIVATE));\r\n }\r\n }\r\n\r\n /**\r\n * Recursively walks the source graph depth-first to find the deepest,\r\n * left-most `Computed` node that is in a `~dirty~` or `~checked~` state.\r\n *\r\n * This ensures that recalculation always starts from the bottom of the\r\n * dependency graph, so every node sees already-updated dependencies —\r\n * the core of glitch-free evaluation.\r\n *\r\n * Cuts off the search when hitting a `~clean~` `Computed` source, since\r\n * its subtree is guaranteed to be up-to-date.\r\n *\r\n * @returns The deepest stale `Computed` found, or `node` itself if none of\r\n * its sources are stale.\r\n */\r\n #findDeepestStale(): Computed {\r\n const unclearNode = [...this.#sources].find((source): source is Computed => source instanceof Computed && (source.#state === 'dirty' || source.#state === 'checked'));\r\n return unclearNode ? unclearNode.#findDeepestStale() : this;\r\n }\r\n\r\n /**\r\n * Executes `#callback` to recompute this Signal's value.\r\n *\r\n * Implements the \"recalculate dirty computed Signal\" algorithm:\r\n * 1. Clears stale sources and removes this Signal from their sinks.\r\n * 2. Sets `computing` to this Signal for automatic dependency tracking.\r\n * 3. Runs the callback, caching the return value or any thrown exception.\r\n * 4. Restores the previous `computing` value.\r\n * 5. Runs the \"set Signal value\" algorithm to detect value changes.\r\n * 6. Transitions state to `~clean~`.\r\n * 7. Propagates `~dirty~` to sinks (or attempts `~clean~` if value unchanged).\r\n *\r\n * @see Signal algorithms — \"Algorithm: recalculate dirty computed Signal\"\r\n */\r\n #computeValue(): void {\r\n this.#sources.forEach(source => source.removeSink(this, PRIVATE));\r\n this.#sources.clear();\r\n\r\n pushComputed(this);\r\n this.#state = 'computing';\r\n\r\n let newValue: T | { isError: true; value: Error };\r\n\r\n try {\r\n newValue = this.#callback.call(this);\r\n } catch (error) {\r\n newValue = { isError: true, value: error as Error };\r\n } finally {\r\n popComputed();\r\n }\r\n\r\n const outcome = this.#setValue(newValue);\r\n\r\n outcome === 'dirty'\r\n ? this.#sinks.forEach(sink => sink instanceof Computed ? sink.setState('dirty', PRIVATE) : sink.notify(PRIVATE))\r\n : this.#propagateClean();\r\n\r\n this.#state = 'clean';\r\n }\r\n\r\n /**\r\n * Implements the \"set Signal value\" algorithm.\r\n *\r\n * Compares the new value against the cached one using `#equals`. If equal,\r\n * returns `~clean~` and leaves `#value` untouched. Otherwise updates\r\n * `#value` and returns `~dirty~`.\r\n *\r\n * Special cases:\r\n * - If `newValue` is a boxed error, `#equals` is skipped and the error is\r\n * cached directly.\r\n * - If `#equals` itself throws, the exception is cached as a boxed error\r\n * and the outcome is `~dirty~`.\r\n *\r\n * @param newValue - The value (or boxed error) produced by `#callback`.\r\n * @returns `~clean~` if the value is unchanged, `~dirty~` otherwise.\r\n *\r\n * @see Signal algorithms — \"Set Signal value algorithm\"\r\n */\r\n #setValue(newValue: T | { isError: true; value: Error }): 'clean' | 'dirty' {\r\n const oldValue = this.#value;\r\n\r\n /*\r\n If new value is an error we always update without calling equals\r\n */\r\n if (this.#isErrorValue(newValue)) {\r\n this.#value = newValue;\r\n return 'dirty';\r\n }\r\n\r\n try {\r\n if (!this.#isErrorValue(oldValue) && this.#equals.call(this, oldValue, newValue)) {\r\n return 'clean';\r\n }\r\n } catch (equalsError) {\r\n this.#value = { isError: true, value: equalsError as Error };\r\n return 'dirty';\r\n }\r\n\r\n this.#value = newValue;\r\n return 'dirty';\r\n }\r\n\r\n /**\r\n * Recursively marks `~checked~` sinks as `~clean~` when all of their\r\n * immediate sources are already `~clean~`.\r\n *\r\n * Called after a recalculation that produced an unchanged value (`~clean~`\r\n * outcome from `#setValue`). Propagates the clean signal upward through\r\n * the graph so that Computed nodes that were only transitively dirty — and\r\n * whose dependencies have not actually changed — are not needlessly\r\n * re-evaluated on the next read.\r\n *\r\n * @see Signal algorithms — \"Algorithm: recalculate dirty computed Signal\"\r\n */\r\n #propagateClean(): void {\r\n [...this.#sinks]\r\n .filter((sink): sink is Computed<unknown> => sink instanceof Computed && sink.#state === 'checked')\r\n .forEach(sink => {\r\n const allSourcesClean = [...sink.#sources].every(source => !(source instanceof Computed) || source.#state === 'clean');\r\n if (allSourcesClean) {\r\n sink.#state = 'clean';\r\n sink.#propagateClean();\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * Type guard that checks whether a value is a boxed error object.\r\n *\r\n * Used to distinguish a legitimately computed value from a cached\r\n * exception produced by `#callback` or `#equals`.\r\n *\r\n * @param value - The value to inspect.\r\n * @returns `true` if `value` is `{ isError: true; value: Error }`.\r\n */\r\n #isErrorValue(value: unknown): value is { isError: true; value: Error } {\r\n return typeof value === 'object' && !!value && 'isError' in value;\r\n }\r\n\r\n #isValidTransition(from: ComputedState, to: ComputedState): boolean {\r\n switch (from) {\r\n case 'checked':\r\n return to === 'clean' || to === 'dirty';\r\n case 'clean':\r\n return to === 'checked' || to === 'dirty';\r\n case 'dirty':\r\n return to === 'computing';\r\n case 'computing':\r\n return to === 'clean';\r\n }\r\n }\r\n}","import { NoArgsVoidFunction } from '@xaendar/common';\r\nimport { GLOBAL_STATE } from '../globals';\r\nimport { PRIVATE, assertPrivateContext } from '../private-symbol';\r\nimport { SignalEqual } from '../types/signal-equal.type';\r\nimport { SignalOptions } from '../types/signal-options.type';\r\nimport { Computed } from './computed';\r\nimport { Watcher } from './watcher';\r\n\r\nexport class State<T = any> {\r\n /**\r\n * The current value of the signal.\r\n *\r\n * Initialised to `initialValue` in the constructor and updated by `set`\r\n * whenever the new value is not equal to the current one according to\r\n * `#equals`.\r\n *\r\n * @internalSlot\r\n * @see Signal algorithms — 'Signal.State internal slots'\r\n */\r\n #value: T;\r\n /**\r\n * The equality function used to determine whether a new value is\r\n * meaningfully different from the current one.\r\n *\r\n * Called as `equals.call(signal, oldValue, newValue)`. If it returns\r\n * `true` the signal is considered unchanged and no propagation occurs.\r\n * Defaults to `Object.is` when not provided via options.\r\n *\r\n * @internalSlot\r\n * @see Signal algorithms — 'Signal.State internal slots'\r\n * @see Algorithm — 'Set Signal value'\r\n */\r\n #equals: SignalEqual<T>\r\n /**\r\n * Optional callback invoked (with `frozen = true`) the first time this\r\n * Signal gains a sink — i.e. when it transitions from un-observed to\r\n * observed by at least one `Watcher` (directly or transitively).\r\n *\r\n * @internalSlot\r\n * @see Signal algorithms — 'Signal.State internal slots'\r\n * @see Method — `Signal.subtle.Watcher.prototype.watch`\r\n */\r\n #watched?: NoArgsVoidFunction;\r\n /**\r\n * Optional callback invoked (with `frozen = true`) when this Signal loses\r\n * its last sink — i.e. when it transitions from observed back to\r\n * un-observed.\r\n *\r\n * @internalSlot\r\n * @see Signal algorithms — 'Signal.State internal slots'\r\n * @see Method — `Signal.subtle.Watcher.prototype.unwatch`\r\n */\r\n #unwatched?: NoArgsVoidFunction;\r\n /**\r\n * The set of watched signals that directly depend on this one.\r\n *\r\n * Populated only when this Signal is reachable from at least one active\r\n * `Watcher` — un-watched Signals have an empty sinks set, which allows\r\n * them to be garbage-collected independently from the rest of the graph.\r\n *\r\n * Should contain both `Computed` and `Watcher` instances, as both can be\r\n * direct dependents of a `State`.\r\n *\r\n * @internalSlot\r\n * @see Signal algorithms — 'Signal.State internal slots'\r\n * @see Method — `Signal.State.prototype.get` (NOTE on sinks)\r\n */\r\n #sinks: Set<Computed<unknown> | Watcher>;\r\n /**\r\n * Returns a snapshot of the current sinks set for introspection.\r\n *\r\n * @param symbol - Private access symbol; rejects calls from outside the library.\r\n * @returns An array of `Computed` and `Watcher` instances that depend on this Signal.\r\n * @internal\r\n */\r\n public getSinks(symbol: symbol): (Computed<unknown> | Watcher)[] {\r\n assertPrivateContext(symbol);\r\n return [...this.#sinks];\r\n }\r\n\r\n /**\r\n * Creates a new `State` signal.\r\n *\r\n * @param initialValue - The initial value of the signal.\r\n * @param options - Optional configuration:\r\n * - `equals` — custom equality function; defaults to `Object.is`.\r\n * - `watched` — called when the signal gains its first sink.\r\n * - `unwatched` — called when the signal loses its last sink.\r\n *\r\n * @see Signal algorithms — 'Constructor: Signal.State(initialValue, options)'\r\n */\r\n constructor(initialValue: T, options?: SignalOptions<T>) {\r\n this.#value = initialValue;\r\n this.#equals = options?.equals ?? Object.is;\r\n this.#watched = options?.watched;\r\n this.#unwatched = options?.unwatched;\r\n this.#sinks = new Set;\r\n }\r\n\r\n /**\r\n * Returns the current value of the signal, registering this Signal as a\r\n * source of the innermost `Computed` currently being evaluated (if any).\r\n *\r\n * @throws If `frozen` is `true` — reads are forbidden while a protected\r\n * callback (`notify`, `watched`, `unwatched`) is executing.\r\n *\r\n * @see Signal algorithms — 'Method: Signal.State.prototype.get()'\r\n */\r\n public get(): T {\r\n if (GLOBAL_STATE.frozen) {\r\n throw new Error('Cannot get value while signals are frozen');\r\n }\r\n\r\n /*\r\n If there is a currently computing `Computed`, register this `State` as a\r\n source of that `Computed`. \r\n This is how the dependency graph is built.\r\n\r\n THis is done every time a `State` is read to guarantee always up-to-date \r\n tracking of dependencies, even if they change between computations\r\n */\r\n GLOBAL_STATE.computing?.addSource(this, PRIVATE)\r\n\r\n return this.#value;\r\n }\r\n\r\n /**\r\n * Updates the signal's value and propagates changes to all dependent\r\n * sinks.\r\n *\r\n * If `equals(currentValue, newValue)` returns `true` the call is a no-op\r\n * and no propagation occurs. Otherwise `#value` is updated, all direct\r\n * `Computed` sinks are marked `~dirty~`, indirect ones `~checked~`, and\r\n * each reachable `Watcher` has its `notify` callback invoked synchronously\r\n * (with `frozen = true`).\r\n *\r\n * @param newValue - The new value to set.\r\n * @throws If `frozen` is `true` — writes are forbidden while a protected\r\n * callback is executing.\r\n *\r\n * @see Signal algorithms — 'Method: Signal.State.prototype.set(newValue)'\r\n * @see Algorithm — 'Set Signal value'\r\n */\r\n public set(newValue: T): void {\r\n if (GLOBAL_STATE.frozen) {\r\n throw new Error('Cannot set value while signals are frozen');\r\n }\r\n\r\n if (!this.#equals.call(this, this.#value, newValue)) {\r\n this.#value = newValue;\r\n this.#sinks.forEach(sink => sink instanceof Computed ? sink.setState('dirty', PRIVATE) : sink.notify(PRIVATE));\r\n }\r\n }\r\n\r\n /**\r\n * Registers a new sink (a `Computed` or `Watcher` that depends on this\r\n * Signal) in the internal sinks set.\r\n *\r\n * Called by `Watcher.prototype.watch` when building the live dependency\r\n * chain, and by `Computed` when propagating sink registration up through\r\n * its sources.\r\n *\r\n * @param sink - The dependent node to register.\r\n * @param symbol - The private symbol for validation.\r\n * @internal\r\n */\r\n public addSink(sink: Computed | Watcher, symbol: symbol): void {\r\n assertPrivateContext(symbol);\r\n const empty = this.#sinks.size === 0;\r\n this.#sinks.add(sink);\r\n\r\n if (empty && this.#watched) {\r\n GLOBAL_STATE.frozen = true;\r\n try {\r\n this.#watched();\r\n } finally {\r\n GLOBAL_STATE.frozen = false;\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Removes a sink from the internal sinks set.\r\n *\r\n * Called by `Watcher.prototype.unwatch` when tearing down the live\r\n * dependency chain. If the sinks set becomes empty after removal, the\r\n * caller is responsible for propagating the removal up through this\r\n * Signal's sources.\r\n *\r\n * @param sink - The dependent node to remove.\r\n * @param symbol - The private symbol for validation.\r\n * @internal\r\n */\r\n public removeSink(sink: Computed | Watcher, symbol: symbol): void {\r\n assertPrivateContext(symbol);\r\n this.#sinks.delete(sink);\r\n const empty = this.#sinks.size === 0;\r\n\r\n if (empty && this.#unwatched) {\r\n GLOBAL_STATE.frozen = true;\r\n try {\r\n this.#unwatched();\r\n } finally {\r\n GLOBAL_STATE.frozen = false;\r\n }\r\n }\r\n }\r\n}\r\n","import { NoArgsVoidFunction } from '@xaendar/common';\r\nimport { GLOBAL_STATE } from '../globals';\r\nimport { PRIVATE, assertPrivateContext } from '../private-symbol';\r\nimport { WatcherState } from '../types/watcher-state.type';\r\nimport { Computed } from './computed';\r\nimport { State } from './state';\r\n\r\n/**\r\n * A `Watcher` observes a set of Signals and fires a `notify` callback\r\n * synchronously when any of their (recursive) dependencies change.\r\n *\r\n * It is the low-level primitive on top of which frameworks implement\r\n * effects and scheduling. It does not hold a value and has no generic\r\n * type parameter.\r\n *\r\n * @see Signal algorithms — 'The `Signal.subtle.Watcher` class'\r\n */\r\nexport class Watcher {\r\n /**\r\n * The current state of the Watcher.\r\n *\r\n * - `~waiting~` — newly created, or `notify` has already been called since\r\n * the last `watch` call. Not actively observing changes.\r\n * - `~watching~` — actively watching; no dependency has changed yet.\r\n * - `~pending~` — a dependency has changed but `notify` has not yet run.\r\n *\r\n * @internalSlot\r\n * @see Signal algorithms — 'Signal.subtle.Watcher State machine'\r\n */\r\n #state: WatcherState;\r\n /**\r\n * The ordered set of Signals this Watcher is currently watching.\r\n * May contain both `State` and `Computed` instances.\r\n *\r\n * @internalSlot\r\n * @see Signal algorithms — 'Signal.subtle.Watcher internal slots'\r\n */\r\n #signals: Set<State<unknown> | Computed<unknown>>;\r\n /**\r\n * Returns a snapshot of the current watched signals set for introspection.\r\n *\r\n * @param symbol - Private access symbol; rejects calls from outside the library.\r\n * @returns An array of `State` and `Computed` instances that this Watcher is watching.\r\n * @internal\r\n */\r\n public getSources(symbol: symbol): (State<unknown> | Computed<unknown>)[] {\r\n assertPrivateContext(symbol);\r\n return [...this.#signals];\r\n }\r\n /**\r\n * The callback invoked synchronously when a watched Signal (or one of its\r\n * recursive dependencies) changes for the first time since the last\r\n * `watch` call.\r\n *\r\n * Receives the Watcher itself as `this`. No Signals may be read or written\r\n * during its execution (`frozen` is `true` for its entire duration).\r\n *\r\n * @internalSlot\r\n * @see Signal algorithms — 'Signal.subtle.Watcher internal slots'\r\n */\r\n #notifyCallback: NoArgsVoidFunction;\r\n\r\n /**\r\n * Creates a new Watcher.\r\n *\r\n * The Watcher starts in the `~waiting~` state with an empty signals set.\r\n *\r\n * @param notifyCallback - Called synchronously (with `frozen = true`) the\r\n * first time a watched dependency changes after each `watch` call. No\r\n * Signals may be read or written inside this callback.\r\n *\r\n * @see Signal algorithms — 'Constructor: new Signal.subtle.Watcher(callback)'\r\n */\r\n constructor(notifyCallback: NoArgsVoidFunction) {\r\n this.#state = 'waiting';\r\n this.#signals = new Set;\r\n this.#notifyCallback = notifyCallback;\r\n }\r\n\r\n /**\r\n * Returns the subset of watched Signals that are `Computed` instances\r\n * currently in a `~dirty~` or `~checked~` state, meaning they may have a\r\n * stale value that has not yet been re-evaluated.\r\n *\r\n * Typically called inside the microtask scheduled by the `notify` callback\r\n * to know which Signals need to be pulled.\r\n *\r\n * @returns An array of `Computed` signals that are dirty or checked.\r\n *\r\n * @see Signal algorithms — 'Method: Signal.subtle.Watcher.prototype.getPending()'\r\n */\r\n public getPending(): Computed<unknown>[] {\r\n return [...this.#signals].filter((signal): signal is Computed<unknown> => {\r\n if (!(signal instanceof Computed)) {\r\n return false;\r\n }\r\n\r\n const state = signal.getState(PRIVATE);\r\n return state === 'dirty' || state === 'checked';\r\n });\r\n }\r\n\r\n /**\r\n * Adds the given Signals to the watched set and transitions the Watcher to\r\n * the `~watching~` state.\r\n *\r\n * For each newly-watched Signal, the Watcher is registered as a sink and —\r\n * if it is the first sink — the sink registration is propagated recursively\r\n * up through the Signal's sources, building the live dependency chain.\r\n *\r\n * The `watched` callback of each Signal (if any) is called with\r\n * `frozen = true`.\r\n *\r\n * @param signals - One or more `State` signals to start watching.\r\n * @throws If `frozen` is `true` at the time of the call.\r\n *\r\n * @see Signal algorithms — 'Method: Signal.subtle.Watcher.prototype.watch(...signals)'\r\n */\r\n public watch(...signals: (State | Computed)[]) {\r\n if (GLOBAL_STATE.frozen) {\r\n throw new Error('Cannot watch signals while frozen');\r\n }\r\n\r\n signals.forEach(signal => {\r\n if (this.#signals.has(signal)) {\r\n throw new Error('Cannot watch a signal that is already being watched');\r\n }\r\n\r\n this.#signals.add(signal)\r\n signal.addSink(this, PRIVATE);\r\n });\r\n\r\n\r\n if (this.#state === 'waiting') {\r\n this.#state = 'watching';\r\n }\r\n }\r\n\r\n /**\r\n * Removes the given Signals from the watched set.\r\n *\r\n * For each removed Signal, the Watcher is unregistered as a sink. If the\r\n * Signal's sink set becomes empty as a result, the removal is propagated\r\n * recursively up through its sources, tearing down the live dependency\r\n * chain and allowing garbage collection of unwatched nodes.\r\n *\r\n * The `unwatched` callback of each Signal (if any) is called with\r\n * `frozen = true`.\r\n *\r\n * If no Signals remain in the watched set, the Watcher transitions back to\r\n * the `~waiting~` state.\r\n *\r\n * @param signals - One or more `State` signals to stop watching.\r\n * @throws If `frozen` is `true` at the time of the call.\r\n * @throws If any of the given Signals is not currently being watched.\r\n *\r\n * @see Signal algorithms — 'Method: Signal.subtle.Watcher.prototype.unwatch(...signals)'\r\n */\r\n public unwatch(...signals: (State | Computed)[]) {\r\n if (GLOBAL_STATE.frozen) {\r\n throw new Error('Cannot unwatch signals while frozen');\r\n }\r\n\r\n signals.forEach(signal => {\r\n if (this.#signals.has(signal)) {\r\n throw new Error('Cannot unwatch a signal that is not being watched');\r\n }\r\n\r\n this.#signals.delete(signal)\r\n signal.removeSink(this, PRIVATE);\r\n });\r\n\r\n if (!this.#signals.size && this.#state === 'watching') {\r\n this.#state = 'waiting';\r\n }\r\n }\r\n\r\n /**\r\n * Get the current state of the Watcher.\r\n * @param symbol - The private symbol for prevent external calls.\r\n */\r\n public getState(symbol: symbol): WatcherState {\r\n assertPrivateContext(symbol);\r\n return this.#state;\r\n }\r\n\r\n /**\r\n * Set the current state of the Watcher.\r\n * @param newState - The new state to set.\r\n * @param symbol - The private symbol for prevent external calls.\r\n * @throws If the transition from `pending` to `watching` is attempted.\r\n */\r\n public setState(newState: WatcherState, symbol: symbol): void {\r\n assertPrivateContext(symbol);\r\n\r\n if (this.#state !== newState && this.#isValidTransition(this.#state, newState)) {\r\n this.#state = newState;\r\n }\r\n }\r\n\r\n /**\r\n * Invoce the notify callback when a watched dependency changes\r\n * @param symbol - The private symbol for prevent external calls.\r\n */\r\n public notify(symbol: symbol): void {\r\n assertPrivateContext(symbol);\r\n\r\n this.#state = 'pending';\r\n\r\n GLOBAL_STATE.frozen = true;\r\n try {\r\n this.#notifyCallback.call(this);\r\n } finally {\r\n this.#state = 'waiting'; \r\n GLOBAL_STATE.frozen = false;\r\n }\r\n }\r\n\r\n #isValidTransition(from: WatcherState, to: WatcherState): boolean {\r\n switch (from) {\r\n case 'waiting':\r\n return to === 'watching';\r\n case 'watching':\r\n return to === 'pending' || to === 'waiting';\r\n case 'pending':\r\n return to === 'waiting';\r\n }\r\n }\r\n}","import { GLOBAL_STATE } from './globals';\r\nimport { Computed } from './models/computed';\r\nimport { State } from './models/state';\r\nimport { Watcher } from './models/watcher';\r\nimport { PRIVATE } from './private-symbol';\r\n\r\n/**\r\n * Executes a function without tracking any dependencies.\r\n * @param fn - The function to execute without tracking.\r\n * @returns The result of the function execution.\r\n */\r\nexport function untrack<T>(fn: () => T): T {\r\n const prevComputing = GLOBAL_STATE.computing;\r\n GLOBAL_STATE.computing = null;\r\n\r\n try {\r\n return fn();\r\n } finally {\r\n GLOBAL_STATE.computing = prevComputing;\r\n }\r\n}\r\n\r\n/**\r\n * Returns the currently active `Computed` instance being evaluated, or `null`\r\n * @returns The currently active `Computed` instance, or `null` if none is being evaluated.\r\n */\r\nexport function currentComputed(): Computed | null {\r\n return GLOBAL_STATE.computing;\r\n}\r\n\r\n/**\r\n * Returns the ordered list of all Signals which the given `Computed` or\r\n * `Watcher` referenced during its last evaluation.\r\n *\r\n * - For a `Computed`, these are the Signals read inside its callback.\r\n * - For a `Watcher`, these are the Signals it is currently watching.\r\n *\r\n * @param s - The `Computed` or `Watcher` to introspect.\r\n * @returns An array of `State` and `Computed` instances.\r\n */\r\nexport function introspectSources(s: Computed | Watcher): (State | Computed)[] {\r\n return s.getSources(PRIVATE);\r\n}\r\n\r\n/**\r\n * Returns the direct dependents of the given Signal — Watchers that contain\r\n * it, plus any `Computed` Signals which read it during their last evaluation\r\n * (if that `Computed` is recursively watched).\r\n *\r\n * @param signal - The `State` or `Computed` Signal to introspect.\r\n * @returns An array of `Computed` and `Watcher` instances.\r\n */\r\nexport function introspectSinks(signal: State | Computed): (Computed | Watcher)[] {\r\n return signal.getSinks(PRIVATE);\r\n}\r\n\r\n/**\r\n * Returns `true` if the given Signal is 'live' — i.e. it is watched by a\r\n * `Watcher`, or it is read by a `Computed` Signal which is (recursively)\r\n * live.\r\n *\r\n * @param signal - The `State` or `Computed` Signal to check.\r\n * @returns `true` if the Signal has at least one sink.\r\n */\r\nexport function hasSinks(signal: State | Computed): boolean {\r\n return signal.getSinks(PRIVATE).length > 0;\r\n}\r\n\r\n/**\r\n * Returns `true` if the given node is 'reactive' — i.e. it depends on some\r\n * other Signal. A `Computed` where `hasSources` is `false` will always\r\n * return the same constant.\r\n *\r\n * @param signal - The `Computed` or `Watcher` to check.\r\n * @returns `true` if the node has at least one source.\r\n */\r\nexport function hasSources(signal: Computed | Watcher): boolean {\r\n return signal.getSources(PRIVATE).length > 0;\r\n}","import { Computed } from './models/computed';\r\nimport { State } from './models/state';\r\nimport { Watcher } from './models/watcher';\r\nimport { currentComputed, hasSinks, hasSources, introspectSinks, introspectSources, untrack } from './subtle';\r\n\r\n/**\r\n * Loads the Signals library by defining the `Signal` global object with the following properties:\r\n * - `State`: The `State` class for creating reactive state variables.\r\n * - `Computed`: The `Computed` class for creating derived reactive values.\r\n * - `Watcher`: The `Watcher` class for observing changes in signals.\r\n * - `subtle`: An object containing internal utility functions for working with signals, including:\r\n * - `untrack`: Executes a function without tracking dependencies.\r\n * - `currentComputed`: Returns the currently active `Computed` instance being evaluated, or `null` if none.\r\n * - `introspectSources`: Returns the list of sources for a given `Computed` or `Watcher`.\r\n * - `introspectSinks`: Returns the list of sinks for a given `State` or `Computed`.\r\n * - `hasSinks`: Returns `true` if a given `State` or `Computed` has at least one sink.\r\n * - `hasSources`: Returns `true` if a given `Computed` or `Watcher` has at least one source.\r\n * - `Watcher`: The `Watcher` class for observing changes in signals.\r\n * \r\n * This function should be called once to initialize the Signals library and make its API available globally.\r\n */\r\nexport function loadSignals(): void {\r\n globalThis.Signal ??= {\r\n State,\r\n Computed,\r\n subtle: {\r\n untrack,\r\n currentComputed,\r\n introspectSources: introspectSources as unknown as typeof Signal.subtle.introspectSources,\r\n introspectSinks: introspectSinks as unknown as typeof Signal.subtle.introspectSinks,\r\n hasSinks: hasSinks as unknown as typeof Signal.subtle.hasSinks,\r\n hasSources: hasSources as unknown as typeof Signal.subtle.hasSources,\r\n Watcher\r\n }\r\n }\r\n}"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"xaendar-signals.es.js","names":["#sources","#sinks","#callback","#equals","#state","#computeValue","#findDeepestStale","#value","#isValidTransition","#setValue","#propagateClean","#isErrorValue","#sinks","#value","#equals","#watched","#unwatched","#signals","#state","#notifyCallback","#isValidTransition"],"sources":["../../../packages/signals/src/lib/globals.ts","../../../packages/signals/src/lib/private-symbol.ts","../../../packages/signals/src/lib/models/computed.ts","../../../packages/signals/src/lib/models/state.ts","../../../packages/signals/src/lib/models/watcher.ts","../../../packages/signals/src/lib/subtle.ts","../../../packages/signals/src/lib/load-signals.ts"],"sourcesContent":["import { Stack } from '@xaendar/common';\r\nimport { Computed } from './models/computed';\r\nimport { GlobalState } from './types/global-state.type';\r\n\r\n/**\r\n * Global state for the signals runtime.\r\n * Tracks the currently computing `Computed` instance, whether the state is frozen,\r\n * and the current generation counter.\r\n *\r\n * @internal\r\n */\r\nexport const GLOBAL_STATE: GlobalState = {\r\n computing: null,\r\n frozen: false\r\n};\r\n\r\nconst computingStack = new Stack<Computed>;\r\n\r\n/**\r\n * Pushes a `Computed` instance onto the computing stack and sets it as the\r\n * currently active computation in `GLOBAL_STATE`.\r\n *\r\n * Should be called before executing a computed function to register\r\n * dependency tracking.\r\n *\r\n * @param computed - The `Computed` instance entering the computation phase.\r\n * @internal\r\n */\r\nexport function pushComputed(computed: Computed): void {\r\n computingStack.push(computed);\r\n GLOBAL_STATE.computing = computed;\r\n}\r\n\r\n/**\r\n * Pops the most recent `Computed` instance from the computing stack and\r\n * restores the previous one as the active computation in `GLOBAL_STATE`.\r\n *\r\n * Should be called after a computed function finishes executing.\r\n *\r\n * @internal\r\n */\r\nexport function popComputed(): void {\r\n computingStack.pop();\r\n GLOBAL_STATE.computing = computingStack[computingStack.length - 1] ?? null;\r\n}","/**\r\n * Symbol not available in public API,\r\n * used to call internal methods of `State` and `Computed` from `Watcher` without exposing them in the public API.\r\n *\r\n * @internal\r\n */\r\nexport const PRIVATE = Symbol('signals-private');\r\n\r\n/**\r\n * Asserts that the provided symbol matches the internal {@link PRIVATE} symbol,\r\n * ensuring the caller has access to internal APIs.\r\n *\r\n * Throws if the symbol does not match, preventing external code from\r\n * invoking methods intended for internal use only.\r\n *\r\n * @param symbol - The symbol to validate against {@link PRIVATE}.\r\n * @throws {Error} If `symbol` does not match {@link PRIVATE}.\r\n * @internal\r\n */\r\nexport function assertPrivateContext(symbol: symbol): void {\r\n if (symbol !== PRIVATE) {\r\n throw new Error('Invalid symbol');\r\n }\r\n}","\r\nimport { GLOBAL_STATE, pushComputed, popComputed } from '../globals';\r\nimport { PRIVATE, assertPrivateContext } from '../private-symbol';\r\nimport { ComputedState } from '../types/computed-state.type';\r\nimport { SignalEqual } from '../types/signal-equal.type';\r\nimport { SignalOptions } from '../types/signal-options.type';\r\nimport { State } from './state';\r\nimport { Watcher } from './watcher';\r\n\r\n/**\r\n * A read-only Signal whose value is derived lazily from other Signals.\r\n *\r\n * The value is recomputed only when explicitly read and only if one or more\r\n * of its (recursive) dependencies have changed since the last evaluation.\r\n * The result is cached and reused until the Signal becomes stale again.\r\n *\r\n * @template T The type of the computed value.\r\n *\r\n * @see Signal algorithms — \"The Signal.Computed class\"\r\n */\r\nexport class Computed<T = any> {\r\n /**\r\n * The current value of the signal.\r\n *\r\n * Uninitialised (`!`) until the first evaluation. After that, holds either\r\n * the return value of `#callback` or a boxed error\r\n * `{ isError: true; value: Error }` if the last evaluation threw.\r\n *\r\n * @internalSlot\r\n * @see Signal algorithms — \"Signal.Computed internal slots\"\r\n */\r\n #value!: T | { isError: true; value: Error };\r\n /**\r\n * The current evaluation state of this Signal.\r\n *\r\n * - `~dirty~` — value is known to be stale or has never been evaluated.\r\n * - `~checked~` — an indirect source changed; may or may not be stale.\r\n * - `~computing~` — `#callback` is currently executing; guards against cycles.\r\n * - `~clean~` — cached value is up-to-date.\r\n *\r\n * @internalSlot\r\n * @see Signal algorithms — \"Signal.Computed State machine\"\r\n */\r\n #state: ComputedState;\r\n /**\r\n * The ordered set of Signals read during the last evaluation of\r\n * `#callback`. Cleared and rebuilt on every re-evaluation so that\r\n * conditional branches that are no longer taken stop being tracked.\r\n *\r\n * May contain both `State` and `Computed` instances.\r\n *\r\n * @internalSlot\r\n * @see Signal algorithms — \"Signal.Computed internal slots\"\r\n */\r\n #sources: Set<State<unknown> | Computed<unknown>>;\r\n /**\r\n * Returns a snapshot of the current sources set for introspection.\r\n *\r\n * @param symbol - Private access symbol; rejects calls from outside the library.\r\n * @returns An array of `State` and `Computed` instances that this Signal depends on.\r\n * @internal\r\n */\r\n public getSources(symbol: symbol): (State<unknown> | Computed<unknown>)[] {\r\n assertPrivateContext(symbol);\r\n return [...this.#sources];\r\n }\r\n /**\r\n * The set of Signals and Watchers that directly depend on this Signal.\r\n *\r\n * Populated only when this Signal is reachable from at least one active\r\n * `Watcher`. An un-watched `Computed` has an empty sinks set, which allows\r\n * it to be garbage-collected independently from the rest of the graph.\r\n *\r\n * @internalSlot\r\n * @see Signal algorithms — \"Signal.Computed internal slots\"\r\n * @see Method — `Signal.Computed.prototype.get` (NOTE on sinks)\r\n */\r\n #sinks: Set<Computed<unknown> | Watcher>;\r\n /**\r\n * Returns a snapshot of the current sinks set for introspection.\r\n *\r\n * @param symbol - Private access symbol; rejects calls from outside the library.\r\n * @returns An array of `Computed` and `Watcher` instances that depend on this Signal.\r\n * @internal\r\n */\r\n public getSinks(symbol: symbol): (Computed<unknown> | Watcher)[] {\r\n assertPrivateContext(symbol);\r\n return [...this.#sinks];\r\n }\r\n /**\r\n * The equality function used to determine whether a newly computed value\r\n * is meaningfully different from the previously cached one.\r\n *\r\n * Called as `equals.call(computed, oldValue, newValue)`. Returns `true` if\r\n * the values are considered equal, in which case no downstream propagation\r\n * occurs. Defaults to `Object.is` when not provided via options.\r\n *\r\n * If this function throws, the exception is cached as the Signal's value\r\n * and the outcome is treated as `~dirty~`.\r\n *\r\n * @internalSlot\r\n * @see Signal algorithms — \"Signal.Computed internal slots\"\r\n * @see Algorithm — \"Set Signal value\"\r\n */\r\n #equals: SignalEqual<T>;\r\n /**\r\n * The pure function that produces this Signal's value. Evaluated lazily\r\n * whenever the Signal is read while in a `~dirty~` or `~checked~` state.\r\n *\r\n * Called with `this` bound to the `Computed` instance itself so that\r\n * internal methods (e.g. `addSource`) are accessible if needed.\r\n * Any exception thrown by this function is caught and cached.\r\n *\r\n * @internalSlot\r\n * @see Signal algorithms — \"Signal.Computed internal slots\"\r\n */\r\n #callback: (this: Computed<T>) => T;\r\n\r\n /**\r\n * Creates a new `Computed` signal.\r\n *\r\n * The Signal starts in the `~dirty~` state with an uninitialised value, so\r\n * `#callback` will be invoked on the first `get()`.\r\n *\r\n * @param cb - Pure function evaluated lazily to produce the value.\r\n * Receives the `Computed` instance as `this`.\r\n * @param options - Optional configuration:\r\n * - `equals` — custom equality function; defaults to `Object.is`.\r\n *\r\n * @see Signal algorithms — \"Signal.Computed Constructor\"\r\n */\r\n constructor(cb: (this: Computed<T>) => T, options?: SignalOptions<T>) {\r\n this.#callback = cb;\r\n this.#equals = options?.equals ?? Object.is;\r\n this.#sources = new Set;\r\n this.#sinks = new Set;\r\n this.#state = 'dirty';\r\n }\r\n\r\n /**\r\n * Returns the current value of this Signal, re-evaluating `#callback` if\r\n * the cached value may be stale.\r\n *\r\n * Registers this Signal as a source of any outer `Computed` currently\r\n * being evaluated (automatic dependency tracking).\r\n *\r\n * If the state is `~dirty~` or `~checked~`, walks the source graph\r\n * depth-first to find and recalculate the deepest stale `Computed` first,\r\n * then re-checks upward until this Signal is `~clean~`.\r\n *\r\n * @returns The current computed value, or a boxed error object if the last\r\n * evaluation threw.\r\n * @throws If `frozen` is `true`.\r\n * @throws If the Signal is in the `~computing~` state (cyclic dependency).\r\n *\r\n * @see Signal algorithms — \"Method: Signal.Computed.prototype.get\"\r\n */\r\n public get(): T | { isError: true; value: Error } {\r\n if (GLOBAL_STATE.frozen) {\r\n throw new Error('Cannot get value of a Computed signal while the global state is frozen');\r\n }\r\n\r\n if (this.#state === 'computing') {\r\n throw new Error('Circular dependency detected while computing a Computed signal');\r\n }\r\n\r\n GLOBAL_STATE.computing?.addSource(this, PRIVATE);\r\n\r\n if (this.#sinks.size === 0) {\r\n this.#computeValue();\r\n } else if (this.#state === 'dirty' || this.#state === 'checked') {\r\n while (this.#state === 'dirty' || this.#state === 'checked') {\r\n const deepest = this.#findDeepestStale();\r\n deepest.#computeValue();\r\n }\r\n }\r\n\r\n return this.#value;\r\n }\r\n\r\n /**\r\n * Registers a Signal as a source of this `Computed`, discovered during the\r\n * execution of `#callback`.\r\n *\r\n * If this `Computed` is currently being watched (has at least one sink),\r\n * the source is also informed of this Signal as a new sink, building the\r\n * live push-notification chain upward.\r\n *\r\n * @param source - The Signal read during evaluation.\r\n * @param symbol - Private access symbol; rejects calls from outside the library.\r\n * @internal\r\n */\r\n public addSource(source: State | Computed, symbol: symbol) {\r\n assertPrivateContext(symbol);\r\n this.#sources.add(source);\r\n source.addSink(this, PRIVATE)\r\n }\r\n\r\n /**\r\n * Returns the current evaluation state of this Signal.\r\n *\r\n * @param symbol - Private access symbol; rejects calls from outside the library.\r\n * @internal\r\n * @see Signal algorithms — \"Signal.Computed State machine\"\r\n */\r\n public getState(symbol: symbol): ComputedState {\r\n assertPrivateContext(symbol);\r\n return this.#state;\r\n }\r\n\r\n /**\r\n * Transitions this Signal to a new evaluation state.\r\n *\r\n * Only valid transitions (as defined by the state machine) are allowed.\r\n * Invalid transitions throw an error.\r\n *\r\n * @param newState - The target state.\r\n * @param symbol - Private access symbol; rejects calls from outside the library.\r\n * @throws If the transition from the current state to `newState` is not allowed.\r\n * @internal\r\n * @see Signal algorithms — \"Signal.Computed State machine\"\r\n */\r\n public setState(newState: ComputedState, symbol: symbol): void {\r\n assertPrivateContext(symbol);\r\n\r\n if (this.#state !== newState && this.#isValidTransition(this.#state, newState)) {\r\n this.#state = newState;\r\n }\r\n }\r\n\r\n /**\r\n * Registers a new sink (a `Computed` or `Watcher` that directly depends on\r\n * this Signal) in the internal sinks set.\r\n *\r\n * If this is the first sink, propagates the sink registration recursively\r\n * up through `#sources`, building the live dependency chain that enables\r\n * push-based invalidation.\r\n *\r\n * @param sink - The dependent node to register.\r\n * @param symbol - Private access symbol; rejects calls from outside the library.\r\n * @internal\r\n */\r\n public addSink(sink: Computed<unknown> | Watcher, symbol: symbol) {\r\n assertPrivateContext(symbol);\r\n if (this.#sinks.size === 0) {\r\n this.#sources.forEach(source => source.addSink(this, PRIVATE));\r\n }\r\n this.#sinks.add(sink);\r\n }\r\n\r\n /**\r\n * Removes a sink from the internal sinks set.\r\n *\r\n * If the sinks set becomes empty after removal, propagates the removal\r\n * recursively up through `#sources`, tearing down the live dependency\r\n * chain and allowing garbage collection of un-watched nodes.\r\n *\r\n * @param sink - The dependent node to remove.\r\n * @param symbol - Private access symbol; rejects calls from outside the library.\r\n * @internal\r\n */\r\n public removeSink(sink: Computed | Watcher, symbol: symbol) {\r\n assertPrivateContext(symbol);\r\n this.#sinks.delete(sink);\r\n\r\n if (this.#sinks.size === 0) {\r\n this.#sources.forEach(source => source.removeSink(this, PRIVATE));\r\n }\r\n }\r\n\r\n /**\r\n * Recursively walks the source graph depth-first to find the deepest,\r\n * left-most `Computed` node that is in a `~dirty~` or `~checked~` state.\r\n *\r\n * This ensures that recalculation always starts from the bottom of the\r\n * dependency graph, so every node sees already-updated dependencies —\r\n * the core of glitch-free evaluation.\r\n *\r\n * Cuts off the search when hitting a `~clean~` `Computed` source, since\r\n * its subtree is guaranteed to be up-to-date.\r\n *\r\n * @returns The deepest stale `Computed` found, or `node` itself if none of\r\n * its sources are stale.\r\n */\r\n #findDeepestStale(): Computed {\r\n const unclearNode = [...this.#sources].find((source): source is Computed => source instanceof Computed && (source.#state === 'dirty' || source.#state === 'checked'));\r\n return unclearNode ? unclearNode.#findDeepestStale() : this;\r\n }\r\n\r\n /**\r\n * Executes `#callback` to recompute this Signal's value.\r\n *\r\n * Implements the \"recalculate dirty computed Signal\" algorithm:\r\n * 1. Clears stale sources and removes this Signal from their sinks.\r\n * 2. Sets `computing` to this Signal for automatic dependency tracking.\r\n * 3. Runs the callback, caching the return value or any thrown exception.\r\n * 4. Restores the previous `computing` value.\r\n * 5. Runs the \"set Signal value\" algorithm to detect value changes.\r\n * 6. Transitions state to `~clean~`.\r\n * 7. Propagates `~dirty~` to sinks (or attempts `~clean~` if value unchanged).\r\n *\r\n * @see Signal algorithms — \"Algorithm: recalculate dirty computed Signal\"\r\n */\r\n #computeValue(): void {\r\n this.#sources.forEach(source => source.removeSink(this, PRIVATE));\r\n this.#sources.clear();\r\n\r\n pushComputed(this);\r\n this.#state = 'computing';\r\n\r\n let newValue: T | { isError: true; value: Error };\r\n\r\n try {\r\n newValue = this.#callback.call(this);\r\n } catch (error) {\r\n newValue = { isError: true, value: error as Error };\r\n } finally {\r\n popComputed();\r\n }\r\n\r\n const outcome = this.#setValue(newValue);\r\n\r\n outcome === 'dirty'\r\n ? this.#sinks.forEach(sink => sink instanceof Computed ? sink.setState('dirty', PRIVATE) : sink.notify(PRIVATE))\r\n : this.#propagateClean();\r\n\r\n this.#state = 'clean';\r\n }\r\n\r\n /**\r\n * Implements the \"set Signal value\" algorithm.\r\n *\r\n * Compares the new value against the cached one using `#equals`. If equal,\r\n * returns `~clean~` and leaves `#value` untouched. Otherwise updates\r\n * `#value` and returns `~dirty~`.\r\n *\r\n * Special cases:\r\n * - If `newValue` is a boxed error, `#equals` is skipped and the error is\r\n * cached directly.\r\n * - If `#equals` itself throws, the exception is cached as a boxed error\r\n * and the outcome is `~dirty~`.\r\n *\r\n * @param newValue - The value (or boxed error) produced by `#callback`.\r\n * @returns `~clean~` if the value is unchanged, `~dirty~` otherwise.\r\n *\r\n * @see Signal algorithms — \"Set Signal value algorithm\"\r\n */\r\n #setValue(newValue: T | { isError: true; value: Error }): 'clean' | 'dirty' {\r\n const oldValue = this.#value;\r\n\r\n /*\r\n If new value is an error we always update without calling equals\r\n */\r\n if (this.#isErrorValue(newValue)) {\r\n this.#value = newValue;\r\n return 'dirty';\r\n }\r\n\r\n try {\r\n if (!this.#isErrorValue(oldValue) && this.#equals.call(this, oldValue, newValue)) {\r\n return 'clean';\r\n }\r\n } catch (equalsError) {\r\n this.#value = { isError: true, value: equalsError as Error };\r\n return 'dirty';\r\n }\r\n\r\n this.#value = newValue;\r\n return 'dirty';\r\n }\r\n\r\n /**\r\n * Recursively marks `~checked~` sinks as `~clean~` when all of their\r\n * immediate sources are already `~clean~`.\r\n *\r\n * Called after a recalculation that produced an unchanged value (`~clean~`\r\n * outcome from `#setValue`). Propagates the clean signal upward through\r\n * the graph so that Computed nodes that were only transitively dirty — and\r\n * whose dependencies have not actually changed — are not needlessly\r\n * re-evaluated on the next read.\r\n *\r\n * @see Signal algorithms — \"Algorithm: recalculate dirty computed Signal\"\r\n */\r\n #propagateClean(): void {\r\n [...this.#sinks]\r\n .filter((sink): sink is Computed<unknown> => sink instanceof Computed && sink.#state === 'checked')\r\n .forEach(sink => {\r\n const allSourcesClean = [...sink.#sources].every(source => !(source instanceof Computed) || source.#state === 'clean');\r\n if (allSourcesClean) {\r\n sink.#state = 'clean';\r\n sink.#propagateClean();\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * Type guard that checks whether a value is a boxed error object.\r\n *\r\n * Used to distinguish a legitimately computed value from a cached\r\n * exception produced by `#callback` or `#equals`.\r\n *\r\n * @param value - The value to inspect.\r\n * @returns `true` if `value` is `{ isError: true; value: Error }`.\r\n */\r\n #isErrorValue(value: unknown): value is { isError: true; value: Error } {\r\n return typeof value === 'object' && !!value && 'isError' in value;\r\n }\r\n\r\n #isValidTransition(from: ComputedState, to: ComputedState): boolean {\r\n switch (from) {\r\n case 'checked':\r\n return to === 'clean' || to === 'dirty';\r\n case 'clean':\r\n return to === 'checked' || to === 'dirty';\r\n case 'dirty':\r\n return to === 'computing';\r\n case 'computing':\r\n return to === 'clean';\r\n }\r\n }\r\n}","import { NoArgsVoidFunction } from '@xaendar/common';\r\nimport { GLOBAL_STATE } from '../globals';\r\nimport { PRIVATE, assertPrivateContext } from '../private-symbol';\r\nimport { SignalEqual } from '../types/signal-equal.type';\r\nimport { SignalOptions } from '../types/signal-options.type';\r\nimport { Computed } from './computed';\r\nimport { Watcher } from './watcher';\r\n\r\nexport class State<T = any> {\r\n /**\r\n * The current value of the signal.\r\n *\r\n * Initialised to `initialValue` in the constructor and updated by `set`\r\n * whenever the new value is not equal to the current one according to\r\n * `#equals`.\r\n *\r\n * @internalSlot\r\n * @see Signal algorithms — 'Signal.State internal slots'\r\n */\r\n #value: T;\r\n /**\r\n * The equality function used to determine whether a new value is\r\n * meaningfully different from the current one.\r\n *\r\n * Called as `equals.call(signal, oldValue, newValue)`. If it returns\r\n * `true` the signal is considered unchanged and no propagation occurs.\r\n * Defaults to `Object.is` when not provided via options.\r\n *\r\n * @internalSlot\r\n * @see Signal algorithms — 'Signal.State internal slots'\r\n * @see Algorithm — 'Set Signal value'\r\n */\r\n #equals: SignalEqual<T>\r\n /**\r\n * Optional callback invoked (with `frozen = true`) the first time this\r\n * Signal gains a sink — i.e. when it transitions from un-observed to\r\n * observed by at least one `Watcher` (directly or transitively).\r\n *\r\n * @internalSlot\r\n * @see Signal algorithms — 'Signal.State internal slots'\r\n * @see Method — `Signal.subtle.Watcher.prototype.watch`\r\n */\r\n #watched?: NoArgsVoidFunction;\r\n /**\r\n * Optional callback invoked (with `frozen = true`) when this Signal loses\r\n * its last sink — i.e. when it transitions from observed back to\r\n * un-observed.\r\n *\r\n * @internalSlot\r\n * @see Signal algorithms — 'Signal.State internal slots'\r\n * @see Method — `Signal.subtle.Watcher.prototype.unwatch`\r\n */\r\n #unwatched?: NoArgsVoidFunction;\r\n /**\r\n * The set of watched signals that directly depend on this one.\r\n *\r\n * Populated only when this Signal is reachable from at least one active\r\n * `Watcher` — un-watched Signals have an empty sinks set, which allows\r\n * them to be garbage-collected independently from the rest of the graph.\r\n *\r\n * Should contain both `Computed` and `Watcher` instances, as both can be\r\n * direct dependents of a `State`.\r\n *\r\n * @internalSlot\r\n * @see Signal algorithms — 'Signal.State internal slots'\r\n * @see Method — `Signal.State.prototype.get` (NOTE on sinks)\r\n */\r\n #sinks: Set<Computed<unknown> | Watcher>;\r\n /**\r\n * Returns a snapshot of the current sinks set for introspection.\r\n *\r\n * @param symbol - Private access symbol; rejects calls from outside the library.\r\n * @returns An array of `Computed` and `Watcher` instances that depend on this Signal.\r\n * @internal\r\n */\r\n public getSinks(symbol: symbol): (Computed<unknown> | Watcher)[] {\r\n assertPrivateContext(symbol);\r\n return [...this.#sinks];\r\n }\r\n\r\n /**\r\n * Creates a new `State` signal.\r\n *\r\n * @param initialValue - The initial value of the signal.\r\n * @param options - Optional configuration:\r\n * - `equals` — custom equality function; defaults to `Object.is`.\r\n * - `watched` — called when the signal gains its first sink.\r\n * - `unwatched` — called when the signal loses its last sink.\r\n *\r\n * @see Signal algorithms — 'Constructor: Signal.State(initialValue, options)'\r\n */\r\n constructor(initialValue: T, options?: SignalOptions<T>) {\r\n this.#value = initialValue;\r\n this.#equals = options?.equals ?? Object.is;\r\n this.#watched = options?.watched;\r\n this.#unwatched = options?.unwatched;\r\n this.#sinks = new Set;\r\n }\r\n\r\n /**\r\n * Returns the current value of the signal, registering this Signal as a\r\n * source of the innermost `Computed` currently being evaluated (if any).\r\n *\r\n * @throws If `frozen` is `true` — reads are forbidden while a protected\r\n * callback (`notify`, `watched`, `unwatched`) is executing.\r\n *\r\n * @see Signal algorithms — 'Method: Signal.State.prototype.get()'\r\n */\r\n public get(): T {\r\n if (GLOBAL_STATE.frozen) {\r\n throw new Error('Cannot get value while signals are frozen');\r\n }\r\n\r\n /*\r\n If there is a currently computing `Computed`, register this `State` as a\r\n source of that `Computed`. \r\n This is how the dependency graph is built.\r\n\r\n THis is done every time a `State` is read to guarantee always up-to-date \r\n tracking of dependencies, even if they change between computations\r\n */\r\n GLOBAL_STATE.computing?.addSource(this, PRIVATE)\r\n\r\n return this.#value;\r\n }\r\n\r\n /**\r\n * Updates the signal's value and propagates changes to all dependent\r\n * sinks.\r\n *\r\n * If `equals(currentValue, newValue)` returns `true` the call is a no-op\r\n * and no propagation occurs. Otherwise `#value` is updated, all direct\r\n * `Computed` sinks are marked `~dirty~`, indirect ones `~checked~`, and\r\n * each reachable `Watcher` has its `notify` callback invoked synchronously\r\n * (with `frozen = true`).\r\n *\r\n * @param newValue - The new value to set.\r\n * @throws If `frozen` is `true` — writes are forbidden while a protected\r\n * callback is executing.\r\n *\r\n * @see Signal algorithms — 'Method: Signal.State.prototype.set(newValue)'\r\n * @see Algorithm — 'Set Signal value'\r\n */\r\n public set(newValue: T): void {\r\n if (GLOBAL_STATE.frozen) {\r\n throw new Error('Cannot set value while signals are frozen');\r\n }\r\n\r\n if (!this.#equals.call(this, this.#value, newValue)) {\r\n this.#value = newValue;\r\n this.#sinks.forEach(sink => sink instanceof Computed ? sink.setState('dirty', PRIVATE) : sink.notify(PRIVATE));\r\n }\r\n }\r\n\r\n /**\r\n * Registers a new sink (a `Computed` or `Watcher` that depends on this\r\n * Signal) in the internal sinks set.\r\n *\r\n * Called by `Watcher.prototype.watch` when building the live dependency\r\n * chain, and by `Computed` when propagating sink registration up through\r\n * its sources.\r\n *\r\n * @param sink - The dependent node to register.\r\n * @param symbol - The private symbol for validation.\r\n * @internal\r\n */\r\n public addSink(sink: Computed | Watcher, symbol: symbol): void {\r\n assertPrivateContext(symbol);\r\n const empty = this.#sinks.size === 0;\r\n this.#sinks.add(sink);\r\n\r\n if (empty && this.#watched) {\r\n GLOBAL_STATE.frozen = true;\r\n try {\r\n this.#watched();\r\n } finally {\r\n GLOBAL_STATE.frozen = false;\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Removes a sink from the internal sinks set.\r\n *\r\n * Called by `Watcher.prototype.unwatch` when tearing down the live\r\n * dependency chain. If the sinks set becomes empty after removal, the\r\n * caller is responsible for propagating the removal up through this\r\n * Signal's sources.\r\n *\r\n * @param sink - The dependent node to remove.\r\n * @param symbol - The private symbol for validation.\r\n * @internal\r\n */\r\n public removeSink(sink: Computed | Watcher, symbol: symbol): void {\r\n assertPrivateContext(symbol);\r\n this.#sinks.delete(sink);\r\n const empty = this.#sinks.size === 0;\r\n\r\n if (empty && this.#unwatched) {\r\n GLOBAL_STATE.frozen = true;\r\n try {\r\n this.#unwatched();\r\n } finally {\r\n GLOBAL_STATE.frozen = false;\r\n }\r\n }\r\n }\r\n}\r\n","import { NoArgsVoidFunction } from '@xaendar/common';\r\nimport { GLOBAL_STATE } from '../globals';\r\nimport { PRIVATE, assertPrivateContext } from '../private-symbol';\r\nimport { WatcherState } from '../types/watcher-state.type';\r\nimport { Computed } from './computed';\r\nimport { State } from './state';\r\n\r\n/**\r\n * A `Watcher` observes a set of Signals and fires a `notify` callback\r\n * synchronously when any of their (recursive) dependencies change.\r\n *\r\n * It is the low-level primitive on top of which frameworks implement\r\n * effects and scheduling. It does not hold a value and has no generic\r\n * type parameter.\r\n *\r\n * @see Signal algorithms — 'The `Signal.subtle.Watcher` class'\r\n */\r\nexport class Watcher {\r\n /**\r\n * The current state of the Watcher.\r\n *\r\n * - `~waiting~` — newly created, or `notify` has already been called since\r\n * the last `watch` call. Not actively observing changes.\r\n * - `~watching~` — actively watching; no dependency has changed yet.\r\n * - `~pending~` — a dependency has changed but `notify` has not yet run.\r\n *\r\n * @internalSlot\r\n * @see Signal algorithms — 'Signal.subtle.Watcher State machine'\r\n */\r\n #state: WatcherState;\r\n /**\r\n * The ordered set of Signals this Watcher is currently watching.\r\n * May contain both `State` and `Computed` instances.\r\n *\r\n * @internalSlot\r\n * @see Signal algorithms — 'Signal.subtle.Watcher internal slots'\r\n */\r\n #signals: Set<State<unknown> | Computed<unknown>>;\r\n /**\r\n * Returns a snapshot of the current watched signals set for introspection.\r\n *\r\n * @param symbol - Private access symbol; rejects calls from outside the library.\r\n * @returns An array of `State` and `Computed` instances that this Watcher is watching.\r\n * @internal\r\n */\r\n public getSources(symbol: symbol): (State<unknown> | Computed<unknown>)[] {\r\n assertPrivateContext(symbol);\r\n return [...this.#signals];\r\n }\r\n /**\r\n * The callback invoked synchronously when a watched Signal (or one of its\r\n * recursive dependencies) changes for the first time since the last\r\n * `watch` call.\r\n *\r\n * Receives the Watcher itself as `this`. No Signals may be read or written\r\n * during its execution (`frozen` is `true` for its entire duration).\r\n *\r\n * @internalSlot\r\n * @see Signal algorithms — 'Signal.subtle.Watcher internal slots'\r\n */\r\n #notifyCallback: NoArgsVoidFunction;\r\n\r\n /**\r\n * Creates a new Watcher.\r\n *\r\n * The Watcher starts in the `~waiting~` state with an empty signals set.\r\n *\r\n * @param notifyCallback - Called synchronously (with `frozen = true`) the\r\n * first time a watched dependency changes after each `watch` call. No\r\n * Signals may be read or written inside this callback.\r\n *\r\n * @see Signal algorithms — 'Constructor: new Signal.subtle.Watcher(callback)'\r\n */\r\n constructor(notifyCallback: NoArgsVoidFunction) {\r\n this.#state = 'waiting';\r\n this.#signals = new Set;\r\n this.#notifyCallback = notifyCallback;\r\n }\r\n\r\n /**\r\n * Returns the subset of watched Signals that are `Computed` instances\r\n * currently in a `~dirty~` or `~checked~` state, meaning they may have a\r\n * stale value that has not yet been re-evaluated.\r\n *\r\n * Typically called inside the microtask scheduled by the `notify` callback\r\n * to know which Signals need to be pulled.\r\n *\r\n * @returns An array of `Computed` signals that are dirty or checked.\r\n *\r\n * @see Signal algorithms — 'Method: Signal.subtle.Watcher.prototype.getPending()'\r\n */\r\n public getPending(): Computed<unknown>[] {\r\n return [...this.#signals].filter((signal): signal is Computed<unknown> => {\r\n if (!(signal instanceof Computed)) {\r\n return false;\r\n }\r\n\r\n const state = signal.getState(PRIVATE);\r\n return state === 'dirty' || state === 'checked';\r\n });\r\n }\r\n\r\n /**\r\n * Adds the given Signals to the watched set and transitions the Watcher to\r\n * the `~watching~` state.\r\n *\r\n * For each newly-watched Signal, the Watcher is registered as a sink and —\r\n * if it is the first sink — the sink registration is propagated recursively\r\n * up through the Signal's sources, building the live dependency chain.\r\n *\r\n * The `watched` callback of each Signal (if any) is called with\r\n * `frozen = true`.\r\n *\r\n * @param signals - One or more `State` signals to start watching.\r\n * @throws If `frozen` is `true` at the time of the call.\r\n *\r\n * @see Signal algorithms — 'Method: Signal.subtle.Watcher.prototype.watch(...signals)'\r\n */\r\n public watch(...signals: (State | Computed)[]) {\r\n if (GLOBAL_STATE.frozen) {\r\n throw new Error('Cannot watch signals while frozen');\r\n }\r\n\r\n signals.forEach(signal => {\r\n if (this.#signals.has(signal)) {\r\n throw new Error('Cannot watch a signal that is already being watched');\r\n }\r\n\r\n this.#signals.add(signal)\r\n signal.addSink(this, PRIVATE);\r\n });\r\n\r\n\r\n if (this.#state === 'waiting') {\r\n this.#state = 'watching';\r\n }\r\n }\r\n\r\n /**\r\n * Removes the given Signals from the watched set.\r\n *\r\n * For each removed Signal, the Watcher is unregistered as a sink. If the\r\n * Signal's sink set becomes empty as a result, the removal is propagated\r\n * recursively up through its sources, tearing down the live dependency\r\n * chain and allowing garbage collection of unwatched nodes.\r\n *\r\n * The `unwatched` callback of each Signal (if any) is called with\r\n * `frozen = true`.\r\n *\r\n * If no Signals remain in the watched set, the Watcher transitions back to\r\n * the `~waiting~` state.\r\n *\r\n * @param signals - One or more `State` signals to stop watching.\r\n * @throws If `frozen` is `true` at the time of the call.\r\n * @throws If any of the given Signals is not currently being watched.\r\n *\r\n * @see Signal algorithms — 'Method: Signal.subtle.Watcher.prototype.unwatch(...signals)'\r\n */\r\n public unwatch(...signals: (State | Computed)[]) {\r\n if (GLOBAL_STATE.frozen) {\r\n throw new Error('Cannot unwatch signals while frozen');\r\n }\r\n\r\n signals.forEach(signal => {\r\n if (this.#signals.has(signal)) {\r\n throw new Error('Cannot unwatch a signal that is not being watched');\r\n }\r\n\r\n this.#signals.delete(signal)\r\n signal.removeSink(this, PRIVATE);\r\n });\r\n\r\n if (!this.#signals.size && this.#state === 'watching') {\r\n this.#state = 'waiting';\r\n }\r\n }\r\n\r\n /**\r\n * Get the current state of the Watcher.\r\n * @param symbol - The private symbol for prevent external calls.\r\n */\r\n public getState(symbol: symbol): WatcherState {\r\n assertPrivateContext(symbol);\r\n return this.#state;\r\n }\r\n\r\n /**\r\n * Set the current state of the Watcher.\r\n * @param newState - The new state to set.\r\n * @param symbol - The private symbol for prevent external calls.\r\n * @throws If the transition from `pending` to `watching` is attempted.\r\n */\r\n public setState(newState: WatcherState, symbol: symbol): void {\r\n assertPrivateContext(symbol);\r\n\r\n if (this.#state !== newState && this.#isValidTransition(this.#state, newState)) {\r\n this.#state = newState;\r\n }\r\n }\r\n\r\n /**\r\n * Invoce the notify callback when a watched dependency changes\r\n * @param symbol - The private symbol for prevent external calls.\r\n */\r\n public notify(symbol: symbol): void {\r\n assertPrivateContext(symbol);\r\n\r\n this.#state = 'pending';\r\n\r\n GLOBAL_STATE.frozen = true;\r\n try {\r\n this.#notifyCallback.call(this);\r\n } finally {\r\n this.#state = 'waiting'; \r\n GLOBAL_STATE.frozen = false;\r\n }\r\n }\r\n\r\n #isValidTransition(from: WatcherState, to: WatcherState): boolean {\r\n switch (from) {\r\n case 'waiting':\r\n return to === 'watching';\r\n case 'watching':\r\n return to === 'pending' || to === 'waiting';\r\n case 'pending':\r\n return to === 'waiting';\r\n }\r\n }\r\n}","import { GLOBAL_STATE } from './globals';\r\nimport { Computed } from './models/computed';\r\nimport { State } from './models/state';\r\nimport { Watcher } from './models/watcher';\r\nimport { PRIVATE } from './private-symbol';\r\n\r\n/**\r\n * Executes a function without tracking any dependencies.\r\n * @param fn - The function to execute without tracking.\r\n * @returns The result of the function execution.\r\n */\r\nexport function untrack<T>(fn: () => T): T {\r\n const prevComputing = GLOBAL_STATE.computing;\r\n GLOBAL_STATE.computing = null;\r\n\r\n try {\r\n return fn();\r\n } finally {\r\n GLOBAL_STATE.computing = prevComputing;\r\n }\r\n}\r\n\r\n/**\r\n * Returns the currently active `Computed` instance being evaluated, or `null`\r\n * @returns The currently active `Computed` instance, or `null` if none is being evaluated.\r\n */\r\nexport function currentComputed(): Computed | null {\r\n return GLOBAL_STATE.computing;\r\n}\r\n\r\n/**\r\n * Returns the ordered list of all Signals which the given `Computed` or\r\n * `Watcher` referenced during its last evaluation.\r\n *\r\n * - For a `Computed`, these are the Signals read inside its callback.\r\n * - For a `Watcher`, these are the Signals it is currently watching.\r\n *\r\n * @param s - The `Computed` or `Watcher` to introspect.\r\n * @returns An array of `State` and `Computed` instances.\r\n */\r\nexport function introspectSources(s: Computed | Watcher): (State | Computed)[] {\r\n return s.getSources(PRIVATE);\r\n}\r\n\r\n/**\r\n * Returns the direct dependents of the given Signal — Watchers that contain\r\n * it, plus any `Computed` Signals which read it during their last evaluation\r\n * (if that `Computed` is recursively watched).\r\n *\r\n * @param signal - The `State` or `Computed` Signal to introspect.\r\n * @returns An array of `Computed` and `Watcher` instances.\r\n */\r\nexport function introspectSinks(signal: State | Computed): (Computed | Watcher)[] {\r\n return signal.getSinks(PRIVATE);\r\n}\r\n\r\n/**\r\n * Returns `true` if the given Signal is 'live' — i.e. it is watched by a\r\n * `Watcher`, or it is read by a `Computed` Signal which is (recursively)\r\n * live.\r\n *\r\n * @param signal - The `State` or `Computed` Signal to check.\r\n * @returns `true` if the Signal has at least one sink.\r\n */\r\nexport function hasSinks(signal: State | Computed): boolean {\r\n return signal.getSinks(PRIVATE).length > 0;\r\n}\r\n\r\n/**\r\n * Returns `true` if the given node is 'reactive' — i.e. it depends on some\r\n * other Signal. A `Computed` where `hasSources` is `false` will always\r\n * return the same constant.\r\n *\r\n * @param signal - The `Computed` or `Watcher` to check.\r\n * @returns `true` if the node has at least one source.\r\n */\r\nexport function hasSources(signal: Computed | Watcher): boolean {\r\n return signal.getSources(PRIVATE).length > 0;\r\n}","import { Computed } from './models/computed';\r\nimport { State } from './models/state';\r\nimport { Watcher } from './models/watcher';\r\nimport { currentComputed, hasSinks, hasSources, introspectSinks, introspectSources, untrack } from './subtle';\r\n\r\n/**\r\n * Loads the Signals library by defining the `Signal` global object with the following properties:\r\n * - `State`: The `State` class for creating reactive state variables.\r\n * - `Computed`: The `Computed` class for creating derived reactive values.\r\n * - `Watcher`: The `Watcher` class for observing changes in signals.\r\n * - `subtle`: An object containing internal utility functions for working with signals, including:\r\n * - `untrack`: Executes a function without tracking dependencies.\r\n * - `currentComputed`: Returns the currently active `Computed` instance being evaluated, or `null` if none.\r\n * - `introspectSources`: Returns the list of sources for a given `Computed` or `Watcher`.\r\n * - `introspectSinks`: Returns the list of sinks for a given `State` or `Computed`.\r\n * - `hasSinks`: Returns `true` if a given `State` or `Computed` has at least one sink.\r\n * - `hasSources`: Returns `true` if a given `Computed` or `Watcher` has at least one source.\r\n * - `Watcher`: The `Watcher` class for observing changes in signals.\r\n * \r\n * This function should be called once to initialize the Signals library and make its API available globally.\r\n */\r\nexport function loadSignals(): void {\r\n globalThis.Signal ??= {\r\n State,\r\n Computed,\r\n subtle: {\r\n untrack,\r\n currentComputed,\r\n introspectSources: introspectSources as unknown as typeof Signal.subtle.introspectSources,\r\n introspectSinks: introspectSinks as unknown as typeof Signal.subtle.introspectSinks,\r\n hasSinks: hasSinks as unknown as typeof Signal.subtle.hasSinks,\r\n hasSources: hasSources as unknown as typeof Signal.subtle.hasSources,\r\n Watcher\r\n }\r\n }\r\n}"],"mappings":"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"}
|