From c833d4a0535fddfeac0b1ecc8c56df4b8374661d Mon Sep 17 00:00:00 2001 From: "m.samson" Date: Wed, 4 Mar 2026 20:46:21 +0200 Subject: [PATCH 1/3] - Improved Global Tracker Flow Performance - Fix Bug multiple StateOf had its change callback overriten when different state used different routes. --- doc/docs/0.Versions.md | 11 +++ src/Directory.Packages.props | 2 +- .../Implementations/DispatcherPrepper.cs | 4 +- .../Implementations/PulseGlobalTracker.cs | 55 +++++++++-- .../PulseLazyStateBlazorServer.cs | 11 --- .../PulseLazyStateWebAssembly.cs | 13 --- .../Engine/PulseLazyStateBase.cs | 95 +++++++++++-------- .../Engine/StateCallbackBinder.cs | 73 ++++++++++++++ src/StatePulse.NET/ServiceRegisterExt.cs | 6 +- .../IPulseGlobalTracker.cs | 2 + .../InitializerTests/StatePulseInitTests.cs | 2 +- .../Pages/ComponentTest.razor | 1 + .../Pages/ComponentTest.razor.cs | 12 +++ .../Pages/Counter.razor | 15 ++- .../Pages/Counter.razor.cs | 57 ++++++++++- .../Counter/Effects/CounterChangeEffect.cs | 11 +-- ...tePulse.Net.BlazorServerTest.Client.csproj | 2 +- .../StatePulse.Net.BlazorServerTest.csproj | 2 +- 18 files changed, 281 insertions(+), 93 deletions(-) delete mode 100644 src/StatePulse.NET/Engine/Implementations/PulseLazyStateBlazorServer.cs delete mode 100644 src/StatePulse.NET/Engine/Implementations/PulseLazyStateWebAssembly.cs create mode 100644 src/StatePulse.NET/Engine/StateCallbackBinder.cs create mode 100644 tests/StatePulse.ManualTests/StatePulse.Net.BlazorServerTest.Client/Pages/ComponentTest.razor create mode 100644 tests/StatePulse.ManualTests/StatePulse.Net.BlazorServerTest.Client/Pages/ComponentTest.razor.cs diff --git a/doc/docs/0.Versions.md b/doc/docs/0.Versions.md index 9275bfd..18368c4 100644 --- a/doc/docs/0.Versions.md +++ b/doc/docs/0.Versions.md @@ -3,6 +3,17 @@ slug: versions title: Updates sidebar_position: 0 --- + +## v2.1.0 +### Performance +- The Global Tracker now uses an event‑based mechanism to invoke Self Disposal Check directly on each `IStatePulse` instance, removing the need to iterate the internal registry. The registry itself is scheduled for removal in version 3.0 and is now marked with the appropriate `Obsolete` attribute. Cleanup is no longer driven by a continuous loop; instead, the tracker schedules a self‑check only when activity occurs in the registry and goes idle when the application becomes inactive. This preserves memory‑leak protection without a permanent cycle. A minimum delay of 10 seconds is enforced between checks, and when burst activity occurs, only the final activity triggers a single final cleanup run rather than multiple redundant executions. + +### Fix +This is one of those “bug or feature?” situations. By design, `StateOf()` is meant to notify the component when its state changes. However, earlier Blazor assumptions overlooked scenarios where one state might render through one route while another state uses a different route. + +Before version 2.0.11, StatePulse always treated the latest route as the default route for all states registered by a component, this is was **BUG**. Starting with 2.0.11, each state that a component subscribes to StatePulse now respects those route differences if any. + + ## v2.0.1 ### Fixes - Moved Fluent API Extension Methods into Abstraction Package. diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 2739a24..7c8eaea 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -1,6 +1,6 @@ - 2.0.1 + 2.0.114 Maksim Shimshon © 2026 diff --git a/src/StatePulse.NET/Engine/Implementations/DispatcherPrepper.cs b/src/StatePulse.NET/Engine/Implementations/DispatcherPrepper.cs index 2cfb573..cd905c0 100644 --- a/src/StatePulse.NET/Engine/Implementations/DispatcherPrepper.cs +++ b/src/StatePulse.NET/Engine/Implementations/DispatcherPrepper.cs @@ -87,10 +87,8 @@ protected async Task ProcessDispatch(bool entryPoint, Guid nextId) Version = _currentVersion, Tracker = () => _tracker }; - bool preCancel = _tracker.CreateExecutingAction(nextChain.Id, this, nextChain.Version); + _tracker.CreateExecutingAction(nextChain.Id, this, nextChain.Version); nextChain.TrackedEntry = _tracker.CreateEntryPoint(nextChain.Id, this); - if (!preCancel) - return; } diff --git a/src/StatePulse.NET/Engine/Implementations/PulseGlobalTracker.cs b/src/StatePulse.NET/Engine/Implementations/PulseGlobalTracker.cs index 4deae93..61ca0e9 100644 --- a/src/StatePulse.NET/Engine/Implementations/PulseGlobalTracker.cs +++ b/src/StatePulse.NET/Engine/Implementations/PulseGlobalTracker.cs @@ -1,11 +1,25 @@ namespace StatePulse.Net.Engine.Implementations; using StatePulse.Net; + + internal class PulseGlobalTracker : IPulseGlobalTracker { + private delegate void SelfCheckRequired(); private readonly object _lock = new(); + /// + /// TODO: V3.0 Change to _notifyForSelfCheck?.GetInvocationList().Length ?? 0 + /// public int ActivePulsars { get => _registry.Count; } + /// + /// TODO: REMOVE AT 3.0 + /// private readonly List _registry = new(); + private readonly SemaphoreSlim _signal = new SemaphoreSlim(0); + private event SelfCheckRequired? _notifyForSelfCheck; + private bool _pendingCheck; + private bool _isLoopRunning; + private IReadOnlyList _readRegistry { get @@ -17,30 +31,59 @@ private IReadOnlyList _readRegistry public IReadOnlyCollection Registered => _registry; - private readonly Timer _timer; public event EventHandler? onAfterCleanUp; public PulseGlobalTracker() { - _timer = new Timer(GarbageCollecting, null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(10)); + _ = Task.Run(async () => await GarbageCollecting()); } public void Register(IStatePulse pulsar) { lock (_lock) + { + _notifyForSelfCheck += pulsar.SelfDisposeCheck; _registry.Add(pulsar); + if (_isLoopRunning) + _pendingCheck = true; + else + _signal.Release(); + } + + } public void UnRegister(IStatePulse pulsar) { lock (_lock) + { + _notifyForSelfCheck -= pulsar.SelfDisposeCheck; _registry.Remove(pulsar); + if (_isLoopRunning) + _pendingCheck = true; + else + _signal.Release(); + } } - private void GarbageCollecting(object? _) + private async Task GarbageCollecting() { - foreach (var item in _readRegistry) - item.SelfDisposeCheck(); - onAfterCleanUp?.Invoke(this, new()); + do + { + if (!_pendingCheck) + await _signal.WaitAsync(); + if (_isLoopRunning) + continue; // Already running → coalesce + lock (_lock) + _pendingCheck = false; + + _isLoopRunning = true; + _notifyForSelfCheck?.Invoke(); + onAfterCleanUp?.Invoke(this, new()); + // Enforce a minimum delay before checking again + await Task.Delay(TimeSpan.FromSeconds(10)); + _isLoopRunning = false; + } while (true); + } } diff --git a/src/StatePulse.NET/Engine/Implementations/PulseLazyStateBlazorServer.cs b/src/StatePulse.NET/Engine/Implementations/PulseLazyStateBlazorServer.cs deleted file mode 100644 index 96ac7bf..0000000 --- a/src/StatePulse.NET/Engine/Implementations/PulseLazyStateBlazorServer.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Concurrent; -namespace StatePulse.Net.Engine.Implementations; - -internal sealed class PulseLazyStateBlazorServer : PulseLazyStateBase -{ - private readonly ConcurrentDictionary _stash = new(); - public PulseLazyStateBlazorServer(IServiceProvider services) : base(services) - { - } - protected override IDictionary GetState() => _stash; -} diff --git a/src/StatePulse.NET/Engine/Implementations/PulseLazyStateWebAssembly.cs b/src/StatePulse.NET/Engine/Implementations/PulseLazyStateWebAssembly.cs deleted file mode 100644 index 3b54cfc..0000000 --- a/src/StatePulse.NET/Engine/Implementations/PulseLazyStateWebAssembly.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace StatePulse.Net.Engine.Implementations; - -using System; -internal sealed class PulseLazyStateWebAssembly : PulseLazyStateBase -{ - private readonly Dictionary _stash = new(); - - public PulseLazyStateWebAssembly(IServiceProvider services) : base(services) - { - } - - protected override IDictionary GetState() => _stash; -} \ No newline at end of file diff --git a/src/StatePulse.NET/Engine/PulseLazyStateBase.cs b/src/StatePulse.NET/Engine/PulseLazyStateBase.cs index 84e0ba9..4f05de4 100644 --- a/src/StatePulse.NET/Engine/PulseLazyStateBase.cs +++ b/src/StatePulse.NET/Engine/PulseLazyStateBase.cs @@ -4,17 +4,20 @@ namespace StatePulse.Net.Engine; -internal abstract class PulseLazyStateBase : IStatePulse +/// +/// TODO: Create a SP Binder Object which will bind TState to Callback and stash is multiple different methods can used to trigger a call. +/// +internal class PulseLazyStateBase : IStatePulse { private bool _disposed; private readonly IServiceProvider _services; private readonly IPulseGlobalTracker _globalStash; private WeakReference _instance = new WeakReference(default); - private Func _compiledListener = default!; - + private readonly object _lock = new(); public IDispatcher Dispatcher { get; private set; } + private readonly Dictionary _stash = new(); - protected PulseLazyStateBase(IServiceProvider services) + public PulseLazyStateBase(IServiceProvider services) { _services = services ?? throw new ArgumentNullException(nameof(services)); _globalStash = services.GetRequiredService(); @@ -25,27 +28,8 @@ protected PulseLazyStateBase(IServiceProvider services) /// /// /// - protected virtual IDictionary GetState() => throw new NotImplementedException(); - private TState StateOf(Func getInstance) where TState : IStateFeature - { - var instance = getInstance(); - var type = typeof(TState); - if (!_instance.TryGetTarget(out var target) || !ReferenceEquals(target, instance)) - _instance = new(instance); - if (GetState().TryGetValue(type, out var existing)) - return ((IStateAccessor)existing).State; - else - { - var accessorType = typeof(IStateAccessor<>).MakeGenericType(type); - var service = _services.GetRequiredService(accessorType); - GetState().TryAdd(type, service); - var accessor = (IStateAccessor)service; - _globalStash.Register(this); - accessor.OnStateChangedNoDetails -= OnStateChanged; - accessor.OnStateChangedNoDetails += OnStateChanged; - return accessor.State; - } - } + protected virtual IDictionary GetState() => _stash; + public bool IsReferenceAlive() => _instance.TryGetTarget(out var _); public void SelfDisposeCheck() { @@ -55,30 +39,65 @@ public void SelfDisposeCheck() return; } } - private void OnStateChanged(object? sender, EventArgs Args) + + private WeakReference CheckInstanceAlive() { if (!_instance.TryGetTarget(out var target)) { Dispose(); - return; } - _compiledListener(target); + return _instance; } - public void Dispose() + + public TState StateOf(Func getInstance, Func onStateChanged) where TState : IStateFeature + { + var instance = getInstance(); + if (!_instance.TryGetTarget(out var target) || !ReferenceEquals(target, instance)) + _instance = new(instance); + + IStateCallbackBinder? binder = default; + Type stateType = typeof(TState); + lock (_lock) + { + if (GetState().ContainsKey(stateType)) + binder = GetState()[stateType]; + } + if (binder == default) + { + var c = onStateChanged.GetMethodInfoOrThrow(); + var service = _services.GetRequiredService>(); + binder = new StateCallbackBinder(service) + { + Callback = c.CreateDynamicInvoker(), + CheckIfInstanceAlive = CheckInstanceAlive, + }; + lock (_lock) + { + GetState().TryAdd(stateType, binder); + _globalStash.Register(this); + } + } + + return binder.GetStateAs(); + + } + + protected virtual void Dispose(bool disposing) { if (_disposed) return; - _disposed = true; - _globalStash.UnRegister(this); - foreach (var item in GetState().Values.Select(p => (IStateAccessor)p)) - item.OnStateChangedNoDetails -= OnStateChanged; + if (disposing) + { + _globalStash.UnRegister(this); + foreach (var item in GetState().Values) + item.Dispose(); + } + _disposed = true; } - public TState StateOf(Func getInstance, Func onStateChanged) where TState : IStateFeature + public void Dispose() { - var instance = getInstance(); - var c = onStateChanged.GetMethodInfoOrThrow(); - _compiledListener = c.CreateDynamicInvoker(); - return StateOf(() => instance); + Dispose(disposing: true); + GC.SuppressFinalize(this); } } diff --git a/src/StatePulse.NET/Engine/StateCallbackBinder.cs b/src/StatePulse.NET/Engine/StateCallbackBinder.cs new file mode 100644 index 0000000..3b70275 --- /dev/null +++ b/src/StatePulse.NET/Engine/StateCallbackBinder.cs @@ -0,0 +1,73 @@ +namespace StatePulse.Net.Engine; + +internal interface IStateCallbackBinder : IDisposable +{ + Type StateType { get; } + object GetState(); + T GetStateAs(); + Task TriggerCallback(); +} +internal sealed record StateCallbackBinder : IStateCallbackBinder +{ + public Func Callback { get; init; } = default!; + public Type StateType { get; } + public Func> CheckIfInstanceAlive = default!; + private bool _disposedValue; + + public IStateAccessor StateAccess { get; } + + public StateCallbackBinder(IStateAccessor stateAccessor) + { + StateType = typeof(TState); + StateAccess = stateAccessor; + StateAccess.OnStateChangedNoDetails += OnStateChanged; + } + public bool Equals(StateCallbackBinder? other) + { + if (other is null) + return false; + + // Equality based ONLY on the Type + return StateType == other.StateType; + } + + public override int GetHashCode() + { + // Hash based ONLY on the Type + return StateType.GetHashCode(); + } + + public object GetState() => StateAccess.State!; + public T GetStateAs() => (T)GetState(); + public Task TriggerCallback() => throw new NotImplementedException(); + public void OnStateChanged(object? sender, EventArgs args) + { + var instance = CheckIfInstanceAlive?.Invoke() ?? default; + if (instance != default && instance.TryGetTarget(out var target)) + { + _ = Callback(target); + } + } + + private void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + StateAccess.OnStateChangedNoDetails -= OnStateChanged; + } + + // TODO: free unmanaged resources (unmanaged objects) and override finalizer + // TODO: set large fields to null + _disposedValue = true; + } + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } +} diff --git a/src/StatePulse.NET/ServiceRegisterExt.cs b/src/StatePulse.NET/ServiceRegisterExt.cs index 1bccc5d..d29efb0 100644 --- a/src/StatePulse.NET/ServiceRegisterExt.cs +++ b/src/StatePulse.NET/ServiceRegisterExt.cs @@ -23,10 +23,8 @@ public static IServiceCollection AddStatePulseServices(this IServiceCollection s configure(ConfigureOptions); bool isSingleThreadModel = ConfigureOptions.PulseTrackingPerformance == PulseTrackingModel.SingleThreadFast || ConfigureOptions.PulseTrackingPerformance == PulseTrackingModel.BlazorWebAssemblyFast; - if (isSingleThreadModel) - services.AddTransient(); - else - services.AddTransient(); + services.AddTransient(); + services.AddScoped(); services.AddSingleton(Registry); diff --git a/src/StatePulse.Net.Abstractions/IPulseGlobalTracker.cs b/src/StatePulse.Net.Abstractions/IPulseGlobalTracker.cs index 9d78f34..152b974 100644 --- a/src/StatePulse.Net.Abstractions/IPulseGlobalTracker.cs +++ b/src/StatePulse.Net.Abstractions/IPulseGlobalTracker.cs @@ -1,9 +1,11 @@ namespace StatePulse.Net; + public interface IPulseGlobalTracker { int ActivePulsars { get; } void Register(IStatePulse pulsar); void UnRegister(IStatePulse pulsar); + [Obsolete("Will be removed in in V3.0 Do not use or rely on this")] IReadOnlyCollection Registered { get; } event EventHandler? onAfterCleanUp; } diff --git a/tests/StatPulse.NET.Tests/TestCases/InitializerTests/StatePulseInitTests.cs b/tests/StatPulse.NET.Tests/TestCases/InitializerTests/StatePulseInitTests.cs index bd77a52..9fb14ef 100644 --- a/tests/StatPulse.NET.Tests/TestCases/InitializerTests/StatePulseInitTests.cs +++ b/tests/StatPulse.NET.Tests/TestCases/InitializerTests/StatePulseInitTests.cs @@ -50,7 +50,7 @@ public async Task DispatchingActionShouldChangeStateUsingStateOf() var state = () => sp.StateOf(() => this, OnUpdate); // Dispatch action that changes state var action = new ProfileCardDefineAction(); - await dispatcher.Prepare(() => action).Await().DispatchAsync(); + await dispatcher.Prepared(action).Await().DispatchAsync(); Assert.Equal("Maksim Shimshon", state().ProfileName); } diff --git a/tests/StatePulse.ManualTests/StatePulse.Net.BlazorServerTest.Client/Pages/ComponentTest.razor b/tests/StatePulse.ManualTests/StatePulse.Net.BlazorServerTest.Client/Pages/ComponentTest.razor new file mode 100644 index 0000000..b9132a2 --- /dev/null +++ b/tests/StatePulse.ManualTests/StatePulse.Net.BlazorServerTest.Client/Pages/ComponentTest.razor @@ -0,0 +1 @@ +@State.Count diff --git a/tests/StatePulse.ManualTests/StatePulse.Net.BlazorServerTest.Client/Pages/ComponentTest.razor.cs b/tests/StatePulse.ManualTests/StatePulse.Net.BlazorServerTest.Client/Pages/ComponentTest.razor.cs new file mode 100644 index 0000000..c3ab70b --- /dev/null +++ b/tests/StatePulse.ManualTests/StatePulse.Net.BlazorServerTest.Client/Pages/ComponentTest.razor.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.Components; +using StatePulse.Net.BlazorServerTest.Client.Pulses.Counter.States; + +namespace StatePulse.Net.BlazorServerTest.Client.Pages; + +public partial class ComponentTest : ComponentBase +{ + [Inject] IStatePulse StatePulse { get; set; } = default!; + private CounterState State => StatePulse.StateOf(() => this, OnUpdate); + private async Task OnUpdate() => await InvokeAsync(StateHasChanged); + +} diff --git a/tests/StatePulse.ManualTests/StatePulse.Net.BlazorServerTest.Client/Pages/Counter.razor b/tests/StatePulse.ManualTests/StatePulse.Net.BlazorServerTest.Client/Pages/Counter.razor index 13debff..4748ac2 100644 --- a/tests/StatePulse.ManualTests/StatePulse.Net.BlazorServerTest.Client/Pages/Counter.razor +++ b/tests/StatePulse.ManualTests/StatePulse.Net.BlazorServerTest.Client/Pages/Counter.razor @@ -25,4 +25,17 @@ @foreach (var t in StatePulseRegistry.KnownStateToAccessors) {

