├── .ai ├── 00-confirmation.md ├── 01-user.md ├── 02-development-process.md ├── 03-environment.md ├── 04-csharp-coding-standards.md ├── 05-dotnet-conventions.md ├── Index.md └── other │ ├── blog.md │ ├── create-prompt.md │ ├── nugets.md │ ├── project-structure.md │ ├── references.md │ ├── shell-commands.md │ └── tools.md ├── .aider.conf.yml ├── .aiderignore ├── .cline-rules ├── .config └── dotnet-tools.json ├── .devcontainer └── devcontainer.json ├── .editorconfig ├── .github ├── dependabot.yml ├── pull_request_template.md └── workflows │ ├── build.ps1 │ ├── ci-build.yml │ ├── common.ps1 │ ├── publish-documentation.yml │ ├── release-build.yml │ ├── scorecard.yml │ └── test.ps1 ├── .gitignore ├── .mailmap ├── Assets ├── Logo.png └── Logo.svg ├── BuildAndPackageAnalyzer.ps1 ├── BuildNuGets.ps1 ├── ConvertTimestamp.ps1 ├── Directory.Build.props ├── Directory.Build.targets ├── Directory.Packages.props ├── Documentation ├── .gitignore ├── ArchitecturalDecisionRecords │ └── 0000-use-markdown-architectural-decision-records.md ├── Blips │ ├── 2024-02-ActionTracking-Sample.md │ ├── 2024-02-ReduxDevTools-Sample.md │ ├── 2024-02-Routing-Sample.md │ ├── 2024-02-StateActionHandler-Sample.md │ ├── 2024-02-TimeWarp-State-11-Release.md │ └── Index.md ├── Blogs │ ├── 2024-01-17-enforcing-state-action-architecture.md │ └── 2024-02-TimeWarp-State-11-Release.md ├── Contributing │ └── Overview.md ├── DevOps │ ├── DevOps.md │ └── toc.yml ├── Features │ ├── Features.md │ └── toc.yml ├── Images │ ├── ReduxDevTools.png │ ├── ReduxRouteState.png │ ├── TimeWarpStateOneWayFlow.drawio │ └── TimeWarpStateOneWayFlow.drawio.svg ├── Migrations │ ├── Migration1-2.md │ ├── Migration10-11.md │ ├── Migration2-3.md │ ├── Migration3-4.md │ ├── Migration4-5.md │ ├── Migration5-6.md │ ├── Migration6-7.md │ ├── Migration7-8.md │ ├── Migration8-9.md │ ├── Migration9-10.md │ └── toc.yml ├── Model │ ├── ArchitectureOverviewModel.mdj │ ├── Main.png │ └── SampleState.mdj ├── Overview.md ├── Partials │ ├── Acknowledgements.md │ ├── Badges.md │ ├── Contact.md │ ├── Contributing.md │ ├── GettingStarted.md │ ├── GiveAStar.md │ ├── Installation.md │ ├── License.md │ ├── Releases.md │ ├── Summary.md │ └── Terminology.md ├── ReleaseNotes │ ├── Release1.0.0.md │ ├── Release10.0.0.md │ ├── Release11.0.0.md │ ├── Release2.0.0.md │ ├── Release3.0.0.md │ ├── Release4.0.0.md │ ├── Release5.0.0.md │ ├── Release6.0.0.md │ ├── Release7.0.0.md │ ├── Release8.0.0.md │ ├── Release9.0.0.md │ └── toc.yml ├── Topics │ ├── AddReduxDevTools.md │ ├── EnableJavascriptInterop.md │ ├── Routing.md │ └── toc.yml ├── Tutorial │ └── DefaultTemplateScreenShot.png ├── docfx.json ├── index.md └── toc.yml ├── Kanban ├── Backlog │ ├── Scratch │ │ ├── Done.md │ │ ├── Overview.md │ │ ├── Rejected.md │ │ └── Todo.md │ └── _._ ├── Done │ ├── 001_Build-TimeWarp-State-Tutorial.md │ ├── 002_Create-TimeWarp-State-Server-Tutorial.md │ ├── 003_Create-TimeWarp-State-WebAssembly-Tutorial.md │ ├── 004_Fix-Source-Generator-Visibility-Bug.md │ ├── 005_Build-ReduxDevTools-Tutorial.md │ ├── 006_Rename-Sample00-Application.md │ ├── 007-Create-Action-Tracking-Sample.md │ ├── 008-Move-Active-Actions-Section.md │ ├── 009-Create-Routing-Sample.md │ ├── 012-Create-TrackedPageTitle-Component.md │ ├── 013_Update-Breadcrumb-Navigation.md │ ├── 014-Validate-Program-Main-Usage-In-Samples.md │ ├── 015-Optimize-TwPageTitle-Rendering.md │ ├── 016-Create-Sample03Wasm-Tutorial.md │ ├── 017-Push-To-Production.md │ ├── 018-Fix-Routing-State-Persistence.md │ ├── 019-Fix-Auto-Sample-Project-Structure.md │ ├── 021-Write-Release-Announcements.md │ ├── 023-Create-Samples-Index.md │ ├── 024-Create-StateActionHandler-Sample-Blip.md │ ├── 025-Create-ReduxDevTools-Sample-Blip.md │ ├── 026-Create-ActionTracking-Sample-Blip.md │ ├── 027-Create-Routing-Sample-Blip.md │ └── _._ ├── ReadMe.md ├── Task-Template.md └── ToDo │ ├── -.- │ ├── 010-Update-PersistenceStateSourceGenerator-To-Incremental.md │ ├── 011-Update-ActionSetMethodSourceGenerator-To-Incremental.md │ ├── 020-Fix-Documentation-Warnings.md │ └── 022-Create-TimeWarpStateComponent-Samples.md ├── NuGet.config ├── README.md ├── RunE2ETests.ps1 ├── RunTestApp.ps1 ├── RunTests.ps1 ├── Samples ├── 00-StateActionHandler │ ├── Auto │ │ ├── Readme.md │ │ └── Sample00Auto │ │ │ ├── Sample00Auto.Client │ │ │ ├── Features │ │ │ │ └── Counter │ │ │ │ │ ├── CounterState.IncrementCount.cs │ │ │ │ │ └── CounterState.cs │ │ │ ├── GlobalUsings.cs │ │ │ ├── Pages │ │ │ │ └── Counter.razor │ │ │ ├── Program.cs │ │ │ ├── Sample00Auto.Client.csproj │ │ │ ├── _Imports.razor │ │ │ └── wwwroot │ │ │ │ ├── appsettings.Development.json │ │ │ │ └── appsettings.json │ │ │ ├── Sample00Auto.sln │ │ │ └── Sample00Auto │ │ │ ├── Components │ │ │ ├── App.razor │ │ │ ├── Layout │ │ │ │ ├── MainLayout.razor │ │ │ │ ├── MainLayout.razor.css │ │ │ │ ├── NavMenu.razor │ │ │ │ └── NavMenu.razor.css │ │ │ ├── Pages │ │ │ │ ├── Error.razor │ │ │ │ ├── Home.razor │ │ │ │ └── Weather.razor │ │ │ ├── Routes.razor │ │ │ └── _Imports.razor │ │ │ ├── GlobalUsings.cs │ │ │ ├── Program.cs │ │ │ ├── Properties │ │ │ └── launchSettings.json │ │ │ ├── Sample00Auto.csproj │ │ │ ├── appsettings.Development.json │ │ │ ├── appsettings.json │ │ │ └── wwwroot │ │ │ ├── app.css │ │ │ ├── bootstrap │ │ │ ├── bootstrap.min.css │ │ │ └── bootstrap.min.css.map │ │ │ └── favicon.png │ ├── README.md │ ├── Server │ │ ├── README.md │ │ └── Sample00Server │ │ │ ├── Components │ │ │ ├── App.razor │ │ │ ├── Layout │ │ │ │ ├── MainLayout.razor │ │ │ │ ├── MainLayout.razor.css │ │ │ │ ├── NavMenu.razor │ │ │ │ └── NavMenu.razor.css │ │ │ ├── Pages │ │ │ │ ├── Counter.razor │ │ │ │ ├── Error.razor │ │ │ │ ├── Home.razor │ │ │ │ └── Weather.razor │ │ │ ├── Routes.razor │ │ │ └── _Imports.razor │ │ │ ├── Features │ │ │ └── Counter │ │ │ │ ├── CounterState.IncrementCount.cs │ │ │ │ └── CounterState.cs │ │ │ ├── GlobalUsings.cs │ │ │ ├── Program.cs │ │ │ ├── Properties │ │ │ └── launchSettings.json │ │ │ ├── Sample00Server.csproj │ │ │ ├── appsettings.Development.json │ │ │ ├── appsettings.json │ │ │ └── wwwroot │ │ │ ├── app.css │ │ │ ├── bootstrap │ │ │ ├── bootstrap.min.css │ │ │ └── bootstrap.min.css.map │ │ │ └── favicon.png │ └── Wasm │ │ ├── README.md │ │ └── Sample00Wasm │ │ ├── App.razor │ │ ├── Features │ │ └── Counter │ │ │ ├── CounterState.IncrementCount.cs │ │ │ └── CounterState.cs │ │ ├── GlobalUsings.cs │ │ ├── Layout │ │ ├── MainLayout.razor │ │ ├── MainLayout.razor.css │ │ ├── NavMenu.razor │ │ └── NavMenu.razor.css │ │ ├── Pages │ │ ├── Counter.razor │ │ ├── Home.razor │ │ └── Weather.razor │ │ ├── Program.cs │ │ ├── Properties │ │ └── launchSettings.json │ │ ├── Sample00Wasm.csproj │ │ ├── _Imports.razor │ │ └── wwwroot │ │ ├── css │ │ ├── app.css │ │ └── bootstrap │ │ │ ├── bootstrap.min.css │ │ │ └── bootstrap.min.css.map │ │ ├── favicon.png │ │ ├── icon-192.png │ │ ├── index.html │ │ └── sample-data │ │ └── weather.json ├── 01-ReduxDevTools │ ├── Images │ │ ├── BlazorWasmHostedScreenShot.jpeg │ │ ├── BlazorWasmHostedScreenShot.png │ │ ├── ReduxDevTools.png │ │ └── ReduxRouteState.png │ └── Wasm │ │ ├── Readme.md │ │ └── Sample01Wasm │ │ ├── App.razor │ │ ├── Features │ │ └── Counter │ │ │ ├── CounterState.IncrementCount.cs │ │ │ └── CounterState.cs │ │ ├── GlobalUsings.cs │ │ ├── Layout │ │ ├── MainLayout.razor │ │ ├── MainLayout.razor.css │ │ ├── NavMenu.razor │ │ └── NavMenu.razor.css │ │ ├── Pages │ │ ├── Counter.razor │ │ ├── Home.razor │ │ └── Weather.razor │ │ ├── Program.cs │ │ ├── Properties │ │ └── launchSettings.json │ │ ├── Sample01Wasm.csproj │ │ ├── _Imports.razor │ │ └── wwwroot │ │ ├── css │ │ ├── app.css │ │ └── bootstrap │ │ │ ├── bootstrap.min.css │ │ │ └── bootstrap.min.css.map │ │ ├── favicon.png │ │ ├── icon-192.png │ │ ├── index.html │ │ └── sample-data │ │ └── weather.json ├── 02-ActionTracking │ └── Wasm │ │ ├── README.md │ │ └── Sample02Wasm │ │ ├── App.razor │ │ ├── Features │ │ └── Demo │ │ │ ├── DemoState.FiveSecondAction.cs │ │ │ ├── DemoState.TwoSecondAction.cs │ │ │ └── DemoState.cs │ │ ├── GlobalUsings.cs │ │ ├── Layout │ │ ├── MainLayout.razor │ │ ├── MainLayout.razor.css │ │ ├── NavMenu.razor │ │ └── NavMenu.razor.css │ │ ├── Pages │ │ ├── Counter.razor │ │ ├── Demo.razor │ │ ├── Home.razor │ │ └── Weather.razor │ │ ├── Program.cs │ │ ├── Properties │ │ └── launchSettings.json │ │ ├── Sample02Wasm.csproj │ │ ├── _Imports.razor │ │ └── wwwroot │ │ ├── css │ │ ├── app.css │ │ └── bootstrap │ │ │ ├── bootstrap.min.css │ │ │ └── bootstrap.min.css.map │ │ ├── favicon.png │ │ ├── icon-192.png │ │ ├── index.html │ │ └── sample-data │ │ └── weather.json ├── 03-Routing │ ├── README.md │ └── Wasm │ │ ├── AI.prompt.md │ │ ├── README.md │ │ └── Sample03Wasm │ │ ├── App.razor │ │ ├── Features │ │ └── Counter │ │ │ ├── CounterState.IncrementCount.cs │ │ │ └── CounterState.cs │ │ ├── GlobalUsings.cs │ │ ├── Layout │ │ ├── MainLayout.razor │ │ ├── MainLayout.razor.css │ │ ├── NavMenu.razor │ │ └── NavMenu.razor.css │ │ ├── Pages │ │ ├── Counter.razor │ │ ├── Home.razor │ │ └── Weather.razor │ │ ├── Program.cs │ │ ├── Properties │ │ └── launchSettings.json │ │ ├── Run.ps1 │ │ ├── Sample03Wasm.csproj │ │ ├── _Imports.razor │ │ └── wwwroot │ │ ├── css │ │ ├── app.css │ │ └── bootstrap │ │ │ ├── bootstrap.min.css │ │ │ └── bootstrap.min.css.map │ │ ├── favicon.png │ │ ├── icon-192.png │ │ ├── index.html │ │ └── sample-data │ │ └── weather.json ├── Directory.Build.props └── README.md ├── Scripts ├── BuildReadMe.ps1 ├── FixAnalyzerDebug.reg └── Readme.md ├── Source ├── TimeWarp.State.Analyzer │ ├── AnalyzerReleases.Shipped.md │ ├── AnalyzerReleases.Unshipped.md │ ├── GlobalUsings.cs │ ├── StateImplementationAnalyzer.cs │ ├── StateInheritanceAnalyzer.cs │ ├── StateReadOnlyPublicPropertiesAnalyzer.cs │ ├── TimeWarp.State.Analyzer.csproj │ ├── TimeWarpStateActionAnalyzer.cs │ ├── TimeWarpStateActionAnalyzer.cs.md │ └── packages.lock.json ├── TimeWarp.State.Plus │ ├── AssemblyMarker.cs │ ├── EventIds.cs │ ├── Extensions │ │ ├── AssemblyExtensions.cs │ │ └── ServiceCollectionExtensions.cs │ ├── Features │ │ ├── ActionTracking │ │ │ ├── ActionTrackingState │ │ │ │ ├── ActionTrackingState.CompleteProcessing.cs │ │ │ │ ├── ActionTrackingState.Debug.cs │ │ │ │ ├── ActionTrackingState.StartProcessing.cs │ │ │ │ └── ActionTrackingState.cs │ │ │ └── Pipeline │ │ │ │ ├── ActionTrackingBehavior.cs │ │ │ │ └── TrackActionAttribute.cs │ │ ├── FeatureFlags │ │ │ └── FeatureFlagState │ │ │ │ └── FeatureFlagState.cs │ │ ├── Persistentence │ │ │ ├── Pipeline │ │ │ │ └── PersistentStatePostProcessor.cs │ │ │ ├── Services │ │ │ │ └── PersistenceService.cs │ │ │ └── StateInitializedNotificationHandler.cs │ │ ├── Routing │ │ │ ├── Components │ │ │ │ ├── TimeWarpPageRenderNotifier.razor.cs │ │ │ │ ├── TwBreadcrumb.razor │ │ │ │ ├── TwPageTitle.md │ │ │ │ └── TwPageTitle.razor │ │ │ └── RouteState │ │ │ │ ├── RouteState.ChangeRoute.cs │ │ │ │ ├── RouteState.GoBack.cs │ │ │ │ ├── RouteState.PushRouteInfo.cs │ │ │ │ └── RouteState.cs │ │ ├── Theme │ │ │ └── ThemeState │ │ │ │ ├── ThemeState.Debug.cs │ │ │ │ ├── ThemeState.Update.cs │ │ │ │ └── ThemeState.cs │ │ └── Timers │ │ │ ├── MultiTimerOptions.cs │ │ │ ├── MultiTimerPostProcessor.cs │ │ │ ├── README.md │ │ │ ├── TimerConfig.cs │ │ │ ├── TimerElapsedNotification.cs │ │ │ └── TimerState │ │ │ ├── TimerState.AddTimer.cs │ │ │ ├── TimerState.RemoveTimer.cs │ │ │ ├── TimerState.ResetTimersOnActivity.cs │ │ │ ├── TimerState.UpdateTimer.cs │ │ │ └── TimerState.cs │ ├── GlobalUsings.cs │ ├── README.md │ ├── State │ │ ├── ITimeWarpCacheableState.cs │ │ └── TimeWarpCacheableState.cs │ ├── TimeWarp.State.Plus.csproj │ ├── _Imports.razor │ ├── packages.lock.json │ └── wwwroot │ │ └── js │ │ └── downloadFile.js ├── TimeWarp.State.Policies │ ├── BeNestedInStateCustomRule.cs │ ├── Extensions │ │ └── NetArchExtensions.cs │ ├── GlobalUsings.cs │ ├── HaveInjectableConstructor.cs │ ├── HaveJsonConstructor.cs │ ├── Policies.ActionHandlerPolicy.cs │ ├── Policies.ActionPolicy.cs │ ├── Policies.ActionSetPolicy.cs │ ├── Policies.StatePolicy.cs │ ├── README.md │ ├── TimeWarp.State.Policies.csproj │ └── packages.lock.json ├── TimeWarp.State.SourceGenerator │ ├── ActionSetMethodGenerator.cs │ ├── GlobalUsings.cs │ ├── PersistenceStateSourceGenerator.cs │ ├── TimeWarp.State.SourceGenerator.csproj │ └── packages.lock.json └── TimeWarp.State │ ├── AssemblyMarker.cs │ ├── Base │ ├── Action.cs │ └── ActionHandler.cs │ ├── Components │ ├── ITimeWarpStateComponent.cs │ ├── TimeWarpStateComponent.CheckComplexParameterChanged.cs │ ├── TimeWarpStateComponent.RegisterRenderTrigger.cs │ ├── TimeWarpStateComponent.RenderMode.cs │ ├── TimeWarpStateComponent.RenderReasons.cs │ ├── TimeWarpStateComponent.cs │ ├── TimeWarpStateComponent.md │ └── TimeWarpStateInputComponent.cs │ ├── EventIds.cs │ ├── Extensions │ ├── MethodInfoExtensions.cs │ ├── ServiceCollectionExtensions.AddTimeWarpState.cs │ ├── ServiceCollectionExtensions.LogTimeWarpStateMiddleware.cs │ ├── ServiceCollectionExtensions.UseReduxDevTools.cs │ ├── TimeWarpStateOptions.cs │ └── TypeExtensions.cs │ ├── Features │ ├── CloneState │ │ └── Pipeline │ │ │ ├── ExceptionNotification.cs │ │ │ ├── InvalidCloneException.cs │ │ │ └── StateTransactionBehavior.cs │ ├── Developer │ │ └── Components │ │ │ ├── ReduxDevTools.razor │ │ │ └── RenderModeDisplay.razor │ ├── JavaScriptInterop │ │ ├── Components │ │ │ └── TimeWarpJavaScriptInterop.razor │ │ ├── InvalidRequestTypeException.cs │ │ ├── JsonRequest.cs │ │ └── JsonRequestHandler.cs │ ├── Persistence │ │ ├── Abstractions │ │ │ └── IPersistenceService.cs │ │ ├── Attributes │ │ │ └── PersistentStateAttribute.cs │ │ └── PersistentStateMethod.cs │ ├── ReduxDevTools │ │ ├── Components │ │ │ └── TimeWarpStateDevComponent.cs │ │ ├── DispatchRequest.cs │ │ ├── ReduxAction.cs │ │ ├── ReduxDevToolsBehavior.cs │ │ ├── ReduxDevToolsInterop.cs │ │ ├── ReduxDevToolsOptions.cs │ │ └── Requests │ │ │ ├── Commit │ │ │ ├── CommitHandler.cs │ │ │ └── CommitRequest.cs │ │ │ ├── IReduxRequest.cs │ │ │ └── Start │ │ │ ├── StartHandler.cs │ │ │ └── StartRequest.cs │ ├── RenderSubscriptions │ │ ├── NonNestedClassException.cs │ │ ├── RenderSubscriptionContext.cs │ │ ├── RenderSubscriptionContext.md │ │ └── RenderSubscriptionsPostProcessor.cs │ └── StateInitialization │ │ └── StateInitializationPreProcessor.cs │ ├── GlobalSuppressions.cs │ ├── GlobalUsings.cs │ ├── Json │ └── CamelCase.cs │ ├── State │ ├── IState.cs │ └── State.cs │ ├── Store │ ├── IStore.cs │ ├── StateInitializedNotification.cs │ ├── Store.ReduxDevTools.cs │ └── Store.cs │ ├── Subscriptions.cs │ ├── TimeWarp.State.csproj │ ├── package.json.does-not-work │ ├── packages.lock.json │ ├── tsconfig.json │ └── wwwroot │ ├── TypeScript │ ├── Constants.ts │ ├── DotNetReference.d.ts │ ├── Logger.ts │ ├── ReduxDevTools.ts │ ├── ReduxDevToolsTypes.ts │ ├── TimeWarp.State.lib.module.ts │ └── TimeWarpState.ts │ ├── js │ ├── Constants.js │ ├── Constants.js.map │ ├── Logger.js │ ├── Logger.js.map │ ├── ReduxDevTools.js │ ├── ReduxDevTools.js.map │ ├── ReduxDevToolsTypes.js │ ├── ReduxDevToolsTypes.js.map │ ├── TimeWarp.State.lib.module.js │ ├── TimeWarp.State.lib.module.js.map │ ├── TimeWarpState.js │ └── TimeWarpState.js.map │ └── types │ ├── Constants.d.ts │ ├── Logger.d.ts │ ├── ReduxDevTools.d.ts │ ├── ReduxDevToolsTypes.d.ts │ ├── TimeWarp.State.lib.module.d.ts │ └── TimeWarpState.d.ts ├── Tests ├── Client.Integration.Tests │ ├── Client.Integration.Tests.csproj │ ├── Clone │ │ ├── TestState.cs │ │ └── TestState_Clone_Tests.cs │ ├── Convention_Tests.cs │ ├── Features │ │ ├── Application │ │ │ └── ApplicationState_Clone_Tests.cs │ │ ├── Blue │ │ │ └── BlueState_Deseralization_Tests.cs │ │ └── Counter │ │ │ ├── CounterState_Clone_Tests.cs │ │ │ └── CounterState_Deseralization_Tests.cs │ ├── GlobalUsings.cs │ ├── Infrastructure │ │ ├── BaseTest.cs │ │ ├── ClientHost.cs │ │ ├── ClientHostBuilder.cs │ │ └── TestingConvention.cs │ └── packages.lock.json ├── Test.App.Architecture.Tests │ ├── ArchitectureTests.cs │ ├── ConventionTests.cs │ ├── GlobalUsings.cs │ ├── Test.App.Architecture.Tests.csproj │ ├── TestingConvention.cs │ └── packages.lock.json ├── Test.App.EndToEnd.Tests │ ├── CacheableWeatherPageTests.cs │ ├── ChangeRoutePageTests.cs │ ├── Configuration.cs │ ├── ConfiguredRenderModes.cs │ ├── CounterPageTest.cs │ ├── EventStreamPageTests.cs │ ├── GlobalUsings.cs │ ├── GoBackPageTests.cs │ ├── HomePageTest.cs │ ├── JavaScriptInteropPageTests.cs │ ├── PageUtilities.cs │ ├── PersistenceTestPageTests.cs │ ├── PlaywrightSettings │ │ ├── chrome.runsettings │ │ ├── edge.runsettings │ │ ├── firefox.runsettings │ │ └── webkit.runsettings │ ├── RenderModes.cs │ ├── ResetStorePageTests.cs │ ├── SampleTest.cs │ ├── StaticWeatherForecastsPageTests.cs │ ├── Test.App.EndToEnd.Tests.csproj │ ├── ThrowExceptionPageTests.cs │ └── packages.lock.json ├── Test.App │ ├── Test.App.Client │ │ ├── AssemblyMarker.cs │ │ ├── Components │ │ │ ├── CustomInput.razor │ │ │ ├── CustomInput.razor.cs │ │ │ ├── NavMenu.razor │ │ │ └── NavMenu.razor.css │ │ ├── Extensions │ │ │ └── CollectionExtensions.cs │ │ ├── Features │ │ │ ├── Application │ │ │ │ ├── ApplicationState │ │ │ │ │ ├── ApplicationState.Debug.cs │ │ │ │ │ ├── ApplicationState.FiveSecondTask.cs │ │ │ │ │ ├── ApplicationState.ResetStore.cs │ │ │ │ │ ├── ApplicationState.TwoSecondTask.cs │ │ │ │ │ └── ApplicationState.cs │ │ │ │ └── Notification │ │ │ │ │ └── ApplicationState.ExceptionNotificationHandler.cs │ │ │ ├── Base │ │ │ │ ├── BaseActionHandler.cs │ │ │ │ └── Components │ │ │ │ │ ├── BaseComponent.cs │ │ │ │ │ ├── BaseInputComponent.cs │ │ │ │ │ └── ResetButton.razor │ │ │ ├── Blue │ │ │ │ ├── Actions │ │ │ │ │ └── BlueState.IncrementCount.cs │ │ │ │ └── BlueState.cs │ │ │ ├── CacheableWeather │ │ │ │ ├── Actions │ │ │ │ │ └── CacheableWeatherState.FetchWeatherForecasts.cs │ │ │ │ └── CacheableWeatherState.cs │ │ │ ├── CloneTest │ │ │ │ ├── Actions │ │ │ │ │ └── CloneableState.CloneTest.cs │ │ │ │ ├── CloneTestState.Debug.cs │ │ │ │ ├── CloneableState.cs │ │ │ │ └── Pages │ │ │ │ │ └── ClonablePage.razor │ │ │ ├── Color │ │ │ │ ├── Actions │ │ │ │ │ └── ColorState.Update.cs │ │ │ │ ├── ColorState.Debug.cs │ │ │ │ ├── ColorState.cs │ │ │ │ └── Components │ │ │ │ │ └── InputColor.razor │ │ │ ├── Counter │ │ │ │ ├── Actions │ │ │ │ │ ├── CounterState.IncrementCounter.cs │ │ │ │ │ ├── CounterState.ThrowException.cs │ │ │ │ │ ├── CounterState.ThrowServerSideException.cs │ │ │ │ │ ├── ImproperNestedAction │ │ │ │ │ │ ├── ImproperNestedAction.cs │ │ │ │ │ │ └── ImproperNestedHandler.cs │ │ │ │ │ └── NonNestedAction │ │ │ │ │ │ ├── NonNestedAction.cs │ │ │ │ │ │ └── NonNestedHandler.cs │ │ │ │ ├── Components │ │ │ │ │ ├── Counter.razor │ │ │ │ │ └── Counter.razor.cs │ │ │ │ ├── CounterState.Debug.cs │ │ │ │ ├── CounterState.cs │ │ │ │ ├── Notification │ │ │ │ │ ├── IncrementCountNotificationHandler.cs │ │ │ │ │ └── PreIncrementCountNotificationHandler.cs │ │ │ │ └── Pages │ │ │ │ │ └── CounterPage.razor │ │ │ ├── EventStream │ │ │ │ ├── Actions │ │ │ │ │ └── AddEvent │ │ │ │ │ │ └── EventStreamState.AddEventAction.cs │ │ │ │ ├── Components │ │ │ │ │ └── EventStream.razor │ │ │ │ ├── EventStreamState.Debug.cs │ │ │ │ ├── EventStreamState.cs │ │ │ │ └── Pipeline │ │ │ │ │ └── EventStreamBehavior.cs │ │ │ ├── Purple │ │ │ │ ├── Actions │ │ │ │ │ └── PurpleState.IncrementCount.cs │ │ │ │ ├── Components │ │ │ │ │ └── PurpleCounter.razor │ │ │ │ └── PurpleState.cs │ │ │ └── WeatherForecast │ │ │ │ ├── Actions │ │ │ │ └── WeatherForecastsState.FetchWeatherForecasts.cs │ │ │ │ └── WeatherForecastState.cs │ │ ├── GlobalUsings.cs │ │ ├── Pages │ │ │ ├── ActiveActionsPage.razor │ │ │ ├── CachableWeatherForecastsPage.razor │ │ │ ├── ChangeRoutePage.razor │ │ │ ├── CloneTestPage.razor │ │ │ ├── CustomInputExamplePage.razor │ │ │ ├── EventStreamPage.razor │ │ │ ├── GoBackPage.razor │ │ │ ├── JavaScriptInteropPage.razor │ │ │ ├── PersistenceTestPage.razor │ │ │ ├── ResetStorePage.razor │ │ │ ├── ServerSidePersistenceTestPage.razor │ │ │ ├── ServicesPage.razor │ │ │ ├── ShouldRenderStateTriggersTestPage │ │ │ │ ├── ChildComponentWithCounterStateRenderTrigger.razor │ │ │ │ ├── ChildComponentWithCounterStateSubscription.razor │ │ │ │ └── ShouldRenderStateTriggersTestPage.razor │ │ │ ├── ShouldRenderTestPage │ │ │ │ ├── ChildComponentParameterless.razor │ │ │ │ ├── ChildComponentWithAllParameters.razor │ │ │ │ ├── ChildComponentWithCascade.razor │ │ │ │ ├── ChildComponentWithCollection.razor │ │ │ │ ├── ChildComponentWithComplex.razor │ │ │ │ ├── ChildComponentWithComplexConstrained.razor │ │ │ │ ├── ChildComponentWithEventCallback.razor │ │ │ │ ├── ChildComponentWithPrimitives.razor │ │ │ │ └── ShouldRenderTestPage.razor │ │ │ ├── ThrowExceptionPage.razor │ │ │ └── WasmWeatherForecastsPage.razor │ │ ├── Pipeline │ │ │ ├── MyBehavior.cs │ │ │ ├── NotificationPostProcessor │ │ │ │ ├── PostPipelineNotification.cs │ │ │ │ └── PostPipelineNotificationRequestPostProcessor.cs │ │ │ └── NotificationPreProcessor │ │ │ │ ├── PrePipelineNotification.cs │ │ │ │ └── PrePipelineNotificationRequestPreProcessor.cs │ │ ├── Program.cs │ │ ├── Test.App.Client.csproj │ │ ├── TestObjects │ │ │ ├── ArrayObject.cs │ │ │ ├── BasicObject.cs │ │ │ ├── BasicObjectWithIgnore.cs │ │ │ ├── CollectionObject.cs │ │ │ ├── ComplexObject.cs │ │ │ ├── CustomCollectionObject.cs │ │ │ ├── DictionaryObject.cs │ │ │ ├── EqualityComparers.cs │ │ │ ├── ITestInterface.cs │ │ │ ├── InterfaceObject.cs │ │ │ ├── MultiDimensional2dArrayObject.cs │ │ │ ├── MultiDimensional3dArrayObject.cs │ │ │ └── TestEnum.cs │ │ ├── Tests │ │ │ └── CloneProviderTests.cs │ │ ├── _Imports.razor │ │ ├── packages.lock.json │ │ └── wwwroot │ │ │ ├── Test.App.Client.lib.module.js │ │ │ ├── appsettings.Development.json │ │ │ └── appsettings.json │ ├── Test.App.Contracts │ │ ├── Features │ │ │ ├── ExceptionHandling │ │ │ │ └── ThrowServerSideException │ │ │ │ │ ├── ThrowServerSideExceptionRequest.cs │ │ │ │ │ └── ThrowServerSideExceptionResponse.cs │ │ │ └── WeatherForecast │ │ │ │ └── Queries │ │ │ │ └── GetWeatherForecasts.cs │ │ ├── GlobalUsings.cs │ │ ├── Test.App.Contracts.csproj │ │ └── packages.lock.json │ ├── Test.App.Server │ │ ├── Components │ │ │ ├── App.razor │ │ │ ├── Layout │ │ │ │ ├── MainLayout.razor │ │ │ │ └── MainLayout.razor.css │ │ │ ├── Pages │ │ │ │ ├── Error.razor │ │ │ │ ├── Home.razor │ │ │ │ └── StaticWeatherForecastsPage.razor │ │ │ ├── Routes.razor │ │ │ └── _Imports.razor │ │ ├── GlobalUsings.cs │ │ ├── Program.cs │ │ ├── Properties │ │ │ └── launchSettings.json │ │ ├── Test.App.Server.csproj │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ ├── packages.lock.json │ │ └── wwwroot │ │ │ ├── app.css │ │ │ ├── bootstrap │ │ │ ├── bootstrap.min.css │ │ │ └── bootstrap.min.css.map │ │ │ └── favicon.png │ └── Test.App.sln ├── TimeWarp.State.Analyzer.Tests │ ├── .editorconfig │ ├── FixieVerifier.cs │ ├── GlobalUsings.cs │ ├── StateReadOnlyPublicPropertiesAnalyzer_Tests.cs │ ├── TestingConvention.cs │ ├── TimeWarp.State.Analyzer.Tests.csproj │ ├── TimeWarpStateActionAnalyser_Tests.cs │ └── packages.lock.json ├── TimeWarp.State.Plus.Tests │ ├── ArchitectureTests.cs │ ├── ConventionTests.cs │ ├── Features │ │ └── Routing │ │ │ └── RoutesState_Clone_Tests.cs │ ├── GlobalUsings.cs │ ├── TestingConvention.cs │ ├── TimeWarp.State.Plus.Tests.csproj │ └── packages.lock.json └── TimeWarp.State.Tests │ ├── ConventionTests.cs │ ├── GlobalUsings.cs │ ├── TestingConvention.cs │ ├── TimeWarp.State.Tests.csproj │ ├── TimeWarpStateComponent │ └── RegisterRenderTriggerTests.cs │ ├── TypeExtensionsTests.cs │ └── packages.lock.json ├── TimeWarp.State.sln ├── TimeWarp.State.sln.DotSettings ├── TimeWarp.State.slnx ├── UNLICENSE.txt ├── aider.instructions.md ├── cline.ps1 ├── global.json └── qodana.yaml /.ai/00-confirmation.md: -------------------------------------------------------------------------------- 1 | AI INSTRUCTION SET: 2 | 3 | CONFIRMATION REQUIREMENT: 4 | 5 | To ensure you understand my prompt, I need a piece of confirmation from you. Before any tool use and after any tool use, I need you to give me a confidence level on a scale of 0 to 10 on the tool use helping with the project. Remember to do this every time you are using a tool. 6 | -------------------------------------------------------------------------------- /.ai/01-user.md: -------------------------------------------------------------------------------- 1 | USER: 2 | - Name: Steven T. Cramer 3 | - Projects (Author): 4 | - https://github.com/TimeWarpEngineering/timewarp-state 5 | - https://github.com/TimeWarpEngineering/timewarp-architecture 6 | - https://github.com/TimeWarpEngineering/timewarp-fixie 7 | - https://github.com/TimeWarpEngineering/timewarp-options-validation 8 | - https://github.com/TimeWarpEngineering/timewarp-source-generators 9 | - Focus Areas: 10 | - State Management 11 | - Blazor 12 | - Clean Architecture 13 | - Domain-Driven Design 14 | - Test-Driven Development 15 | - Preferred Patterns: 16 | - CQRS 17 | - Language Preferences: 18 | - TypeScript over JavaScript -------------------------------------------------------------------------------- /.ai/02-development-process.md: -------------------------------------------------------------------------------- 1 | DEVELOPMENT PROCESS: 2 | 3 | KANBAN STRUCTURE: 4 | - Track work using Kanban tasks 5 | - Folders: 6 | - Kanban/Backlog/ 7 | - Kanban/ToDo/ 8 | - Kanban/InProgress/ 9 | - Kanban/Done/ 10 | 11 | TASK MANAGEMENT: 12 | - Task Template Location: `Kanban\Task-Template.md` 13 | - Task File Format: _.md 14 | ✓ `002_Create-Game-Logic.md` 15 | 16 | COMMIT CONVENTIONS: 17 | - Make git commits between steps 18 | - Format: Task: = 19 | ✓ `Task: 002 = Complete Create Game Logic` 20 | 21 | TASK WORKFLOW: 22 | ✓ Example of proper task movement: 23 | ```pwsh 24 | git mv Kanban/InProgress/002_Create-Game-Logic.md Kanban/Done/002_Create-Game-Logic.md 25 | git commit -m "Task: 002 = Complete Create Game Logic" 26 | ``` 27 | -------------------------------------------------------------------------------- /.ai/03-environment.md: -------------------------------------------------------------------------------- 1 | ENVIRONMENT: 2 | 3 | COMMAND SHELL: 4 | - Format commands for pwsh 5 | -------------------------------------------------------------------------------- /.ai/05-dotnet-conventions.md: -------------------------------------------------------------------------------- 1 | .NET CONVENTIONS: 2 | 3 | FRAMEWORK: 4 | - Target net8.0 5 | 6 | PROJECT CONFIGURATION: 7 | - Use Directory.Build.props for shared project properties 8 | - Use Directory.Packages.props for centralized package versioning 9 | - Enable nullable reference types 10 | 11 | SOLUTION MANAGEMENT: 12 | - Never edit .sln file directly 13 | ✓ `dotnet sln add ./src/MyProject/MyProject.csproj` 14 | ✗ Manual .sln file editing 15 | 16 | TOOLING: 17 | - Initialize local tool manifest 18 | ✓ ```pwsh 19 | dotnet new tool-manifest 20 | ``` 21 | Creates: .config/dotnet-tools.json 22 | -------------------------------------------------------------------------------- /.ai/Index.md: -------------------------------------------------------------------------------- 1 | # Index 2 | 3 | ## Purpose of the .ai folder 4 | 5 | The `.ai` folder is specifically designed to contain documents intended for Large Language Model (LLM) consumption. These files provide context, guidelines, and structured information about the project, which helps LLMs like myself to better understand and assist with the project. 6 | 7 | The contents of this folder are not typically used directly by the application or developers during normal development. Instead, they serve as a knowledge base for AI assistants, enabling more accurate and context-aware responses when working on the project. 8 | 9 | ## Usage 10 | 11 | When interacting with an AI assistant about this project, you can refer to the contents of the `.ai` folder or ask the AI to consider specific files within this folder for more informed assistance. 12 | -------------------------------------------------------------------------------- /.ai/other/blog.md: -------------------------------------------------------------------------------- 1 | --- 2 | destination: Documentation/Blogs/ 3 | example post: D:\git\github\TheFreezeTeam\TheFreezeTeamBlog\Source\TheFreezeTeam.com\input\posts\steven-t-cramer\2023\07\2023-07-06.md 4 | --- 5 | 6 | # Blog 7 | 8 | Information needed to generate blog posts. 9 | The destination is the folder where the blog posts will be generated. 10 | The example post is the path to the markdown file that will be used as a template for the blog post. 11 | -------------------------------------------------------------------------------- /.ai/other/create-prompt.md: -------------------------------------------------------------------------------- 1 | Read this document in full and then generate a prompt with specific instructions to complete this task for a coding LLM to use -------------------------------------------------------------------------------- /.ai/other/project-structure.md: -------------------------------------------------------------------------------- 1 | --- 2 | structure: 3 | - .ai/ 4 | - Assets/ 5 | - DevOps/ 6 | - Documentation/ 7 | - C4/ 8 | - Developer/ 9 | - StarUml/ 10 | - User/ 11 | - Kanban/ 12 | - Backlog/ 13 | - Done/ 14 | - InProgress/ 15 | - ToDo/ 16 | - Samples/ 17 | - Scripts/ 18 | - Git/ 19 | - Postgres/ 20 | - Windows/ 21 | - Source/ 22 | - Tests/ 23 | - Tools/ 24 | --- 25 | 26 | # Project Structure 27 | `.ai` folders can be used to store AI-related files, in any directory as needed to give more specific context. 28 | -------------------------------------------------------------------------------- /.ai/other/shell-commands.md: -------------------------------------------------------------------------------- 1 | --- 2 | shell-commands: 3 | - name: fixie 4 | type: dotnet tool 5 | description: A .NET global tool for running tests with Fixie 6 | command: dotnet fixie 7 | install: dotnet tool install Fixie.Console 8 | - name: timewarp-sync 9 | type: dotnet tool 10 | description: A .NET global tool to synchronize and manage shared files across multiple repositories. 11 | --- 12 | # Shell Commands 13 | Shell commands available that we want AI to be aware of and recommend. Although they may not be in this repo. 14 | -------------------------------------------------------------------------------- /.ai/other/tools.md: -------------------------------------------------------------------------------- 1 | --- 2 | tools: 3 | - name: Beyond Compare 4 | description: A powerful file and directory comparison tool 5 | - name: PowerGrep 6 | description: A powerful text search tool 7 | --- 8 | 9 | # Tools 10 | 11 | This is a list of tools the user has installed that we want AI to be aware of. 12 | -------------------------------------------------------------------------------- /.aiderignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeWarpEngineering/timewarp-state/68e25aedbc6d41fcded8ad71a1466a81f9d997b4/.aiderignore -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "image": "mcr.microsoft.com/devcontainers/universal:2", 3 | "features": { 4 | "ghcr.io/devcontainers/features/powershell:1": {} 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "github-actions" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeWarpEngineering/timewarp-state/68e25aedbc6d41fcded8ad71a1466a81f9d997b4/.github/pull_request_template.md -------------------------------------------------------------------------------- /.github/workflows/common.ps1: -------------------------------------------------------------------------------- 1 | # Function to execute command and exit on error 2 | function Invoke-CommandWithExit { 3 | param([string]$Command) 4 | Write-Host "Executing: $Command" 5 | Invoke-Expression $Command 6 | if ($LASTEXITCODE -ne 0) { 7 | Write-Host "Error executing: $Command" 8 | exit $LASTEXITCODE 9 | } 10 | } 11 | 12 | # Validate environment variables 13 | if ([string]::IsNullOrEmpty($env:GITHUB_WORKSPACE)) { 14 | throw "GITHUB_WORKSPACE environment variable is not set or is empty" 15 | } 16 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | Logan R. Golema 2 | Steven T. Cramer 3 | Steven T. Cramer 4 | Steven T. Cramer 5 | Steven T. Cramer 6 | Steven T. Cramer Steven Cramer 7 | Steven T. Cramer StevenTCramer 8 | Stefan L. Bemelmans 9 | Stefan L. Bemelmans 10 | -------------------------------------------------------------------------------- /Assets/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeWarpEngineering/timewarp-state/68e25aedbc6d41fcded8ad71a1466a81f9d997b4/Assets/Logo.png -------------------------------------------------------------------------------- /BuildNuGets.ps1: -------------------------------------------------------------------------------- 1 | Push-Location $PSScriptRoot 2 | try 3 | { 4 | Get-Process dotnet -ErrorAction SilentlyContinue | Stop-Process -ErrorAction SilentlyContinue 5 | dotnet nuget locals all --clear 6 | $Configuration = "Release" 7 | dotnet tool restore 8 | dotnet cleanup -y 9 | Remove-Item "./LocalNugetFeed" -Recurse -Force -ErrorAction SilentlyContinue 10 | Remove-Item "./Source/TimeWarp.State/wwwroot\js" -Recurse -Force -ErrorAction SilentlyContinue 11 | New-Item -ItemType Directory -Force -Path "./LocalNugetFeed" 12 | 13 | $projects = @( 14 | "./Source/TimeWarp.State.Analyzer/", 15 | "./Source/TimeWarp.State.SourceGenerator/", 16 | "./Source/TimeWarp.State/", 17 | "./Source/TimeWarp.State.Plus/", 18 | "./Source/TimeWarp.State.Policies/" 19 | ) 20 | 21 | foreach ($project in $projects) { 22 | Push-Location $project 23 | dotnet build --configuration $Configuration 24 | Pop-Location 25 | } 26 | 27 | Write-Host "Projects have been built successfully" 28 | } 29 | finally 30 | { 31 | Pop-Location 32 | } 33 | -------------------------------------------------------------------------------- /Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $(Version) 6 | $(Version) 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Documentation/.gitignore: -------------------------------------------------------------------------------- 1 | ############### 2 | # folder # 3 | ############### 4 | /**/DROP/ 5 | /**/TEMP/ 6 | /**/packages/ 7 | /**/bin/ 8 | /**/obj/ 9 | _site 10 | -------------------------------------------------------------------------------- /Documentation/Blips/2024-02-ActionTracking-Sample.md: -------------------------------------------------------------------------------- 1 | # Action Tracking Sample Released 2 | 3 | 🎯 Announcing the Action Tracking sample for TimeWarp.State! 4 | 5 | See it in action: https://yoclip.app/share/ejqq4l6k52eca8gth9xnm251?pw=uju1sp3lzuszbfmuffe0c9zk 6 | 7 | The sample demonstrates tracking multiple concurrent actions of varying durations (2s and 5s) with real-time feedback in the UI. 8 | 9 | Explore the implementation: https://github.com/TimeWarpEngineering/timewarp-state/tree/main/Samples/02-ActionTracking 10 | 11 | #blazor #dotnet #webdev #opensource 12 | -------------------------------------------------------------------------------- /Documentation/Blips/2024-02-ReduxDevTools-Sample.md: -------------------------------------------------------------------------------- 1 | # Redux DevTools Integration Sample Released 2 | 3 | 🔍 Announcing the Redux DevTools integration sample for TimeWarp.State! 4 | 5 | Enhance your debugging experience with practical tools: 6 | 7 | 📊 Monitor dispatched actions in real-time 8 | 🌳 Inspect your application's state tree 9 | 📝 Examine action payloads and timestamps 10 | 🔄 Track state changes as they happen 11 | 12 | Explore real-world debugging capabilities in our comprehensive sample: https://github.com/TimeWarpEngineering/timewarp-state/tree/main/Samples/01-ReduxDevTools 13 | 14 | #blazor #dotnet #debugging #webdev #opensource 15 | -------------------------------------------------------------------------------- /Documentation/Blips/2024-02-Routing-Sample.md: -------------------------------------------------------------------------------- 1 | # Routing Sample Released 2 | 3 | 🧭 Announcing the Routing sample for TimeWarp.State! 4 | 5 | Manage complex navigation flows with stack-based routing: 6 | 7 | 📚 Maintain history of visited routes 8 | 🔄 Enable breadcrumb-style navigation 9 | ⬅️ Support multi-step back navigation 10 | 📝 Automatic page title synchronization 11 | 12 | Explore the implementation: https://github.com/TimeWarpEngineering/timewarp-state/tree/main/Samples/03-Routing 13 | 14 | #blazor #dotnet #webdev #opensource 15 | -------------------------------------------------------------------------------- /Documentation/Blips/2024-02-StateActionHandler-Sample.md: -------------------------------------------------------------------------------- 1 | # StateActionHandler Pattern Samples Released 2 | 3 | 🎓 Announcing comprehensive samples for the StateActionHandler pattern in TimeWarp.State! 4 | 5 | Learn the core pattern through three different Blazor render mode implementations: 6 | 7 | 🔄 Auto render mode 8 | 🖥️ Server render mode 9 | ⚡ WebAssembly render mode 10 | 11 | Each sample demonstrates: 12 | - Immutable state management 13 | - Unidirectional data flow 14 | - Clean separation of concerns 15 | - Best practices for state handling 16 | 17 | Explore the samples: https://github.com/TimeWarpEngineering/timewarp-state/tree/main/Samples/00-StateActionHandler 18 | 19 | #blazor #dotnet #statemanagement #webdev #opensource 20 | -------------------------------------------------------------------------------- /Documentation/Blips/2024-02-TimeWarp-State-11-Release.md: -------------------------------------------------------------------------------- 1 | # TimeWarp.State 11.0.0 Release 2 | 3 | 🚀 Announcing TimeWarp.State 11.0.0! 4 | 5 | Blazor-State has evolved into TimeWarp.State, bringing powerful new features for #Blazor state management: 6 | 7 | ✨ New TimeWarp.State.Plus package 8 | 🛡️ Architecture enforcement with TimeWarp.State.Policies 9 | ⚡ Enhanced performance & developer tools 10 | 🎯 Targeting .NET 8 11 | 12 | Learn more: [Blog Post Link] 13 | 14 | #dotnet #blazor #webdev #opensource 15 | -------------------------------------------------------------------------------- /Documentation/Contributing/Overview.md: -------------------------------------------------------------------------------- 1 | # Contributing Overview -------------------------------------------------------------------------------- /Documentation/DevOps/DevOps.md: -------------------------------------------------------------------------------- 1 | # DevOps 2 | 3 | Production Artifacts: 4 | 5 | * TimeWarp.State Nuget Package on Nuget.org 6 | * TimeWarp.State.Plus Nuget Package on Nuget.org 7 | * [Documentation Site](https://timewarpengineering.github.io/timewarp-state/) 8 | -------------------------------------------------------------------------------- /Documentation/DevOps/toc.yml: -------------------------------------------------------------------------------- 1 | # auto-generated 2 | - name: Dev Ops 3 | href: DevOps.md 4 | -------------------------------------------------------------------------------- /Documentation/Features/Features.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: TimeWarp.State:Features.md 3 | title: Features 4 | --- 5 | 6 | * Oneway Data Flow 7 | * Automatic Subscriptions 8 | * Encapsulated State. Each State exposes its API. Like a micro-site 9 | * Extensible Pipeline 10 | * Auto-clone with ability to override 11 | * Precise control of ReRendering 12 | * Async handling of actions 13 | * RouteState management 14 | * BaseCacheableState to simplify client side cache with ability to NOT reRender if using cache. 15 | * A Clean abstraction for Sending of Actions 16 | * BaseComponent that provides handling of Blazors RenderModes. 17 | 18 | # Roadmap 19 | * Action Based Cloning. To reduce the size of the clone based on the action. 20 | * TimeWarp.State DevTools 21 | * Event Stream 22 | * Console and CLI 23 | * Object Inspector 24 | * Middleware Viewer 25 | * Subscriptions Viewer 26 | * IndexedDb/LocalStorage MiddleWare. 27 | * ObjectSpace synchronization with server side Entities. 28 | -------------------------------------------------------------------------------- /Documentation/Features/toc.yml: -------------------------------------------------------------------------------- 1 | - name: Features Overview 2 | topicUid: TimeWarp.State:Features.md 3 | -------------------------------------------------------------------------------- /Documentation/Images/ReduxDevTools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeWarpEngineering/timewarp-state/68e25aedbc6d41fcded8ad71a1466a81f9d997b4/Documentation/Images/ReduxDevTools.png -------------------------------------------------------------------------------- /Documentation/Images/ReduxRouteState.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeWarpEngineering/timewarp-state/68e25aedbc6d41fcded8ad71a1466a81f9d997b4/Documentation/Images/ReduxRouteState.png -------------------------------------------------------------------------------- /Documentation/Migrations/Migration1-2.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: BlazorState:Migration1-2.md 3 | title: Migrate From 1.X to 2.X 4 | --- 5 | 6 | # Migration 7 | 8 | ## From 1.X to 2.X 9 | 10 | > [!WARNING] 11 | > ReduxDevTools should NOT be enabled in production. 12 | 13 | In 1.0 ReduxDevTools were enabled by default, in 2.0 they are disabled by default. 14 | 15 | If your code previously used the default to enabled ReduxDevTools you will now need to explicitly enable it as in the following example `ConfigureServices` method inside `Startup.cs`: 16 | 17 | ```csharp 18 | aServiceCollection.AddBlazorState 19 | ( 20 | (aOptions) => 21 | { 22 | aOptions.UseReduxDevToolsBehavior = true; // Add This Line 23 | aOptions.Assemblies = 24 | new Assembly[] 25 | { 26 | typeof(Startup).GetTypeInfo().Assembly, 27 | }; 28 | } 29 | ); 30 | ``` -------------------------------------------------------------------------------- /Documentation/Migrations/Migration4-5.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: BlazorState:Migration4-5.md 3 | title: Migrate From 4.X to 5.X 4 | --- 5 | 6 | # Migration 7 | 8 | ## From 4.X to 5.X 9 | 10 | The biggest requirement will be to migrate to dotnet 6. See [Migrate from ASP.NET Core 5.0 to 6.0](https://docs.microsoft.com/en-us/aspnet/core/migration/50-to-60?view=aspnetcore-6.0&tabs=visual-studio) 11 | 12 | After completing that dotnet 6 migration the blazor-state specific changes are as follows: 13 | 14 | ### Remove the script tag in your index.html or _Host.cshtml files 15 | 16 | Delete: 17 | 18 | ```html 19 | 20 | ``` 21 | -------------------------------------------------------------------------------- /Documentation/Migrations/Migration6-7.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: BlazorState:Migration6-7.md 3 | title: Migrate From 6.X to 7.X 4 | --- 5 | 6 | # Migration 7 | 8 | ## From 6.x to 7.x 9 | 10 | ### Update to dotnet 7 11 | 12 | Only change is updating to dotnet 7 is required -------------------------------------------------------------------------------- /Documentation/Migrations/Migration7-8.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: BlazorState:Migration7-8.md 3 | title: Migrate From 7.X to 8.X 4 | --- 5 | 6 | # Migration 7 | 8 | ## From 7.x to 8.x 9 | 10 | ### MediatR 11 to 12 migration 11 | 12 | In this release we migrate from MediatR 11 to 12 see the MediatR migration notes here https://github.com/jbogard/MediatR/wiki/Migration-Guide-11.x-to-12.0 13 | -------------------------------------------------------------------------------- /Documentation/Migrations/Migration8-9.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: BlazorState:Migration8-9.md 3 | title: Migrate From 8.X to 9.X 4 | --- 5 | 6 | # Migration 7 | 8 | ## From 8.x to 9.x 9 | 10 | You now have to explicitly register all middleware. 11 | 12 | ### MediatR 12.0 to 12.1 migration 13 | 14 | In this release we migrate from MediatR 11 to 12 see the MediatR migration notes here https://github.com/jbogard/MediatR/wiki/Migration-Guide-12.0-to-12.1 15 | -------------------------------------------------------------------------------- /Documentation/Migrations/Migration9-10.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: BlazorState:Migration9-10.md 3 | title: Migrate From 9.X to 10.X 4 | --- 5 | 6 | # Migration 7 | 8 | ## From 9.x to 10.x 9 | 10 | If you were implementing IBlazorStateComponent in your own component. You no longer need to implement IMediator or IStore. 11 | 12 | If you were accessing either of these properties via the IBlazorStateComponent interface you will need to create your own interface to use that implements IBlazorStateComponent plus the properties you need. 13 | -------------------------------------------------------------------------------- /Documentation/Migrations/toc.yml: -------------------------------------------------------------------------------- 1 | - name: From 10.x to 11.x 2 | topicUid: TimeWarpState:Migration10-11.md 3 | - name: From 9.x to 10.x 4 | topicUid: BlazorState:Migration9-10.md 5 | - name: From 8.x to 9.x 6 | topicUid: BlazorState:Migration8-9.md 7 | - name: From 7.x to 8.x 8 | topicUid: BlazorState:Migration7-8.md 9 | - name: From 6.x to 7.x 10 | topicUid: BlazorState:Migration6-7.md 11 | - name: From 5.x to 6.x 12 | topicUid: BlazorState:Migration5-6.md 13 | - name: From 4.x to 5.x 14 | topicUid: BlazorState:Migration4-5.md 15 | - name: From 3.x to 4.x 16 | topicUid: BlazorState:Migration3-4.md 17 | - name: From 2.x to 3.x 18 | topicUid: BlazorState:Migration2-3.md 19 | - name: From 1.x to 2.x 20 | topicUid: BlazorState:Migration1-2.md 21 | -------------------------------------------------------------------------------- /Documentation/Model/Main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeWarpEngineering/timewarp-state/68e25aedbc6d41fcded8ad71a1466a81f9d997b4/Documentation/Model/Main.png -------------------------------------------------------------------------------- /Documentation/Partials/Acknowledgements.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: TimeWarp.State.Acknowledgements.md 3 | title: Acknowledgements 4 | --- 5 | 6 | ## Acknowledgements 7 | 8 | Jimmy Bogard ([MediatR](https://github.com/jbogard/MediatR)). 9 | Jimmy is an amazing developer and knowledge sharer. 10 | Through his course at [11x Engieering](https://11xengineering.com/), 11 | his many blog posts on Los Techies and now [JimmyBogard.com](https://jimmybogard.com/), 12 | I have learned a great amount. 13 | 14 | Peter Morris and I have been friends for many years. 15 | He is an amazing developer and a person who has taught me a great deal. 16 | Not surprisingly, Pete and I think a lot alike. 17 | We both, independently, started working on our own State Management 18 | components. Although I started first. :P 19 | Pete's component attempts to solve most of the same problems. 20 | TimeWarp.State draws on the strengths of a proven async pipeline in MediatR, where as Fluxor 21 | implements its own middleware. 22 | If TimeWarp.State does not meet your needs be sure to checkout Fluxor. 23 | -------------------------------------------------------------------------------- /Documentation/Partials/Contact.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: TimeWarp.State.Contact.md 3 | title: Contact 4 | --- 5 | 6 | ## Contact 7 | 8 | If you have an issue and don't receive a timely response, feel free to reach out on our [Discord server](https://discord.gg/A55JARGKKP). 9 | 10 | [![Discord](https://img.shields.io/discord/715274085940199487?logo=discord)](https://discord.gg/7F4bS2T) 11 | -------------------------------------------------------------------------------- /Documentation/Partials/Contributing.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: TimeWarp.State.Contributing.md 3 | title: Contributing 4 | --- 5 | 6 | ## Contributing 7 | 8 | Your contributions are welcome! Before starting any work, please open a [discussion](https://github.com/TimeWarpEngineering/timewarp-state/discussions). 9 | 10 | Help with the [documentation](https://timewarpengineering.github.io/timewarp-state/) is also greatly appreciated. 11 | -------------------------------------------------------------------------------- /Documentation/Partials/GettingStarted.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: TimeWarp.State.GettingStarted.md 3 | title: Getting Started 4 | --- 5 | 6 | ## Getting Started 7 | 8 | I recommend the [samples](https://github.com/TimeWarpEngineering/timewarp-state/tree/master/Samples) for step-by-step guides to building Blazor apps with TimeWarp.State. 9 | 10 | 👉 Official [documentation](https://timewarpengineering.github.io/timewarp-state/). 11 | 12 | logo 13 | -------------------------------------------------------------------------------- /Documentation/Partials/GiveAStar.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: TimeWarp.State.GiveAStar.md 3 | title: Give a Star 4 | --- 5 | 6 | ## Give a Star! :star: 7 | 8 | If you find this project useful, please give it a star. Thanks! 9 | -------------------------------------------------------------------------------- /Documentation/Partials/Installation.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: TimeWarp.State.Installation.md 3 | title: Installation 4 | --- 5 | 6 | ## Installation 7 | 8 | ```console 9 | dotnet add package TimeWarp.State 10 | dotnet add package TimeWarp.State.Plus 11 | ``` 12 | 13 | Check out the latest NuGet packages on the [TimeWarp Enterprises NuGet page](https://www.nuget.org/profiles/TimeWarp.Enterprises). 14 | 15 | * [TimeWarp.State](https://www.nuget.org/packages/TimeWarp.State/) [![nuget](https://img.shields.io/nuget/v/TimeWarp.State?logo=nuget)](https://www.nuget.org/packages/TimeWarp.State/) 16 | * [TimeWarp.State.Plus](https://www.nuget.org/packages/TimeWarp.State.Plus/) [![nuget](https://img.shields.io/nuget/v/TimeWarp.State.Plus?logo=nuget)](https://www.nuget.org/packages/TimeWarp.State.Plus/) 17 | -------------------------------------------------------------------------------- /Documentation/Partials/License.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: TimeWarp.State.License.md 3 | title: License 4 | --- 5 | 6 | ## Unlicense 7 | 8 | [![License](https://img.shields.io/github/license/TimeWarpEngineering/timewarp-state.svg?style=flat-square&logo=github)](https://unlicense.org) 9 | This project is licensed under the [Unlicense](https://unlicense.org). 10 | -------------------------------------------------------------------------------- /Documentation/Partials/Releases.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: TimeWarp.State.Releases.md 3 | title: Releases 4 | --- 5 | 6 | ## Releases 7 | 8 | View the [Release Notes](https://timewarpengineering.github.io/timewarp-state/ReleaseNotes/Release11.0.0.html) for detailed information on each release. 9 | -------------------------------------------------------------------------------- /Documentation/ReleaseNotes/Release1.0.0.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: BlazorState:Release.1.0.0.md 3 | title: Release 1.0.0 4 | --- 5 | 6 | # Migration 7 | 8 | ## From 1.X to 2.X 9 | 10 | > [!WARNING] 11 | > ReduxDevTools should NOT be enabled in production. 12 | 13 | In 1.0 ReduxDevTools were enabled by default, in 2.0 they are disabled by default. 14 | 15 | If your code previously used the default to enabled ReduxDevTools you will now need to explicitly enable it as in the following example `ConfigureServices` method inside `Startup.cs`: 16 | 17 | ```csharp 18 | aServiceCollection.AddBlazorState 19 | ( 20 | (aOptions) => 21 | { 22 | aOptions.UseReduxDevToolsBehavior = true; // Add This Line 23 | aOptions.Assemblies = 24 | new Assembly[] 25 | { 26 | typeof(Startup).GetTypeInfo().Assembly, 27 | }; 28 | } 29 | ); 30 | ``` -------------------------------------------------------------------------------- /Documentation/ReleaseNotes/Release10.0.0.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: BlazorState:Release.10.0.0.md 3 | title: Release 10.0.0 4 | --- 5 | 6 | ## Release 10.0.0 7 | 8 | ### Breaking Changes 9 | 10 | * IBlazorStateComponent no longer has Mediator or Store properties. 11 | 12 | See [Migrations](xref:BlazorState:Migration9-10.md) for instructions on how to migrate from version 9.0 to 10.0. 13 | -------------------------------------------------------------------------------- /Documentation/ReleaseNotes/Release2.0.0.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: BlazorState:Release.2.0.0.md 3 | title: Release 2.0.0 4 | --- 5 | 6 | ## Release 2.0.0 7 | 8 | ### Breaking Changes 9 | Since have moved from preview builds to release, and ReduxDevTools should be disabled in production, we are changing the default option setting to be disabled. 10 | 11 | `BlazorStateOptions.UseReduxDevToolsBehavior` now defaults to false; 12 | 13 | See [Migrations](xref:BlazorState:Migration1-2.md) for how to migrate existing projects from 1.0 to 2.0. 14 | 15 | ### Improvements 16 | 17 | `Action`s and their `Handler`s should be nested classes of the `State` they act on. Previously the developer could forget this and it would take some time to figure out the problem (I know I did it.). Now RenderSubscriptionsPostProcessor will throw an exception if the IAction is not nested within an IState. 18 | -------------------------------------------------------------------------------- /Documentation/ReleaseNotes/Release3.0.0.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: BlazorState:Release.3.0.0.md 3 | title: Release 3.0.0 4 | --- 5 | 6 | ## Release 3.0.0 7 | 8 | ### Breaking Changes 9 | Blazor-State has moved to netstandard 2.1 10 | The BlazorState Javascript location has changed. 11 | 12 | See [Migrations](xref:BlazorState:Migration1-2.md) for how to migrate existing projects from 1.0 to 2.0. 13 | 14 | ## Release 3.0.1 15 | 16 | Update Nuget Dev Dependencies: SourceLink, Fixie 17 | Update Nuget Production Dependency: TypeSupport now supports HashTables. 18 | 19 | ## Release 3.1.0 20 | 21 | AddBlazorState Extention method has been updated to automatically register all classes that inherit from `State<>`. 22 | Thus we no longer need to explicitly register States. 23 | 24 | Updated Mediator to 8.0 https://jimmybogard.com/mediatr-8-0-released/ -------------------------------------------------------------------------------- /Documentation/ReleaseNotes/Release6.0.0.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: BlazorState:Release.6.0.0.md 3 | title: Release 6.0.0 4 | --- 5 | 6 | ## Release 6.0.0 7 | 8 | ### Breaking Changes 9 | 10 | * Blazor-State has moved from MediatR 10 to 11 11 | * UseReduxDevToolsBehavior changes from a boolean to a function that uses the options pattern. 12 | This now gives the ability to enable stack traces in ReduxDevTools. 13 | 14 | See [Migrations](xref:BlazorState:Migration5-6.md) for how to migrate existing projects from 5.0 to 6.0. 15 | 16 | ### Other Changes 17 | 18 | * Remove esproj and add move typescript directly into BlazorState.csproj utilizing `Microsoft.TypeScript.MSBuild` package. 19 | * Fix the tags on the project they should be separated by semicolons. 20 | * Migrate to Fixie 3.2.0 21 | * Use the TimeWarp.Fixie testing convention. 22 | * Update All Nuget packages to the latest 23 | * Add dotnet-cleanup to dotnet-tools.json 24 | * Update Nuget.config to include packageSourceMapping 25 | -------------------------------------------------------------------------------- /Documentation/ReleaseNotes/Release7.0.0.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: BlazorState:Release.7.0.0.md 3 | title: Release 7.0.0 4 | --- 5 | 6 | ## Release 7.0.0 7 | 8 | ### Breaking Changes 9 | 10 | * Blazor-State now requires dotnet 7 11 | 12 | See [Migrations](xref:BlazorState:Migration6-7.md) for instructions on how to migrate from version 6.0 to 7.0. 13 | -------------------------------------------------------------------------------- /Documentation/ReleaseNotes/Release9.0.0.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: BlazorState:Release.9.0.0.md 3 | title: Release 9.0.0 4 | --- 5 | 6 | ## Release 9.0.0 7 | 8 | ### Breaking Changes 9 | 10 | * Blazor-State now requires MediatR version 12.1.1 which actually should have been a breaking change on MediatR. 11 | 12 | See [Migrations](xref:BlazorState:Migration8-9.md) for instructions on how to migrate from version 8.0 to 9.0. 13 | 14 | ### Other Changes 15 | 16 | * Updates to the Sample app and test app to use MediatR version 12.1 17 | * Update Microsoft.CodeAnalysis.Common 4.6.0 -> 4.7.0 18 | * Update Microsoft.CodeAnalysis.CSharp.Workspaces 4.6.0 -> 4.7.0 19 | * Updates other development dependencies 20 | 21 | 22 | ## Release 9.0.1 23 | 24 | * Added InvalidCloneException to assist in detecting when the clone did not work as expected. -------------------------------------------------------------------------------- /Documentation/ReleaseNotes/toc.yml: -------------------------------------------------------------------------------- 1 | - name: Release 11.0.0 2 | topicUid: TimeWarpState:Release.11.0.0.md 3 | - name: Release 10.0.0 4 | topicUid: BlazorState:Release.10.0.0.md 5 | - name: Release 9.0.0 6 | topicUid: BlazorState:Release.9.0.0.md 7 | - name: Release 8.0.0 8 | topicUid: BlazorState:Release.8.0.0.md 9 | - name: Release 7.0.0 10 | topicUid: BlazorState:Release.7.0.0.md 11 | - name: Release 6.0.0 12 | topicUid: BlazorState:Release.6.0.0.md 13 | - name: Release 5.0.0 14 | topicUid: BlazorState:Release.5.0.0.md 15 | - name: Release 4.0.0 16 | topicUid: BlazorState:Release.4.0.0.md 17 | - name: Release 3.0.0 18 | topicUid: BlazorState:Release.3.0.0.md 19 | - name: Release 2.0.0 20 | topicUid: BlazorState:Release.2.0.0.md 21 | - name: Release 1.0.0 22 | topicUid: BlazorState:Release.1.0.0.md 23 | -------------------------------------------------------------------------------- /Documentation/Topics/EnableJavascriptInterop.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: TimeWarp.State:EnableJavascriptInterop.md 3 | title: Enable Javascript Interop 4 | --- 5 | -------------------------------------------------------------------------------- /Documentation/Topics/Routing.md: -------------------------------------------------------------------------------- 1 | --- 2 | uid: TimeWarp.State:Routing.md 3 | title: Enable Routing 4 | --- 5 | -------------------------------------------------------------------------------- /Documentation/Topics/toc.yml: -------------------------------------------------------------------------------- 1 | - name: Add Redux Dev Tools 2 | topicUid: TimeWarpState:AddReduxDevTools.md 3 | -------------------------------------------------------------------------------- /Documentation/Tutorial/DefaultTemplateScreenShot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeWarpEngineering/timewarp-state/68e25aedbc6d41fcded8ad71a1466a81f9d997b4/Documentation/Tutorial/DefaultTemplateScreenShot.png -------------------------------------------------------------------------------- /Documentation/index.md: -------------------------------------------------------------------------------- 1 | [!include[Overview](Overview.md)] 2 | -------------------------------------------------------------------------------- /Documentation/toc.yml: -------------------------------------------------------------------------------- 1 | # auto-generated 2 | - name: Overview 3 | topicUid: TimeWarpState:Overview.md 4 | - name: Topics 5 | href: Topics/ 6 | - name: Release Notes 7 | href: ReleaseNotes/ 8 | - name: Migrations 9 | href: Migrations/ 10 | -------------------------------------------------------------------------------- /Kanban/Backlog/Scratch/Done.md: -------------------------------------------------------------------------------- 1 | - [x] Update RouteManager to subscribe to Route Change and update the URL if not already there. 2 | - [x] Search with powergrep for "" 3 | if blazor-state\Directory.Build.props has `preview` then do we need it in all the others? 4 | - [x] Extract common items to Directory.Build.props 5 | - [x] ReduxDevToolsBehavoir uses the pipeline but we probably don't care to log those interaction. 6 | So Maybe we should add a Filter to ignore logging some request IReduxAction marker or something? 7 | - [x] Move ReduxDevToolsBehavoir to top of pipeline. 8 | So when doing time travel we can disable actions from actually being executed. 9 | - [x] Move JavaScriptInterop Folder out of Behaviors (It isn't a behavior) although ReduxDevTools depends on it. 10 | This will let dev tools go back to pages. 11 | -------------------------------------------------------------------------------- /Kanban/Backlog/Scratch/Overview.md: -------------------------------------------------------------------------------- 1 | # Scratch pad for Kanban board stuff. 2 | -------------------------------------------------------------------------------- /Kanban/Backlog/Scratch/Rejected.md: -------------------------------------------------------------------------------- 1 | - [ ] Implement DevTools Handlers (Currently only Jump works) 2 | See the MobX example for how to implement more ReduxDevTools functionality 3 | https://github.com/zalmoxisus/mobx-remotedev/blob/master/src/monitorActions.js 4 | > I have changed my mind. The fancy stuff in ReduxDevTools I don't really use and is not high ROI 5 | -------------------------------------------------------------------------------- /Kanban/Backlog/Scratch/Todo.md: -------------------------------------------------------------------------------- 1 | - [ ] Improve usefulness of ILogger. 2 | - [ ] Document all public interfaces 3 | - [ ] Check visibility on classes props etc... private public internal etc.. 4 | - [x] Convert js to ts. See Blazor package for example and Logging. 5 | - [ ] Consider splitting Packages for DevTools as production we will not want to deploy. TimeWarp.State.ReduxDevTools 6 | - [ ] Review TODOs in source 7 | -------------------------------------------------------------------------------- /Kanban/Backlog/_._: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeWarpEngineering/timewarp-state/68e25aedbc6d41fcded8ad71a1466a81f9d997b4/Kanban/Backlog/_._ -------------------------------------------------------------------------------- /Kanban/Done/_._: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeWarpEngineering/timewarp-state/68e25aedbc6d41fcded8ad71a1466a81f9d997b4/Kanban/Done/_._ -------------------------------------------------------------------------------- /Kanban/ToDo/-.-: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeWarpEngineering/timewarp-state/68e25aedbc6d41fcded8ad71a1466a81f9d997b4/Kanban/ToDo/-.- -------------------------------------------------------------------------------- /NuGet.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /RunTestApp.ps1: -------------------------------------------------------------------------------- 1 | $Env:ASPNETCORE_ENVIRONMENT = "Development" 2 | $Env:UseHttp = "false" # Set to "true" if you want to use HTTP locally 3 | 4 | Push-Location $PSScriptRoot 5 | try { 6 | Clear-Host 7 | # The analyzer is not directly referenced by the test app, so we need to build it first 8 | dotnet build ./Source/TimeWarp.State.Analyzer/TimeWarp.State.Analyzer.csproj 9 | dotnet build ./Source/TimeWarp.State.SourceGenerator/TimeWarp.State.SourceGenerator.csproj 10 | dotnet watch --project ./Tests/Test.App/Test.App.Server/Test.App.Server.csproj 11 | } 12 | finally { 13 | Pop-Location 14 | } 15 | -------------------------------------------------------------------------------- /RunTests.ps1: -------------------------------------------------------------------------------- 1 | Push-Location $PSScriptRoot 2 | try { 3 | # List files in LocalNugetFeed 4 | Get-ChildItem ./LocalNugetFeed 5 | dotnet restore 6 | dotnet build --project ./Tests/TimeWarp.State.Analyzer.Tests/TimeWarp.State.Analyzer.Tests.csproj 7 | dotnet fixie TimeWarp.State.Analyzer.Tests TimeWarp.State.Tests TimeWarp.State.Plus.Tests Client.Integration.Tests Test.App.Architecture.Tests 8 | } 9 | finally { 10 | Pop-Location 11 | } 12 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Auto/Sample00Auto/Sample00Auto.Client/Features/Counter/CounterState.IncrementCount.cs: -------------------------------------------------------------------------------- 1 | namespace Sample00Auto.Client.Features.Counter; 2 | 3 | partial class CounterState 4 | { 5 | public static class IncrementCountActionSet 6 | { 7 | public sealed class Action : IAction 8 | { 9 | public int Amount { get; } 10 | public Action(int amount) 11 | { 12 | Amount = amount; 13 | } 14 | } 15 | 16 | public sealed class Handler : ActionHandler 17 | { 18 | public Handler(IStore store) : base(store) { } 19 | 20 | private CounterState CounterState => Store.GetState(); 21 | 22 | public override Task Handle(Action action, CancellationToken cancellationToken) 23 | { 24 | CounterState.Count += action.Amount; 25 | return Task.CompletedTask; 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Auto/Sample00Auto/Sample00Auto.Client/Features/Counter/CounterState.cs: -------------------------------------------------------------------------------- 1 | namespace Sample00Auto.Client.Features.Counter; 2 | 3 | internal sealed partial class CounterState : State 4 | { 5 | public int Count { get; private set; } 6 | public override void Initialize() 7 | { 8 | Count = 3; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Auto/Sample00Auto/Sample00Auto.Client/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using Microsoft.AspNetCore.Components; 2 | global using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 3 | global using Microsoft.Extensions.DependencyInjection; 4 | global using TimeWarp.State; 5 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Auto/Sample00Auto/Sample00Auto.Client/Pages/Counter.razor: -------------------------------------------------------------------------------- 1 | @page "/counter" 2 | @rendermode InteractiveAuto 3 | @using Sample00Auto.Client.Features.Counter 4 | 5 | @inherits TimeWarp.State.TimeWarpStateComponent 6 | 7 | Counter 8 | 9 |

