From c196191a14fc3e584d14eec8bafeb4abc6a3c14e Mon Sep 17 00:00:00 2001 From: "m.samson" Date: Fri, 1 May 2026 15:01:31 +0300 Subject: [PATCH] Major fix of Middleware not able to be registered multiple times therefore prevent multiple middleware to work for same interface. --- doc/build/404.html | 2 +- ...d74f7.35cfaf6e.js => 22dd74f7.f784a3d1.js} | 2 +- doc/build/assets/js/94cf9043.a31f232c.js | 1 - doc/build/assets/js/94cf9043.cdbd9e88.js | 1 + ...n.ff5087c2.js => runtime~main.4fb4416e.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 +- .../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 | 15 +++-- doc/docs/0.Versions.md | 3 + src/Directory.Packages.props | 2 +- src/StatePulse.NET/ServiceRegisterExt.cs | 12 ++-- tests/StatPulse.NET.Tests/TestBase.cs | 3 + .../Middlewares/ThirdDispatchMiddleware.cs | 57 +++++++++++++++++++ 45 files changed, 120 insertions(+), 48 deletions(-) rename doc/build/assets/js/{22dd74f7.35cfaf6e.js => 22dd74f7.f784a3d1.js} (97%) delete mode 100644 doc/build/assets/js/94cf9043.a31f232c.js create mode 100644 doc/build/assets/js/94cf9043.cdbd9e88.js rename doc/build/assets/js/{runtime~main.ff5087c2.js => runtime~main.4fb4416e.js} (97%) create mode 100644 tests/StatPulse.NET.Tests/TestCases/Pulsars/MainMenu/Middlewares/ThirdDispatchMiddleware.cs diff --git a/doc/build/404.html b/doc/build/404.html index 01149f6..21386bc 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.35cfaf6e.js b/doc/build/assets/js/22dd74f7.f784a3d1.js similarity index 97% rename from doc/build/assets/js/22dd74f7.35cfaf6e.js rename to doc/build/assets/js/22dd74f7.f784a3d1.js index 2e22a6e..d4b92bc 100644 --- a/doc/build/assets/js/22dd74f7.35cfaf6e.js +++ b/doc/build/assets/js/22dd74f7.f784a3d1.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.1.0","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.2.0","sidebar":"tutorialSidebar"}}}}')}}]); \ No newline at end of file diff --git a/doc/build/assets/js/94cf9043.a31f232c.js b/doc/build/assets/js/94cf9043.a31f232c.js deleted file mode 100644 index df9524f..0000000 --- a/doc/build/assets/js/94cf9043.a31f232c.js +++ /dev/null @@ -1 +0,0 @@ -"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/94cf9043.cdbd9e88.js b/doc/build/assets/js/94cf9043.cdbd9e88.js new file mode 100644 index 0000000..816f467 --- /dev/null +++ b/doc/build/assets/js/94cf9043.cdbd9e88.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.2.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.2.0",id:"v220",level:2},{value:"Fix",id:"fix",level:3},{value:"v2.1.0",id:"v210",level:2},{value:"Performance",id:"performance",level:3},{value:"Fix",id:"fix-1",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:"v220",children:"v2.2.0"}),"\n",(0,r.jsx)(n.h3,{id:"fix",children:"Fix"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Major fix of Middleware not able to be registered multiple times therefore prevent multiple middleware to work for same interface."}),"\n"]}),"\n",(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-1",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.ff5087c2.js b/doc/build/assets/js/runtime~main.4fb4416e.js similarity index 97% rename from doc/build/assets/js/runtime~main.ff5087c2.js rename to doc/build/assets/js/runtime~main.4fb4416e.js index 9461772..326b736 100644 --- a/doc/build/assets/js/runtime~main.ff5087c2.js +++ b/doc/build/assets/js/runtime~main.4fb4416e.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:"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{"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:"f784a3d1",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:"cdbd9e88",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 b08bd6d..c1eaa1f 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 d86476b..5bbb3fc 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 bc5f853..0cb9200 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 7b6d0fc..4dc29f0 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 5523ce6..acf23a7 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 4564477..342d229 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 d059fc4..015eb06 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 2169d09..4d42d28 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 d280d4d..f007143 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 5603247..2b7511f 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 e8511c7..f77aa71 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 3902a70..b4d1960 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 8c295c7..3267951 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 2b4db00..5269487 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 0865266..10e47be 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 e460ef2..19000cb 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 927fe41..6d59c07 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 3c6a6d1..ed282dd 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 9740c48..8eaaef1 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 51d1620..d613fdc 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 1c5d0b1..77cd151 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 d123931..575dce7 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 c7499e5..e4542c5 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 5ae1ba5..4c1903b 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 f1bf5e5..bed8426 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 2447d70..0d825ba 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 c3a163f..40d96ae 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 60551a8..f8f1073 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 619cb03..8b6cc09 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 925f03e..92cd79b 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 642c1ff..c737809 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 eb2771c..ce02c2a 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 969e8ca..707f33d 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 6f51f1a..7a44baf 100644 --- a/doc/build/versions/index.html +++ b/doc/build/versions/index.html @@ -3,20 +3,25 @@ -Updates | StatePulse.NET - +Updates | StatePulse.NET + -

Updates

v2.1.0

+

Updates

v2.2.0

+

Fix

+
    +
  • Major fix of Middleware not able to be registered multiple times therefore prevent multiple middleware to work for same interface.
  • +
+

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

+

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

@@ -217,6 +222,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 diff --git a/doc/docs/0.Versions.md b/doc/docs/0.Versions.md index 18368c4..b9a1b46 100644 --- a/doc/docs/0.Versions.md +++ b/doc/docs/0.Versions.md @@ -3,6 +3,9 @@ slug: versions title: Updates sidebar_position: 0 --- +## v2.2.0 +### Fix +- Major fix of Middleware not able to be registered multiple times therefore prevent multiple middleware to work for same interface. ## v2.1.0 ### Performance diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 85f4fcd..7a4786c 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -1,6 +1,6 @@ - 2.1.0 + 2.2.0 Maksim Shimshon © 2026 diff --git a/src/StatePulse.NET/ServiceRegisterExt.cs b/src/StatePulse.NET/ServiceRegisterExt.cs index d29efb0..67a29af 100644 --- a/src/StatePulse.NET/ServiceRegisterExt.cs +++ b/src/StatePulse.NET/ServiceRegisterExt.cs @@ -180,10 +180,14 @@ public static bool IsReducerRegistered(this IServiceCollection services, Type re public static bool IsImplementationRegistered(this IServiceCollection services, Type implementationType, Type ifaceType) { return services.Any(s => - s.ImplementationType == implementationType && - s.ServiceType.IsGenericType && - s.ServiceType.GetGenericTypeDefinition() == ifaceType || - !s.ServiceType.IsGenericType && s.ServiceType == ifaceType + { + bool isSameImplementation = s.ImplementationType == implementationType; + bool isTheServiceSameAsInterface = + s.ServiceType.IsGenericType && s.ServiceType.GetGenericTypeDefinition() == ifaceType || + !s.ServiceType.IsGenericType && s.ServiceType == ifaceType; + + return isSameImplementation && isTheServiceSameAsInterface; + } ); } public static bool IsEffectValidatorImplementationRegistered(this IServiceCollection services, Type implementationType) diff --git a/tests/StatPulse.NET.Tests/TestBase.cs b/tests/StatPulse.NET.Tests/TestBase.cs index 325c3ec..f04363c 100644 --- a/tests/StatPulse.NET.Tests/TestBase.cs +++ b/tests/StatPulse.NET.Tests/TestBase.cs @@ -6,6 +6,7 @@ using StatePulse.NET.Tests.TestCases.Pulsars.MainMenu.Actions; using StatePulse.NET.Tests.TestCases.Pulsars.MainMenu.Effects; using StatePulse.NET.Tests.TestCases.Pulsars.MainMenu.Effects.Validators; +using StatePulse.NET.Tests.TestCases.Pulsars.MainMenu.Middlewares; using StatePulse.NET.Tests.TestCases.Pulsars.MainMenu.Reducers; using StatePulse.NET.Tests.TestCases.Pulsars.MainMenu.Store; using StatePulse.NET.Tests.TestCases.Pulsars.Profile.Actions; @@ -53,6 +54,8 @@ protected TestBase() ServiceCollection.AddStatePulseService(); ServiceCollection.AddStatePulseService(); + ServiceCollection.AddStatePulseService(); + ServiceCollection.AddStatePulseService(); // Register your services ServiceProvider = ServiceCollection.BuildServiceProvider(); } diff --git a/tests/StatPulse.NET.Tests/TestCases/Pulsars/MainMenu/Middlewares/ThirdDispatchMiddleware.cs b/tests/StatPulse.NET.Tests/TestCases/Pulsars/MainMenu/Middlewares/ThirdDispatchMiddleware.cs new file mode 100644 index 0000000..ff5e391 --- /dev/null +++ b/tests/StatPulse.NET.Tests/TestCases/Pulsars/MainMenu/Middlewares/ThirdDispatchMiddleware.cs @@ -0,0 +1,57 @@ +using StatePulse.Net; + +namespace StatePulse.NET.Tests.TestCases.Pulsars.MainMenu.Middlewares; + +internal class ThirdDispatchMiddleware : IDispatcherMiddleware, IEffectMiddleware, IReducerMiddleware +{ + public Task AfterDispatch(object action) + { + Console.WriteLine($"{action.GetType().Name} Executed."); + return Task.CompletedTask; + } + + public Task AfterEffect(object action) + { + Console.WriteLine($"{action.GetType().Name} Executed."); + return Task.CompletedTask; + } + + public Task AfterReducing(object state, object action) + { + + Console.WriteLine($"{action.GetType().Name} Executed."); + return Task.CompletedTask; + } + + public Task BeforeDispatch(object action) + { + Console.WriteLine($"{action.GetType().Name} Starting."); + return Task.CompletedTask; + } + + public Task BeforeEffect(object action) + { + Console.WriteLine($"{action.GetType().Name} Executed."); + return Task.CompletedTask; + } + + public Task BeforeReducing(object state, object action) + { + Console.WriteLine($"{state.GetType().Name} {action.GetType().Name} Executed."); + return Task.CompletedTask; + } + + public Task OnDispatchFailure(Exception exception, object action) + => Task.CompletedTask; + + public Task WhenEffectValidationFailed(object action, object effectValidator) + { + Console.WriteLine($"{action.GetType().Name} {effectValidator.GetType().Name}r Executed."); + return Task.CompletedTask; + } + public Task WhenEffectValidationSucceed(object action, object effectValidator) + { + Console.WriteLine($"{action.GetType().Name} {effectValidator.GetType().Name}e Executed."); + return Task.CompletedTask; + } +}