@t.Value.FullName

-} \ No newline at end of file +} + + + + + +@if (Visible) +{ + +} +@foreach (var item in _lastChecks) +{ + @item.ToString() +} diff --git a/tests/StatePulse.ManualTests/StatePulse.Net.BlazorServerTest.Client/Pages/Counter.razor.cs b/tests/StatePulse.ManualTests/StatePulse.Net.BlazorServerTest.Client/Pages/Counter.razor.cs index 396e03d..55b592a 100644 --- a/tests/StatePulse.ManualTests/StatePulse.Net.BlazorServerTest.Client/Pages/Counter.razor.cs +++ b/tests/StatePulse.ManualTests/StatePulse.Net.BlazorServerTest.Client/Pages/Counter.razor.cs @@ -14,9 +14,11 @@ public partial class Counter : ComponentBase private CounterSingletonState Shared => StatePulse.StateOf(() => this, OnUpdate); private CounterState State => StatePulse.StateOf(() => this, OnUpdate); private async Task OnUpdate() => await InvokeAsync(StateHasChanged); - + private CancellationTokenSource? _leakTestCTS; protected override void OnInitialized() { + PulseGlobalTracker.onAfterCleanUp += OnStateChanged; + } private async Task SingletonIncrease() { @@ -35,13 +37,60 @@ private async Task Increase() Random d = new Random(); foreach (var item in stressvalues) { - await StatePulse.Dispatcher .Prepare() - .With(p => p.NewCounter, 100) - .DispatchAsync(true); + .With(p => p.NewCounter, item) + .DispatchAsync(); } } + + + List _lastChecks = new(); + public void OnStateChanged(object? _, EventArgs __) + { + _lastChecks.Add(DateTime.Now); + InvokeAsync(StateHasChanged); + } + public async Task CancelMassTest() + { + if (_leakTestCTS != default) + _leakTestCTS.Cancel(); + } + public async Task MassTest() + { + if (_leakTestCTS != default) + { + _ = CancelMassTest(); + return; + } + _leakTestCTS = new(); + do + { + await Task.Delay(10); + Visible = true; + StateHasChanged(); + await Task.Delay(10); + Visible = false; + StateHasChanged(); + } while (_leakTestCTS != default && !_leakTestCTS.Token.IsCancellationRequested); + + _leakTestCTS = default; + } + public bool Visible { get; set; } + private Task ForceGC() + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + StateHasChanged(); + return Task.CompletedTask; + } + + public void Dispose() + { + PulseGlobalTracker.onAfterCleanUp -= OnStateChanged; + + } } diff --git a/tests/StatePulse.ManualTests/StatePulse.Net.BlazorServerTest.Client/Pulses/Counter/Effects/CounterChangeEffect.cs b/tests/StatePulse.ManualTests/StatePulse.Net.BlazorServerTest.Client/Pulses/Counter/Effects/CounterChangeEffect.cs index 4c9abb2..21ab014 100644 --- a/tests/StatePulse.ManualTests/StatePulse.Net.BlazorServerTest.Client/Pulses/Counter/Effects/CounterChangeEffect.cs +++ b/tests/StatePulse.ManualTests/StatePulse.Net.BlazorServerTest.Client/Pulses/Counter/Effects/CounterChangeEffect.cs @@ -8,18 +8,11 @@ public async Task EffectAsync(CounterChangeAction action, IDispatcher dispatcher { var rnd = new Random(); await Task.Delay(rnd.Next(10, 100)); - try - { - - } - catch (Exception) - { - - throw; - } await dispatcher.Prepare() .With(p => p.NewCounter, action.NewCounter) .DispatchAsync(); await dispatcher.Prepare().With(p => p.NewCounter, action.NewCounter).DispatchAsync(); + + } } diff --git a/tests/StatePulse.ManualTests/StatePulse.Net.BlazorServerTest.Client/StatePulse.Net.BlazorServerTest.Client.csproj b/tests/StatePulse.ManualTests/StatePulse.Net.BlazorServerTest.Client/StatePulse.Net.BlazorServerTest.Client.csproj index d62490f..f7e9032 100644 --- a/tests/StatePulse.ManualTests/StatePulse.Net.BlazorServerTest.Client/StatePulse.Net.BlazorServerTest.Client.csproj +++ b/tests/StatePulse.ManualTests/StatePulse.Net.BlazorServerTest.Client/StatePulse.Net.BlazorServerTest.Client.csproj @@ -12,7 +12,7 @@ - + diff --git a/tests/StatePulse.ManualTests/StatePulse.Net.BlazorServerTest/StatePulse.Net.BlazorServerTest.csproj b/tests/StatePulse.ManualTests/StatePulse.Net.BlazorServerTest/StatePulse.Net.BlazorServerTest.csproj index b73e549..965c08e 100644 --- a/tests/StatePulse.ManualTests/StatePulse.Net.BlazorServerTest/StatePulse.Net.BlazorServerTest.csproj +++ b/tests/StatePulse.ManualTests/StatePulse.Net.BlazorServerTest/StatePulse.Net.BlazorServerTest.csproj @@ -9,7 +9,7 @@ - + From 4095ea60695c39af2bcf8f4194fc51475caa1c8c Mon Sep 17 00:00:00 2001 From: "m.samson" Date: Wed, 4 Mar 2026 20:48:14 +0200 Subject: [PATCH 2/3] - Updated the Doc --- doc/build/404.html | 2 +- ...22dd74f7.993ce3a5.js => 22dd74f7.35cfaf6e.js} | 2 +- doc/build/assets/js/94cf9043.78d41392.js | 1 - doc/build/assets/js/94cf9043.a31f232c.js | 1 + ...main.b23f14dc.js => runtime~main.ff5087c2.js} | 2 +- doc/build/blog/authors/index.html | 2 +- doc/build/blog/authors/mshimshon/index.html | 2 +- doc/build/gs-state/index.html | 2 +- doc/build/gs-the-action/index.html | 2 +- doc/build/gs-the-dispatcher/index.html | 2 +- doc/build/gs-the-effect/index.html | 2 +- doc/build/gs-the-middlewares/index.html | 2 +- doc/build/gs-the-reducer/index.html | 2 +- doc/build/index.html | 2 +- doc/build/markdown-page/index.html | 2 +- doc/build/setup-blazor-project/index.html | 2 +- doc/build/tags/actions/index.html | 2 +- doc/build/tags/async/index.html | 2 +- doc/build/tags/await/index.html | 2 +- doc/build/tags/blazor/index.html | 2 +- doc/build/tags/csharp/index.html | 2 +- doc/build/tags/dependency-injection/index.html | 2 +- doc/build/tags/dispatcher/index.html | 2 +- doc/build/tags/effects/index.html | 2 +- doc/build/tags/immutable/index.html | 2 +- doc/build/tags/index.html | 2 +- doc/build/tags/installation/index.html | 2 +- doc/build/tags/isafeaction/index.html | 2 +- doc/build/tags/net/index.html | 2 +- doc/build/tags/performance/index.html | 2 +- doc/build/tags/pure-functions/index.html | 2 +- doc/build/tags/reducer/index.html | 2 +- doc/build/tags/redux/index.html | 2 +- doc/build/tags/safedispatch/index.html | 2 +- doc/build/tags/setup/index.html | 2 +- doc/build/tags/side-effects/index.html | 2 +- doc/build/tags/state-management/index.html | 2 +- doc/build/tags/state/index.html | 2 +- doc/build/tags/statepulse/index.html | 2 +- doc/build/versions/index.html | 16 ++++++++++++---- 40 files changed, 50 insertions(+), 42 deletions(-) rename doc/build/assets/js/{22dd74f7.993ce3a5.js => 22dd74f7.35cfaf6e.js} (97%) delete mode 100644 doc/build/assets/js/94cf9043.78d41392.js create mode 100644 doc/build/assets/js/94cf9043.a31f232c.js rename doc/build/assets/js/{runtime~main.b23f14dc.js => runtime~main.ff5087c2.js} (97%) diff --git a/doc/build/404.html b/doc/build/404.html index 243db4c..01149f6 100644 --- a/doc/build/404.html +++ b/doc/build/404.html @@ -4,7 +4,7 @@ StatePulse.NET - + diff --git a/doc/build/assets/js/22dd74f7.993ce3a5.js b/doc/build/assets/js/22dd74f7.35cfaf6e.js similarity index 97% rename from doc/build/assets/js/22dd74f7.993ce3a5.js rename to doc/build/assets/js/22dd74f7.35cfaf6e.js index 8b8ba82..2e22a6e 100644 --- a/doc/build/assets/js/22dd74f7.993ce3a5.js +++ b/doc/build/assets/js/22dd74f7.35cfaf6e.js @@ -1 +1 @@ -"use strict";(self.webpackChunkstatepulse_doc=self.webpackChunkstatepulse_doc||[]).push([[1567],{5226:e=>{e.exports=JSON.parse('{"version":{"pluginId":"default","version":"current","label":"Next","banner":null,"badge":false,"noIndex":false,"className":"docs-version-current","isLast":true,"docsSidebars":{"tutorialSidebar":[{"type":"link","label":"Updates","href":"/versions","docId":"Versions","unlisted":false},{"type":"link","label":"Get Started","href":"/","docId":"Getting Started","unlisted":false},{"type":"link","label":"Setup Blazor Project","href":"/setup-blazor-project","docId":"Setup Blazor Project","unlisted":false},{"type":"link","label":"The Actions","href":"/gs-the-action","docId":"Creating Actions","unlisted":false},{"type":"link","label":"The States","href":"/gs-state","docId":"Create State","unlisted":false},{"type":"link","label":"The Effects","href":"/gs-the-effect","docId":"4 Create Effects","unlisted":false},{"type":"link","label":"The Reducers","href":"/gs-the-reducer","docId":"Create Reducer","unlisted":false},{"type":"link","label":"The Dispatcher","href":"/gs-the-dispatcher","docId":"The Dispatcher","unlisted":false},{"type":"link","label":"The Middlewares","href":"/gs-the-middlewares","docId":"Middlewares","unlisted":false}]},"docs":{"4 Create Effects":{"id":"4 Create Effects","title":"The Effects","description":"What are Effects","sidebar":"tutorialSidebar"},"Create Reducer":{"id":"Create Reducer","title":"The Reducers","description":"Reducers \u2013 Pure State Updates","sidebar":"tutorialSidebar"},"Create State":{"id":"Create State","title":"The States","description":"Defining a State","sidebar":"tutorialSidebar"},"Creating Actions":{"id":"Creating Actions","title":"The Actions","description":"Type of Actions","sidebar":"tutorialSidebar"},"Getting Started":{"id":"Getting Started","title":"Get Started","description":"License: MIT","sidebar":"tutorialSidebar"},"Middlewares":{"id":"Middlewares","title":"The Middlewares","description":"\u2699\ufe0f What are Middlewares?","sidebar":"tutorialSidebar"},"Setup Blazor Project":{"id":"Setup Blazor Project","title":"Setup Blazor Project","description":"\ud83d\udce6 Installation & Setup","sidebar":"tutorialSidebar"},"The Dispatcher":{"id":"The Dispatcher","title":"The Dispatcher","description":"Dispatcher \u2013 Executing Actions in StatePulse","sidebar":"tutorialSidebar"},"Versions":{"id":"Versions","title":"Updates","description":"v2.0.1","sidebar":"tutorialSidebar"}}}}')}}]); \ No newline at end of file +"use strict";(self.webpackChunkstatepulse_doc=self.webpackChunkstatepulse_doc||[]).push([[1567],{5226:e=>{e.exports=JSON.parse('{"version":{"pluginId":"default","version":"current","label":"Next","banner":null,"badge":false,"noIndex":false,"className":"docs-version-current","isLast":true,"docsSidebars":{"tutorialSidebar":[{"type":"link","label":"Updates","href":"/versions","docId":"Versions","unlisted":false},{"type":"link","label":"Get Started","href":"/","docId":"Getting Started","unlisted":false},{"type":"link","label":"Setup Blazor Project","href":"/setup-blazor-project","docId":"Setup Blazor Project","unlisted":false},{"type":"link","label":"The Actions","href":"/gs-the-action","docId":"Creating Actions","unlisted":false},{"type":"link","label":"The States","href":"/gs-state","docId":"Create State","unlisted":false},{"type":"link","label":"The Effects","href":"/gs-the-effect","docId":"4 Create Effects","unlisted":false},{"type":"link","label":"The Reducers","href":"/gs-the-reducer","docId":"Create Reducer","unlisted":false},{"type":"link","label":"The Dispatcher","href":"/gs-the-dispatcher","docId":"The Dispatcher","unlisted":false},{"type":"link","label":"The Middlewares","href":"/gs-the-middlewares","docId":"Middlewares","unlisted":false}]},"docs":{"4 Create Effects":{"id":"4 Create Effects","title":"The Effects","description":"What are Effects","sidebar":"tutorialSidebar"},"Create Reducer":{"id":"Create Reducer","title":"The Reducers","description":"Reducers \u2013 Pure State Updates","sidebar":"tutorialSidebar"},"Create State":{"id":"Create State","title":"The States","description":"Defining a State","sidebar":"tutorialSidebar"},"Creating Actions":{"id":"Creating Actions","title":"The Actions","description":"Type of Actions","sidebar":"tutorialSidebar"},"Getting Started":{"id":"Getting Started","title":"Get Started","description":"License: MIT","sidebar":"tutorialSidebar"},"Middlewares":{"id":"Middlewares","title":"The Middlewares","description":"\u2699\ufe0f What are Middlewares?","sidebar":"tutorialSidebar"},"Setup Blazor Project":{"id":"Setup Blazor Project","title":"Setup Blazor Project","description":"\ud83d\udce6 Installation & Setup","sidebar":"tutorialSidebar"},"The Dispatcher":{"id":"The Dispatcher","title":"The Dispatcher","description":"Dispatcher \u2013 Executing Actions in StatePulse","sidebar":"tutorialSidebar"},"Versions":{"id":"Versions","title":"Updates","description":"v2.1.0","sidebar":"tutorialSidebar"}}}}')}}]); \ No newline at end of file diff --git a/doc/build/assets/js/94cf9043.78d41392.js b/doc/build/assets/js/94cf9043.78d41392.js deleted file mode 100644 index 7b99d6b..0000000 --- a/doc/build/assets/js/94cf9043.78d41392.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkstatepulse_doc=self.webpackChunkstatepulse_doc||[]).push([[6683],{8453:(e,n,i)=>{i.d(n,{R:()=>t,x:()=>c});var s=i(6540);const r={},l=s.createContext(r);function t(e){const n=s.useContext(l);return s.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function c(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:t(e.components),s.createElement(l.Provider,{value:n},e.children)}},8886:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>d,contentTitle:()=>c,default:()=>h,frontMatter:()=>t,metadata:()=>s,toc:()=>o});const s=JSON.parse('{"id":"Versions","title":"Updates","description":"v2.0.1","source":"@site/docs/0.Versions.md","sourceDirName":".","slug":"/versions","permalink":"/versions","draft":false,"unlisted":false,"editUrl":"https://github.com/mshimshon/StatePulse.NET/docs/0.Versions.md","tags":[],"version":"current","sidebarPosition":0,"frontMatter":{"slug":"versions","title":"Updates","sidebar_position":0},"sidebar":"tutorialSidebar","next":{"title":"Get Started","permalink":"/"}}');var r=i(4848),l=i(8453);const t={slug:"versions",title:"Updates",sidebar_position:0},c=void 0,d={},o=[{value:"v2.0.1",id:"v201",level:2},{value:"Fixes",id:"fixes",level:3},{value:"v2.0.0",id:"v200",level:2},{value:"BREAKING CHANGES",id:"breaking-changes",level:3},{value:"New Features",id:"new-features",level:3},{value:"Security",id:"security",level:3},{value:"Fixes",id:"fixes-1",level:3},{value:"v1.1.0",id:"v110",level:2},{value:"Minor Change",id:"minor-change",level:3},{value:"v1.0.2",id:"v102",level:2},{value:"Fixes",id:"fixes-2",level:3},{value:"v1.0.1",id:"v101",level:2},{value:"Minor Change",id:"minor-change-1",level:3},{value:"v1.0.0",id:"v100",level:2},{value:"New Features",id:"new-features-1",level:3},{value:"\ud83d\udca5 Breaking Changes",id:"-breaking-changes",level:3},{value:"\ud83d\ude80 Performance Improvements",id:"-performance-improvements",level:3},{value:"\ud83e\uddfc Clean Code Improvements",id:"-clean-code-improvements",level:3},{value:"\ud83d\udc1e Fixes",id:"-fixes",level:3},{value:"v0.9.41",id:"v0941",level:2},{value:"v0.9.4",id:"v094",level:2},{value:"v0.9.21",id:"v0921",level:2},{value:"v0.9.2 (Blazor Packages)",id:"v092-blazor-packages",level:2}];function a(e){const n={br:"br",code:"code",h2:"h2",h3:"h3",li:"li",p:"p",strong:"strong",ul:"ul",...(0,l.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.h2,{id:"v201",children:"v2.0.1"}),"\n",(0,r.jsx)(n.h3,{id:"fixes",children:"Fixes"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Moved Fluent API Extension Methods into Abstraction Package."}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"v200",children:"v2.0.0"}),"\n",(0,r.jsx)(n.h3,{id:"breaking-changes",children:"BREAKING CHANGES"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Interface signatures have changed."}),(0,r.jsx)(n.br,{}),"\n","Core interface updates may break plugin\u2011based systems if the core updates while plugins do not.",(0,r.jsx)(n.br,{}),"\n","This is only safe when proper plugin isolation is respected."]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Reducers now run before effects."}),(0,r.jsx)(n.br,{}),"\n","The default dispatch order has changed.",(0,r.jsx)(n.br,{}),"\n","Pipelines that rely on reducers running after effects must update their configuration to restore the previous behavior."]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Middleware between individual effects has been removed."}),(0,r.jsx)(n.br,{}),"\n","Per\u2011effect middleware proved unreliable and is no longer supported.",(0,r.jsx)(n.br,{}),"\n","All effects now run as a batch, followed by a single AfterEffect phase.",(0,r.jsx)(n.br,{}),"\n","Middleware can still be awaited before the BeforeEffect phase and after the AfterEffect phase.\r\nMiddlewares for effects if not awaited they are still ganrantueed to be running sequentially BeforeEffect Parallel with Effects then AfterEffects."]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Effects, Reducers, Middleware, EffectValidators no long transient"}),(0,r.jsx)(n.br,{}),"\n","Transient lifetime for those is unecessary as none of them should ever hold state of their own therefore they act like static method filled with logic alone... let's eliminate transient overhead and put scoped as default and singleton with the singleton interfaces."]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Configuration Scan Assembly Type Changed"}),(0,r.jsx)(n.br,{}),"\n","The property ",(0,r.jsx)(n.code,{children:"ConfigureOptions.ScanAssemblies"})," is now taking a ",(0,r.jsx)(n.code,{children:"Assembly[]"})," instead of ",(0,r.jsx)(n.code,{children:"Type[]"}),"."]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"IPulseGlobalTracker registration changed"}),(0,r.jsx)(n.br,{}),"\n","The IPulseGlobalTracker is now registered as Scoped instead of singleton... issues were occuring with some blazor server projects."]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsxs)(n.strong,{children:["Removed ",(0,r.jsx)(n.code,{children:".AddStatePulse"})]}),(0,r.jsx)(n.br,{}),"\n","the extension method were removed from public access... now you use ",(0,r.jsx)(n.code,{children:".AddStatePulseService()"})," which will auto detect from action to effect to reducers to middlewares.\r\nReason: Better dev experience less confusing and annoying when having to register stuff manually instead of by scanning.\r\nAlternative: You can also add types into ",(0,r.jsx)(n.code,{children:".AddStatePulseServices(o=>{ o.AutoRegisterTypes = [typeof(AAA)];});"})," which will perform manual registration automatically without scanning assemblies."]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Added to IDispatchMiddleware"}),"\r\n",(0,r.jsx)(n.code,{children:"OnDispatchFailure(Exception exception, object action)"})," is now available to middleware hit is receive on any dispatch failure..."]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Removed Throw for Dispatch Failure"}),"\r\nRe-Throw only occurs when using Synchronous ",(0,r.jsx)(n.code,{children:"Await()"})," otherwise dispatch will swallow any uncaught exception thrown at it."]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Reducers are no longer Tasks"}),"\r\nWith the full introduction of middlewares it is no longer justifiable to have reducers as Task... nothing should be in the reducers to the exception of generating a new state."]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"ReducerExt is removed"}),"\r\nReducerExt is no longer available as it was used as helper for Task return."]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsxs)(n.strong,{children:[(0,r.jsx)(n.code,{children:"IDispatcherPrepper Prepare(Func createInstance)"})," deprecated"]}),"\r\nUse Prepared(instance).DispatchAsync() instead;"]}),"\n"]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"new-features",children:"New Features"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Enhanced Configuration Options"})," - New global configuration properties:","\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"DispatchOrderBehavior"})," - Set default ordering (EffectsFirst/ReducersFirst)"]}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Configurable Dispatch Ordering"})," - Choose between ",(0,r.jsx)(n.code,{children:"EffectsFirst"})," (default) or ",(0,r.jsx)(n.code,{children:"ReducersFirst"})," execution order globally or per-dispatch"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Configurable Dispatch Ordering"})," - The Middleware BeforeReducers will run before the AfterReducer as garantueed..."]}),"\n",(0,r.jsx)(n.li,{children:"The reducer will however run in parallel with BeforeReducing if settings is to not await for middlewares."}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Per-Dispatch Execution Control"})," - Override global settings per dispatch with fluent API:","\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:".EffectsFirst()"})," - Run effects before reducers"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:".ReducersFirst()"})," - Run reducers before effects"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:".SequentialEffects()"})," - Force effects to run in sequence"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:".ParallelEffects()"})," - Force effects to run in parallel"]}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Invalid Configuration Detection"})," - New ",(0,r.jsx)(n.code,{children:"InvalidDispatchCombinationException"})," thrown at dispatch time for incompatible configuration combinations (Plan to generate compiler errors later)"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Recursive Dispatch Support"})," - Fire-and-forget mode enables safe recursive dispatch patterns without deadlocks"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"PulseTrackingModel"})," - Clear option for the tracking model! Either Thread-Safe (",(0,r.jsx)(n.code,{children:"Default"}),") or Single Threaded Fast Application... WASM/Blazor Server."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"GlobalTrackerLifetime"})," - Define clearly lifetime scope for the Global Tracker singleton or scoped. ",(0,r.jsx)(n.code,{children:"BlazorServer"}),", ",(0,r.jsx)(n.code,{children:"WASM"}),", ",(0,r.jsx)(n.code,{children:"Scoped"})," (Default) or ",(0,r.jsx)(n.code,{children:"Singleton"}),"."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"IStateFeatureSingleton"})," - Define a singleton state across your app... not useful in WASM but very useful in Blazor Server... where one can share the state across client each client run their own action but the state update spread across all circuits."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"StateVersioning"})," long global ticker used to calculate version of a state... this will help prevent further race conditions related to multi-threading (blazor server singleton)."]}),"\n",(0,r.jsxs)(n.li,{children:["State update will automatically discard stale states. you can also acces the information using ",(0,r.jsx)(n.code,{children:"IStateAccessor"})]}),"\n",(0,r.jsxs)(n.li,{children:["Roslyn Analyzer will now trigger error ",(0,r.jsx)(n.code,{children:".Prepare(entity)"})," if constructor object does not match the underlying object's constructor type and it supports overload."]}),"\n",(0,r.jsxs)(n.li,{children:["Roslyn Analyzer will now trigger error ",(0,r.jsx)(n.code,{children:".Prepare().With(p => p.PROP, data)"})," when PROP is ",(0,r.jsx)(n.code,{children:"init;"})," or ",(0,r.jsx)(n.code,{children:"get;"})," only... this will eleminate runtime issues."]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"security",children:"Security"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["Roslyn will now enforce ",(0,r.jsx)(n.code,{children:"_statePulse.StateOf(() => this, OnUpdate);"})," on the two arguments this will avoid runtime errors and potential memory leaks."]}),"\n",(0,r.jsx)(n.li,{children:"Fixed All Configure Internal..."}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"fixes-1",children:"Fixes"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Fixed Various Warnings"}),"\n",(0,r.jsx)(n.li,{children:"Fixed All Configure Internal..."}),"\n",(0,r.jsx)(n.li,{children:"Fixed Middleware are now added via Scanned Assemblies."}),"\n",(0,r.jsxs)(n.li,{children:["Fixed some issues where ",(0,r.jsx)(n.code,{children:"Await()"})," was not respected."]}),"\n",(0,r.jsx)(n.li,{children:"Fixed major issue where all race condition elements where cancelled leading to full chain cancellation including the last action."}),"\n",(0,r.jsx)(n.li,{children:"Fixed inconsistence with race conditions."}),"\n",(0,r.jsxs)(n.li,{children:["Fixed\t",(0,r.jsx)(n.code,{children:"StateOf()"})," throwing inability to cast to ",(0,r.jsx)(n.code,{children:"IStateAccessor"}),"."]}),"\n",(0,r.jsx)(n.li,{children:"Fixed AddStatePulseServices() configuration not optional."}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"v110",children:"v1.1.0"}),"\n",(0,r.jsx)(n.h3,{id:"minor-change",children:"Minor Change"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Upgraded to .NET 10"}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"v102",children:"v1.0.2"}),"\n",(0,r.jsx)(n.h3,{id:"fixes-2",children:"Fixes"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Added StatePulse.Net.Abstractions package reference instead of project reference to fix IL trimming issues."}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"v101",children:"v1.0.1"}),"\n",(0,r.jsx)(n.h3,{id:"minor-change-1",children:"Minor Change"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Splited Abstractions into StatePulse.Net.Abstractions (Will not break anything Namespace is the same)"}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"v100",children:"v1.0.0"}),"\n",(0,r.jsx)(n.h3,{id:"new-features-1",children:"New Features"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Action Effect Validator"}),": Allows effects to run conditionally by validating them before execution."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Middleware Support"}),":","\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"IEffectMiddleware"})}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"IReducerMiddleware"})}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"IDispatchMiddleware"})}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Behavior Configuration"}),": You can configure execution behaviors via:","\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"DispatchEffectBehavior"})}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"MiddlewareEffectBehavior"})}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"MiddlewareTaskBehavior"})}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Strict Manual Registration"}),": Manual service registration ",(0,r.jsx)(n.strong,{children:"must use"})," extension methods:","\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"AddStatePulse()"})}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"AddStatePulseEffect<>()"})}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"AddStatePulseAction<>()"})}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"AddStatePulseReducer<>()"})}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"AddStatePulseStateFeature<>()"})}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"AddStatePulseEffectValidator<>()"})}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"-breaking-changes",children:"\ud83d\udca5 Breaking Changes"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["Removed ",(0,r.jsx)(n.strong,{children:"Action Validator"})," \u2013 validating action data is not the responsibility of the state management layer."]}),"\n",(0,r.jsxs)(n.li,{children:["Renamed:","\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"IStateAccessor<>.StateChanged"})," \u2192 ",(0,r.jsx)(n.code,{children:"OnStateChanged"})]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"UsingSynchronousMode"})," \u2192 ",(0,r.jsx)(n.strong,{children:"Removed"})]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"Sync()"})," \u2192 ",(0,r.jsx)(n.code,{children:"Await()"})," for clarity and accuracy"]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"-performance-improvements",children:"\ud83d\ude80 Performance Improvements"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["Improved ",(0,r.jsx)(n.strong,{children:"dispatcher caching"})]}),"\n",(0,r.jsxs)(n.li,{children:["Enhanced ",(0,r.jsx)(n.strong,{children:"type cache"})," in ",(0,r.jsx)(n.code,{children:"StatePulseRegistry"})]}),"\n",(0,r.jsxs)(n.li,{children:["Replaced reflection with ",(0,r.jsx)(n.strong,{children:"dynamic method caching"})," for faster dispatching"]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"-clean-code-improvements",children:"\ud83e\uddfc Clean Code Improvements"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["Refactored ",(0,r.jsx)(n.code,{children:"DispatchPrepper"})," for cleaner and lighter internal logic"]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"-fixes",children:"\ud83d\udc1e Fixes"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["Resolved several ",(0,r.jsx)(n.strong,{children:"null reference warnings"})]}),"\n",(0,r.jsxs)(n.li,{children:["Removed leftover ",(0,r.jsx)(n.strong,{children:"internal artifacts"})]}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"v0941",children:"v0.9.41"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Fix: Added Anti-Service duplication to avoid double triggers."}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"v094",children:"v0.9.4"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Breaking Change, StateOf no longer accept lambda will throw exception you must define a Task directly... this was necessary due to Garbage Collector and tracking behavior."}),"\n",(0,r.jsx)(n.li,{children:"Deprecated UsingSynchronousMode() instead use Sync()."}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"v0921",children:"v0.9.21"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Implement the Blazor Package and removed dependencies to Blazor ComponentBase which is no longer required..."}),"\n",(0,r.jsx)(n.li,{children:"Any objects within .NET can now use IStatePulse and benefit from state management without extra implementations."}),"\n",(0,r.jsx)(n.li,{children:"Renamed IPulse to IStatePulse"}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"using IStatePulse.StateOf(()=>this, () => InvokeAsync(StateHasChanged));"})}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"v092-blazor-packages",children:"v0.9.2 (Blazor Packages)"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Deprecated now part of StatePulse regular since we have removed the dependencies to blazor component.\r\n... that was quick!"}),"\n"]})]})}function h(e={}){const{wrapper:n}={...(0,l.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(a,{...e})}):a(e)}}}]); \ No newline at end of file diff --git a/doc/build/assets/js/94cf9043.a31f232c.js b/doc/build/assets/js/94cf9043.a31f232c.js new file mode 100644 index 0000000..df9524f --- /dev/null +++ b/doc/build/assets/js/94cf9043.a31f232c.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkstatepulse_doc=self.webpackChunkstatepulse_doc||[]).push([[6683],{8453:(e,n,i)=>{i.d(n,{R:()=>l,x:()=>c});var s=i(6540);const r={},t=s.createContext(r);function l(e){const n=s.useContext(t);return s.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function c(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:l(e.components),s.createElement(t.Provider,{value:n},e.children)}},8886:(e,n,i)=>{i.r(n),i.d(n,{assets:()=>d,contentTitle:()=>c,default:()=>h,frontMatter:()=>l,metadata:()=>s,toc:()=>a});const s=JSON.parse('{"id":"Versions","title":"Updates","description":"v2.1.0","source":"@site/docs/0.Versions.md","sourceDirName":".","slug":"/versions","permalink":"/versions","draft":false,"unlisted":false,"editUrl":"https://github.com/mshimshon/StatePulse.NET/docs/0.Versions.md","tags":[],"version":"current","sidebarPosition":0,"frontMatter":{"slug":"versions","title":"Updates","sidebar_position":0},"sidebar":"tutorialSidebar","next":{"title":"Get Started","permalink":"/"}}');var r=i(4848),t=i(8453);const l={slug:"versions",title:"Updates",sidebar_position:0},c=void 0,d={},a=[{value:"v2.1.0",id:"v210",level:2},{value:"Performance",id:"performance",level:3},{value:"Fix",id:"fix",level:3},{value:"v2.0.1",id:"v201",level:2},{value:"Fixes",id:"fixes",level:3},{value:"v2.0.0",id:"v200",level:2},{value:"BREAKING CHANGES",id:"breaking-changes",level:3},{value:"New Features",id:"new-features",level:3},{value:"Security",id:"security",level:3},{value:"Fixes",id:"fixes-1",level:3},{value:"v1.1.0",id:"v110",level:2},{value:"Minor Change",id:"minor-change",level:3},{value:"v1.0.2",id:"v102",level:2},{value:"Fixes",id:"fixes-2",level:3},{value:"v1.0.1",id:"v101",level:2},{value:"Minor Change",id:"minor-change-1",level:3},{value:"v1.0.0",id:"v100",level:2},{value:"New Features",id:"new-features-1",level:3},{value:"\ud83d\udca5 Breaking Changes",id:"-breaking-changes",level:3},{value:"\ud83d\ude80 Performance Improvements",id:"-performance-improvements",level:3},{value:"\ud83e\uddfc Clean Code Improvements",id:"-clean-code-improvements",level:3},{value:"\ud83d\udc1e Fixes",id:"-fixes",level:3},{value:"v0.9.41",id:"v0941",level:2},{value:"v0.9.4",id:"v094",level:2},{value:"v0.9.21",id:"v0921",level:2},{value:"v0.9.2 (Blazor Packages)",id:"v092-blazor-packages",level:2}];function o(e){const n={br:"br",code:"code",h2:"h2",h3:"h3",li:"li",p:"p",strong:"strong",ul:"ul",...(0,t.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.h2,{id:"v210",children:"v2.1.0"}),"\n",(0,r.jsx)(n.h3,{id:"performance",children:"Performance"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["The Global Tracker now uses an event\u2011based mechanism to invoke Self Disposal Check directly on each ",(0,r.jsx)(n.code,{children:"IStatePulse"})," instance, removing the need to iterate the internal registry. The registry itself is scheduled for removal in version 3.0 and is now marked with the appropriate ",(0,r.jsx)(n.code,{children:"Obsolete"})," attribute. Cleanup is no longer driven by a continuous loop; instead, the tracker schedules a self\u2011check only when activity occurs in the registry and goes idle when the application becomes inactive. This preserves memory\u2011leak protection without a permanent cycle. A minimum delay of 10 seconds is enforced between checks, and when burst activity occurs, only the final activity triggers a single final cleanup run rather than multiple redundant executions."]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"fix",children:"Fix"}),"\n",(0,r.jsxs)(n.p,{children:["This is one of those \u201cbug or feature?\u201d situations. By design, ",(0,r.jsx)(n.code,{children:"StateOf()"})," is meant to notify the component when its state changes. However, earlier Blazor assumptions overlooked scenarios where one state might render through one route while another state uses a different route."]}),"\n",(0,r.jsxs)(n.p,{children:["Before version 2.0.11, StatePulse always treated the latest route as the default route for all states registered by a component, this is was ",(0,r.jsx)(n.strong,{children:"BUG"}),". Starting with 2.0.11, each state that a component subscribes to StatePulse now respects those route differences if any."]}),"\n",(0,r.jsx)(n.h2,{id:"v201",children:"v2.0.1"}),"\n",(0,r.jsx)(n.h3,{id:"fixes",children:"Fixes"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Moved Fluent API Extension Methods into Abstraction Package."}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"v200",children:"v2.0.0"}),"\n",(0,r.jsx)(n.h3,{id:"breaking-changes",children:"BREAKING CHANGES"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Interface signatures have changed."}),(0,r.jsx)(n.br,{}),"\n","Core interface updates may break plugin\u2011based systems if the core updates while plugins do not.",(0,r.jsx)(n.br,{}),"\n","This is only safe when proper plugin isolation is respected."]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Reducers now run before effects."}),(0,r.jsx)(n.br,{}),"\n","The default dispatch order has changed.",(0,r.jsx)(n.br,{}),"\n","Pipelines that rely on reducers running after effects must update their configuration to restore the previous behavior."]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Middleware between individual effects has been removed."}),(0,r.jsx)(n.br,{}),"\n","Per\u2011effect middleware proved unreliable and is no longer supported.",(0,r.jsx)(n.br,{}),"\n","All effects now run as a batch, followed by a single AfterEffect phase.",(0,r.jsx)(n.br,{}),"\n","Middleware can still be awaited before the BeforeEffect phase and after the AfterEffect phase.\r\nMiddlewares for effects if not awaited they are still ganrantueed to be running sequentially BeforeEffect Parallel with Effects then AfterEffects."]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Effects, Reducers, Middleware, EffectValidators no long transient"}),(0,r.jsx)(n.br,{}),"\n","Transient lifetime for those is unecessary as none of them should ever hold state of their own therefore they act like static method filled with logic alone... let's eliminate transient overhead and put scoped as default and singleton with the singleton interfaces."]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Configuration Scan Assembly Type Changed"}),(0,r.jsx)(n.br,{}),"\n","The property ",(0,r.jsx)(n.code,{children:"ConfigureOptions.ScanAssemblies"})," is now taking a ",(0,r.jsx)(n.code,{children:"Assembly[]"})," instead of ",(0,r.jsx)(n.code,{children:"Type[]"}),"."]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"IPulseGlobalTracker registration changed"}),(0,r.jsx)(n.br,{}),"\n","The IPulseGlobalTracker is now registered as Scoped instead of singleton... issues were occuring with some blazor server projects."]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsxs)(n.strong,{children:["Removed ",(0,r.jsx)(n.code,{children:".AddStatePulse"})]}),(0,r.jsx)(n.br,{}),"\n","the extension method were removed from public access... now you use ",(0,r.jsx)(n.code,{children:".AddStatePulseService()"})," which will auto detect from action to effect to reducers to middlewares.\r\nReason: Better dev experience less confusing and annoying when having to register stuff manually instead of by scanning.\r\nAlternative: You can also add types into ",(0,r.jsx)(n.code,{children:".AddStatePulseServices(o=>{ o.AutoRegisterTypes = [typeof(AAA)];});"})," which will perform manual registration automatically without scanning assemblies."]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Added to IDispatchMiddleware"}),"\r\n",(0,r.jsx)(n.code,{children:"OnDispatchFailure(Exception exception, object action)"})," is now available to middleware hit is receive on any dispatch failure..."]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Removed Throw for Dispatch Failure"}),"\r\nRe-Throw only occurs when using Synchronous ",(0,r.jsx)(n.code,{children:"Await()"})," otherwise dispatch will swallow any uncaught exception thrown at it."]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Reducers are no longer Tasks"}),"\r\nWith the full introduction of middlewares it is no longer justifiable to have reducers as Task... nothing should be in the reducers to the exception of generating a new state."]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"ReducerExt is removed"}),"\r\nReducerExt is no longer available as it was used as helper for Task return."]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsxs)(n.strong,{children:[(0,r.jsx)(n.code,{children:"IDispatcherPrepper Prepare(Func createInstance)"})," deprecated"]}),"\r\nUse Prepared(instance).DispatchAsync() instead;"]}),"\n"]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"new-features",children:"New Features"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Enhanced Configuration Options"})," - New global configuration properties:","\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"DispatchOrderBehavior"})," - Set default ordering (EffectsFirst/ReducersFirst)"]}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Configurable Dispatch Ordering"})," - Choose between ",(0,r.jsx)(n.code,{children:"EffectsFirst"})," (default) or ",(0,r.jsx)(n.code,{children:"ReducersFirst"})," execution order globally or per-dispatch"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Configurable Dispatch Ordering"})," - The Middleware BeforeReducers will run before the AfterReducer as garantueed..."]}),"\n",(0,r.jsx)(n.li,{children:"The reducer will however run in parallel with BeforeReducing if settings is to not await for middlewares."}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Per-Dispatch Execution Control"})," - Override global settings per dispatch with fluent API:","\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:".EffectsFirst()"})," - Run effects before reducers"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:".ReducersFirst()"})," - Run reducers before effects"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:".SequentialEffects()"})," - Force effects to run in sequence"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:".ParallelEffects()"})," - Force effects to run in parallel"]}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Invalid Configuration Detection"})," - New ",(0,r.jsx)(n.code,{children:"InvalidDispatchCombinationException"})," thrown at dispatch time for incompatible configuration combinations (Plan to generate compiler errors later)"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Recursive Dispatch Support"})," - Fire-and-forget mode enables safe recursive dispatch patterns without deadlocks"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"PulseTrackingModel"})," - Clear option for the tracking model! Either Thread-Safe (",(0,r.jsx)(n.code,{children:"Default"}),") or Single Threaded Fast Application... WASM/Blazor Server."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"GlobalTrackerLifetime"})," - Define clearly lifetime scope for the Global Tracker singleton or scoped. ",(0,r.jsx)(n.code,{children:"BlazorServer"}),", ",(0,r.jsx)(n.code,{children:"WASM"}),", ",(0,r.jsx)(n.code,{children:"Scoped"})," (Default) or ",(0,r.jsx)(n.code,{children:"Singleton"}),"."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"IStateFeatureSingleton"})," - Define a singleton state across your app... not useful in WASM but very useful in Blazor Server... where one can share the state across client each client run their own action but the state update spread across all circuits."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"StateVersioning"})," long global ticker used to calculate version of a state... this will help prevent further race conditions related to multi-threading (blazor server singleton)."]}),"\n",(0,r.jsxs)(n.li,{children:["State update will automatically discard stale states. you can also acces the information using ",(0,r.jsx)(n.code,{children:"IStateAccessor"})]}),"\n",(0,r.jsxs)(n.li,{children:["Roslyn Analyzer will now trigger error ",(0,r.jsx)(n.code,{children:".Prepare(entity)"})," if constructor object does not match the underlying object's constructor type and it supports overload."]}),"\n",(0,r.jsxs)(n.li,{children:["Roslyn Analyzer will now trigger error ",(0,r.jsx)(n.code,{children:".Prepare().With(p => p.PROP, data)"})," when PROP is ",(0,r.jsx)(n.code,{children:"init;"})," or ",(0,r.jsx)(n.code,{children:"get;"})," only... this will eleminate runtime issues."]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"security",children:"Security"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["Roslyn will now enforce ",(0,r.jsx)(n.code,{children:"_statePulse.StateOf(() => this, OnUpdate);"})," on the two arguments this will avoid runtime errors and potential memory leaks."]}),"\n",(0,r.jsx)(n.li,{children:"Fixed All Configure Internal..."}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"fixes-1",children:"Fixes"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Fixed Various Warnings"}),"\n",(0,r.jsx)(n.li,{children:"Fixed All Configure Internal..."}),"\n",(0,r.jsx)(n.li,{children:"Fixed Middleware are now added via Scanned Assemblies."}),"\n",(0,r.jsxs)(n.li,{children:["Fixed some issues where ",(0,r.jsx)(n.code,{children:"Await()"})," was not respected."]}),"\n",(0,r.jsx)(n.li,{children:"Fixed major issue where all race condition elements where cancelled leading to full chain cancellation including the last action."}),"\n",(0,r.jsx)(n.li,{children:"Fixed inconsistence with race conditions."}),"\n",(0,r.jsxs)(n.li,{children:["Fixed\t",(0,r.jsx)(n.code,{children:"StateOf()"})," throwing inability to cast to ",(0,r.jsx)(n.code,{children:"IStateAccessor"}),"."]}),"\n",(0,r.jsx)(n.li,{children:"Fixed AddStatePulseServices() configuration not optional."}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"v110",children:"v1.1.0"}),"\n",(0,r.jsx)(n.h3,{id:"minor-change",children:"Minor Change"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Upgraded to .NET 10"}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"v102",children:"v1.0.2"}),"\n",(0,r.jsx)(n.h3,{id:"fixes-2",children:"Fixes"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Added StatePulse.Net.Abstractions package reference instead of project reference to fix IL trimming issues."}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"v101",children:"v1.0.1"}),"\n",(0,r.jsx)(n.h3,{id:"minor-change-1",children:"Minor Change"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Splited Abstractions into StatePulse.Net.Abstractions (Will not break anything Namespace is the same)"}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"v100",children:"v1.0.0"}),"\n",(0,r.jsx)(n.h3,{id:"new-features-1",children:"New Features"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Action Effect Validator"}),": Allows effects to run conditionally by validating them before execution."]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Middleware Support"}),":","\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"IEffectMiddleware"})}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"IReducerMiddleware"})}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"IDispatchMiddleware"})}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Behavior Configuration"}),": You can configure execution behaviors via:","\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"DispatchEffectBehavior"})}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"MiddlewareEffectBehavior"})}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"MiddlewareTaskBehavior"})}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Strict Manual Registration"}),": Manual service registration ",(0,r.jsx)(n.strong,{children:"must use"})," extension methods:","\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"AddStatePulse()"})}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"AddStatePulseEffect<>()"})}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"AddStatePulseAction<>()"})}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"AddStatePulseReducer<>()"})}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"AddStatePulseStateFeature<>()"})}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"AddStatePulseEffectValidator<>()"})}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"-breaking-changes",children:"\ud83d\udca5 Breaking Changes"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["Removed ",(0,r.jsx)(n.strong,{children:"Action Validator"})," \u2013 validating action data is not the responsibility of the state management layer."]}),"\n",(0,r.jsxs)(n.li,{children:["Renamed:","\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"IStateAccessor<>.StateChanged"})," \u2192 ",(0,r.jsx)(n.code,{children:"OnStateChanged"})]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"UsingSynchronousMode"})," \u2192 ",(0,r.jsx)(n.strong,{children:"Removed"})]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"Sync()"})," \u2192 ",(0,r.jsx)(n.code,{children:"Await()"})," for clarity and accuracy"]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"-performance-improvements",children:"\ud83d\ude80 Performance Improvements"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["Improved ",(0,r.jsx)(n.strong,{children:"dispatcher caching"})]}),"\n",(0,r.jsxs)(n.li,{children:["Enhanced ",(0,r.jsx)(n.strong,{children:"type cache"})," in ",(0,r.jsx)(n.code,{children:"StatePulseRegistry"})]}),"\n",(0,r.jsxs)(n.li,{children:["Replaced reflection with ",(0,r.jsx)(n.strong,{children:"dynamic method caching"})," for faster dispatching"]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"-clean-code-improvements",children:"\ud83e\uddfc Clean Code Improvements"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["Refactored ",(0,r.jsx)(n.code,{children:"DispatchPrepper"})," for cleaner and lighter internal logic"]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"-fixes",children:"\ud83d\udc1e Fixes"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["Resolved several ",(0,r.jsx)(n.strong,{children:"null reference warnings"})]}),"\n",(0,r.jsxs)(n.li,{children:["Removed leftover ",(0,r.jsx)(n.strong,{children:"internal artifacts"})]}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"v0941",children:"v0.9.41"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Fix: Added Anti-Service duplication to avoid double triggers."}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"v094",children:"v0.9.4"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Breaking Change, StateOf no longer accept lambda will throw exception you must define a Task directly... this was necessary due to Garbage Collector and tracking behavior."}),"\n",(0,r.jsx)(n.li,{children:"Deprecated UsingSynchronousMode() instead use Sync()."}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"v0921",children:"v0.9.21"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Implement the Blazor Package and removed dependencies to Blazor ComponentBase which is no longer required..."}),"\n",(0,r.jsx)(n.li,{children:"Any objects within .NET can now use IStatePulse and benefit from state management without extra implementations."}),"\n",(0,r.jsx)(n.li,{children:"Renamed IPulse to IStatePulse"}),"\n",(0,r.jsx)(n.li,{children:(0,r.jsx)(n.code,{children:"using IStatePulse.StateOf(()=>this, () => InvokeAsync(StateHasChanged));"})}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"v092-blazor-packages",children:"v0.9.2 (Blazor Packages)"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Deprecated now part of StatePulse regular since we have removed the dependencies to blazor component.\r\n... that was quick!"}),"\n"]})]})}function h(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(o,{...e})}):o(e)}}}]); \ No newline at end of file diff --git a/doc/build/assets/js/runtime~main.b23f14dc.js b/doc/build/assets/js/runtime~main.ff5087c2.js similarity index 97% rename from doc/build/assets/js/runtime~main.b23f14dc.js rename to doc/build/assets/js/runtime~main.ff5087c2.js index f1c9a7a..9461772 100644 --- a/doc/build/assets/js/runtime~main.b23f14dc.js +++ b/doc/build/assets/js/runtime~main.ff5087c2.js @@ -1 +1 @@ -(()=>{"use strict";var e,a,t,c,f,r={},d={};function b(e){var a=d[e];if(void 0!==a)return a.exports;var t=d[e]={exports:{}};return r[e].call(t.exports,t,t.exports,b),t.exports}b.m=r,e=[],b.O=(a,t,c,f)=>{if(!t){var r=1/0;for(i=0;i=f)&&Object.keys(b.O).every(e=>b.O[e](t[o]))?t.splice(o--,1):(d=!1,f0&&e[i-1][2]>f;i--)e[i]=e[i-1];e[i]=[t,c,f]},b.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return b.d(a,{a:a}),a},t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,b.t=function(e,c){if(1&c&&(e=this(e)),8&c)return e;if("object"==typeof e&&e){if(4&c&&e.__esModule)return e;if(16&c&&"function"==typeof e.then)return e}var f=Object.create(null);b.r(f);var r={};a=a||[null,t({}),t([]),t(t)];for(var d=2&c&&e;("object"==typeof d||"function"==typeof d)&&!~a.indexOf(d);d=t(d))Object.getOwnPropertyNames(d).forEach(a=>r[a]=()=>e[a]);return r.default=()=>e,b.d(f,r),f},b.d=(e,a)=>{for(var t in a)b.o(a,t)&&!b.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:a[t]})},b.f={},b.e=e=>Promise.all(Object.keys(b.f).reduce((a,t)=>(b.f[t](e,a),a),[])),b.u=e=>"assets/js/"+({177:"7691d1c3",688:"f2ace840",841:"8c3a5864",855:"8edd9378",867:"33fc5bb8",908:"d5b5cc03",972:"02105171",1235:"a7456010",1567:"22dd74f7",1653:"f552f37d",1903:"acecf23e",2220:"0e30184d",2520:"e925e243",2787:"7954e82b",3318:"742328d7",3328:"cc87a84e",3478:"cd69296b",3944:"0d8668ed",4134:"393be207",4157:"f179eaed",4212:"621db11d",4279:"df203c0f",4787:"3720c009",4850:"79199b91",5742:"aba21aa0",5899:"c9c672c1",6061:"1f391b9e",6403:"c5fd082d",6527:"e48d0160",6683:"94cf9043",6980:"936b9f1e",6996:"ce33d687",7098:"a7bd4aaa",7235:"4ebf985a",7472:"814f3328",7687:"90c0aa62",7698:"547f2087",7713:"dbf990ff",7778:"e4b9287f",7853:"9cc31a46",8151:"0bc25be7",8401:"17896441",8672:"2e6f86e1",8947:"ef8b811a",9048:"a94703ab",9174:"59af61a6",9647:"5e95c892",9858:"36994c47",9963:"68ee3bbe",9977:"f6f0caca"}[e]||e)+"."+{177:"41ff88ef",688:"b60b551f",841:"65d8a29a",855:"383d25f9",867:"d2f2b0c1",908:"a0e4c5cf",972:"051f2e89",1235:"336f95fe",1567:"993ce3a5",1653:"f6b5254e",1903:"b595c8e0",2220:"03ddad7e",2237:"2847df75",2520:"eaa2c69c",2787:"724d28ea",3318:"9cf46a79",3328:"05b44a8f",3478:"c353b6fa",3944:"cdb36547",4134:"2823742b",4157:"6ac92fc3",4212:"28d58a98",4279:"c82ea539",4787:"cf7bc292",4850:"c3dab34b",5742:"5a986477",5899:"e6df7d83",6061:"47fc6bcb",6120:"b521cc9a",6403:"5c866409",6527:"acea7c7a",6683:"78d41392",6980:"d8a5a951",6996:"3df1d0b1",7098:"f2d2fe61",7235:"a97ef297",7312:"49620988",7472:"fbffe806",7687:"5eb5689f",7698:"6c1b068d",7713:"f1c7dfd1",7778:"61c20a69",7853:"968cd617",8151:"ad260ac3",8401:"145b706a",8672:"647dd398",8947:"7573ae26",9048:"abc249b9",9174:"c252ee6f",9647:"9334a2c8",9858:"39393642",9963:"20abb41a",9977:"6f80119e"}[e]+".js",b.miniCssF=e=>{},b.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),b.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),c={},f="statepulse-doc:",b.l=(e,a,t,r)=>{if(c[e])c[e].push(a);else{var d,o;if(void 0!==t)for(var n=document.getElementsByTagName("script"),i=0;i{d.onerror=d.onload=null,clearTimeout(s);var f=c[e];if(delete c[e],d.parentNode&&d.parentNode.removeChild(d),f&&f.forEach(e=>e(t)),a)return a(t)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:d}),12e4);d.onerror=l.bind(null,d.onerror),d.onload=l.bind(null,d.onload),o&&document.head.appendChild(d)}},b.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},b.p="/",b.gca=function(e){return e={17896441:"8401","7691d1c3":"177",f2ace840:"688","8c3a5864":"841","8edd9378":"855","33fc5bb8":"867",d5b5cc03:"908","02105171":"972",a7456010:"1235","22dd74f7":"1567",f552f37d:"1653",acecf23e:"1903","0e30184d":"2220",e925e243:"2520","7954e82b":"2787","742328d7":"3318",cc87a84e:"3328",cd69296b:"3478","0d8668ed":"3944","393be207":"4134",f179eaed:"4157","621db11d":"4212",df203c0f:"4279","3720c009":"4787","79199b91":"4850",aba21aa0:"5742",c9c672c1:"5899","1f391b9e":"6061",c5fd082d:"6403",e48d0160:"6527","94cf9043":"6683","936b9f1e":"6980",ce33d687:"6996",a7bd4aaa:"7098","4ebf985a":"7235","814f3328":"7472","90c0aa62":"7687","547f2087":"7698",dbf990ff:"7713",e4b9287f:"7778","9cc31a46":"7853","0bc25be7":"8151","2e6f86e1":"8672",ef8b811a:"8947",a94703ab:"9048","59af61a6":"9174","5e95c892":"9647","36994c47":"9858","68ee3bbe":"9963",f6f0caca:"9977"}[e]||e,b.p+b.u(e)},(()=>{var e={5354:0,1869:0};b.f.j=(a,t)=>{var c=b.o(e,a)?e[a]:void 0;if(0!==c)if(c)t.push(c[2]);else if(/^(1869|5354)$/.test(a))e[a]=0;else{var f=new Promise((t,f)=>c=e[a]=[t,f]);t.push(c[2]=f);var r=b.p+b.u(a),d=new Error;b.l(r,t=>{if(b.o(e,a)&&(0!==(c=e[a])&&(e[a]=void 0),c)){var f=t&&("load"===t.type?"missing":t.type),r=t&&t.target&&t.target.src;d.message="Loading chunk "+a+" failed.\n("+f+": "+r+")",d.name="ChunkLoadError",d.type=f,d.request=r,c[1](d)}},"chunk-"+a,a)}},b.O.j=a=>0===e[a];var a=(a,t)=>{var c,f,r=t[0],d=t[1],o=t[2],n=0;if(r.some(a=>0!==e[a])){for(c in d)b.o(d,c)&&(b.m[c]=d[c]);if(o)var i=o(b)}for(a&&a(t);n{"use strict";var e,a,t,c,f,r={},d={};function b(e){var a=d[e];if(void 0!==a)return a.exports;var t=d[e]={exports:{}};return r[e].call(t.exports,t,t.exports,b),t.exports}b.m=r,e=[],b.O=(a,t,c,f)=>{if(!t){var r=1/0;for(i=0;i=f)&&Object.keys(b.O).every(e=>b.O[e](t[o]))?t.splice(o--,1):(d=!1,f0&&e[i-1][2]>f;i--)e[i]=e[i-1];e[i]=[t,c,f]},b.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return b.d(a,{a:a}),a},t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,b.t=function(e,c){if(1&c&&(e=this(e)),8&c)return e;if("object"==typeof e&&e){if(4&c&&e.__esModule)return e;if(16&c&&"function"==typeof e.then)return e}var f=Object.create(null);b.r(f);var r={};a=a||[null,t({}),t([]),t(t)];for(var d=2&c&&e;("object"==typeof d||"function"==typeof d)&&!~a.indexOf(d);d=t(d))Object.getOwnPropertyNames(d).forEach(a=>r[a]=()=>e[a]);return r.default=()=>e,b.d(f,r),f},b.d=(e,a)=>{for(var t in a)b.o(a,t)&&!b.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:a[t]})},b.f={},b.e=e=>Promise.all(Object.keys(b.f).reduce((a,t)=>(b.f[t](e,a),a),[])),b.u=e=>"assets/js/"+({177:"7691d1c3",688:"f2ace840",841:"8c3a5864",855:"8edd9378",867:"33fc5bb8",908:"d5b5cc03",972:"02105171",1235:"a7456010",1567:"22dd74f7",1653:"f552f37d",1903:"acecf23e",2220:"0e30184d",2520:"e925e243",2787:"7954e82b",3318:"742328d7",3328:"cc87a84e",3478:"cd69296b",3944:"0d8668ed",4134:"393be207",4157:"f179eaed",4212:"621db11d",4279:"df203c0f",4787:"3720c009",4850:"79199b91",5742:"aba21aa0",5899:"c9c672c1",6061:"1f391b9e",6403:"c5fd082d",6527:"e48d0160",6683:"94cf9043",6980:"936b9f1e",6996:"ce33d687",7098:"a7bd4aaa",7235:"4ebf985a",7472:"814f3328",7687:"90c0aa62",7698:"547f2087",7713:"dbf990ff",7778:"e4b9287f",7853:"9cc31a46",8151:"0bc25be7",8401:"17896441",8672:"2e6f86e1",8947:"ef8b811a",9048:"a94703ab",9174:"59af61a6",9647:"5e95c892",9858:"36994c47",9963:"68ee3bbe",9977:"f6f0caca"}[e]||e)+"."+{177:"41ff88ef",688:"b60b551f",841:"65d8a29a",855:"383d25f9",867:"d2f2b0c1",908:"a0e4c5cf",972:"051f2e89",1235:"336f95fe",1567:"35cfaf6e",1653:"f6b5254e",1903:"b595c8e0",2220:"03ddad7e",2237:"2847df75",2520:"eaa2c69c",2787:"724d28ea",3318:"9cf46a79",3328:"05b44a8f",3478:"c353b6fa",3944:"cdb36547",4134:"2823742b",4157:"6ac92fc3",4212:"28d58a98",4279:"c82ea539",4787:"cf7bc292",4850:"c3dab34b",5742:"5a986477",5899:"e6df7d83",6061:"47fc6bcb",6120:"b521cc9a",6403:"5c866409",6527:"acea7c7a",6683:"a31f232c",6980:"d8a5a951",6996:"3df1d0b1",7098:"f2d2fe61",7235:"a97ef297",7312:"49620988",7472:"fbffe806",7687:"5eb5689f",7698:"6c1b068d",7713:"f1c7dfd1",7778:"61c20a69",7853:"968cd617",8151:"ad260ac3",8401:"145b706a",8672:"647dd398",8947:"7573ae26",9048:"abc249b9",9174:"c252ee6f",9647:"9334a2c8",9858:"39393642",9963:"20abb41a",9977:"6f80119e"}[e]+".js",b.miniCssF=e=>{},b.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),b.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),c={},f="statepulse-doc:",b.l=(e,a,t,r)=>{if(c[e])c[e].push(a);else{var d,o;if(void 0!==t)for(var n=document.getElementsByTagName("script"),i=0;i{d.onerror=d.onload=null,clearTimeout(s);var f=c[e];if(delete c[e],d.parentNode&&d.parentNode.removeChild(d),f&&f.forEach(e=>e(t)),a)return a(t)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:d}),12e4);d.onerror=l.bind(null,d.onerror),d.onload=l.bind(null,d.onload),o&&document.head.appendChild(d)}},b.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},b.p="/",b.gca=function(e){return e={17896441:"8401","7691d1c3":"177",f2ace840:"688","8c3a5864":"841","8edd9378":"855","33fc5bb8":"867",d5b5cc03:"908","02105171":"972",a7456010:"1235","22dd74f7":"1567",f552f37d:"1653",acecf23e:"1903","0e30184d":"2220",e925e243:"2520","7954e82b":"2787","742328d7":"3318",cc87a84e:"3328",cd69296b:"3478","0d8668ed":"3944","393be207":"4134",f179eaed:"4157","621db11d":"4212",df203c0f:"4279","3720c009":"4787","79199b91":"4850",aba21aa0:"5742",c9c672c1:"5899","1f391b9e":"6061",c5fd082d:"6403",e48d0160:"6527","94cf9043":"6683","936b9f1e":"6980",ce33d687:"6996",a7bd4aaa:"7098","4ebf985a":"7235","814f3328":"7472","90c0aa62":"7687","547f2087":"7698",dbf990ff:"7713",e4b9287f:"7778","9cc31a46":"7853","0bc25be7":"8151","2e6f86e1":"8672",ef8b811a:"8947",a94703ab:"9048","59af61a6":"9174","5e95c892":"9647","36994c47":"9858","68ee3bbe":"9963",f6f0caca:"9977"}[e]||e,b.p+b.u(e)},(()=>{var e={5354:0,1869:0};b.f.j=(a,t)=>{var c=b.o(e,a)?e[a]:void 0;if(0!==c)if(c)t.push(c[2]);else if(/^(1869|5354)$/.test(a))e[a]=0;else{var f=new Promise((t,f)=>c=e[a]=[t,f]);t.push(c[2]=f);var r=b.p+b.u(a),d=new Error;b.l(r,t=>{if(b.o(e,a)&&(0!==(c=e[a])&&(e[a]=void 0),c)){var f=t&&("load"===t.type?"missing":t.type),r=t&&t.target&&t.target.src;d.message="Loading chunk "+a+" failed.\n("+f+": "+r+")",d.name="ChunkLoadError",d.type=f,d.request=r,c[1](d)}},"chunk-"+a,a)}},b.O.j=a=>0===e[a];var a=(a,t)=>{var c,f,r=t[0],d=t[1],o=t[2],n=0;if(r.some(a=>0!==e[a])){for(c in d)b.o(d,c)&&(b.m[c]=d[c]);if(o)var i=o(b)}for(a&&a(t);n Authors | StatePulse.NET - + diff --git a/doc/build/blog/authors/mshimshon/index.html b/doc/build/blog/authors/mshimshon/index.html index 44161e1..b08bd6d 100644 --- a/doc/build/blog/authors/mshimshon/index.html +++ b/doc/build/blog/authors/mshimshon/index.html @@ -4,7 +4,7 @@ Maksim Shimshon - 0 posts | StatePulse.NET - + diff --git a/doc/build/gs-state/index.html b/doc/build/gs-state/index.html index 029b857..d86476b 100644 --- a/doc/build/gs-state/index.html +++ b/doc/build/gs-state/index.html @@ -4,7 +4,7 @@ The States | StatePulse.NET - + diff --git a/doc/build/gs-the-action/index.html b/doc/build/gs-the-action/index.html index 2dba6f8..bc5f853 100644 --- a/doc/build/gs-the-action/index.html +++ b/doc/build/gs-the-action/index.html @@ -4,7 +4,7 @@ The Actions | StatePulse.NET - + diff --git a/doc/build/gs-the-dispatcher/index.html b/doc/build/gs-the-dispatcher/index.html index 6f48ce1..7b6d0fc 100644 --- a/doc/build/gs-the-dispatcher/index.html +++ b/doc/build/gs-the-dispatcher/index.html @@ -4,7 +4,7 @@ The Dispatcher | StatePulse.NET - + diff --git a/doc/build/gs-the-effect/index.html b/doc/build/gs-the-effect/index.html index 5d7588c..5523ce6 100644 --- a/doc/build/gs-the-effect/index.html +++ b/doc/build/gs-the-effect/index.html @@ -4,7 +4,7 @@ The Effects | StatePulse.NET - + diff --git a/doc/build/gs-the-middlewares/index.html b/doc/build/gs-the-middlewares/index.html index ad13663..4564477 100644 --- a/doc/build/gs-the-middlewares/index.html +++ b/doc/build/gs-the-middlewares/index.html @@ -4,7 +4,7 @@ The Middlewares | StatePulse.NET - + diff --git a/doc/build/gs-the-reducer/index.html b/doc/build/gs-the-reducer/index.html index 795ce26..d059fc4 100644 --- a/doc/build/gs-the-reducer/index.html +++ b/doc/build/gs-the-reducer/index.html @@ -4,7 +4,7 @@ The Reducers | StatePulse.NET - + diff --git a/doc/build/index.html b/doc/build/index.html index ce3ae45..2169d09 100644 --- a/doc/build/index.html +++ b/doc/build/index.html @@ -4,7 +4,7 @@ Get Started | StatePulse.NET - + diff --git a/doc/build/markdown-page/index.html b/doc/build/markdown-page/index.html index f709173..d280d4d 100644 --- a/doc/build/markdown-page/index.html +++ b/doc/build/markdown-page/index.html @@ -4,7 +4,7 @@ Markdown page example | StatePulse.NET - + diff --git a/doc/build/setup-blazor-project/index.html b/doc/build/setup-blazor-project/index.html index 51321dc..5603247 100644 --- a/doc/build/setup-blazor-project/index.html +++ b/doc/build/setup-blazor-project/index.html @@ -4,7 +4,7 @@ Setup Blazor Project | StatePulse.NET - + diff --git a/doc/build/tags/actions/index.html b/doc/build/tags/actions/index.html index 436d571..e8511c7 100644 --- a/doc/build/tags/actions/index.html +++ b/doc/build/tags/actions/index.html @@ -4,7 +4,7 @@ 2 docs tagged with "actions" | StatePulse.NET - + diff --git a/doc/build/tags/async/index.html b/doc/build/tags/async/index.html index 7d74229..3902a70 100644 --- a/doc/build/tags/async/index.html +++ b/doc/build/tags/async/index.html @@ -4,7 +4,7 @@ 4 docs tagged with "async" | StatePulse.NET - + diff --git a/doc/build/tags/await/index.html b/doc/build/tags/await/index.html index b25787a..8c295c7 100644 --- a/doc/build/tags/await/index.html +++ b/doc/build/tags/await/index.html @@ -4,7 +4,7 @@ One doc tagged with "await" | StatePulse.NET - + diff --git a/doc/build/tags/blazor/index.html b/doc/build/tags/blazor/index.html index 264e7d2..2b4db00 100644 --- a/doc/build/tags/blazor/index.html +++ b/doc/build/tags/blazor/index.html @@ -4,7 +4,7 @@ 7 docs tagged with "blazor" | StatePulse.NET - + diff --git a/doc/build/tags/csharp/index.html b/doc/build/tags/csharp/index.html index a3eea56..0865266 100644 --- a/doc/build/tags/csharp/index.html +++ b/doc/build/tags/csharp/index.html @@ -4,7 +4,7 @@ 6 docs tagged with "csharp" | StatePulse.NET - + diff --git a/doc/build/tags/dependency-injection/index.html b/doc/build/tags/dependency-injection/index.html index 6c4de61..e460ef2 100644 --- a/doc/build/tags/dependency-injection/index.html +++ b/doc/build/tags/dependency-injection/index.html @@ -4,7 +4,7 @@ One doc tagged with "dependency-injection" | StatePulse.NET - + diff --git a/doc/build/tags/dispatcher/index.html b/doc/build/tags/dispatcher/index.html index eb3a63f..927fe41 100644 --- a/doc/build/tags/dispatcher/index.html +++ b/doc/build/tags/dispatcher/index.html @@ -4,7 +4,7 @@ One doc tagged with "dispatcher" | StatePulse.NET - + diff --git a/doc/build/tags/effects/index.html b/doc/build/tags/effects/index.html index 79af58c..3c6a6d1 100644 --- a/doc/build/tags/effects/index.html +++ b/doc/build/tags/effects/index.html @@ -4,7 +4,7 @@ One doc tagged with "effects" | StatePulse.NET - + diff --git a/doc/build/tags/immutable/index.html b/doc/build/tags/immutable/index.html index 963e076..9740c48 100644 --- a/doc/build/tags/immutable/index.html +++ b/doc/build/tags/immutable/index.html @@ -4,7 +4,7 @@ One doc tagged with "immutable" | StatePulse.NET - + diff --git a/doc/build/tags/index.html b/doc/build/tags/index.html index a84dc45..51d1620 100644 --- a/doc/build/tags/index.html +++ b/doc/build/tags/index.html @@ -4,7 +4,7 @@ Tags | StatePulse.NET - + diff --git a/doc/build/tags/installation/index.html b/doc/build/tags/installation/index.html index da194b1..1c5d0b1 100644 --- a/doc/build/tags/installation/index.html +++ b/doc/build/tags/installation/index.html @@ -4,7 +4,7 @@ One doc tagged with "installation" | StatePulse.NET - + diff --git a/doc/build/tags/isafeaction/index.html b/doc/build/tags/isafeaction/index.html index b29bf16..d123931 100644 --- a/doc/build/tags/isafeaction/index.html +++ b/doc/build/tags/isafeaction/index.html @@ -4,7 +4,7 @@ One doc tagged with "isafeaction" | StatePulse.NET - + diff --git a/doc/build/tags/net/index.html b/doc/build/tags/net/index.html index a67a40d..c7499e5 100644 --- a/doc/build/tags/net/index.html +++ b/doc/build/tags/net/index.html @@ -4,7 +4,7 @@ 6 docs tagged with ".net" | StatePulse.NET - + diff --git a/doc/build/tags/performance/index.html b/doc/build/tags/performance/index.html index 35faa13..5ae1ba5 100644 --- a/doc/build/tags/performance/index.html +++ b/doc/build/tags/performance/index.html @@ -4,7 +4,7 @@ 2 docs tagged with "performance" | StatePulse.NET - + diff --git a/doc/build/tags/pure-functions/index.html b/doc/build/tags/pure-functions/index.html index 8eaecbb..f1bf5e5 100644 --- a/doc/build/tags/pure-functions/index.html +++ b/doc/build/tags/pure-functions/index.html @@ -4,7 +4,7 @@ 2 docs tagged with "pure-functions" | StatePulse.NET - + diff --git a/doc/build/tags/reducer/index.html b/doc/build/tags/reducer/index.html index 3289ac1..2447d70 100644 --- a/doc/build/tags/reducer/index.html +++ b/doc/build/tags/reducer/index.html @@ -4,7 +4,7 @@ 2 docs tagged with "reducer" | StatePulse.NET - + diff --git a/doc/build/tags/redux/index.html b/doc/build/tags/redux/index.html index 15a64ec..c3a163f 100644 --- a/doc/build/tags/redux/index.html +++ b/doc/build/tags/redux/index.html @@ -4,7 +4,7 @@ One doc tagged with "redux" | StatePulse.NET - + diff --git a/doc/build/tags/safedispatch/index.html b/doc/build/tags/safedispatch/index.html index 87d6f22..60551a8 100644 --- a/doc/build/tags/safedispatch/index.html +++ b/doc/build/tags/safedispatch/index.html @@ -4,7 +4,7 @@ One doc tagged with "safedispatch" | StatePulse.NET - + diff --git a/doc/build/tags/setup/index.html b/doc/build/tags/setup/index.html index 078921e..619cb03 100644 --- a/doc/build/tags/setup/index.html +++ b/doc/build/tags/setup/index.html @@ -4,7 +4,7 @@ One doc tagged with "setup" | StatePulse.NET - + diff --git a/doc/build/tags/side-effects/index.html b/doc/build/tags/side-effects/index.html index 70d3d4f..925f03e 100644 --- a/doc/build/tags/side-effects/index.html +++ b/doc/build/tags/side-effects/index.html @@ -4,7 +4,7 @@ One doc tagged with "side-effects" | StatePulse.NET - + diff --git a/doc/build/tags/state-management/index.html b/doc/build/tags/state-management/index.html index ebebbef..642c1ff 100644 --- a/doc/build/tags/state-management/index.html +++ b/doc/build/tags/state-management/index.html @@ -4,7 +4,7 @@ 6 docs tagged with "state-management" | StatePulse.NET - + diff --git a/doc/build/tags/state/index.html b/doc/build/tags/state/index.html index a0e7c4d..eb2771c 100644 --- a/doc/build/tags/state/index.html +++ b/doc/build/tags/state/index.html @@ -4,7 +4,7 @@ One doc tagged with "state" | StatePulse.NET - + diff --git a/doc/build/tags/statepulse/index.html b/doc/build/tags/statepulse/index.html index 49e8861..969e8ca 100644 --- a/doc/build/tags/statepulse/index.html +++ b/doc/build/tags/statepulse/index.html @@ -4,7 +4,7 @@ 7 docs tagged with "statepulse" | StatePulse.NET - + diff --git a/doc/build/versions/index.html b/doc/build/versions/index.html index 42fa319..6f51f1a 100644 --- a/doc/build/versions/index.html +++ b/doc/build/versions/index.html @@ -3,15 +3,23 @@ -Updates | StatePulse.NET - +Updates | StatePulse.NET + -