Counter

10 | 11 |

Current count: @currentCount

12 | 13 | 14 | 15 | @code 16 | { 17 | CounterState CounterState => GetState(); 18 | private int currentCount => CounterState.Count; 19 | 20 | private async Task IncrementCount() 21 | { 22 | await CounterState.IncrementCount(amount:5); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Auto/Sample00Auto/Sample00Auto.Client/Program.cs: -------------------------------------------------------------------------------- 1 | namespace Sample00Auto.Client; 2 | 3 | public class Program 4 | { 5 | static async Task Main(string[] args) 6 | { 7 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 8 | ConfigureServices(builder.Services); 9 | await builder.Build().RunAsync(); 10 | } 11 | 12 | public static void ConfigureServices(IServiceCollection serviceCollection) 13 | { 14 | serviceCollection.AddTimeWarpState(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Auto/Sample00Auto/Sample00Auto.Client/Sample00Auto.Client.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | true 8 | Default 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Auto/Sample00Auto/Sample00Auto.Client/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using static Microsoft.AspNetCore.Components.Web.RenderMode 7 | @using Microsoft.AspNetCore.Components.Web.Virtualization 8 | @using Microsoft.JSInterop 9 | @using Sample00Auto.Client 10 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Auto/Sample00Auto/Sample00Auto.Client/wwwroot/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Auto/Sample00Auto/Sample00Auto.Client/wwwroot/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Auto/Sample00Auto/Sample00Auto/Components/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Auto/Sample00Auto/Sample00Auto/Components/Layout/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 |
4 | 7 | 8 |
9 |
10 | About 11 |
12 | 13 |
14 | @Body 15 |
16 |
17 |
18 | 19 |
20 | An unhandled error has occurred. 21 | Reload 22 | 🗙 23 |
24 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Auto/Sample00Auto/Sample00Auto/Components/Pages/Home.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 | Home 4 | 5 |

Hello, world!

6 | 7 | Welcome to your new app. 8 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Auto/Sample00Auto/Sample00Auto/Components/Routes.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Auto/Sample00Auto/Sample00Auto/Components/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using static Microsoft.AspNetCore.Components.Web.RenderMode 7 | @using Microsoft.AspNetCore.Components.Web.Virtualization 8 | @using Microsoft.JSInterop 9 | @using Sample00Auto 10 | @using Sample00Auto.Client 11 | @using Sample00Auto.Components 12 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Auto/Sample00Auto/Sample00Auto/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using Microsoft.AspNetCore.Builder; 2 | global using Microsoft.AspNetCore.Components; 3 | global using Microsoft.AspNetCore.Components.Web; 4 | global using Microsoft.Extensions.DependencyInjection; 5 | global using Sample00Auto.Client.Pages; 6 | global using Sample00Auto.Components; 7 | global using TimeWarp.State; 8 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Auto/Sample00Auto/Sample00Auto/Sample00Auto.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Auto/Sample00Auto/Sample00Auto/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Auto/Sample00Auto/Sample00Auto/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Auto/Sample00Auto/Sample00Auto/wwwroot/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeWarpEngineering/timewarp-state/68e25aedbc6d41fcded8ad71a1466a81f9d997b4/Samples/00-StateActionHandler/Auto/Sample00Auto/Sample00Auto/wwwroot/favicon.png -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Server/Sample00Server/Components/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Server/Sample00Server/Components/Layout/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 |
4 | 7 | 8 |
9 |
10 | About 11 |
12 | 13 |
14 | @Body 15 |
16 |
17 |
18 | 19 |
20 | An unhandled error has occurred. 21 | Reload 22 | 🗙 23 |
24 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Server/Sample00Server/Components/Pages/Counter.razor: -------------------------------------------------------------------------------- 1 | @page "/counter" 2 | @rendermode InteractiveServer 3 | @using Sample00Server.Features.Counter 4 | @inherits TimeWarp.State.TimeWarpStateComponent 5 | 6 | Counter 7 | 8 |

Counter

9 | 10 |

Current count: @currentCount

11 | 12 | 13 | 14 | @code { 15 | CounterState CounterState => GetState(); 16 | private int currentCount => CounterState.Count; 17 | 18 | private async Task IncrementCount() 19 | { 20 | await CounterState.IncrementCount(amount: 5); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Server/Sample00Server/Components/Pages/Home.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 | Home 4 | 5 |

Hello, world!

6 | 7 | Welcome to your new app. 8 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Server/Sample00Server/Components/Routes.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Server/Sample00Server/Components/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using static Microsoft.AspNetCore.Components.Web.RenderMode 7 | @using Microsoft.AspNetCore.Components.Web.Virtualization 8 | @using Microsoft.JSInterop 9 | @using Sample00Server 10 | @using Sample00Server.Components 11 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Server/Sample00Server/Features/Counter/CounterState.IncrementCount.cs: -------------------------------------------------------------------------------- 1 | namespace Sample00Server.Features.Counter; 2 | 3 | partial class CounterState 4 | { 5 | public static class IncrementCountActionSet 6 | { 7 | public sealed class Action : IAction 8 | { 9 | public int Amount { get; } 10 | 11 | public Action(int amount) 12 | { 13 | Amount = amount; 14 | } 15 | } 16 | 17 | public sealed class Handler : ActionHandler 18 | { 19 | public Handler(IStore store) : base(store) { } 20 | 21 | private CounterState CounterState => Store.GetState(); 22 | 23 | public override Task Handle(Action action, CancellationToken cancellationToken) 24 | { 25 | CounterState.Count += action.Amount; 26 | return Task.CompletedTask; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Server/Sample00Server/Features/Counter/CounterState.cs: -------------------------------------------------------------------------------- 1 | namespace Sample00Server.Features.Counter; 2 | 3 | internal sealed partial class CounterState : State 4 | { 5 | public int Count { get; private set; } 6 | 7 | public override void Initialize() 8 | { 9 | Count = 3; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Server/Sample00Server/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using Microsoft.AspNetCore.Builder; 2 | global using Microsoft.AspNetCore.Components; 3 | global using Microsoft.AspNetCore.Components.Web; 4 | global using Microsoft.Extensions.DependencyInjection; 5 | global using Sample00Server.Components; 6 | global using TimeWarp.State; 7 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Server/Sample00Server/Sample00Server.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Server/Sample00Server/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Server/Sample00Server/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Server/Sample00Server/wwwroot/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeWarpEngineering/timewarp-state/68e25aedbc6d41fcded8ad71a1466a81f9d997b4/Samples/00-StateActionHandler/Server/Sample00Server/wwwroot/favicon.png -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Wasm/Sample00Wasm/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Not found 8 | 9 |

Sorry, there's nothing at this address.

10 |
11 |
12 |
13 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Wasm/Sample00Wasm/Features/Counter/CounterState.IncrementCount.cs: -------------------------------------------------------------------------------- 1 | namespace Sample00Wasm.Features.Counter; 2 | 3 | partial class CounterState 4 | { 5 | public static class IncrementCountActionSet 6 | { 7 | public sealed class Action : IAction 8 | { 9 | public int Amount { get; } 10 | 11 | public Action(int amount) 12 | { 13 | Amount = amount; 14 | } 15 | } 16 | 17 | public sealed class Handler : ActionHandler 18 | { 19 | public Handler(IStore store) : base(store) { } 20 | 21 | private CounterState CounterState => Store.GetState(); 22 | 23 | public override Task Handle(Action action, CancellationToken cancellationToken) 24 | { 25 | CounterState.Count += action.Amount; 26 | return Task.CompletedTask; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Wasm/Sample00Wasm/Features/Counter/CounterState.cs: -------------------------------------------------------------------------------- 1 | namespace Sample00Wasm.Features.Counter; 2 | 3 | internal sealed partial class CounterState : State 4 | { 5 | public int Count { get; private set; } 6 | 7 | public override void Initialize() 8 | { 9 | Count = 3; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Wasm/Sample00Wasm/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using Microsoft.AspNetCore.Components; 2 | global using Microsoft.AspNetCore.Components.Web; 3 | global using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 4 | global using Microsoft.Extensions.DependencyInjection; 5 | global using TimeWarp.State; 6 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Wasm/Sample00Wasm/Layout/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 |
3 | 6 | 7 |
8 |
9 | About 10 |
11 | 12 |
13 | @Body 14 |
15 |
16 |
17 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Wasm/Sample00Wasm/Pages/Counter.razor: -------------------------------------------------------------------------------- 1 | @page "/counter" 2 | @using Sample00Wasm.Features.Counter 3 | @inherits TimeWarp.State.TimeWarpStateComponent 4 | 5 | Counter 6 | 7 |

Counter

8 | 9 |

Current count: @currentCount

10 | 11 | 12 | 13 | @code { 14 | CounterState CounterState => GetState(); 15 | private int currentCount => CounterState.Count; 16 | 17 | private async Task IncrementCount() 18 | { 19 | await CounterState.IncrementCount(amount: 5); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Wasm/Sample00Wasm/Pages/Home.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 | Home 4 | 5 |

Hello, world!

6 | 7 | Welcome to your new app. 8 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Wasm/Sample00Wasm/Program.cs: -------------------------------------------------------------------------------- 1 | namespace Sample00Wasm; 2 | 3 | public class Program 4 | { 5 | public static async Task Main(string[] args) 6 | { 7 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 8 | builder.RootComponents.Add("#app"); 9 | builder.RootComponents.Add("head::after"); 10 | 11 | builder.Services.AddTimeWarpState(); 12 | 13 | await builder.Build().RunAsync(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Wasm/Sample00Wasm/Sample00Wasm.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Wasm/Sample00Wasm/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using Microsoft.AspNetCore.Components.Web.Virtualization 7 | @using Microsoft.AspNetCore.Components.WebAssembly.Http 8 | @using Microsoft.JSInterop 9 | @using Sample00Wasm 10 | @using Sample00Wasm.Layout 11 | -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Wasm/Sample00Wasm/wwwroot/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeWarpEngineering/timewarp-state/68e25aedbc6d41fcded8ad71a1466a81f9d997b4/Samples/00-StateActionHandler/Wasm/Sample00Wasm/wwwroot/favicon.png -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Wasm/Sample00Wasm/wwwroot/icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeWarpEngineering/timewarp-state/68e25aedbc6d41fcded8ad71a1466a81f9d997b4/Samples/00-StateActionHandler/Wasm/Sample00Wasm/wwwroot/icon-192.png -------------------------------------------------------------------------------- /Samples/00-StateActionHandler/Wasm/Sample00Wasm/wwwroot/sample-data/weather.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "date": "2022-01-06", 4 | "temperatureC": 1, 5 | "summary": "Freezing" 6 | }, 7 | { 8 | "date": "2022-01-07", 9 | "temperatureC": 14, 10 | "summary": "Bracing" 11 | }, 12 | { 13 | "date": "2022-01-08", 14 | "temperatureC": -13, 15 | "summary": "Freezing" 16 | }, 17 | { 18 | "date": "2022-01-09", 19 | "temperatureC": -16, 20 | "summary": "Balmy" 21 | }, 22 | { 23 | "date": "2022-01-10", 24 | "temperatureC": -2, 25 | "summary": "Chilly" 26 | } 27 | ] 28 | -------------------------------------------------------------------------------- /Samples/01-ReduxDevTools/Images/BlazorWasmHostedScreenShot.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeWarpEngineering/timewarp-state/68e25aedbc6d41fcded8ad71a1466a81f9d997b4/Samples/01-ReduxDevTools/Images/BlazorWasmHostedScreenShot.jpeg -------------------------------------------------------------------------------- /Samples/01-ReduxDevTools/Images/BlazorWasmHostedScreenShot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeWarpEngineering/timewarp-state/68e25aedbc6d41fcded8ad71a1466a81f9d997b4/Samples/01-ReduxDevTools/Images/BlazorWasmHostedScreenShot.png -------------------------------------------------------------------------------- /Samples/01-ReduxDevTools/Images/ReduxDevTools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeWarpEngineering/timewarp-state/68e25aedbc6d41fcded8ad71a1466a81f9d997b4/Samples/01-ReduxDevTools/Images/ReduxDevTools.png -------------------------------------------------------------------------------- /Samples/01-ReduxDevTools/Images/ReduxRouteState.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeWarpEngineering/timewarp-state/68e25aedbc6d41fcded8ad71a1466a81f9d997b4/Samples/01-ReduxDevTools/Images/ReduxRouteState.png -------------------------------------------------------------------------------- /Samples/01-ReduxDevTools/Wasm/Sample01Wasm/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Not found 8 | 9 |

Sorry, there's nothing at this address.

10 |
11 |
12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Samples/01-ReduxDevTools/Wasm/Sample01Wasm/Features/Counter/CounterState.IncrementCount.cs: -------------------------------------------------------------------------------- 1 | namespace Sample01Wasm.Features.Counter; 2 | 3 | partial class CounterState 4 | { 5 | public static class IncrementCountActionSet 6 | { 7 | public sealed class Action : IAction 8 | { 9 | public int Amount { get; } 10 | 11 | public Action(int amount) 12 | { 13 | Amount = amount; 14 | } 15 | } 16 | 17 | public sealed class Handler : ActionHandler 18 | { 19 | public Handler(IStore store) : base(store) { } 20 | 21 | private CounterState CounterState => Store.GetState(); 22 | 23 | public override Task Handle(Action action, CancellationToken cancellationToken) 24 | { 25 | CounterState.Count += action.Amount; 26 | return Task.CompletedTask; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Samples/01-ReduxDevTools/Wasm/Sample01Wasm/Features/Counter/CounterState.cs: -------------------------------------------------------------------------------- 1 | namespace Sample01Wasm.Features.Counter; 2 | 3 | internal sealed partial class CounterState : State 4 | { 5 | public int Count { get; private set; } 6 | 7 | public override void Initialize() 8 | { 9 | Count = 3; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Samples/01-ReduxDevTools/Wasm/Sample01Wasm/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using Microsoft.AspNetCore.Components; 2 | global using Microsoft.AspNetCore.Components.Web; 3 | global using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 4 | global using Microsoft.Extensions.DependencyInjection; 5 | global using TimeWarp.State; 6 | global using System.Reflection; 7 | global using TimeWarp.Features.Routing; 8 | global using Sample01Wasm; 9 | -------------------------------------------------------------------------------- /Samples/01-ReduxDevTools/Wasm/Sample01Wasm/Layout/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 |
3 | 6 | 7 |
8 |
9 | About 10 |
11 | 12 |
13 | @Body 14 |
15 |
16 |
17 | -------------------------------------------------------------------------------- /Samples/01-ReduxDevTools/Wasm/Sample01Wasm/Pages/Counter.razor: -------------------------------------------------------------------------------- 1 | @page "/counter" 2 | @using Sample01Wasm.Features.Counter 3 | @inherits TimeWarp.State.TimeWarpStateComponent 4 | 5 | Counter 6 | 7 |

Counter

8 | 9 |

Current count: @currentCount

10 | 11 | 12 | 13 | @code { 14 | CounterState CounterState => GetState(); 15 | private int currentCount => CounterState.Count; 16 | 17 | private async Task IncrementCount() 18 | { 19 | await CounterState.IncrementCount(amount: 5); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Samples/01-ReduxDevTools/Wasm/Sample01Wasm/Pages/Home.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 | Home 4 | 5 |

Hello, world!

6 | 7 | Welcome to your new app. 8 | -------------------------------------------------------------------------------- /Samples/01-ReduxDevTools/Wasm/Sample01Wasm/Program.cs: -------------------------------------------------------------------------------- 1 | namespace Sample01Wasm; 2 | 3 | public class Program 4 | { 5 | public static async Task Main(string[] args) 6 | { 7 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 8 | builder.RootComponents.Add("#app"); 9 | builder.RootComponents.Add("head::after"); 10 | 11 | builder.Services.AddTimeWarpState 12 | ( 13 | options => 14 | { 15 | options.UseReduxDevTools(); // Enable Redux DevTools 16 | } 17 | ); 18 | 19 | await builder.Build().RunAsync(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Samples/01-ReduxDevTools/Wasm/Sample01Wasm/Sample01Wasm.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Samples/01-ReduxDevTools/Wasm/Sample01Wasm/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using Microsoft.AspNetCore.Components.Web.Virtualization 7 | @using Microsoft.AspNetCore.Components.WebAssembly.Http 8 | @using Microsoft.JSInterop 9 | @using Sample01Wasm 10 | @using Sample01Wasm.Layout 11 | @using TimeWarp.Features.JavaScriptInterop 12 | @using TimeWarp.Features.Routing 13 | @using TimeWarp.Features.Developer 14 | -------------------------------------------------------------------------------- /Samples/01-ReduxDevTools/Wasm/Sample01Wasm/wwwroot/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeWarpEngineering/timewarp-state/68e25aedbc6d41fcded8ad71a1466a81f9d997b4/Samples/01-ReduxDevTools/Wasm/Sample01Wasm/wwwroot/favicon.png -------------------------------------------------------------------------------- /Samples/01-ReduxDevTools/Wasm/Sample01Wasm/wwwroot/icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeWarpEngineering/timewarp-state/68e25aedbc6d41fcded8ad71a1466a81f9d997b4/Samples/01-ReduxDevTools/Wasm/Sample01Wasm/wwwroot/icon-192.png -------------------------------------------------------------------------------- /Samples/01-ReduxDevTools/Wasm/Sample01Wasm/wwwroot/sample-data/weather.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "date": "2022-01-06", 4 | "temperatureC": 1, 5 | "summary": "Freezing" 6 | }, 7 | { 8 | "date": "2022-01-07", 9 | "temperatureC": 14, 10 | "summary": "Bracing" 11 | }, 12 | { 13 | "date": "2022-01-08", 14 | "temperatureC": -13, 15 | "summary": "Freezing" 16 | }, 17 | { 18 | "date": "2022-01-09", 19 | "temperatureC": -16, 20 | "summary": "Balmy" 21 | }, 22 | { 23 | "date": "2022-01-10", 24 | "temperatureC": -2, 25 | "summary": "Chilly" 26 | } 27 | ] 28 | -------------------------------------------------------------------------------- /Samples/02-ActionTracking/Wasm/Sample02Wasm/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Not found 8 | 9 |

Sorry, there's nothing at this address.

10 |
11 |
12 |
13 | -------------------------------------------------------------------------------- /Samples/02-ActionTracking/Wasm/Sample02Wasm/Features/Demo/DemoState.FiveSecondAction.cs: -------------------------------------------------------------------------------- 1 | namespace Sample02Wasm.Features.Demo; 2 | 3 | partial class DemoState 4 | { 5 | public static class FiveSecondActionSet 6 | { 7 | [TrackAction] 8 | public sealed class Action : IAction { } 9 | 10 | public sealed class Handler : ActionHandler 11 | { 12 | public Handler(IStore store) : base(store) { } 13 | 14 | public override async Task Handle 15 | ( 16 | Action action, 17 | CancellationToken cancellationToken 18 | ) 19 | { 20 | // Simulate a 5-second action 21 | await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken); 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Samples/02-ActionTracking/Wasm/Sample02Wasm/Features/Demo/DemoState.TwoSecondAction.cs: -------------------------------------------------------------------------------- 1 | namespace Sample02Wasm.Features.Demo; 2 | 3 | partial class DemoState 4 | { 5 | public static class TwoSecondActionSet 6 | { 7 | [TrackAction] 8 | public sealed class Action : IAction { } 9 | 10 | public sealed class Handler : ActionHandler 11 | { 12 | public Handler(IStore store) : base(store) { } 13 | 14 | public override async Task Handle 15 | ( 16 | Action action, 17 | CancellationToken cancellationToken 18 | ) 19 | { 20 | // Simulate a 2-second action 21 | await Task.Delay(TimeSpan.FromSeconds(2), cancellationToken); 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Samples/02-ActionTracking/Wasm/Sample02Wasm/Features/Demo/DemoState.cs: -------------------------------------------------------------------------------- 1 | namespace Sample02Wasm.Features.Demo; 2 | 3 | internal sealed partial class DemoState : State 4 | { 5 | public override void Initialize() { } 6 | } 7 | -------------------------------------------------------------------------------- /Samples/02-ActionTracking/Wasm/Sample02Wasm/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using Microsoft.AspNetCore.Components; 2 | global using Microsoft.AspNetCore.Components.Web; 3 | global using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 4 | global using Microsoft.Extensions.DependencyInjection; 5 | global using TimeWarp.State; 6 | global using TimeWarp.Features.ActionTracking; 7 | global using Sample02Wasm; 8 | global using MediatR; 9 | -------------------------------------------------------------------------------- /Samples/02-ActionTracking/Wasm/Sample02Wasm/Layout/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 |
3 | 6 | 7 |
8 |
9 | About 10 |
11 | 12 |
13 | @Body 14 |
15 |
16 |
17 | -------------------------------------------------------------------------------- /Samples/02-ActionTracking/Wasm/Sample02Wasm/Pages/Counter.razor: -------------------------------------------------------------------------------- 1 | @page "/counter" 2 | 3 | Counter 4 | 5 |

Counter

6 | 7 |

Current count: @currentCount

8 | 9 | 10 | 11 | @code { 12 | private int currentCount = 0; 13 | 14 | private void IncrementCount() 15 | { 16 | currentCount++; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Samples/02-ActionTracking/Wasm/Sample02Wasm/Pages/Home.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 | Home 4 | 5 |

Hello, world!

6 | 7 | Welcome to your new app. 8 | -------------------------------------------------------------------------------- /Samples/02-ActionTracking/Wasm/Sample02Wasm/Program.cs: -------------------------------------------------------------------------------- 1 | namespace Sample02Wasm; 2 | 3 | public class Program 4 | { 5 | public static async Task Main(string[] args) 6 | { 7 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 8 | builder.RootComponents.Add("#app"); 9 | builder.RootComponents.Add("head::after"); 10 | 11 | builder.Services.AddScoped 12 | ( 13 | sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) } 14 | ); 15 | 16 | builder.Services.AddTimeWarpState 17 | ( 18 | options => 19 | { 20 | options.Assemblies = new[] 21 | { 22 | typeof(Program).Assembly, 23 | typeof(TimeWarp.State.Plus.AssemblyMarker).Assembly 24 | }; 25 | } 26 | ); 27 | 28 | builder.Services.AddScoped 29 | ( 30 | typeof(IPipelineBehavior<,>), 31 | typeof(ActiveActionBehavior<,>) 32 | ); 33 | 34 | await builder.Build().RunAsync(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Samples/02-ActionTracking/Wasm/Sample02Wasm/Sample02Wasm.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Samples/02-ActionTracking/Wasm/Sample02Wasm/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using Microsoft.AspNetCore.Components.Web.Virtualization 7 | @using Microsoft.AspNetCore.Components.WebAssembly.Http 8 | @using Microsoft.JSInterop 9 | @using Sample02Wasm 10 | @using Sample02Wasm.Layout 11 | -------------------------------------------------------------------------------- /Samples/02-ActionTracking/Wasm/Sample02Wasm/wwwroot/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeWarpEngineering/timewarp-state/68e25aedbc6d41fcded8ad71a1466a81f9d997b4/Samples/02-ActionTracking/Wasm/Sample02Wasm/wwwroot/favicon.png -------------------------------------------------------------------------------- /Samples/02-ActionTracking/Wasm/Sample02Wasm/wwwroot/icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeWarpEngineering/timewarp-state/68e25aedbc6d41fcded8ad71a1466a81f9d997b4/Samples/02-ActionTracking/Wasm/Sample02Wasm/wwwroot/icon-192.png -------------------------------------------------------------------------------- /Samples/02-ActionTracking/Wasm/Sample02Wasm/wwwroot/sample-data/weather.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "date": "2022-01-06", 4 | "temperatureC": 1, 5 | "summary": "Freezing" 6 | }, 7 | { 8 | "date": "2022-01-07", 9 | "temperatureC": 14, 10 | "summary": "Bracing" 11 | }, 12 | { 13 | "date": "2022-01-08", 14 | "temperatureC": -13, 15 | "summary": "Freezing" 16 | }, 17 | { 18 | "date": "2022-01-09", 19 | "temperatureC": -16, 20 | "summary": "Balmy" 21 | }, 22 | { 23 | "date": "2022-01-10", 24 | "temperatureC": -2, 25 | "summary": "Chilly" 26 | } 27 | ] 28 | -------------------------------------------------------------------------------- /Samples/03-Routing/Wasm/Sample03Wasm/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Not found 8 | 9 |

Sorry, there's nothing at this address.

10 |
11 |
12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /Samples/03-Routing/Wasm/Sample03Wasm/Features/Counter/CounterState.IncrementCount.cs: -------------------------------------------------------------------------------- 1 | namespace Sample03Wasm.Features.Counter; 2 | 3 | partial class CounterState 4 | { 5 | public static class IncrementCountActionSet 6 | { 7 | public sealed class Action : IAction 8 | { 9 | public int Amount { get; } 10 | 11 | public Action(int amount) 12 | { 13 | Amount = amount; 14 | } 15 | } 16 | 17 | public sealed class Handler : ActionHandler 18 | { 19 | public Handler(IStore store) : base(store) { } 20 | 21 | private CounterState CounterState => Store.GetState(); 22 | 23 | public override Task Handle(Action action, CancellationToken cancellationToken) 24 | { 25 | CounterState.Count += action.Amount; 26 | return Task.CompletedTask; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Samples/03-Routing/Wasm/Sample03Wasm/Features/Counter/CounterState.cs: -------------------------------------------------------------------------------- 1 | namespace Sample03Wasm.Features.Counter; 2 | 3 | internal sealed partial class CounterState : State 4 | { 5 | public int Count { get; private set; } 6 | 7 | public override void Initialize() 8 | { 9 | Count = 3; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Samples/03-Routing/Wasm/Sample03Wasm/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using Microsoft.AspNetCore.Components; 2 | global using Microsoft.AspNetCore.Components.Web; 3 | global using Microsoft.AspNetCore.Components.WebAssembly.Hosting; 4 | global using Microsoft.Extensions.DependencyInjection; 5 | global using TimeWarp.State; 6 | global using TimeWarp.Features.Routing; 7 | global using TimeWarp.State.Plus; 8 | global using TimeWarp.State.Plus.Extensions; 9 | -------------------------------------------------------------------------------- /Samples/03-Routing/Wasm/Sample03Wasm/Layout/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 |
4 | 7 | 8 |
9 |
10 | About 11 |
12 | 13 |
14 | 15 |
16 | 17 |
18 | @Body 19 |
20 |
21 |
22 | 23 | -------------------------------------------------------------------------------- /Samples/03-Routing/Wasm/Sample03Wasm/Pages/Counter.razor: -------------------------------------------------------------------------------- 1 | @page "/counter" 2 | @using Sample03Wasm.Features.Counter 3 | @inherits TimeWarp.State.TimeWarpStateComponent 4 | 5 | Counter 6 | 7 |

Counter

8 | 9 |

Current count: @currentCount

10 | 11 | 12 | 13 | @code { 14 | CounterState CounterState => GetState(); 15 | private int currentCount => CounterState.Count; 16 | 17 | private async Task IncrementCount() 18 | { 19 | await CounterState.IncrementCount(amount: 5); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Samples/03-Routing/Wasm/Sample03Wasm/Pages/Home.razor: -------------------------------------------------------------------------------- 1 | @page "/" 2 | 3 | Home 4 | 5 |

Hello, world!

6 | 7 | Welcome to your new app. 8 | -------------------------------------------------------------------------------- /Samples/03-Routing/Wasm/Sample03Wasm/Program.cs: -------------------------------------------------------------------------------- 1 | namespace Sample03Wasm; 2 | 3 | public class Program 4 | { 5 | public static async Task Main(string[] args) 6 | { 7 | var builder = WebAssemblyHostBuilder.CreateDefault(args); 8 | builder.RootComponents.Add("#app"); 9 | builder.RootComponents.Add("head::after"); 10 | 11 | builder.Services.AddScoped 12 | ( 13 | sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) } 14 | ); 15 | 16 | builder.Services.AddTimeWarpState 17 | ( 18 | options => 19 | { 20 | options.UseReduxDevTools(); 21 | } 22 | ); 23 | 24 | builder.Services.AddTimeWarpStateRouting(); 25 | 26 | await builder.Build().RunAsync(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Samples/03-Routing/Wasm/Sample03Wasm/Run.ps1: -------------------------------------------------------------------------------- 1 | dotnet restore --force-evaluate 2 | dotnet build 3 | dotnet run -------------------------------------------------------------------------------- /Samples/03-Routing/Wasm/Sample03Wasm/Sample03Wasm.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Samples/03-Routing/Wasm/Sample03Wasm/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Net.Http 2 | @using System.Net.Http.Json 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using Microsoft.AspNetCore.Components.Web.Virtualization 7 | @using Microsoft.AspNetCore.Components.WebAssembly.Http 8 | @using Microsoft.JSInterop 9 | @using Sample03Wasm 10 | @using Sample03Wasm.Layout 11 | @using TimeWarp.Features.Developer 12 | @using TimeWarp.Features.JavaScriptInterop 13 | @using TimeWarp.Features.Routing 14 | @using TimeWarp.State 15 | -------------------------------------------------------------------------------- /Samples/03-Routing/Wasm/Sample03Wasm/wwwroot/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeWarpEngineering/timewarp-state/68e25aedbc6d41fcded8ad71a1466a81f9d997b4/Samples/03-Routing/Wasm/Sample03Wasm/wwwroot/favicon.png -------------------------------------------------------------------------------- /Samples/03-Routing/Wasm/Sample03Wasm/wwwroot/icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeWarpEngineering/timewarp-state/68e25aedbc6d41fcded8ad71a1466a81f9d997b4/Samples/03-Routing/Wasm/Sample03Wasm/wwwroot/icon-192.png -------------------------------------------------------------------------------- /Samples/03-Routing/Wasm/Sample03Wasm/wwwroot/sample-data/weather.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "date": "2022-01-06", 4 | "temperatureC": 1, 5 | "summary": "Freezing" 6 | }, 7 | { 8 | "date": "2022-01-07", 9 | "temperatureC": 14, 10 | "summary": "Bracing" 11 | }, 12 | { 13 | "date": "2022-01-08", 14 | "temperatureC": -13, 15 | "summary": "Freezing" 16 | }, 17 | { 18 | "date": "2022-01-09", 19 | "temperatureC": -16, 20 | "summary": "Balmy" 21 | }, 22 | { 23 | "date": "2022-01-10", 24 | "temperatureC": -2, 25 | "summary": "Chilly" 26 | } 27 | ] 28 | -------------------------------------------------------------------------------- /Samples/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | 6 | 7 | -------------------------------------------------------------------------------- /Scripts/FixAnalyzerDebug.reg: -------------------------------------------------------------------------------- 1 | Windows Registry Editor Version 5.00 2 | 3 | [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Notify] 4 | 5 | [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Notify\SensLogn] 6 | "DLLName"="WlNotify.dll" 7 | "Lock"="SensLockEvent" 8 | "Logon"="SensLogonEvent" 9 | "Logoff"="SensLogoffEvent" 10 | "Safe"=dword:00000001 11 | "MaxWait"=dword:00000258 12 | "StartScreenSaver"="SensStartScreenSaverEvent" 13 | "StopScreenSaver"="SensStopScreenSaverEvent" 14 | "Startup"="SensStartupEvent" 15 | "Shutdown"="SensShutdownEvent" 16 | "StartShell"="SensStartShellEvent" 17 | "PostShell"="SensPostShellEvent" 18 | "Disconnect"="SensDisconnectEvent" 19 | "Reconnect"="SensReconnectEvent" 20 | "Unlock"="SensUnlockEvent" 21 | "Impersonate"=dword:00000001 22 | "Asynchronous"=dword:00000001 -------------------------------------------------------------------------------- /Scripts/Readme.md: -------------------------------------------------------------------------------- 1 | # How to debug analyzer 2 | 3 | https://peterlesliemorris.com/debugging-my-published-rolsyn-source-generator-nuget-package/ -------------------------------------------------------------------------------- /Source/TimeWarp.State.Analyzer/AnalyzerReleases.Shipped.md: -------------------------------------------------------------------------------- 1 | ; Shipped analyzer releases 2 | ; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md 3 | 4 | -------------------------------------------------------------------------------- /Source/TimeWarp.State.Analyzer/AnalyzerReleases.Unshipped.md: -------------------------------------------------------------------------------- 1 | ; Unshipped analyzer release 2 | ; https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md 3 | 4 | ### New Rules 5 | 6 | Rule ID | Category | Severity | Notes 7 | --------|----------|----------|------- 8 | StateInheritanceTypeArgumentRule | Design | Error | StateInheritanceAnalyzer 9 | StateReadOnlyPublicPropertiesRule | Design | Error | StateReadOnlyPublicPropertiesAnalyzer 10 | StateSealedClassRule | Design | Warning | StateInheritanceAnalyzer 11 | TW0001 | TimeWarp.State | Error | TimeWarpStateActionAnalyzer 12 | TWD001 | Debug | Info | TimeWarpStateActionAnalyzer 13 | TWS001 | Design | Error | StateImplementationAnalyzer 14 | -------------------------------------------------------------------------------- /Source/TimeWarp.State.Analyzer/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using Microsoft.CodeAnalysis; 2 | global using Microsoft.CodeAnalysis.CSharp.Syntax; 3 | global using Microsoft.CodeAnalysis.Diagnostics; 4 | global using System.Collections.Immutable; 5 | global using System.Linq; 6 | -------------------------------------------------------------------------------- /Source/TimeWarp.State.Analyzer/TimeWarp.State.Analyzer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | enable 6 | true 7 | true 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Source/TimeWarp.State.Plus/AssemblyMarker.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.State.Plus; 2 | 3 | /// 4 | /// Serves as a marker for the assembly, facilitating easy identification and reflection-based operations. 5 | /// 6 | /// 7 | /// This class is intended to be used as a reference point within the assembly for scenarios such as assembly scanning, 8 | /// where a stable, known type is required to locate the assembly at runtime. The class is sealed to indicate it is not 9 | /// designed for inheritance or extension, reinforcing its role as a simple marker. 10 | /// 11 | public sealed class AssemblyMarker; 12 | -------------------------------------------------------------------------------- /Source/TimeWarp.State.Plus/Features/ActionTracking/ActionTrackingState/ActionTrackingState.Debug.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.Features.ActionTracking; 2 | 3 | public partial class ActionTrackingState 4 | { 5 | public override ActionTrackingState Hydrate(IDictionary keyValuePairs) 6 | { 7 | string guidKey = CamelCase.MemberNameToCamelCase(nameof(Guid)); 8 | 9 | if (!keyValuePairs.TryGetValue(guidKey, out object? guidValue)) 10 | { 11 | throw new InvalidOperationException($"Expected key '{guidKey}' not found or value is null."); 12 | } 13 | 14 | var processingState = new ActionTrackingState(this.Sender) 15 | { 16 | Guid = Guid.Parse(guidValue.ToString()!) 17 | }; 18 | return processingState; 19 | } 20 | 21 | internal void Initialize(List processingList) 22 | { 23 | ThrowIfNotTestAssembly(Assembly.GetCallingAssembly()); 24 | ActiveActionList = processingList; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Source/TimeWarp.State.Plus/Features/ActionTracking/ActionTrackingState/ActionTrackingState.StartProcessing.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.Features.ActionTracking; 2 | 3 | public partial class ActionTrackingState 4 | { 5 | public static class StartProcessingActionSet 6 | { 7 | internal sealed class Action : IAction 8 | { 9 | public Action(IAction theAction) 10 | { 11 | TheAction = theAction; 12 | } 13 | public IAction TheAction { get; } 14 | } 15 | 16 | internal sealed class Handler : ActionHandler 17 | { 18 | public Handler(IStore store) : base(store) {} 19 | private ActionTrackingState ActionTrackingState => Store.GetState(); 20 | 21 | public override Task Handle(Action action, CancellationToken cancellationToken) 22 | { 23 | ActionTrackingState.ActiveActionList.Add(action.TheAction); 24 | return Task.CompletedTask; 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Source/TimeWarp.State.Plus/Features/ActionTracking/Pipeline/TrackActionAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.Features.ActionTracking; 2 | 3 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] 4 | public class TrackActionAttribute : Attribute { } 5 | -------------------------------------------------------------------------------- /Source/TimeWarp.State.Plus/Features/FeatureFlags/FeatureFlagState/FeatureFlagState.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.State.Plus.Features.FeatureFlags.Actions; 2 | 3 | public sealed class FeatureFlagState : State 4 | { 5 | public FeatureFlagState(ISender sender) : base(sender) {} 6 | 7 | [JsonConstructor] 8 | public FeatureFlagState() {} 9 | 10 | public override void Initialize() => throw new NotImplementedException(); 11 | } 12 | -------------------------------------------------------------------------------- /Source/TimeWarp.State.Plus/Features/Routing/Components/TwPageTitle.razor: -------------------------------------------------------------------------------- 1 | @namespace TimeWarp.Features.Routing 2 | @inherits TimeWarp.State.TimeWarpStateComponent 3 | 4 | @* 5 | A component that automatically updates the page title and route state. 6 | This component wraps the built-in PageTitle component and ensures the RouteState 7 | is updated whenever the title changes. 8 | 9 | Usage: 10 | Your Page Title 11 | *@ 12 | 13 | @ChildContent 14 | 15 | @code { 16 | private RouteState RouteState => GetState(placeSubscription:false); //Don't subscribe to avoid infinite recursion 17 | 18 | [Parameter] 19 | public RenderFragment? ChildContent { get; set; } 20 | 21 | protected override async Task OnAfterRenderAsync(bool firstRender) 22 | { 23 | await base.OnAfterRenderAsync(firstRender); 24 | 25 | if (firstRender) 26 | { 27 | await RouteState.PushRouteInfo(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Source/TimeWarp.State.Plus/Features/Theme/ThemeState/ThemeState.Update.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.Features.Theme; 2 | 3 | public partial class ThemeState 4 | { 5 | public static class UpdateActionSet 6 | { 7 | internal sealed class Action : IAction 8 | { 9 | public Theme NewTheme { get; } 10 | public Action(Theme newTheme) 11 | { 12 | NewTheme = newTheme; 13 | } 14 | } 15 | 16 | internal sealed class Handler 17 | ( 18 | IStore store 19 | ): ActionHandler(store) 20 | { 21 | private ThemeState ThemeState => Store.GetState(); 22 | 23 | public override Task Handle 24 | ( 25 | Action action, 26 | CancellationToken cancellationToken 27 | ) 28 | { 29 | ThemeState.CurrentTheme = action.NewTheme; 30 | return Task.CompletedTask; 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Source/TimeWarp.State.Plus/Features/Theme/ThemeState/ThemeState.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.Features.Theme; 2 | 3 | public sealed partial class ThemeState : State 4 | { 5 | public Theme CurrentTheme { get; private set; } 6 | 7 | public ThemeState(ISender sender) : base(sender) {} 8 | 9 | [JsonConstructor] 10 | public ThemeState() {} 11 | 12 | public override void Initialize() => CurrentTheme = Theme.System; 13 | 14 | /// 15 | /// Represents the different themes that the app can have. 16 | /// 17 | public enum Theme 18 | { 19 | Light, 20 | Dark, 21 | System 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Source/TimeWarp.State.Plus/Features/Timers/MultiTimerOptions.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.State.Plus.Features.Timers; 2 | 3 | public class MultiTimerOptions 4 | { 5 | // ReSharper disable once CollectionNeverUpdated.Global Set from Configuration 6 | public Dictionary Timers { get; init; } = new(); 7 | } 8 | -------------------------------------------------------------------------------- /Source/TimeWarp.State.Plus/Features/Timers/MultiTimerPostProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.State.Plus.Features.Timers; 2 | 3 | public class MultiTimerPostProcessor : IRequestPostProcessor 4 | where TRequest : notnull 5 | { 6 | private readonly ILogger> Logger; 7 | private readonly TimerState TimerState; 8 | 9 | public MultiTimerPostProcessor 10 | ( 11 | ILogger> logger, 12 | TimerState timerState 13 | ) 14 | { 15 | Logger = logger; 16 | TimerState = timerState; 17 | } 18 | 19 | public async Task Process(TRequest request, TResponse response, CancellationToken cancellationToken) 20 | { 21 | Logger.LogDebug(EventIds.MultiTimerPostProcessor_ProcessingRequest, message: "Processing request and checking timers"); 22 | await TimerState.ResetTimersOnActivity(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Source/TimeWarp.State.Plus/Features/Timers/TimerConfig.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.State.Plus.Features.Timers; 2 | 3 | public class TimerConfig 4 | { 5 | public double Duration { get; set; } 6 | public bool ResetOnActivity { get; init; } = true; 7 | } 8 | -------------------------------------------------------------------------------- /Source/TimeWarp.State.Plus/Features/Timers/TimerElapsedNotification.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.State.Plus.Features.Timers; 2 | 3 | public class TimerElapsedNotification : INotification 4 | { 5 | public string TimerName { get; } 6 | public Action RestartTimer { get; } 7 | 8 | public TimerElapsedNotification(string timerName, Action restartTimer) 9 | { 10 | TimerName = timerName; 11 | RestartTimer = restartTimer; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Source/TimeWarp.State.Plus/Features/Timers/TimerState/TimerState.AddTimer.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.State.Plus.Features.Timers; 2 | 3 | using System.Timers; 4 | 5 | public partial class TimerState 6 | { 7 | public static class AddTimerActionSet 8 | { 9 | internal sealed class Action : IAction 10 | { 11 | public string TimerName { get; } 12 | public TimerConfig TimerConfig { get; } 13 | public Action(string timerName, TimerConfig timerConfig) 14 | { 15 | TimerName = timerName; 16 | TimerConfig = timerConfig; 17 | } 18 | } 19 | 20 | internal sealed class Handler : ActionHandler 21 | { 22 | private TimerState TimerState => Store.GetState(); 23 | public Handler(IStore store) : base(store) {} 24 | 25 | public override Task Handle(Action action, CancellationToken cancellationToken) 26 | { 27 | TimerState.Timers[action.TimerName] = (new Timer(action.TimerConfig.Duration), action.TimerConfig); 28 | return Task.CompletedTask; 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Source/TimeWarp.State.Plus/Features/Timers/TimerState/TimerState.ResetTimersOnActivity.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.State.Plus.Features.Timers; 2 | 3 | using System.Timers; 4 | public partial class TimerState 5 | { 6 | public static class ResetTimersOnActivityActionSet 7 | { 8 | internal sealed class Action : IAction; 9 | 10 | internal sealed class Handler : ActionHandler 11 | { 12 | private TimerState TimerState => Store.GetState(); 13 | public Handler(IStore store) : base(store) { } 14 | 15 | public override Task Handle(Action action, CancellationToken cancellationToken) 16 | { 17 | foreach ((string timerName, (Timer _, TimerConfig timerConfig)) in TimerState.Timers) 18 | { 19 | if (timerConfig.ResetOnActivity) 20 | { 21 | TimerState.RestartTimer(timerName); 22 | } 23 | } 24 | return Task.CompletedTask; 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Source/TimeWarp.State.Plus/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using Blazored.LocalStorage; 2 | global using Blazored.SessionStorage; 3 | global using TimeWarp.State; 4 | global using TimeWarp.Features.Persistence; 5 | global using JetBrains.Annotations; 6 | global using MediatR; 7 | global using MediatR.Pipeline; 8 | global using Microsoft.AspNetCore.Components; 9 | global using Microsoft.Extensions.Logging; 10 | global using Microsoft.Extensions.Options; 11 | global using Microsoft.JSInterop; 12 | global using System.Reflection; 13 | global using System.Text.Json; 14 | global using System.Text.Json.Serialization; 15 | global using TimeWarp.State.Plus; 16 | -------------------------------------------------------------------------------- /Source/TimeWarp.State.Plus/State/ITimeWarpCacheableState.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.State.Plus.State; 2 | 3 | public interface ITimeWarpCacheableState 4 | { 5 | string? CacheKey { get; } 6 | DateTime? TimeStamp { get; } 7 | TimeSpan CacheDuration { get;} 8 | } 9 | -------------------------------------------------------------------------------- /Source/TimeWarp.State.Plus/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using Microsoft.AspNetCore.Components.Web 2 | -------------------------------------------------------------------------------- /Source/TimeWarp.State.Policies/BeNestedInStateCustomRule.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.State.Policies; 2 | 3 | public class BeNestedInStateCustomRule:ICustomRule 4 | { 5 | public bool MeetsRule(TypeDefinition typeDefinition) 6 | { 7 | var type = typeDefinition.ToType(); 8 | 9 | while (type.DeclaringType != null && !typeof(IState).IsAssignableFrom(type)) 10 | { 11 | type = type.DeclaringType; 12 | } 13 | 14 | bool result = typeof(IState).IsAssignableFrom(type); 15 | 16 | return result; 17 | } 18 | } 19 | 20 | public class BeNestedInActionSetCustomRule:ICustomRule 21 | { 22 | public bool MeetsRule(TypeDefinition typeDefinition) 23 | { 24 | var type = typeDefinition.ToType(); 25 | bool result = type?.DeclaringType?.Name.EndsWith("ActionSet") == true; 26 | 27 | return result; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Source/TimeWarp.State.Policies/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using FluentAssertions; 2 | global using Mono.Cecil; 3 | global using NetArchTest.Policies; 4 | global using NetArchTest.Rules; 5 | global using System.Linq; 6 | global using System.Reflection; 7 | global using System.Text; 8 | global using System.Text.Json.Serialization; 9 | -------------------------------------------------------------------------------- /Source/TimeWarp.State.Policies/HaveInjectableConstructor.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.State.Policies; 2 | 3 | public class HaveInjectableConstructor : ICustomRule 4 | { 5 | public bool MeetsRule(TypeDefinition typeDefinition) 6 | { 7 | var type = typeDefinition.ToType(); 8 | ConstructorInfo[] constructors = type.GetConstructors(BindingFlags.Public | BindingFlags.Instance); 9 | 10 | return constructors.Any(IsInjectableConstructor); 11 | } 12 | 13 | private static bool IsInjectableConstructor(ConstructorInfo constructor) 14 | { 15 | ParameterInfo[] parameters = constructor.GetParameters(); 16 | return parameters.Length == 0 || parameters.All(p => !p.ParameterType.IsPrimitive && p.ParameterType != typeof(string)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Source/TimeWarp.State.Policies/HaveJsonConstructor.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.State.Policies; 2 | 3 | public class HaveJsonConstructor : ICustomRule 4 | { 5 | public bool MeetsRule(TypeDefinition typeDefinition) 6 | { 7 | var type = typeDefinition.ToType(); 8 | ConstructorInfo[] constructors = type.GetConstructors(BindingFlags.Public | BindingFlags.Instance); 9 | 10 | return constructors.Any(c => c.GetCustomAttributes(typeof(JsonConstructorAttribute), false).Length != 0); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Source/TimeWarp.State.SourceGenerator/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using Microsoft.CodeAnalysis; 2 | global using Microsoft.CodeAnalysis.CSharp.Syntax; 3 | global using Microsoft.CodeAnalysis.Text; 4 | global using System.Text; 5 | -------------------------------------------------------------------------------- /Source/TimeWarp.State.SourceGenerator/TimeWarp.State.SourceGenerator.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | enable 6 | true 7 | true 8 | 9 | 10 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/AssemblyMarker.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.State; 2 | 3 | /// 4 | /// Serves as a marker for the assembly, facilitating easy identification and reflection-based operations. 5 | /// 6 | /// 7 | /// This class is intended to be used as a reference point within the assembly for scenarios such as assembly scanning, 8 | /// where a stable, known type is required to locate the assembly at runtime. The class is sealed to indicate it is not 9 | /// designed for inheritance or extension, reinforcing its role as a simple marker. 10 | /// 11 | public sealed class AssemblyMarker; 12 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/Base/Action.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.State; 2 | 3 | public interface IAction : IRequest { } 4 | 5 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/Base/ActionHandler.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.State; 2 | 3 | public abstract class ActionHandler 4 | ( 5 | IStore store 6 | ) : IRequestHandler where TAction : IAction 7 | { 8 | protected IStore Store { get; set; } = store; 9 | 10 | public abstract Task Handle(TAction action, CancellationToken cancellationToken); 11 | } 12 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/Components/TimeWarpStateComponent.RenderReasons.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.State; 2 | 3 | public partial class TimeWarpStateComponent 4 | { 5 | public RenderReasonCategory RenderReason { get; private set; } = RenderReasonCategory.None; 6 | public string? RenderReasonDetail { get; private set; } 7 | public string? ShouldRenderWasCalledBy { get; private set; } 8 | 9 | public enum RenderReasonCategory 10 | { 11 | None, 12 | Event, 13 | ParameterChanged, 14 | UntrackedParameter, 15 | Subscription, 16 | RenderTrigger, 17 | StateHasChanged, 18 | Forced, 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/Extensions/MethodInfoExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace System.Reflection; 2 | 3 | /// 4 | /// Use reflection to invoke an Async method. 5 | /// 6 | /// 7 | public static class MethodInfoExtensions 8 | { 9 | public static async Task InvokeAsync(this MethodInfo methodInfo, object @object, params object[] parameters) 10 | { 11 | dynamic awaitable = methodInfo.Invoke(@object, parameters) ?? throw new InvalidOperationException(); 12 | await awaitable; 13 | return awaitable.GetAwaiter().GetResult(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/Extensions/TypeExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.State.Extensions; 2 | 3 | public static class TypeExtensions 4 | { 5 | public static Type GetEnclosingStateType(this Type type) 6 | { 7 | string name = type.Name; 8 | while (type.DeclaringType != null && !typeof(IState).IsAssignableFrom(type)) 9 | { 10 | type = type.DeclaringType; 11 | } 12 | 13 | if (!typeof(IState).IsAssignableFrom(type)) 14 | { 15 | throw new NonNestedClassException 16 | ($"{name} must be nested in a class that implements {nameof(IState)}"); 17 | } 18 | 19 | return type; 20 | } 21 | 22 | public static string GetSimpleName(this Type type) 23 | { 24 | ReadOnlySpan nameSpan = type.Name.AsSpan(); 25 | int backtickIndex = nameSpan.IndexOf('`'); 26 | return backtickIndex >= 0 ? nameSpan[..backtickIndex].ToString() : nameSpan.ToString(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/Features/CloneState/Pipeline/ExceptionNotification.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.Features.StateTransactions; 2 | 3 | public class ExceptionNotification : INotification 4 | { 5 | public ExceptionNotification(string requestName, Exception exception) 6 | { 7 | RequestName = requestName; 8 | Exception = exception; 9 | } 10 | public string RequestName { get; } 11 | 12 | public Exception Exception { get; } 13 | } 14 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/Features/CloneState/Pipeline/InvalidCloneException.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.Features.StateTransactions; 2 | 3 | public class InvalidCloneException 4 | ( 5 | Type enclosingStateType 6 | ) : Exception($"State of type {enclosingStateType} has an invalid clone. For the default clone to work, a parameterless constructor is required.") 7 | { 8 | public Type EnclosingStateType { get; } = enclosingStateType; 9 | } 10 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/Features/Developer/Components/ReduxDevTools.razor: -------------------------------------------------------------------------------- 1 | @namespace TimeWarp.Features.Developer 2 | @code { 3 | 4 | [Inject] private ReduxDevToolsInterop ReduxDevToolsInterop { get; set; } = null!; 5 | 6 | /// 7 | protected override async Task OnAfterRenderAsync(bool firstRender) 8 | { 9 | await ReduxDevToolsInterop.InitAsync(); 10 | await base.OnAfterRenderAsync(firstRender); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/Features/JavaScriptInterop/Components/TimeWarpJavaScriptInterop.razor: -------------------------------------------------------------------------------- 1 | @namespace TimeWarp.Features.JavaScriptInterop 2 | 3 | @code { 4 | 5 | [Inject] private JsonRequestHandler JsonRequestHandler { get; set; } = null!; 6 | 7 | /// 8 | protected override async Task OnAfterRenderAsync(bool firstRender) 9 | { 10 | await JsonRequestHandler.InitAsync(); 11 | await base.OnAfterRenderAsync(firstRender); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/Features/Persistence/Abstractions/IPersistenceService.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.Features.Persistence; 2 | 3 | public interface IPersistenceService 4 | { 5 | Task LoadState(Type stateType, PersistentStateMethod persistentStateMethod); 6 | } 7 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/Features/Persistence/Attributes/PersistentStateAttribute.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.Features.Persistence; 2 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] 3 | public class PersistentStateAttribute 4 | ( 5 | PersistentStateMethod PersistentStateMethod 6 | ) : Attribute 7 | { 8 | public readonly PersistentStateMethod PersistentStateMethod = PersistentStateMethod; 9 | } 10 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/Features/Persistence/PersistentStateMethod.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.Features.Persistence; 2 | 3 | public enum PersistentStateMethod 4 | { 5 | PreRender, 6 | Server, 7 | SessionStorage, 8 | LocalStorage 9 | } 10 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/Features/ReduxDevTools/Components/TimeWarpStateDevComponent.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.Features.ReduxDevTools; 2 | 3 | /// 4 | /// Adds a RenderModeDisplay RenderFragment to the TimeWarpStateComponent 5 | /// 6 | public class TimeWarpStateDevComponent : TimeWarpStateComponent 7 | { 8 | protected readonly RenderFragment RenderModeDisplay; 9 | 10 | protected string RenderModeDisplayString => $"CurrentRenderMode: {CurrentRenderMode}\nConfiguredRenderMode: {ConfiguredRenderMode}"; 11 | protected TimeWarpStateDevComponent() 12 | { 13 | RenderModeDisplay = builder => 14 | { 15 | builder.OpenComponent(0); 16 | builder.AddComponentParameter(0, nameof(TimeWarp.Features.Developer.RenderModeDisplay.CurrentRenderMode), CurrentRenderMode); 17 | builder.AddComponentParameter(0, nameof(TimeWarp.Features.Developer.RenderModeDisplay.ConfiguredRenderMode), ConfiguredRenderMode); 18 | builder.CloseComponent(); 19 | }; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/Features/ReduxDevTools/DispatchRequest.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.Features.ReduxDevTools; 2 | 3 | internal class DispatchRequest 4 | { 5 | protected DispatchRequest(int id, TPayload payload, string source, string state, string type) 6 | { 7 | Id = id; 8 | Payload = payload; 9 | Source = source; 10 | State = state; 11 | Type = type; 12 | } 13 | public int Id { get; set; } 14 | public TPayload Payload { get; set; } 15 | public string Source { get; set; } 16 | public string State { get; set; } 17 | public string Type { get; set; } 18 | } 19 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/Features/ReduxDevTools/ReduxAction.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.Features.ReduxDevTools; 2 | 3 | internal class ReduxAction 4 | { 5 | public ReduxAction(object request) 6 | { 7 | ArgumentNullException.ThrowIfNull(request); 8 | 9 | Type = request.GetType().FullName ?? throw new InvalidOperationException(); 10 | Payload = request; 11 | } 12 | 13 | public object Payload { get; set; } 14 | public string Type { get; set; } 15 | } 16 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/Features/ReduxDevTools/Requests/Commit/CommitRequest.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.Features.ReduxDevTools; 2 | 3 | internal class CommitRequest : DispatchRequest, IRequest, IReduxRequest 4 | { 5 | internal class PayloadClass 6 | { 7 | public PayloadClass(string type) 8 | { 9 | Type = type; 10 | } 11 | public string Type { get; } 12 | } 13 | 14 | public CommitRequest(int id, PayloadClass payload, string source, string state, string type) : base(id, payload, source, state, type) {} 15 | } 16 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/Features/ReduxDevTools/Requests/IReduxRequest.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.Features.ReduxDevTools; 2 | 3 | /// 4 | /// Marker Interface to allow for filtering of Devtools Requests 5 | /// 6 | internal interface IReduxRequest { } 7 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/Features/ReduxDevTools/Requests/Start/StartHandler.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.Features.ReduxDevTools; 2 | 3 | /// 4 | /// Redux Devtools will send the Request once on startup 5 | /// 6 | /// currently we do nothing at start up other than log 7 | internal class StartHandler : IRequestHandler 8 | { 9 | private readonly ILogger Logger; 10 | 11 | public StartHandler 12 | ( 13 | ILogger logger 14 | ) 15 | { 16 | Logger = logger; 17 | Logger.LogDebug(EventIds.JumpToStateHandler_RequestHandled, "constructing"); 18 | } 19 | 20 | /// 21 | /// Currently does nothing 22 | /// 23 | /// 24 | /// 25 | /// 26 | public Task Handle(StartRequest request, CancellationToken cancellationToken) => 27 | Task.CompletedTask; 28 | } 29 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/Features/ReduxDevTools/Requests/Start/StartRequest.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.Features.ReduxDevTools; 2 | 3 | /// 4 | /// Request received from Redux Dev Tools when one presses the Start Button. 5 | /// 6 | internal class StartRequest : DispatchRequest, IRequest, IReduxRequest 7 | { 8 | internal class PayloadClass; 9 | public StartRequest(int id, PayloadClass payload, string source, string state, string type) 10 | : base(id, payload, source, state, type) {} 11 | } 12 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/Features/RenderSubscriptions/NonNestedClassException.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.Features.RenderSubscriptions; 2 | 3 | public class NonNestedClassException : ArgumentException 4 | { 5 | public NonNestedClassException(string? message) : base(message) { } 6 | public NonNestedClassException(string? message, string? paramName) : base(message, paramName) { } 7 | } 8 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/Features/RenderSubscriptions/RenderSubscriptionContext.md: -------------------------------------------------------------------------------- 1 | # RenderSubscriptionContext 2 | 3 | The `RenderSubscriptionContext` class provides Handlers with control over when subscriptions should be re-rendered. 4 | 5 | ## Purpose 6 | 7 | This class offers a mechanism for Handlers to manage the automatic re-rendering of subscriptions for specific actions, allowing for fine-grained control over the rendering process. 8 | 9 | ## Usage in Action Handlers 10 | 11 | To modify the default re-rendering behavior in your Handler, follow these steps: 12 | 13 | TODO: Insert real example code and maybe a link 14 | 15 | [Link to line 10](./path/to/file.cs:10) 16 | 17 | ## Cross-Middleware Communication 18 | 19 | It's important to note that any middleware or handler within the pipeline can inject the `RenderSubscriptionContext` and thereby affect the re-rendering process. This capability enables cross-middleware communication, allowing different parts of the application to influence when and how subscriptions are re-rendered. 20 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // This file is used by Code Analysis to maintain SuppressMessage 2 | // attributes that are applied to this project. 3 | // Project-level suppressions either have no target or are given 4 | // a specific target and scoped to a namespace, type, member, etc. 5 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/State/IState.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.State; 2 | 3 | public interface IState 4 | { 5 | ISender Sender { get; set; } 6 | Guid Guid { get; } 7 | // string? CacheKey { get; } 8 | // DateTime? TimeStamp { get; } 9 | // TimeSpan CacheDuration { get; } 10 | // bool IsCacheValid(string currentCacheKey); 11 | 12 | void Initialize(); 13 | public void CancelOperations(); 14 | } 15 | 16 | public interface IState : IState 17 | { 18 | /// 19 | /// Set the state from Dictionary 20 | /// Used by ReduxDevTools to support TimeTravel 21 | /// 22 | /// 23 | /// 24 | /// Only needed for time travel which I think is waste anyway. 25 | TState Hydrate(IDictionary keyValuePairs); 26 | } 27 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/Store/IStore.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.State; 2 | 3 | public interface IReduxDevToolsStore 4 | { 5 | IDictionary GetSerializableState(); 6 | 7 | void LoadStatesFromJson(string jsonString); 8 | } 9 | 10 | public interface IStore 11 | { 12 | Guid Guid { get; } 13 | 14 | TState GetState() where TState : IState; 15 | 16 | TState? GetPreviousState() where TState : IState; 17 | 18 | object GetState(Type stateType); 19 | 20 | SemaphoreSlim? GetSemaphore(Type stateType); 21 | 22 | void SetState(IState newState); 23 | 24 | void RemoveState() where TState : IState; 25 | 26 | void Reset(); 27 | 28 | ConcurrentDictionary StateInitializationTasks { get; } 29 | } 30 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/Store/StateInitializedNotification.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.State; 2 | 3 | public class StateInitializedNotification 4 | ( 5 | Type stateType 6 | ) : INotification 7 | { 8 | public Type StateType { get; } = stateType; 9 | } 10 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/package.json.does-not-work: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | }, 14 | "devDependencies": { 15 | "@redux-devtools/app": "^6.0.0", 16 | "@redux-devtools/extension": "^3.3.0", 17 | "@redux-devtools/core": "^4.0.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/wwwroot/TypeScript/Constants.ts: -------------------------------------------------------------------------------- 1 | export const TimeWarpStateName: string = "TimeWarpState"; 2 | export const DevToolsName: string = "devTools" 3 | export const InitializeJavaScriptInteropName: string = "InitializeJavaScriptInterop"; 4 | export const JsonRequestHandlerMethodName: string = "Handle"; 5 | export const ReduxDevToolsFactoryName: string = "ReduxDevToolsFactory"; 6 | export const ReduxDevToolsName: string = "reduxDevTools"; 7 | export const ReduxExtensionName: string = "__REDUX_DEVTOOLS_EXTENSION__"; 8 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/wwwroot/TypeScript/DotNetReference.d.ts: -------------------------------------------------------------------------------- 1 | export interface DotNetReference { 2 | invokeMethodAsync(methodName: string, requestTypeFullName: string, requestAsJson: string): Promise; 3 | } 4 | 5 | // Optionally, if you have more methods or properties, add them here 6 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/wwwroot/TypeScript/Logger.ts: -------------------------------------------------------------------------------- 1 | // Logger.ts 2 | export interface LogStyles { 3 | info: string; 4 | success: string; 5 | warning: string; 6 | error: string; 7 | function: string; 8 | } 9 | 10 | export const logStyles: LogStyles = { 11 | info: "color: deepskyblue; font-weight: bold;", 12 | success: "color: limegreen; font-weight: bold;", 13 | warning: "color: darkorange; font-weight: bold;", 14 | error: "color: crimson; font-weight: bold;", 15 | function: "color: mediumorchid; font-weight: bold;", 16 | }; 17 | 18 | export type LogLevel = keyof LogStyles; 19 | 20 | export enum LogAction { 21 | Begin, 22 | End 23 | } 24 | 25 | export const log = (tag: string, message: string, level: LogLevel = 'info', action?: LogAction): void => { 26 | const style = logStyles[level]; 27 | 28 | if (action === LogAction.Begin) { 29 | console.group(`%c${tag}`, style); 30 | } 31 | 32 | console.log(`%c${tag} ${message}`, style); 33 | 34 | if (action === LogAction.End) { 35 | console.groupEnd(); 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/wwwroot/js/Constants.js: -------------------------------------------------------------------------------- 1 | export const TimeWarpStateName = "TimeWarpState"; 2 | export const DevToolsName = "devTools"; 3 | export const InitializeJavaScriptInteropName = "InitializeJavaScriptInterop"; 4 | export const JsonRequestHandlerMethodName = "Handle"; 5 | export const ReduxDevToolsFactoryName = "ReduxDevToolsFactory"; 6 | export const ReduxDevToolsName = "reduxDevTools"; 7 | export const ReduxExtensionName = "__REDUX_DEVTOOLS_EXTENSION__"; 8 | //# sourceMappingURL=Constants.js.map -------------------------------------------------------------------------------- /Source/TimeWarp.State/wwwroot/js/Constants.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"Constants.js","sourceRoot":"","sources":["../TypeScript/Constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,iBAAiB,GAAW,eAAe,CAAC;AACzD,MAAM,CAAC,MAAM,YAAY,GAAW,UAAU,CAAA;AAC9C,MAAM,CAAC,MAAM,+BAA+B,GAAW,6BAA6B,CAAC;AACrF,MAAM,CAAC,MAAM,4BAA4B,GAAW,QAAQ,CAAC;AAC7D,MAAM,CAAC,MAAM,wBAAwB,GAAW,sBAAsB,CAAC;AACvE,MAAM,CAAC,MAAM,iBAAiB,GAAW,eAAe,CAAC;AACzD,MAAM,CAAC,MAAM,kBAAkB,GAAW,8BAA8B,CAAC"} -------------------------------------------------------------------------------- /Source/TimeWarp.State/wwwroot/js/Logger.js: -------------------------------------------------------------------------------- 1 | export const logStyles = { 2 | info: "color: deepskyblue; font-weight: bold;", 3 | success: "color: limegreen; font-weight: bold;", 4 | warning: "color: darkorange; font-weight: bold;", 5 | error: "color: crimson; font-weight: bold;", 6 | function: "color: mediumorchid; font-weight: bold;", 7 | }; 8 | export var LogAction; 9 | (function (LogAction) { 10 | LogAction[LogAction["Begin"] = 0] = "Begin"; 11 | LogAction[LogAction["End"] = 1] = "End"; 12 | })(LogAction || (LogAction = {})); 13 | export const log = (tag, message, level = 'info', action) => { 14 | const style = logStyles[level]; 15 | if (action === LogAction.Begin) { 16 | console.group(`%c${tag}`, style); 17 | } 18 | console.log(`%c${tag} ${message}`, style); 19 | if (action === LogAction.End) { 20 | console.groupEnd(); 21 | } 22 | }; 23 | //# sourceMappingURL=Logger.js.map -------------------------------------------------------------------------------- /Source/TimeWarp.State/wwwroot/js/Logger.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"Logger.js","sourceRoot":"","sources":["../TypeScript/Logger.ts"],"names":[],"mappings":"AASA,MAAM,CAAC,MAAM,SAAS,GAAc;IAClC,IAAI,EAAE,wCAAwC;IAC9C,OAAO,EAAE,sCAAsC;IAC/C,OAAO,EAAE,uCAAuC;IAChD,KAAK,EAAE,oCAAoC;IAC3C,QAAQ,EAAE,yCAAyC;CACpD,CAAC;AAIF,MAAM,CAAN,IAAY,SAGX;AAHD,WAAY,SAAS;IACnB,2CAAK,CAAA;IACL,uCAAG,CAAA;AACL,CAAC,EAHW,SAAS,KAAT,SAAS,QAGpB;AAED,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,GAAW,EAAE,OAAe,EAAE,QAAkB,MAAM,EAAE,MAAkB,EAAQ,EAAE;IACtG,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IAE/B,IAAI,MAAM,KAAK,SAAS,CAAC,KAAK,EAAE,CAAC;QAC/B,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,EAAE,EAAE,KAAK,CAAC,CAAC;IACnC,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,KAAK,GAAG,IAAI,OAAO,EAAE,EAAE,KAAK,CAAC,CAAC;IAE1C,IAAI,MAAM,KAAK,SAAS,CAAC,GAAG,EAAE,CAAC;QAC7B,OAAO,CAAC,QAAQ,EAAE,CAAC;IACrB,CAAC;AACH,CAAC,CAAC"} -------------------------------------------------------------------------------- /Source/TimeWarp.State/wwwroot/js/ReduxDevToolsTypes.js: -------------------------------------------------------------------------------- 1 | //# sourceMappingURL=ReduxDevToolsTypes.js.map -------------------------------------------------------------------------------- /Source/TimeWarp.State/wwwroot/js/ReduxDevToolsTypes.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"ReduxDevToolsTypes.js","sourceRoot":"","sources":["../TypeScript/ReduxDevToolsTypes.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /Source/TimeWarp.State/wwwroot/js/TimeWarpState.js: -------------------------------------------------------------------------------- 1 | import { JsonRequestHandlerMethodName } from './Constants.js'; 2 | import { log } from './Logger.js'; 3 | export class TimeWarpState { 4 | jsonRequestHandler; 5 | reduxDevTools; 6 | async DispatchRequest(requestTypeFullName, request) { 7 | if (!this.jsonRequestHandler) { 8 | throw new Error('jsonRequestHandler is not initialized. Add '); 9 | } 10 | const requestAsJson = JSON.stringify(request); 11 | log("DispatchRequest", `Dispatching request of Type ${requestTypeFullName}: ${requestAsJson}`, "function"); 12 | await this.jsonRequestHandler.invokeMethodAsync(JsonRequestHandlerMethodName, requestTypeFullName, requestAsJson); 13 | } 14 | } 15 | export const timeWarpState = new TimeWarpState(); 16 | //# sourceMappingURL=TimeWarpState.js.map -------------------------------------------------------------------------------- /Source/TimeWarp.State/wwwroot/js/TimeWarpState.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"TimeWarpState.js","sourceRoot":"","sources":["../TypeScript/TimeWarpState.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,4BAA4B,EAAC,MAAM,gBAAgB,CAAC;AAE5D,OAAO,EAAC,GAAG,EAAC,MAAM,aAAa,CAAC;AAGhC,MAAM,OAAO,aAAa;IAGxB,kBAAkB,CAAkB;IAGpC,aAAa,CAAgB;IAQ7B,KAAK,CAAC,eAAe,CAAC,mBAA2B,EAAE,OAAgB;QACjE,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;QACjE,CAAC;QAGD,MAAM,aAAa,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAE9C,GAAG,CAAC,iBAAiB,EAAE,+BAA+B,mBAAmB,KAAK,aAAa,EAAE,EAAE,UAAU,CAAE,CAAC;QAG5G,MAAM,IAAI,CAAC,kBAAkB,CAAC,iBAAiB,CAAC,4BAA4B,EAAE,mBAAmB,EAAE,aAAa,CAAC,CAAC;IACpH,CAAC;CACF;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,aAAa,EAAE,CAAC"} -------------------------------------------------------------------------------- /Source/TimeWarp.State/wwwroot/types/Constants.d.ts: -------------------------------------------------------------------------------- 1 | export declare const TimeWarpStateName: string; 2 | export declare const DevToolsName: string; 3 | export declare const InitializeJavaScriptInteropName: string; 4 | export declare const JsonRequestHandlerMethodName: string; 5 | export declare const ReduxDevToolsFactoryName: string; 6 | export declare const ReduxDevToolsName: string; 7 | export declare const ReduxExtensionName: string; 8 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/wwwroot/types/Logger.d.ts: -------------------------------------------------------------------------------- 1 | export interface LogStyles { 2 | info: string; 3 | success: string; 4 | warning: string; 5 | error: string; 6 | function: string; 7 | } 8 | export declare const logStyles: LogStyles; 9 | export type LogLevel = keyof LogStyles; 10 | export declare enum LogAction { 11 | Begin = 0, 12 | End = 1 13 | } 14 | export declare const log: (tag: string, message: string, level?: LogLevel, action?: LogAction) => void; 15 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/wwwroot/types/ReduxDevTools.d.ts: -------------------------------------------------------------------------------- 1 | import { TimeWarpState } from './TimeWarpState.js'; 2 | type Config = any; 3 | type ConnectResponse = any; 4 | type ReduxDevtoolsExtension = any; 5 | export declare class ReduxDevTools { 6 | IsEnabled: boolean; 7 | DevTools: ConnectResponse; 8 | Extension: ReduxDevtoolsExtension; 9 | Config: Config; 10 | TimeWarpState: TimeWarpState; 11 | StackTrace: string | undefined; 12 | private IsInitialized; 13 | constructor(reduxDevToolsOptions: Config); 14 | Init(): void; 15 | GetExtension(): ReduxDevtoolsExtension | undefined; 16 | GetDevTools(): ConnectResponse | undefined; 17 | MapRequestType(message: any): string; 18 | MessageHandler: (message: any) => void; 19 | ReduxDevToolsDispatch(action: any, state: unknown, stackTrace: any): any; 20 | GetStackTraceForAction(_action: any): string; 21 | } 22 | export {}; 23 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/wwwroot/types/ReduxDevToolsTypes.d.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeWarpEngineering/timewarp-state/68e25aedbc6d41fcded8ad71a1466a81f9d997b4/Source/TimeWarp.State/wwwroot/types/ReduxDevToolsTypes.d.ts -------------------------------------------------------------------------------- /Source/TimeWarp.State/wwwroot/types/TimeWarp.State.lib.module.d.ts: -------------------------------------------------------------------------------- 1 | export declare function beforeWebStart(_options: any, _extensions: any): void; 2 | export declare function afterWebStarted(_blazor: any): void; 3 | export declare function beforeWebAssemblyStart(_options: any, _extensions: any): void; 4 | export declare function afterWebAssemblyStarted(_blazor: any): void; 5 | export declare function beforeServerStart(_options: any, _extensions: any): void; 6 | export declare function afterServerStarted(_blazor: any): void; 7 | -------------------------------------------------------------------------------- /Source/TimeWarp.State/wwwroot/types/TimeWarpState.d.ts: -------------------------------------------------------------------------------- 1 | import { DotNetReference } from './DotNetReference'; 2 | import { ReduxDevTools } from "./ReduxDevTools"; 3 | export declare class TimeWarpState { 4 | jsonRequestHandler: DotNetReference; 5 | reduxDevTools: ReduxDevTools; 6 | DispatchRequest(requestTypeFullName: string, request: unknown): Promise; 7 | } 8 | export declare const timeWarpState: TimeWarpState; 9 | -------------------------------------------------------------------------------- /Tests/Client.Integration.Tests/Client.Integration.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | enable 5 | $(NoWarn);1591 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Tests/Client.Integration.Tests/Clone/TestState.cs: -------------------------------------------------------------------------------- 1 | namespace TestApp.Client.Integration.Tests.Clone; 2 | 3 | 4 | [NotTest] 5 | public class TestState 6 | { 7 | // Create an array of strings to sort. 8 | public string[] Fruits { get; set; } = ["apricot", "orange", "banana", "mango", "apple", "grape", "strawberry"]; 9 | public IOrderedEnumerable SortedFruits => Fruits.OrderBy(fruit => fruit.Length).ThenBy(fruit => fruit); 10 | } 11 | -------------------------------------------------------------------------------- /Tests/Client.Integration.Tests/Clone/TestState_Clone_Tests.cs: -------------------------------------------------------------------------------- 1 | namespace TestState_; 2 | 3 | using TestApp.Client.Integration.Tests.Clone; 4 | 5 | public class Clone_Should 6 | { 7 | 8 | public static void Clone() 9 | { 10 | 11 | // Arrange 12 | var testState = new TestState(); 13 | 14 | // Act 15 | TestState clone = testState.Clone(); 16 | 17 | // Assert 18 | clone.SortedFruits.Count().Should().Be(7); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Tests/Client.Integration.Tests/Convention_Tests.cs: -------------------------------------------------------------------------------- 1 | namespace ConventionTest_; 2 | 3 | [TestTag(TestTags.Fast)] 4 | public class SimpleNoApplicationTest_Should_ 5 | { 6 | public static void AlwaysPass() => true.Should().BeTrue(); 7 | 8 | [Skip("Demonstrates skip attribute")] 9 | public static void SkipExample() => true.Should().BeFalse(); 10 | 11 | [TestTag(TestTags.Fast)] 12 | public static void TagExample() => true.Should().BeTrue(); 13 | 14 | [Input(5, 3, 2)] 15 | [Input(8, 5, 3)] 16 | public static void Subtract(int x, int y, int expectedDifference) 17 | { 18 | int result = x - y; 19 | result.Should().Be(expectedDifference); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Tests/Client.Integration.Tests/Features/Application/ApplicationState_Clone_Tests.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable UnusedType.Global 2 | namespace ApplicationState_; 3 | 4 | public class Clone_Should : BaseTest 5 | { 6 | public Clone_Should(ClientHost clientHost) : base(clientHost) 7 | { 8 | ApplicationState = Store.GetState(); 9 | } 10 | 11 | private ApplicationState ApplicationState { get; } 12 | 13 | public void Clone() 14 | { 15 | //Arrange 16 | ApplicationState.Initialize(name: "TestName", exceptionMessage: "Some ExceptionMessage"); 17 | 18 | //Act 19 | ApplicationState clone = ApplicationState.Clone(); 20 | 21 | //Assert 22 | ApplicationState.Should().NotBeSameAs(clone); 23 | ApplicationState.Name.Should().Be(clone.Name); 24 | ApplicationState.ExceptionMessage.Should().Be(clone.ExceptionMessage); 25 | ApplicationState.Guid.Should().NotBe(clone.Guid); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Tests/Client.Integration.Tests/Features/Counter/CounterState_Clone_Tests.cs: -------------------------------------------------------------------------------- 1 | namespace CounterState; 2 | 3 | using Test.App.Client.Features.Counter; 4 | 5 | public class Clone_Should : BaseTest 6 | { 7 | public Clone_Should(ClientHost webAssemblyHost) : base(webAssemblyHost) 8 | { 9 | CounterState = Store.GetState(); 10 | } 11 | 12 | private CounterState CounterState { get; set; } 13 | 14 | public void Clone() 15 | { 16 | //Arrange 17 | CounterState.Initialize(count: 15); 18 | 19 | //Act 20 | CounterState? clone = CounterState.Clone(); 21 | 22 | //Assert 23 | CounterState.Should().NotBeSameAs(clone); 24 | CounterState.Count.Should().Be(clone.Count); 25 | CounterState.Guid.Should().NotBe(clone.Guid); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Tests/Client.Integration.Tests/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using AnyClone; 2 | global using TimeWarp.State; 3 | global using FluentAssertions; 4 | global using JetBrains.Annotations; 5 | global using MediatR; 6 | global using Microsoft.AspNetCore.Mvc.Testing; 7 | global using Microsoft.Extensions.DependencyInjection; 8 | global using System.Linq; 9 | global using System.Net.Http; 10 | global using System.Reflection; 11 | global using System.Text.Json; 12 | global using Test.App.Client.Features.Application; 13 | global using TestApp.Client.Integration.Tests.Infrastructure; 14 | global using TimeWarp.Fixie; 15 | -------------------------------------------------------------------------------- /Tests/Client.Integration.Tests/Infrastructure/ClientHost.cs: -------------------------------------------------------------------------------- 1 | namespace TestApp.Client.Integration.Tests.Infrastructure; 2 | 3 | [NotTest] 4 | public class ClientHost 5 | ( 6 | IServiceProvider serviceProvider 7 | ) 8 | { 9 | 10 | public IServiceProvider ServiceProvider { get; } = serviceProvider; 11 | } 12 | -------------------------------------------------------------------------------- /Tests/Client.Integration.Tests/Infrastructure/ClientHostBuilder.cs: -------------------------------------------------------------------------------- 1 | namespace TestApp.Client.Integration.Tests.Infrastructure; 2 | 3 | [NotTest] 4 | public class ClientHostBuilder 5 | { 6 | public IServiceCollection Services { get; } = new ServiceCollection(); 7 | 8 | public static ClientHostBuilder CreateDefault() => new(); 9 | 10 | public ClientHost Build() => new(Services.BuildServiceProvider()); 11 | } 12 | -------------------------------------------------------------------------------- /Tests/Test.App.Architecture.Tests/ArchitectureTests.cs: -------------------------------------------------------------------------------- 1 | namespace Architecture_; 2 | 3 | public class Should_ 4 | { 5 | public static void FollowActionPolicy() 6 | { 7 | Assembly sut = typeof(Test.App.Client.AssemblyMarker).Assembly; 8 | PolicyDefinition policy = Policies.CreateActionPolicy(sut); 9 | PolicyResults results = policy.Evaluate(); 10 | results.ShouldBeSuccessful(); 11 | } 12 | 13 | public static void FollowActionHandlerPolicy() 14 | { 15 | Assembly sut = typeof(Test.App.Client.AssemblyMarker).Assembly; 16 | PolicyDefinition policy = Policies.CreateActionHandlerPolicy(sut); 17 | PolicyResults results = policy.Evaluate(); 18 | results.ShouldBeSuccessful(); 19 | } 20 | 21 | public static void FollowStatePolicy() 22 | { 23 | Assembly sut = typeof(Test.App.Client.AssemblyMarker).Assembly; 24 | PolicyDefinition policy = Policies.CreateStatePolicy(sut); 25 | PolicyResults results = policy.Evaluate(); 26 | results.ShouldBeSuccessful(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Tests/Test.App.Architecture.Tests/ConventionTests.cs: -------------------------------------------------------------------------------- 1 | namespace ConventionTest_; 2 | 3 | [TestTag(TestTags.Fast)] 4 | public class SimpleNoApplicationTest_Should_ 5 | { 6 | public static void AlwaysPass() => true.Should().BeTrue(); 7 | 8 | [Skip("Demonstrates skip attribute")] 9 | public static void SkipExample() => true.Should().BeFalse(); 10 | 11 | [TestTag(TestTags.Fast)] 12 | public static void TagExample() => true.Should().BeTrue(); 13 | 14 | [Input(5, 3, 2)] 15 | [Input(8, 5, 3)] 16 | public static void Subtract(int aX, int aY, int aExpectedDifference) 17 | { 18 | int result = aX - aY; 19 | result.Should().Be(aExpectedDifference); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Tests/Test.App.Architecture.Tests/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using FluentAssertions; 2 | global using NetArchTest.Policies; 3 | global using System.Reflection; 4 | global using TimeWarp.Fixie; 5 | global using TimeWarp.State.Policies; 6 | global using TimeWarp.State.Policies.Extensions; 7 | -------------------------------------------------------------------------------- /Tests/Test.App.Architecture.Tests/Test.App.Architecture.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | enable 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Tests/Test.App.Architecture.Tests/TestingConvention.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Architecture.Tests; 2 | 3 | class TestingConvention : TimeWarp.Fixie.TestingConvention { } 4 | -------------------------------------------------------------------------------- /Tests/Test.App.EndToEnd.Tests/Configuration.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.EndToEnd.Tests; 2 | 3 | public static class Configuration 4 | { 5 | public static string GetSutBaseUrl() 6 | { 7 | string port = Environment.GetEnvironmentVariable("SutPort") ?? "7011"; 8 | string protocol = Environment.GetEnvironmentVariable("UseHttp") == "true" ? "http" : "https"; 9 | string sutBaseUrl = $"{protocol}://localhost:{port}"; 10 | return sutBaseUrl; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Tests/Test.App.EndToEnd.Tests/ConfiguredRenderModes.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.EndToEnd.Tests; 2 | 3 | public static class ConfiguredRenderModes 4 | { 5 | public const string InteractiveAutoRenderMode = "InteractiveAutoRenderMode"; 6 | public const string InteractiveServerRenderMode = "InteractiveServerRenderMode"; 7 | public const string InteractiveWebAssemblyRenderMode = "InteractiveWebAssemblyRenderMode"; 8 | public const string None = "None"; 9 | // Add other configured render modes as needed 10 | } 11 | -------------------------------------------------------------------------------- /Tests/Test.App.EndToEnd.Tests/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using FluentAssertions; 2 | global using Microsoft.Playwright; 3 | global using Microsoft.Playwright.MSTest; 4 | global using System.Text.RegularExpressions; 5 | global using Test.App.EndToEnd.Tests; 6 | -------------------------------------------------------------------------------- /Tests/Test.App.EndToEnd.Tests/HomePageTest.cs: -------------------------------------------------------------------------------- 1 | namespace HomePage_; 2 | 3 | [TestClass] 4 | public class Should_ : PageTest 5 | { 6 | [TestMethod] 7 | public async Task RenderStaticContent() 8 | { 9 | string sutBaseUrl = Configuration.GetSutBaseUrl() ?? throw new InvalidOperationException("SUT base URL is not configured. Please check your test configuration."); 10 | await Page.GotoAsync(sutBaseUrl); 11 | Console.WriteLine($"Browser: {Page.Context.Browser?.BrowserType.Name}"); 12 | Console.WriteLine($"Browser Version: {Page.Context.Browser?.Version}"); 13 | Console.WriteLine($"User Agent: {await Page.EvaluateAsync("() => navigator.userAgent")}"); 14 | 15 | await Expect(Page).ToHaveTitleAsync("TimeWarp.State Test App"); 16 | await PageUtilities.ValidateRenderModesAsync(this, Page, RenderModes.Static, ConfiguredRenderModes.None); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Tests/Test.App.EndToEnd.Tests/PlaywrightSettings/chrome.runsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 24 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | chromium 17 | 5000 18 | 19 | true 20 | chrome 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Tests/Test.App.EndToEnd.Tests/PlaywrightSettings/edge.runsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 24 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | chromium 17 | 5000 18 | 19 | true 20 | msedge 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Tests/Test.App.EndToEnd.Tests/PlaywrightSettings/firefox.runsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 24 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | firefox 17 | 5000 18 | 19 | true 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Tests/Test.App.EndToEnd.Tests/PlaywrightSettings/webkit.runsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 24 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | webkit 17 | 5000 18 | 19 | true 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Tests/Test.App.EndToEnd.Tests/RenderModes.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.EndToEnd.Tests; 2 | 3 | public static class RenderModes 4 | { 5 | public const string Server = "Server"; 6 | public const string Wasm = "Wasm"; 7 | public const string Static = "Static"; 8 | // Add other render modes as needed 9 | } 10 | -------------------------------------------------------------------------------- /Tests/Test.App.EndToEnd.Tests/SampleTest.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.EndToEnd.Tests; 2 | 3 | [TestClass] 4 | public class ExampleTest : PageTest 5 | { 6 | [TestMethod] 7 | public async Task HasTitle() 8 | { 9 | await Page.GotoAsync("https://playwright.dev"); 10 | 11 | // Expect a title "to contain" a substring. 12 | await Expect(Page).ToHaveTitleAsync(new Regex("Playwright")); 13 | } 14 | 15 | [TestMethod] 16 | public async Task GetStartedLink() 17 | { 18 | await Page.GotoAsync("https://playwright.dev"); 19 | 20 | // Click the get started link. 21 | await Page.GetByRole(AriaRole.Link, new() { Name = "Get started" }).ClickAsync(); 22 | 23 | // Expects page to have a heading with the name of Installation. 24 | await Expect(Page.GetByRole(AriaRole.Heading, new() { Name = "Installation" })).ToBeVisibleAsync(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Tests/Test.App.EndToEnd.Tests/Test.App.EndToEnd.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | enable 5 | false 6 | true 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/AssemblyMarker.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client; 2 | 3 | /// 4 | /// Serves as a marker for the assembly, facilitating easy identification and reflection-based operations. 5 | /// 6 | /// 7 | /// This class is intended to be used as a reference point within the assembly for scenarios such as assembly scanning, 8 | /// where a stable, known type is required to locate the assembly at runtime. The class is sealed to indicate it is not 9 | /// designed for inheritance or extension, reinforcing its role as a simple marker. 10 | /// 11 | public sealed class AssemblyMarker { } 12 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Components/CustomInput.razor: -------------------------------------------------------------------------------- 1 | @typeparam T 2 | @inherits BaseInputComponent 3 | 4 |
5 | @if (!string.IsNullOrWhiteSpace(Label)) 6 | { 7 | 8 | } 9 | 10 |
11 | 12 | 23 | @code 24 | { 25 | // set the CssClass based on the CurrentTheme 26 | private string GetThemeClass() 27 | { 28 | return ThemeState.CurrentTheme switch 29 | { 30 | ThemeState.Theme.Light => "light", 31 | ThemeState.Theme.Dark => "dark", 32 | ThemeState.Theme.System => "dark", 33 | _ => string.Empty 34 | }; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/Application/ApplicationState/ApplicationState.Debug.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client.Features.Application; 2 | 3 | public partial class ApplicationState 4 | { 5 | public override ApplicationState Hydrate(IDictionary keyValuePairs) => new() 6 | { 7 | Guid = 8 | new Guid 9 | ( 10 | keyValuePairs[CamelCase.MemberNameToCamelCase(nameof(Guid))].ToString() ?? 11 | throw new InvalidOperationException("Guid is required.") 12 | ), 13 | Name = 14 | keyValuePairs[CamelCase.MemberNameToCamelCase(nameof(Name))].ToString() ?? 15 | throw new InvalidOperationException("Name is required.") 16 | }; 17 | 18 | internal void Initialize(string name, string exceptionMessage) 19 | { 20 | ThrowIfNotTestAssembly(Assembly.GetCallingAssembly()); 21 | Name = name; 22 | ExceptionMessage = exceptionMessage; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/Application/ApplicationState/ApplicationState.ResetStore.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client.Features.Application; 2 | 3 | public partial class ApplicationState 4 | { 5 | public static class ResetStoreActionSet 6 | { 7 | internal sealed class Action : IAction; 8 | 9 | internal sealed class Handler : IRequestHandler 10 | { 11 | private readonly IStore Store; 12 | public Handler(IStore store) 13 | { 14 | Store = store; 15 | } 16 | public async Task Handle(Action action, CancellationToken cancellationToken) 17 | { 18 | Store.Reset(); 19 | await Store.GetState().ChangeRoute(newRoute: "/", cancellationToken); 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/Application/ApplicationState/ApplicationState.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client.Features.Application; 2 | 3 | public sealed partial class ApplicationState : State 4 | { 5 | public string Name { get; private set; } = null!; 6 | public string? ExceptionMessage { get; private set; } 7 | 8 | public string? Version => GetType().Assembly.GetName().Version?.ToString(); 9 | 10 | public override void Initialize() => Name = "TimeWarp.State Test App"; 11 | } 12 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/Application/Notification/ApplicationState.ExceptionNotificationHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client.Features.Application; 2 | 3 | public partial class ApplicationState 4 | { 5 | internal class ExceptionNotificationHandler 6 | ( 7 | ILogger Logger, 8 | IStore Store 9 | ) : INotificationHandler 10 | { 11 | private readonly ILogger Logger = Logger; 12 | private ApplicationState ApplicationState => Store.GetState(); 13 | 14 | public Task Handle 15 | ( 16 | ExceptionNotification exceptionNotification, 17 | CancellationToken cancellationToken 18 | ) 19 | { 20 | Logger.LogWarning("exceptionNotification.Exception.Message: {ExceptionMessage}", exceptionNotification.Exception.Message); 21 | ApplicationState.ExceptionMessage = exceptionNotification.Exception.Message; 22 | return Task.CompletedTask; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/Base/BaseActionHandler.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable UnusedMember.Global 2 | namespace Test.App.Client.Features.Base; 3 | 4 | /// 5 | /// Base Handler that makes it easy to access state 6 | /// 7 | /// The Type of Action to be handled 8 | internal abstract class BaseActionHandler 9 | ( 10 | IStore store 11 | ) : ActionHandler(store) 12 | where TAction : IAction 13 | { 14 | protected ApplicationState ApplicationState => Store.GetState(); 15 | protected CounterState CounterState => Store.GetState(); 16 | protected EventStreamState EventStreamState => Store.GetState(); 17 | protected WeatherForecastsState WeatherForecastsState => Store.GetState(); 18 | protected CacheableWeatherState CacheableWeatherState => Store.GetState(); 19 | } 20 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/Base/Components/ResetButton.razor: -------------------------------------------------------------------------------- 1 | @namespace Test.App.Client.Features.Base.Components 2 | @inherits BaseComponent 3 | @using static ApplicationState; 4 | 5 | @code { 6 | private async Task ButtonClick() => await Mediator.Send(new ResetStoreActionSet.Action()); 7 | } 8 | 9 |
10 | 11 |
12 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/Blue/Actions/BlueState.IncrementCount.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client.Features.Blue; 2 | 3 | public partial class BlueState 4 | { 5 | public static class IncrementCountActionSet 6 | { 7 | internal sealed class Action : IAction 8 | { 9 | public int Amount { get; init; } 10 | } 11 | 12 | internal sealed class Handler 13 | ( 14 | IStore store 15 | ) : ActionHandler(store) 16 | { 17 | 18 | BlueState BlueState => Store.GetState(); 19 | 20 | public override Task Handle(Action action, CancellationToken cancellationToken) 21 | { 22 | BlueState.Count += action.Amount; 23 | return Task.CompletedTask; 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/Blue/BlueState.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client.Features.Blue; 2 | 3 | [PersistentState(PersistentStateMethod.SessionStorage)] 4 | public sealed partial class BlueState : State 5 | { 6 | public int Count { get; private set; } 7 | 8 | public BlueState() { } 9 | 10 | [JsonConstructor] 11 | public BlueState(Guid guid, int count) 12 | { 13 | Guid = guid; 14 | Count = count; 15 | } 16 | 17 | public override void Initialize() => Count = 2; 18 | } 19 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/CacheableWeather/CacheableWeatherState.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client.Features.WeatherForecast; 2 | 3 | using static Contracts.Features.WeatherForecast.GetWeatherForecasts; 4 | 5 | public sealed partial class CacheableWeatherState: TimeWarpCacheableState 6 | { 7 | private Response? WeatherForecastList; 8 | 9 | public IReadOnlyList? WeatherForecasts => WeatherForecastList?.AsReadOnly(); 10 | 11 | public CacheableWeatherState() 12 | { 13 | CacheDuration = TimeSpan.FromSeconds(10); 14 | } // Set this to short duration for testing 15 | 16 | /// 17 | public override void Initialize() 18 | { 19 | WeatherForecastList = null; 20 | InvalidateCache(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/CloneTest/Actions/CloneableState.CloneTest.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client.Features.CloneTest; 2 | 3 | public partial class CloneableState 4 | { 5 | public static class CloneTestActionSet 6 | { 7 | internal sealed class Action : IAction; 8 | internal sealed class Handler : BaseActionHandler 9 | { 10 | public Handler(IStore store) : base(store) {} 11 | private CloneableState CloneableState => Store.GetState(); 12 | 13 | public override Task Handle 14 | ( 15 | Action action, 16 | CancellationToken cancellationToken 17 | ) 18 | { 19 | // Note: This is a test to verify that the state is cloned. 20 | // It is not an example of any real-world usage. 21 | if ( CloneableState.Count != 42) throw new Exception("Count is not 42 it seems I have failed to clone the state"); 22 | CloneableState.Count++; 23 | return Task.CompletedTask; 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/CloneTest/CloneTestState.Debug.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client.Features.CloneTest; 2 | 3 | public partial class CloneableState 4 | { 5 | public override CloneableState Hydrate(IDictionary keyValuePairs) 6 | { 7 | var counterState = new CloneableState 8 | { 9 | Count = Convert.ToInt32(keyValuePairs[CamelCase.MemberNameToCamelCase(nameof(Count))].ToString()), 10 | Guid = 11 | new Guid 12 | ( 13 | keyValuePairs[CamelCase.MemberNameToCamelCase(nameof(Guid))].ToString() ?? 14 | throw new InvalidOperationException() 15 | ) 16 | }; 17 | 18 | return counterState; 19 | } 20 | 21 | /// 22 | /// Use in Tests ONLY, to initialize the State 23 | /// 24 | /// 25 | public void Initialize(int count) 26 | { 27 | ThrowIfNotTestAssembly(Assembly.GetCallingAssembly()); 28 | Count = count; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/CloneTest/CloneableState.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client.Features.CloneTest; 2 | 3 | public sealed partial class CloneableState : State, ICloneable 4 | { 5 | public int Count { get; private set; } 6 | 7 | /// 8 | /// Set the Initial State 9 | /// 10 | public override void Initialize() => Count = 3; 11 | 12 | /// 13 | /// 14 | /// 15 | /// We are trying to prove ICloneable is used when available instead of AnyClone. 16 | /// New CloneableState object where Count is always 42 17 | public object Clone() => new CloneableState { Count = 42 }; 18 | } 19 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/Color/Actions/ColorState.Update.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client.Features.Counter.Actions; 2 | 3 | public class UpdateColorState 4 | { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/Color/ColorState.Debug.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client.Features.Counter; 2 | 3 | using System.Drawing; 4 | 5 | public partial class ColorState 6 | { 7 | public override ColorState Hydrate(IDictionary keyValuePairs) 8 | { 9 | var colorState = new ColorState 10 | { 11 | FavoriteColor = (Color)keyValuePairs[CamelCase.MemberNameToCamelCase(nameof(FavoriteColor))], 12 | MyColorName = (string)keyValuePairs[CamelCase.MemberNameToCamelCase(nameof(FavoriteColor))], 13 | Guid = 14 | new Guid 15 | ( 16 | keyValuePairs[CamelCase.MemberNameToCamelCase(nameof(Guid))].ToString() ?? 17 | throw new InvalidOperationException() 18 | ) 19 | }; 20 | 21 | return colorState; 22 | } 23 | 24 | public void Initialize(Color color, string myColorName) 25 | { 26 | ThrowIfNotTestAssembly(Assembly.GetCallingAssembly()); 27 | FavoriteColor = color; 28 | MyColorName = myColorName; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/Color/ColorState.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client.Features.Counter; 2 | 3 | using System.Drawing; 4 | 5 | public sealed partial class ColorState : State 6 | { 7 | public string MyColorName { get; private set; } = null!; 8 | 9 | public Color FavoriteColor { get; private set; } 10 | 11 | /// 12 | /// Set the Initial State 13 | /// 14 | public override void Initialize() 15 | { 16 | MyColorName = "Fools Gold"; 17 | FavoriteColor = Color.Gold; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/Counter/Actions/CounterState.IncrementCounter.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client.Features.Counter; 2 | 3 | public partial class CounterState 4 | { 5 | public static class IncrementCountActionSet 6 | { 7 | 8 | internal sealed class Action : IAction 9 | { 10 | public int Amount { get; init; } 11 | } 12 | 13 | internal sealed class Handler 14 | ( 15 | IStore store 16 | ) : ActionHandler(store) 17 | { 18 | private CounterState CounterState => Store.GetState(); 19 | 20 | public override Task Handle 21 | ( 22 | Action action, 23 | CancellationToken cancellationToken 24 | ) 25 | { 26 | CounterState.Count += action.Amount; 27 | return Task.CompletedTask; 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/Counter/Actions/CounterState.ThrowException.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client.Features.Counter; 2 | 3 | public partial class CounterState 4 | { 5 | public static class ThrowExceptionActionSet 6 | { 7 | internal sealed class Action : IAction 8 | { 9 | public string Message { get; } 10 | 11 | public Action(string message) 12 | { 13 | Message = message; 14 | } 15 | } 16 | 17 | internal sealed class Handler : BaseActionHandler 18 | { 19 | public Handler(IStore store) : base(store) {} 20 | 21 | /// 22 | /// Intentionally throw so we can test exception handling. 23 | /// 24 | public override Task Handle 25 | ( 26 | Action action, 27 | CancellationToken cancellationToken 28 | ) => throw new Exception(action.Message); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/Counter/Actions/ImproperNestedAction/ImproperNestedAction.cs: -------------------------------------------------------------------------------- 1 | #if ANALYZER_TEST 2 | // Code examples that the analyzer should fail on 3 | namespace Test.App.Client.Features.Counter; 4 | 5 | public class WrongNesting 6 | { 7 | public class ImproperNestedAction : IAction 8 | { 9 | // ReSharper disable once UnusedMember.Global 10 | public int Amount { get; set; } 11 | } 12 | } 13 | #endif 14 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/Counter/Actions/ImproperNestedAction/ImproperNestedHandler.cs: -------------------------------------------------------------------------------- 1 | #if ANALYZER_TEST 2 | // Code examples that the analyzer should fail on 3 | namespace Test.App.Client.Features.Counter; 4 | 5 | using static WrongNesting; 6 | 7 | public partial class CounterState 8 | { 9 | internal class ImproperNestedHandler 10 | ( 11 | IStore store 12 | ) : BaseActionHandler(store) 13 | { 14 | 15 | public override Task Handle 16 | ( 17 | ImproperNestedAction action, 18 | CancellationToken cancellationToken 19 | ) => Unit.Task; 20 | } 21 | } 22 | #endif 23 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/Counter/Actions/NonNestedAction/NonNestedAction.cs: -------------------------------------------------------------------------------- 1 | #if ANALYZER_TEST 2 | // Code examples that the analyzer should fail on 3 | namespace Test.App.Client.Features.Counter; 4 | 5 | public class NonNestedAction : IAction 6 | { 7 | // ReSharper disable once UnusedMember.Global 8 | public int Amount { get; set; } 9 | } 10 | #endif 11 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/Counter/Actions/NonNestedAction/NonNestedHandler.cs: -------------------------------------------------------------------------------- 1 | #if ANALYZER_TEST 2 | // Code examples that the analyzer should fail on 3 | namespace Test.App.Client.Features.Counter; 4 | 5 | public partial class CounterState 6 | { 7 | internal class NonNestedHandler 8 | ( 9 | IStore store 10 | ) : BaseActionHandler(store) 11 | { 12 | 13 | public override Task Handle 14 | ( 15 | NonNestedAction action, 16 | CancellationToken cancellationToken 17 | ) => Unit.Task; 18 | } 19 | } 20 | #endif 21 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/Counter/Components/Counter.razor: -------------------------------------------------------------------------------- 1 | @namespace Test.App.Client.Features.Counter.Components 2 | @inherits BaseComponent 3 | 4 | @code 5 | { 6 | protected override void OnInitialized() 7 | { 8 | //RegisterRenderTrigger(p => p.Count != CounterState.Count); 9 | RegisterRenderTrigger(p => p.Count); 10 | base.OnInitialized(); 11 | } 12 | } 13 | 14 |
15 |

CounterState.Count: @CounterState.Count

16 | 17 | 18 |
19 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/Counter/Components/Counter.razor.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client.Features.Counter.Components; 2 | 3 | using static CounterState; 4 | 5 | public partial class Counter : BaseComponent 6 | { 7 | private async Task ButtonClick() => 8 | await Mediator.Send(new IncrementCountActionSet.Action { Amount = 5 }); 9 | } 10 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/Counter/CounterState.Debug.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client.Features.Counter; 2 | 3 | public partial class CounterState 4 | { 5 | public override CounterState Hydrate(IDictionary keyValuePairs) 6 | { 7 | var counterState = new CounterState 8 | { 9 | Count = Convert.ToInt32(keyValuePairs[CamelCase.MemberNameToCamelCase(nameof(Count))].ToString()), 10 | Guid = 11 | new Guid 12 | ( 13 | keyValuePairs[CamelCase.MemberNameToCamelCase(nameof(Guid))].ToString() ?? 14 | throw new InvalidOperationException() 15 | ) 16 | }; 17 | 18 | return counterState; 19 | } 20 | 21 | /// 22 | /// Use in Tests ONLY, to initialize the State 23 | /// 24 | /// 25 | public void Initialize(int count) 26 | { 27 | ThrowIfNotTestAssembly(Assembly.GetCallingAssembly()); 28 | Count = count; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/Counter/CounterState.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client.Features.Counter; 2 | 3 | public sealed partial class CounterState : State 4 | { 5 | 6 | public int Count { get; private set; } 7 | 8 | public CounterState() { } 9 | 10 | [JsonConstructor] 11 | public CounterState(Guid guid, int count) 12 | { 13 | Guid = guid; 14 | Count = count; 15 | } 16 | 17 | /// 18 | /// Set the Initial State 19 | /// 20 | public override void Initialize() => Count = 3; 21 | } 22 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/Counter/Notification/IncrementCountNotificationHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client.Features.Counter; 2 | 3 | internal class IncrementCountNotificationHandler 4 | ( 5 | ILogger logger 6 | ) : INotificationHandler> 7 | { 8 | private readonly ILogger Logger = logger; 9 | 10 | public Task Handle 11 | ( 12 | PostPipelineNotification postPipelineNotification, 13 | CancellationToken cancellationToken 14 | ) 15 | { 16 | Logger.LogDebug("{postPipelineNotification_Request_Type_Name}", postPipelineNotification.Request.GetType().Name); 17 | Logger.LogDebug("{postPipelineNotification_Response_Type_Name}", postPipelineNotification.Response.GetType().Name); 18 | Logger.LogDebug("{methodName} handled", nameof(IncrementCountNotificationHandler)); 19 | return Task.CompletedTask; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/Counter/Notification/PreIncrementCountNotificationHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client.Features.Counter; 2 | 3 | internal class PreIncrementCountNotificationHandler 4 | ( 5 | ILogger logger 6 | ) : INotificationHandler> 7 | { 8 | private readonly ILogger Logger = logger; 9 | 10 | public Task Handle 11 | ( 12 | PrePipelineNotification prePipelineNotification, 13 | CancellationToken cancellationToken 14 | ) 15 | { 16 | Logger.LogDebug("{prePipelineNotification_Request_Type_Name}", prePipelineNotification.Request.GetType().Name); 17 | Logger.LogDebug("{methodName} handled", nameof(IncrementCountNotificationHandler)); 18 | return Task.CompletedTask; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/EventStream/Actions/AddEvent/EventStreamState.AddEventAction.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client.Features.EventStream; 2 | 3 | public partial class EventStreamState 4 | { 5 | public static class AddEventActionSet 6 | { 7 | 8 | internal sealed class Action : IAction 9 | { 10 | public string Message { get; } 11 | public Action(string message) 12 | { 13 | Message = message; 14 | } 15 | } 16 | 17 | internal sealed class AddEventHandler 18 | ( 19 | IStore store 20 | ) : BaseActionHandler(store) 21 | { 22 | 23 | public override Task Handle 24 | ( 25 | Action action, 26 | CancellationToken cancellationToken 27 | ) 28 | { 29 | EventStreamState.EventList.Add(action.Message); 30 | return Task.CompletedTask; 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/EventStream/Components/EventStream.razor: -------------------------------------------------------------------------------- 1 | @namespace Test.App.Client.Features.EventStream.Components 2 | @inherits BaseComponent 3 | 4 |
Events
5 |
    6 | @foreach(string item in Events) 7 | { 8 |
  • @item
  • 9 | } 10 |
11 | 12 | @code 13 | { 14 | private IReadOnlyList Events => EventStreamState.Events; 15 | } 16 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/EventStream/EventStreamState.Debug.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client.Features.EventStream; 2 | 3 | public partial class EventStreamState 4 | { 5 | internal void Initialize(List events) 6 | { 7 | ThrowIfNotTestAssembly(Assembly.GetCallingAssembly()); 8 | EventList = events; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/EventStream/EventStreamState.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client.Features.EventStream; 2 | 3 | public sealed partial class EventStreamState : State 4 | { 5 | private List EventList { get; set; } = []; 6 | public IReadOnlyList Events => EventList.AsReadOnly(); 7 | 8 | public override void Initialize() { } 9 | } 10 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/Purple/Actions/PurpleState.IncrementCount.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client.Features.Purple; 2 | 3 | public partial class PurpleState 4 | { 5 | public static class IncrementCountActionSet 6 | { 7 | internal sealed class Action : IAction 8 | { 9 | public int Amount { get; init; } 10 | } 11 | 12 | internal sealed class Handler : ActionHandler 13 | { 14 | public Handler(IStore store) : base(store) {} 15 | PurpleState PurpleState => Store.GetState(); 16 | 17 | public override Task Handle(Action action, CancellationToken cancellationToken) 18 | { 19 | PurpleState.Count += action.Amount; 20 | return Task.CompletedTask; 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/Purple/Components/PurpleCounter.razor: -------------------------------------------------------------------------------- 1 | @inherits BaseComponent 2 |

Counter

3 | @RenderModeDisplay 4 | 5 | @if (IsPreRendering) 6 | { 7 |
CounterState.Guid:
8 |
Counter 1 count:
9 | } 10 | else 11 | { 12 |
CounterState.Guid: @PurpleState.Guid
13 |
Counter 1 count: @PurpleState.Count
14 | 15 | } 16 | 17 | @code 18 | { 19 | private async Task IncrementCount() => await Mediator.Send(new PurpleState.IncrementCountActionSet.Action { Amount = 5 }); 20 | } 21 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/Purple/PurpleState.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client.Features.Purple; 2 | 3 | [PersistentState(PersistentStateMethod.LocalStorage)] 4 | public sealed partial class PurpleState : State 5 | { 6 | public int Count { get; private set; } 7 | 8 | public PurpleState() { } 9 | 10 | [JsonConstructor] 11 | public PurpleState(Guid guid, int count) 12 | { 13 | Guid = guid; 14 | Count = count; 15 | } 16 | 17 | public override void Initialize() => Count = 1; 18 | } 19 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Features/WeatherForecast/WeatherForecastState.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client.Features.WeatherForecast; 2 | 3 | using static Contracts.Features.WeatherForecast.GetWeatherForecasts; 4 | 5 | public sealed partial class WeatherForecastsState: State 6 | { 7 | private Response? WeatherForecastList; 8 | 9 | public IReadOnlyList? WeatherForecasts => WeatherForecastList?.AsReadOnly(); 10 | 11 | /// 12 | public override void Initialize() 13 | { 14 | WeatherForecastList = null; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Pages/ResetStorePage.razor: -------------------------------------------------------------------------------- 1 | @rendermode InteractiveAuto 2 | @attribute [Route(Route)] 3 | @inherits BaseComponent 4 | 5 | @Title 6 |

@Title

7 | @RenderModeDisplay 8 |
9 | 10 | 11 |

Reset button should clear all state in the Store and return you to the home page

12 | 13 | 14 | 15 | 16 |
17 | 18 |

Act: Click the `Reset Store` Button.

19 | 20 | 21 |

22 | Assert: 23 | Verify that the route changes to the home page. 24 |

25 | 26 |
27 |

28 | Repeat the above steps where `Current Render Mode` is Server and Wasm. 29 |

30 | 31 | @code 32 | { 33 | /// 34 | /// The title of the page 35 | /// 36 | public const string Title = "Reset Store Page"; 37 | 38 | 39 | /// 40 | /// The route for the page 41 | /// 42 | public const string Route = "/ResetStorePage"; 43 | } 44 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Pipeline/NotificationPostProcessor/PostPipelineNotification.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client.Pipeline.NotificationPostProcessor; 2 | 3 | public class PostPipelineNotification : INotification 4 | { 5 | public required TRequest Request { get; init; } 6 | public required TResponse Response { get; init; } 7 | } 8 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Pipeline/NotificationPostProcessor/PostPipelineNotificationRequestPostProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client.Pipeline.NotificationPostProcessor; 2 | 3 | internal class PostPipelineNotificationRequestPostProcessor 4 | ( 5 | ILogger> logger, 6 | IPublisher Publisher 7 | ) : 8 | IRequestPostProcessor 9 | where TRequest : notnull 10 | { 11 | private readonly ILogger Logger = logger; 12 | 13 | public Task Process(TRequest request, TResponse response, CancellationToken cancellationToken) 14 | { 15 | var notification = new PostPipelineNotification 16 | { 17 | Request = request, 18 | Response = response 19 | }; 20 | 21 | Logger.LogDebug(nameof(PostPipelineNotificationRequestPostProcessor)); 22 | return Publisher.Publish(notification, cancellationToken); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Pipeline/NotificationPreProcessor/PrePipelineNotification.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client.Pipeline.NotificationPreProcessor; 2 | 3 | public class PrePipelineNotification : INotification 4 | { 5 | public required TRequest Request { get; init; } 6 | } 7 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/Pipeline/NotificationPreProcessor/PrePipelineNotificationRequestPreProcessor.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Client.Pipeline.NotificationPreProcessor; 2 | 3 | internal class PrePipelineNotificationRequestPreProcessor : IRequestPreProcessor 4 | where TRequest : IAction 5 | { 6 | private readonly ILogger Logger; 7 | private readonly IPublisher Publisher; 8 | public PrePipelineNotificationRequestPreProcessor 9 | ( 10 | ILogger> logger, 11 | IPublisher publisher 12 | ) 13 | { 14 | Publisher = publisher; 15 | Logger = logger; 16 | } 17 | 18 | public Task Process(TRequest request, CancellationToken cancellationToken) 19 | { 20 | var notification = new PrePipelineNotification 21 | { 22 | Request = request 23 | }; 24 | 25 | Logger.LogDebug(nameof(PrePipelineNotificationRequestPreProcessor)); 26 | return Publisher.Publish(notification, cancellationToken); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/TestObjects/CustomCollectionObject.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable ConvertToPrimaryConstructor 2 | // ReSharper disable UnusedAutoPropertyAccessor.Global 3 | namespace AnyClone.Tests.TestObjects; 4 | 5 | using System.Collections.ObjectModel; 6 | 7 | public class CustomCollectionObject : Collection 8 | { 9 | public int CustomId { get; set; } 10 | public string CustomName { get; set; } 11 | public CustomCollectionObject(int customId, string customName) 12 | { 13 | CustomId = customId; 14 | CustomName = customName; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/TestObjects/ITestInterface.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable UnusedMemberInSuper.Global 2 | namespace AnyClone.Tests.TestObjects; 3 | 4 | public interface ITestInterface 5 | { 6 | bool BoolValue { get; set; } 7 | int IntValue { get; set; } 8 | IDictionary DictionaryValue { get; set; } 9 | } 10 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/TestObjects/MultiDimensional2dArrayObject.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable BaseObjectGetHashCodeCallInGetHashCode 2 | // ReSharper disable PropertyCanBeMadeInitOnly.Global 3 | // ReSharper disable MemberCanBePrivate.Global 4 | #pragma warning disable CS8765 // Nullability of type of parameter doesn't match overridden member (possibly because of nullability attributes). 5 | namespace AnyClone.Tests.TestObjects; 6 | 7 | public class MultiDimensional2dArrayObject 8 | { 9 | public int[,] Int2DArray { get; set; } = new int[4, 2]; 10 | 11 | public override int GetHashCode() => base.GetHashCode(); 12 | 13 | public override bool Equals(object aObject) 14 | { 15 | var basicObject = (MultiDimensional2dArrayObject)aObject; 16 | return Equals(basicObject); 17 | } 18 | 19 | public bool Equals(MultiDimensional2dArrayObject other) => Int2DArray.EnumerableEqual(other.Int2DArray); 20 | } 21 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/TestObjects/MultiDimensional3dArrayObject.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable PropertyCanBeMadeInitOnly.Global 2 | // ReSharper disable MemberCanBePrivate.Global 3 | // ReSharper disable BaseObjectGetHashCodeCallInGetHashCode 4 | #pragma warning disable CS8765 // Nullability of type of parameter doesn't match overridden member (possibly because of nullability attributes). 5 | namespace AnyClone.Tests.TestObjects; 6 | 7 | public class MultiDimensional3dArrayObject 8 | { 9 | public int[,,] Int3DArray { get; set; } = new int[2, 3, 3]; 10 | 11 | public override int GetHashCode() => base.GetHashCode(); 12 | 13 | public override bool Equals(object aObject) 14 | { 15 | var basicObject = (MultiDimensional3dArrayObject)aObject; 16 | return Equals(basicObject); 17 | } 18 | 19 | public bool Equals(MultiDimensional3dArrayObject other) => Int3DArray.EnumerableEqual(other.Int3DArray); 20 | } 21 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/TestObjects/TestEnum.cs: -------------------------------------------------------------------------------- 1 | // ReSharper disable UnusedType.Global 2 | // ReSharper disable UnusedMember.Global 3 | namespace AnyClone.Tests.TestObjects; 4 | 5 | public enum TestEnum 6 | { 7 | Test1 = 1, 8 | Test2 = 2, 9 | Test3 = 3 10 | } 11 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/_Imports.razor: -------------------------------------------------------------------------------- 1 | @using System.Diagnostics.CodeAnalysis 2 | @using System.Net.Http 3 | @using System.Net.Http.Json 4 | @using System.Text.RegularExpressions 5 | @using Microsoft.AspNetCore.Components.Forms 6 | @using Microsoft.AspNetCore.Components.Routing 7 | @using Microsoft.AspNetCore.Components.Web 8 | @using static Microsoft.AspNetCore.Components.Web.RenderMode 9 | @using Microsoft.AspNetCore.Components.Web.Virtualization 10 | @using Microsoft.JSInterop 11 | @using TimeWarp.Features.ActionTracking 12 | @using TimeWarp.Features.Developer 13 | @using TimeWarp.Features.Routing 14 | 15 | @using Test.App.Client 16 | @using Test.App.Client.Components 17 | @using Test.App.Client.Features.Base.Components 18 | @using Test.App.Client.Features.CloneTest.Pages 19 | @using Test.App.Client.Features.Color.Components 20 | @using Test.App.Client.Features.Counter.Components 21 | @using Test.App.Client.Features.EventStream.Components 22 | @using Test.App.Client.Pages 23 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/wwwroot/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Trace", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Client/wwwroot/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "UseHttp": false 9 | } 10 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Contracts/Features/ExceptionHandling/ThrowServerSideException/ThrowServerSideExceptionRequest.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Contracts.Features.ExceptionHandlings; 2 | 3 | public class ThrowServerSideExceptionRequest : IRequest 4 | { 5 | private const string RouteTemplate = "api/ExceptionHandlings/ThrowServerSideException"; 6 | 7 | /// 8 | /// Set Properties and Update Docs 9 | /// 10 | /// TODO 11 | public string? SampleProperty { get; set; } 12 | 13 | public string GetRoute() => $"{RouteTemplate}?{nameof(SampleProperty)}={SampleProperty}"; 14 | } 15 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Contracts/Features/ExceptionHandling/ThrowServerSideException/ThrowServerSideExceptionResponse.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Contracts.Features.ExceptionHandlings; 2 | 3 | public class ThrowServerSideExceptionResponse; 4 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Contracts/Features/WeatherForecast/Queries/GetWeatherForecasts.cs: -------------------------------------------------------------------------------- 1 | namespace Test.App.Contracts.Features.WeatherForecast; 2 | 3 | public static class GetWeatherForecasts 4 | { 5 | public sealed class Query : IRequest 6 | { 7 | public int Days { get; init; } 8 | 9 | public const string RouteTemplate = "api/weather"; 10 | public string GetRoute() => FormattableString.Invariant($"{RouteTemplate}"); 11 | } 12 | 13 | public sealed class Response : List; 14 | 15 | public sealed class WeatherForecastDto 16 | { 17 | public DateOnly Date { get; } 18 | 19 | public string Summary { get; } 20 | 21 | public int TemperatureC { get; } 22 | 23 | 24 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); 25 | 26 | public WeatherForecastDto(DateOnly date, string summary, int temperatureC) 27 | { 28 | Date = date; 29 | Summary = summary; 30 | TemperatureC = temperatureC; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Contracts/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using MediatR; 2 | global using System.Collections.Generic; 3 | global using System.Text.Json.Serialization; 4 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Contracts/Test.App.Contracts.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | enable 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Server/Components/App.razor: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Server/Components/Layout/MainLayout.razor: -------------------------------------------------------------------------------- 1 | @inherits LayoutComponentBase 2 | 3 |
4 | 7 | 8 |
9 |
10 | About 11 |
12 | 13 |
14 | @Body 15 |
16 |
17 |
18 | 19 |
20 | An unhandled error has occurred. 21 | Reload 22 | 🗙 23 |
24 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Server/Components/Routes.razor: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Server/Components/_Imports.razor: -------------------------------------------------------------------------------- 1 |  2 | @using TimeWarp.State 3 | @using Microsoft.AspNetCore.Components.Forms 4 | @using Microsoft.AspNetCore.Components.Routing 5 | @using Microsoft.AspNetCore.Components.Web 6 | @using Microsoft.AspNetCore.Components.Web.Virtualization 7 | @using Microsoft.JSInterop 8 | @using System.Net.Http 9 | @using System.Net.Http.Json 10 | @using Test.App 11 | @using Test.App.Client 12 | @using Test.App.Client.Components 13 | @using Test.App.Client.Features.Base.Components 14 | @using Test.App.Components 15 | @using TimeWarp.Features.Developer 16 | @using TimeWarp.Features.JavaScriptInterop 17 | @using TimeWarp.Features.Routing 18 | 19 | @using static Microsoft.AspNetCore.Components.Web.RenderMode 20 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Server/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using TimeWarp.State; 2 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Server/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "https": { 5 | "commandName": "Project", 6 | "dotnetRunMessages": true, 7 | "launchBrowser": true, 8 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 9 | "applicationUrl": "https://localhost:7011;http://localhost:5058", 10 | "environmentVariables": { 11 | "ASPNETCORE_ENVIRONMENT": "Development" 12 | } 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Server/Test.App.Server.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | enable 5 | Test.App 6 | false 7 | false 8 | false 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Server/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Server/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning", 5 | "Microsoft.AspNetCore": "Warning", 6 | "Microsoft.AspNetCore.Components": "Warning" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /Tests/Test.App/Test.App.Server/wwwroot/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimeWarpEngineering/timewarp-state/68e25aedbc6d41fcded8ad71a1466a81f9d997b4/Tests/Test.App/Test.App.Server/wwwroot/favicon.png -------------------------------------------------------------------------------- /Tests/TimeWarp.State.Analyzer.Tests/.editorconfig: -------------------------------------------------------------------------------- 1 | # C# files in test projects 2 | [*Tests.cs] 3 | 4 | # Naming styles for test classes 5 | dotnet_naming_rule.test_classes_should_allow_underscore.symbols = test_classes 6 | dotnet_naming_rule.test_classes_should_allow_underscore.style = underscore_allowed_style 7 | dotnet_naming_rule.test_classes_should_allow_underscore.severity = suggestion 8 | 9 | dotnet_naming_symbols.test_classes.applicable_kinds = class 10 | dotnet_naming_symbols.test_classes.applicable_accessibilities = public 11 | 12 | dotnet_naming_style.underscore_allowed_style.capitalization = pascal_case 13 | -------------------------------------------------------------------------------- /Tests/TimeWarp.State.Analyzer.Tests/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using TimeWarp.State.Analyzer; 2 | global using TimeWarp.State.Analyzer.Tests; 3 | global using FluentAssertions; 4 | global using Microsoft.CodeAnalysis; 5 | global using Microsoft.CodeAnalysis.CSharp.Testing; 6 | global using Microsoft.CodeAnalysis.Testing; 7 | global using System.Diagnostics.CodeAnalysis; 8 | global using System.Threading.Tasks; 9 | global using TimeWarp.Fixie; 10 | -------------------------------------------------------------------------------- /Tests/TimeWarp.State.Analyzer.Tests/TestingConvention.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.State.Analyzer.Tests; 2 | 3 | class TestingConvention : TimeWarp.Fixie.TestingConvention { } 4 | -------------------------------------------------------------------------------- /Tests/TimeWarp.State.Analyzer.Tests/TimeWarp.State.Analyzer.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | enable 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Tests/TimeWarp.State.Plus.Tests/ArchitectureTests.cs: -------------------------------------------------------------------------------- 1 | namespace Architecture_; 2 | 3 | public class Should_ 4 | { 5 | public static void FollowActionPolicy() 6 | { 7 | Assembly sut = typeof(TimeWarp.State.Plus.AssemblyMarker).Assembly; 8 | PolicyDefinition policy = Policies.CreateActionPolicy(sut); 9 | PolicyResults results = policy.Evaluate(); 10 | results.ShouldBeSuccessful(); 11 | } 12 | 13 | public static void FollowActionHandlerPolicy() 14 | { 15 | Assembly sut = typeof(TimeWarp.State.Plus.AssemblyMarker).Assembly; 16 | PolicyDefinition policy = Policies.CreateActionHandlerPolicy(sut); 17 | PolicyResults results = policy.Evaluate(); 18 | results.ShouldBeSuccessful(); 19 | } 20 | 21 | public static void FollowStatePolicy() 22 | { 23 | Assembly sut = typeof(TimeWarp.State.Plus.AssemblyMarker).Assembly; 24 | PolicyDefinition policy = Policies.CreateStatePolicy(sut); 25 | PolicyResults results = policy.Evaluate(); 26 | results.ShouldBeSuccessful(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Tests/TimeWarp.State.Plus.Tests/ConventionTests.cs: -------------------------------------------------------------------------------- 1 | namespace ConventionTest_; 2 | 3 | [TestTag(TestTags.Fast)] 4 | public class SimpleNoApplicationTest_Should_ 5 | { 6 | public static void AlwaysPass() => true.Should().BeTrue(); 7 | 8 | [Skip("Demonstrates skip attribute")] 9 | public static void SkipExample() => true.Should().BeFalse(); 10 | 11 | [TestTag(TestTags.Fast)] 12 | public static void TagExample() => true.Should().BeTrue(); 13 | 14 | [Input(5, 3, 2)] 15 | [Input(8, 5, 3)] 16 | public static void Subtract(int aX, int aY, int aExpectedDifference) 17 | { 18 | int result = aX - aY; 19 | result.Should().Be(aExpectedDifference); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Tests/TimeWarp.State.Plus.Tests/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using FluentAssertions; 2 | global using NetArchTest.Policies; 3 | global using System.Reflection; 4 | global using TimeWarp.Fixie; 5 | global using TimeWarp.State.Policies; 6 | global using TimeWarp.State.Policies.Extensions; 7 | -------------------------------------------------------------------------------- /Tests/TimeWarp.State.Plus.Tests/TestingConvention.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.State.Tests; 2 | 3 | class TestingConvention : TimeWarp.Fixie.TestingConvention { } 4 | -------------------------------------------------------------------------------- /Tests/TimeWarp.State.Plus.Tests/TimeWarp.State.Plus.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | enable 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Tests/TimeWarp.State.Tests/ConventionTests.cs: -------------------------------------------------------------------------------- 1 | namespace ConventionTest_; 2 | 3 | [TestTag(TestTags.Fast)] 4 | public class SimpleNoApplicationTest_Should_ 5 | { 6 | public static void AlwaysPass() => true.Should().BeTrue(); 7 | 8 | [Skip("Demonstrates skip attribute")] 9 | public static void SkipExample() => true.Should().BeFalse(); 10 | 11 | [TestTag(TestTags.Fast)] 12 | public static void TagExample() => true.Should().BeTrue(); 13 | 14 | [Input(5, 3, 2)] 15 | [Input(8, 5, 3)] 16 | public static void Subtract(int aX, int aY, int aExpectedDifference) 17 | { 18 | int result = aX - aY; 19 | result.Should().Be(aExpectedDifference); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Tests/TimeWarp.State.Tests/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using FluentAssertions; 2 | global using MediatR; 3 | global using System.Collections.Concurrent; 4 | global using System.Linq.Expressions; 5 | global using System.Reflection; 6 | global using TimeWarp.Fixie; 7 | global using TimeWarp.State; 8 | -------------------------------------------------------------------------------- /Tests/TimeWarp.State.Tests/TestingConvention.cs: -------------------------------------------------------------------------------- 1 | namespace TimeWarp.State.Tests; 2 | 3 | class TestingConvention : TimeWarp.Fixie.TestingConvention { } 4 | -------------------------------------------------------------------------------- /Tests/TimeWarp.State.Tests/TimeWarp.State.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | enable 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /TimeWarp.State.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True -------------------------------------------------------------------------------- /aider.instructions.md: -------------------------------------------------------------------------------- 1 | - line_endings: lf 2 | - do not apologize 3 | 4 | # CSharp 5 | - use explicit types where not obvious 6 | -------------------------------------------------------------------------------- /cline.ps1: -------------------------------------------------------------------------------- 1 | # Define the list of files to concatenate 2 | $files = @( 3 | ".ai/00-confirmation.md", 4 | ".ai/01-user.md", 5 | ".ai/02-development-process.md", 6 | ".ai/03-environment.md", 7 | ".ai/04-csharp-coding-standards.md", 8 | ".ai/05-dotnet-conventions.md" 9 | ) 10 | 11 | # Initialize an empty array to store the content 12 | $content = @() 13 | 14 | # Loop through each file 15 | foreach ($file in $files) { 16 | # Read the file content and add it to the array 17 | $content += (Get-Content $file -Raw) # + "`n" 18 | } 19 | 20 | # Join all the content into a single string 21 | $combinedContent = $content -join "`n" 22 | 23 | # Output the combined content (you can redirect this to a file if needed) 24 | $combinedContent 25 | 26 | # Optionally, save to a file 27 | $combinedContent | Out-File -FilePath ".cline-rules" -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "8.0.100", 4 | "rollForward": "latestFeature", 5 | "allowPrerelease": false 6 | } 7 | } 8 | --------------------------------------------------------------------------------