C# port of stackblitz/alien-signals 3.1.2. Line-for-line, same algorithm, same flags, same DLL semantics.
alien-signals is a small reactive-signals library: signals, computed
values, effects, an automatic dependency graph with pull-on-read and
push-mark-dirty propagation. It was previously available only on
JavaScript runtimes via npm. This artifact ports the same algorithm to
.NET so any C#, F#, or ClojureCLR consumer can use it without a JS
bridge.
src/AlienSignals/
ReactiveFlags.cs Bit-flag constants
ReactiveNode.cs Base node (Deps, DepsTail, Subs, SubsTail, Flags)
Link.cs Doubly-linked-list edge between dep and sub
ReactiveSystem.cs Algorithm core: Link, Unlink, Propagate,
CheckDirty, ShallowPropagate, StartTracking,
EndTracking (port of system.ts)
SignalNode.cs Generic Value + PreviousValue
ComputedNode.cs Generic Value + Getter
EffectNode.cs Fn
Signal.cs User-facing Get / Set
Computed.cs User-facing Get
Effect.cs User-facing Stop
Signals.cs Static facade: Signal, Computed, Effect, Batch,
Untrack, EffectScope, Trigger (port of index.ts)
using Hbt.AlienSignals;
var a = Signals.Signal(1);
var b = Signals.Computed<int>(prev => a.Get() * 2);
var e = Signals.Effect(() => Console.WriteLine(b.Get()));
a.Set(5); // prints 10
Signals.Batch(() => {
a.Set(7);
a.Set(9);
}); // prints 18 once
e.Stop();dotnet build
dotnet test
# Passed! - Failed: 0, Passed: 2121 xUnit tests cover the load-bearing cases ported from upstream's Vitest spec files:
- signal read/write semantics, value-equality skip
- computed memoisation and chained propagation
- effect lifecycle (eager run, dispose, nested stop)
- batch coalescing
- untrack via
Signals.Untrackand via directSetActiveSub - diamond topology — both signal-only and computed-via-checkDirty
- signal-revert: writing then reverting must NOT force recompute
- effectScope dispose
Single-threaded, same as upstream. Static state on Signals
(activeSub, activeScope, batchDepth, notify queue) is not
synchronised. Use Signals.Reset() to clear state between tests or
REPL evaluations within a single process.
- Generics carry the value type (
SignalNode<T>,ComputedNode<T>). The engine callbacks see non-genericReactiveNodeand reach the closed-genericUpdate*Generic<T>methods via reflection. Cold paths only. static Signalsuses a static constructor for lazy initialisation of the algorithm core, accommodating the unwatched callback's self-reference.- Targets
net6.0. Any modern .NET (Core 3.0+, .NET 5+, 6+, 8+) consumes the resultingHbt.AlienSignals.dll.
Tracks upstream npm. When stackblitz publishes a new version, this artifact moves to the same version with the corresponding system.ts and index.ts changes ported.
MIT — same as upstream.