Updates

v2.0.1

+

Updates

v2.1.0

+

Performance

+
    +
  • The Global Tracker now uses an event‑based mechanism to invoke Self Disposal Check directly on each IStatePulse instance, removing the need to iterate the internal registry. The registry itself is scheduled for removal in version 3.0 and is now marked with the appropriate Obsolete attribute. Cleanup is no longer driven by a continuous loop; instead, the tracker schedules a self‑check only when activity occurs in the registry and goes idle when the application becomes inactive. This preserves memory‑leak protection without a permanent cycle. A minimum delay of 10 seconds is enforced between checks, and when burst activity occurs, only the final activity triggers a single final cleanup run rather than multiple redundant executions.
  • +
+

Fix

+

This is one of those “bug or feature?” situations. By design, StateOf<TState>() is meant to notify the component when its state changes. However, earlier Blazor assumptions overlooked scenarios where one state might render through one route while another state uses a different route.

+

Before version 2.0.11, StatePulse always treated the latest route as the default route for all states registered by a component, this is was BUG. Starting with 2.0.11, each state that a component subscribes to StatePulse now respects those route differences if any.

+

v2.0.1

Fixes

  • Moved Fluent API Extension Methods into Abstraction Package.
  • @@ -209,6 +217,6 @@

    v0.9.2
    • Deprecated now part of StatePulse regular since we have removed the dependencies to blazor component. ... that was quick!
    • -

+
\ No newline at end of file From 70f152a93c80993d70a5f4b6884a711333dafaba Mon Sep 17 00:00:00 2001 From: "m.samson" Date: Wed, 4 Mar 2026 20:48:51 +0200 Subject: [PATCH 3/3] updated version --- src/Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 7c8eaea..85f4fcd 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -1,6 +1,6 @@ - 2.0.114 + 2.1.0 Maksim Shimshon © 2026