Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dotnet/aspnetcore.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoranhthidao <107068384+anhthidao@users.noreply.github.com>2022-06-29 02:46:42 +0300
committerGitHub <noreply@github.com>2022-06-29 02:46:42 +0300
commit47e9ecb891955246a50023e18eb86ed217ef0c3a (patch)
tree76de3da4f13b9585632313e4242f6dca9875de45
parent3b624fa5adb70d3bea909686f5bcaf32fe0e405b (diff)
parentf4f4835635a812cfe24b724b8a1c4aab6cee8705 (diff)
Merge branch 'main' into WebApplicationBuilderAnalyzersWebApplicationBuilderAnalyzers
-rw-r--r--.azure/pipelines/jobs/default-build.yml4
-rw-r--r--.github/fabricbot.json4
-rw-r--r--.vsconfig2
-rw-r--r--AspNetCore.sln106
-rw-r--r--eng/ProjectReferences.props2
-rw-r--r--eng/SharedFramework.Local.props1
-rw-r--r--eng/TrimmableProjects.props3
-rw-r--r--eng/Version.Details.xml292
-rw-r--r--eng/Versions.props150
-rw-r--r--eng/common/templates/steps/send-to-helix.yml10
-rw-r--r--eng/common/tools.ps12
-rwxr-xr-xeng/common/tools.sh2
-rw-r--r--eng/scripts/vs.17.buildtools.intpreview.json2
-rw-r--r--eng/scripts/vs.17.buildtools.json2
-rw-r--r--eng/scripts/vs.17.buildtools.preview.json2
-rw-r--r--eng/scripts/vs.17.intpreview.json2
-rw-r--r--eng/scripts/vs.17.json2
-rw-r--r--eng/scripts/vs.17.preview.json2
-rw-r--r--eng/targets/Cpp.Common.props11
-rw-r--r--global.json12
-rw-r--r--src/Components/Authorization/src/AuthorizeRouteView.cs2
-rw-r--r--src/Components/Components.slnf3
-rw-r--r--src/Components/Components/src/RouteView.cs1
-rw-r--r--src/Components/Components/src/Routing/RouteTableFactory.cs1
-rw-r--r--src/Components/CustomElements/src/JSComponentConfigurationExtensions.cs21
-rw-r--r--src/Components/CustomElements/src/Microsoft.AspNetCore.Components.CustomElements.csproj86
-rw-r--r--src/Components/CustomElements/src/PublicAPI.Shipped.txt1
-rw-r--r--src/Components/CustomElements/src/PublicAPI.Unshipped.txt3
-rw-r--r--src/Components/CustomElements/src/js/BlazorCustomElements.ts156
-rw-r--r--src/Components/CustomElements/src/js/babel.config.json3
-rw-r--r--src/Components/CustomElements/src/js/dist/.gitignore3
-rw-r--r--src/Components/CustomElements/src/js/package.json29
-rw-r--r--src/Components/CustomElements/src/js/tsconfig.json11
-rw-r--r--src/Components/CustomElements/src/js/webpack.config.js70
-rw-r--r--src/Components/CustomElements/src/js/yarn.lock2797
-rw-r--r--src/Components/Web.JS/dist/Release/blazor.server.js2
-rw-r--r--src/Components/Web.JS/dist/Release/blazor.webview.js2
-rw-r--r--src/Components/Web.JS/src/Virtualize.ts6
-rw-r--r--src/Components/Web/src/JSComponents/JSComponentConfigurationStore.cs18
-rw-r--r--src/Components/Web/src/JSComponents/JSComponentInterop.cs2
-rw-r--r--src/Components/Web/src/PublicAPI.Unshipped.txt2
-rw-r--r--src/Components/Web/src/Virtualization/Virtualize.cs16
-rw-r--r--src/Components/test/E2ETest/Tests/BindTest.cs15
-rw-r--r--src/Components/test/E2ETest/Tests/CustomElementsTest.cs128
-rw-r--r--src/Components/test/E2ETest/Tests/FormsInputDateTest.cs6
-rw-r--r--src/Components/test/E2ETest/Tests/GlobalizationTest.cs3
-rw-r--r--src/Components/test/E2ETest/Tests/RoutingTest.cs1
-rw-r--r--src/Components/test/E2ETest/Tests/VirtualizationTest.cs24
-rw-r--r--src/Components/test/E2ETest/yarn.lock92
-rw-r--r--src/Components/test/testassets/BasicTestApp/BasicTestApp.csproj3
-rw-r--r--src/Components/test/testassets/BasicTestApp/CustomElementParameterTypes.razor48
-rw-r--r--src/Components/test/testassets/BasicTestApp/CustomElementsComponent.razor75
-rw-r--r--src/Components/test/testassets/BasicTestApp/Index.razor2
-rw-r--r--src/Components/test/testassets/BasicTestApp/Program.cs1
-rw-r--r--src/Components/test/testassets/BasicTestApp/VirtualizationTable.razor28
-rw-r--r--src/Components/test/testassets/BasicTestApp/wwwroot/index.html4
-rw-r--r--src/Components/test/testassets/BasicTestApp/wwwroot/js/customElementTests.js10
-rw-r--r--src/Framework/AspNetCoreAnalyzers/src/Analyzers/DiagnosticDescriptors.cs10
-rw-r--r--src/Framework/AspNetCoreAnalyzers/src/Analyzers/WebApplicationBuilder/WebApplicationBuilderAnalyzer.cs40
-rw-r--r--src/Framework/AspNetCoreAnalyzers/src/Analyzers/WebApplicationBuilder/WellKnownTypes.cs2
-rw-r--r--src/Framework/AspNetCoreAnalyzers/test/WebApplicationBuilder/DisallowConfigureHostLoggingTest.cs278
-rw-r--r--src/Framework/Framework.slnf1
-rw-r--r--src/Framework/test/TestData.cs2
-rw-r--r--src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/GrpcJsonTranscodingServiceExtensions.cs24
-rw-r--r--src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/Binding/JsonTranscodingProviderServiceBinder.cs19
-rw-r--r--src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/CallHandlers/CallHandlerDescriptorInfo.cs5
-rw-r--r--src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/CallHandlers/ServerCallHandlerBase.cs5
-rw-r--r--src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/JsonRequestHelpers.cs2
-rw-r--r--src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/JsonTranscodingRouteAdapter.cs210
-rw-r--r--src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Microsoft.AspNetCore.Grpc.JsonTranscoding.csproj2
-rw-r--r--src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/PublicAPI.Unshipped.txt4
-rw-r--r--src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.Swagger/Internal/GrpcJsonTranscodingDescriptionProvider.cs3
-rw-r--r--src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.Swagger/Microsoft.AspNetCore.Grpc.Swagger.csproj2
-rw-r--r--src/Grpc/JsonTranscoding/src/Shared/HttpRoutePattern.cs35
-rw-r--r--src/Grpc/JsonTranscoding/src/Shared/HttpRoutePatternParser.cs349
-rw-r--r--src/Grpc/JsonTranscoding/src/Shared/ServiceDescriptorHelpers.cs35
-rw-r--r--src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.IntegrationTests/Infrastructure/TestHelpers.cs5
-rw-r--r--src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.IntegrationTests/RouteTests.cs108
-rw-r--r--src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/HttpRoutePatternParserTests.cs325
-rw-r--r--src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/Infrastructure/TestHelpers.cs5
-rw-r--r--src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/JsonTranscodingRouteAdapterTests.cs223
-rw-r--r--src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/JsonTranscodingServerCallContextTests.cs4
-rw-r--r--src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/JsonTranscodingServiceMethodProviderTests.cs3
-rw-r--r--src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/UnaryServerCallHandlerTests.cs18
-rw-r--r--src/Grpc/JsonTranscoding/test/testassets/IntegrationTestsWebsite/Protos/greet.proto28
-rw-r--r--src/Hosting/Hosting/src/Internal/WebHost.cs2
-rw-r--r--src/Hosting/test/FunctionalTests/LinkedApplicationTests.cs1
-rw-r--r--src/Http/Authentication.Core/src/AuthenticationService.cs11
-rw-r--r--src/Http/Routing/src/EndpointMiddleware.cs35
-rw-r--r--src/Http/Routing/test/UnitTests/EndpointMiddlewareTest.cs29
-rw-r--r--src/Identity/UI/src/Microsoft.AspNetCore.Identity.UI.csproj21
-rw-r--r--src/Installers/Windows/AspNetCoreModule-Setup/CustomAction/aspnetcoreCA.vcxproj5
-rw-r--r--src/Installers/Windows/AspNetCoreModule-Setup/IIS-Setup/IIS-Common/lib/IISSetup.CommonLib.vcxproj2
-rw-r--r--src/Installers/Windows/AspNetCoreModule-Setup/IIS-Setup/iisca/lib/iisca.vcxproj5
-rw-r--r--src/Installers/Windows/AspNetCoreModule-Setup/build/settings/common.props2
-rw-r--r--src/Middleware/CORS/test/testassets/CorsMiddlewareWebSite/Properties/launchSettings.json2
-rw-r--r--src/Middleware/Diagnostics/test/testassets/DatabaseErrorPageSample/Properties/launchSettings.json2
-rw-r--r--src/Middleware/Diagnostics/test/testassets/DeveloperExceptionPageSample/Properties/launchSettings.json2
-rw-r--r--src/Middleware/Diagnostics/test/testassets/ExceptionHandlerSample/Properties/launchSettings.json2
-rw-r--r--src/Middleware/Diagnostics/test/testassets/StatusCodePagesSample/Properties/launchSettings.json2
-rw-r--r--src/Middleware/Diagnostics/test/testassets/WelcomePageSample/Properties/launchSettings.json2
-rw-r--r--src/Middleware/HttpLogging/src/W3CLoggingMiddleware.cs34
-rw-r--r--src/Middleware/HttpLogging/test/W3CLoggingMiddlewareTests.cs36
-rw-r--r--src/Middleware/Localization/testassets/LocalizationWebsite/Properties/launchSettings.json2
-rw-r--r--src/Middleware/Middleware.slnf3
-rw-r--r--src/Middleware/OutputCaching/OutputCaching.slnf10
-rw-r--r--src/Middleware/OutputCaching/samples/OutputCachingSample/.vscode/launch.json35
-rw-r--r--src/Middleware/OutputCaching/samples/OutputCachingSample/.vscode/tasks.json41
-rw-r--r--src/Middleware/OutputCaching/samples/OutputCachingSample/Gravatar.cs17
-rw-r--r--src/Middleware/OutputCaching/samples/OutputCachingSample/OutputCachingSample.csproj14
-rw-r--r--src/Middleware/OutputCaching/samples/OutputCachingSample/Properties/launchSettings.json27
-rw-r--r--src/Middleware/OutputCaching/samples/OutputCachingSample/README.md6
-rw-r--r--src/Middleware/OutputCaching/samples/OutputCachingSample/Startup.cs75
-rw-r--r--src/Middleware/OutputCaching/samples/OutputCachingSample/appsettings.Development.json10
-rw-r--r--src/Middleware/OutputCaching/samples/OutputCachingSample/appsettings.json10
-rw-r--r--src/Middleware/OutputCaching/src/CacheEntryHelpers.cs59
-rw-r--r--src/Middleware/OutputCaching/src/CacheVaryByRules.cs37
-rw-r--r--src/Middleware/OutputCaching/src/CachedResponseBody.cs53
-rw-r--r--src/Middleware/OutputCaching/src/DispatcherExtensions.cs89
-rw-r--r--src/Middleware/OutputCaching/src/IOutputCacheFeature.cs15
-rw-r--r--src/Middleware/OutputCaching/src/IOutputCacheKeyProvider.cs14
-rw-r--r--src/Middleware/OutputCaching/src/IOutputCachePolicy.cs30
-rw-r--r--src/Middleware/OutputCaching/src/IOutputCacheStore.cs36
-rw-r--r--src/Middleware/OutputCaching/src/ISystemClock.cs15
-rw-r--r--src/Middleware/OutputCaching/src/LoggerExtensions.cs70
-rw-r--r--src/Middleware/OutputCaching/src/Memory/MemoryOutputCacheStore.cs96
-rw-r--r--src/Middleware/OutputCaching/src/Microsoft.AspNetCore.OutputCaching.csproj26
-rw-r--r--src/Middleware/OutputCaching/src/OutputCacheApplicationBuilderExtensions.cs23
-rw-r--r--src/Middleware/OutputCaching/src/OutputCacheAttribute.cs88
-rw-r--r--src/Middleware/OutputCaching/src/OutputCacheContext.cs85
-rw-r--r--src/Middleware/OutputCaching/src/OutputCacheEntry.cs34
-rw-r--r--src/Middleware/OutputCaching/src/OutputCacheEntryFormatter.cs81
-rw-r--r--src/Middleware/OutputCaching/src/OutputCacheFeature.cs14
-rw-r--r--src/Middleware/OutputCaching/src/OutputCacheKeyProvider.cs190
-rw-r--r--src/Middleware/OutputCaching/src/OutputCacheMiddleware.cs620
-rw-r--r--src/Middleware/OutputCaching/src/OutputCacheOptions.cs96
-rw-r--r--src/Middleware/OutputCaching/src/OutputCacheOptionsSetup.cs21
-rw-r--r--src/Middleware/OutputCaching/src/OutputCachePolicyBuilder.cs244
-rw-r--r--src/Middleware/OutputCaching/src/OutputCacheServiceCollectionExtensions.cs58
-rw-r--r--src/Middleware/OutputCaching/src/Policies/CompositePolicy.cs48
-rw-r--r--src/Middleware/OutputCaching/src/Policies/DefaultPolicy.cs87
-rw-r--r--src/Middleware/OutputCaching/src/Policies/EnableCachePolicy.cs37
-rw-r--r--src/Middleware/OutputCaching/src/Policies/ExpirationPolicy.cs41
-rw-r--r--src/Middleware/OutputCaching/src/Policies/LockingPolicy.cs47
-rw-r--r--src/Middleware/OutputCaching/src/Policies/NamedPolicy.cs69
-rw-r--r--src/Middleware/OutputCaching/src/Policies/NoLookupPolicy.cs36
-rw-r--r--src/Middleware/OutputCaching/src/Policies/NoStorePolicy.cs36
-rw-r--r--src/Middleware/OutputCaching/src/Policies/OutputCacheConventionBuilderExtensions.cs81
-rw-r--r--src/Middleware/OutputCaching/src/Policies/PredicatePolicy.cs76
-rw-r--r--src/Middleware/OutputCaching/src/Policies/TagsPolicy.cs44
-rw-r--r--src/Middleware/OutputCaching/src/Policies/TypedPolicy.cs52
-rw-r--r--src/Middleware/OutputCaching/src/Policies/VaryByHeaderPolicy.cs68
-rw-r--r--src/Middleware/OutputCaching/src/Policies/VaryByQueryPolicy.cs72
-rw-r--r--src/Middleware/OutputCaching/src/Policies/VaryByValuePolicy.cs82
-rw-r--r--src/Middleware/OutputCaching/src/PublicAPI.Shipped.txt1
-rw-r--r--src/Middleware/OutputCaching/src/PublicAPI.Unshipped.txt90
-rw-r--r--src/Middleware/OutputCaching/src/Resources.resx123
-rw-r--r--src/Middleware/OutputCaching/src/Serialization/FormatterEntry.cs12
-rw-r--r--src/Middleware/OutputCaching/src/Serialization/FormatterEntrySerializerContext.cs12
-rw-r--r--src/Middleware/OutputCaching/src/Streams/OutputCacheStream.cs187
-rw-r--r--src/Middleware/OutputCaching/src/Streams/SegmentWriteStream.cs199
-rw-r--r--src/Middleware/OutputCaching/src/Streams/StreamUtilities.cs13
-rw-r--r--src/Middleware/OutputCaching/src/StringBuilderExtensions.cs23
-rw-r--r--src/Middleware/OutputCaching/src/SystemClock.cs15
-rw-r--r--src/Middleware/OutputCaching/startvs.cmd3
-rw-r--r--src/Middleware/OutputCaching/test/CachedResponseBodyTests.cs122
-rw-r--r--src/Middleware/OutputCaching/test/MemoryOutputCacheStoreTests.cs155
-rw-r--r--src/Middleware/OutputCaching/test/Microsoft.AspNetCore.OutputCaching.Tests.csproj18
-rw-r--r--src/Middleware/OutputCaching/test/OutputCacheEntryFormatterTests.cs66
-rw-r--r--src/Middleware/OutputCaching/test/OutputCacheKeyProviderTests.cs214
-rw-r--r--src/Middleware/OutputCaching/test/OutputCacheMiddlewareTests.cs802
-rw-r--r--src/Middleware/OutputCaching/test/OutputCachePoliciesTests.cs288
-rw-r--r--src/Middleware/OutputCaching/test/OutputCachePolicyProviderTests.cs410
-rw-r--r--src/Middleware/OutputCaching/test/OutputCacheTests.cs985
-rw-r--r--src/Middleware/OutputCaching/test/SegmentWriteStreamTests.cs105
-rw-r--r--src/Middleware/OutputCaching/test/TestDocument.txt1
-rw-r--r--src/Middleware/OutputCaching/test/TestUtils.cs374
-rw-r--r--src/Middleware/RequestDecompression/src/Microsoft.AspNetCore.RequestDecompression.csproj1
-rw-r--r--src/Middleware/StaticFiles/src/DefaultFilesMiddleware.cs2
-rw-r--r--src/Middleware/StaticFiles/src/DirectoryBrowserMiddleware.cs4
-rw-r--r--src/Middleware/StaticFiles/src/StaticFileMiddleware.cs6
-rw-r--r--src/Middleware/StaticFiles/test/UnitTests/DefaultFilesMiddlewareTests.cs47
-rw-r--r--src/Middleware/StaticFiles/test/UnitTests/DirectoryBrowserMiddlewareTests.cs41
-rw-r--r--src/Middleware/StaticFiles/test/UnitTests/StaticFileMiddlewareTests.cs82
-rw-r--r--src/Mvc/Mvc.Core/src/Filters/IOutputCacheFilter.cs11
-rw-r--r--src/Mvc/Mvc.Core/src/Filters/OutputCacheFilter.cs56
-rw-r--r--src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.csproj3
-rw-r--r--src/Mvc/Mvc.Core/src/Resources.resx5
-rw-r--r--src/Mvc/Mvc.Core/test/Formatters/SystemTextJsonInputFormatterTest.cs1
-rw-r--r--src/Mvc/Mvc.slnf1
-rw-r--r--src/Mvc/samples/MvcSandbox/MvcSandbox.csproj2
-rwxr-xr-x[-rw-r--r--]src/OpenApi/build.sh0
-rw-r--r--src/OpenApi/src/OpenApiEndpointConventionBuilderExtensions.cs (renamed from src/OpenApi/src/OpenApiRouteHandlerBuilderExtensions.cs)14
-rw-r--r--src/OpenApi/src/PublicAPI.Unshipped.txt6
-rw-r--r--src/ProjectTemplates/BlazorTemplates.Tests/yarn.lock46
-rw-r--r--src/ProjectTemplates/ProjectTemplates.slnf7
-rw-r--r--src/ProjectTemplates/ProjectTemplatesNoDeps.slnf3
-rw-r--r--src/ProjectTemplates/Shared/BlazorTemplateTest.cs (renamed from src/ProjectTemplates/test/Templates.Blazor.Tests/BlazorTemplateTest.cs)0
-rw-r--r--src/ProjectTemplates/Shared/Project.cs2
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/EmptyBlazorServerWeb-CSharp.csproj.in11
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/EmptyComponentsWebAssembly-CSharp.Client.csproj.in33
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/EmptyComponentsWebAssembly-CSharp.Server.csproj.in21
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/EmptyComponentsWebAssembly-CSharp.Shared.csproj.in12
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj4
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/.template.config/template.json7
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/dotnetcli.host.json35
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/ide.host.json18
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/ide/icon.pngbin0 -> 1148 bytes
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.cs.json17
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.de.json17
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.en.json17
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.es.json17
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.fr.json17
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.it.json17
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.ja.json17
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.ko.json17
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.pl.json17
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.pt-BR.json17
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.ru.json17
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.tr.json17
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.zh-Hans.json17
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.zh-Hant.json17
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/template.json198
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/App.razor12
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/MainLayout.razor3
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/Pages/Index.razor3
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/Pages/_Host.cshtml30
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/Program.cs27
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/Properties/launchSettings.json43
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/_Imports.razor4
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/appsettings.Development.json9
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/appsettings.json9
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/wwwroot/css/site.css28
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/dotnetcli.host.json42
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/ide.host.json29
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/ide/icon.pngbin0 -> 1148 bytes
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.cs.json23
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.de.json23
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.en.json23
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.es.json23
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.fr.json23
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.it.json23
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.ja.json23
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.ko.json23
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.pl.json23
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.pt-BR.json23
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.ru.json23
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.tr.json23
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.zh-Hans.json23
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.zh-Hant.json23
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/template.json276
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/App.razor12
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/MainLayout.razor3
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/Pages/Index.razor3
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/Program.cs22
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/Properties/launchSettings.json46
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/_Imports.razor11
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/wwwroot/css/app.css28
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/wwwroot/icon-512.pngbin0 -> 6311 bytes
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/wwwroot/index.html27
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/wwwroot/manifest.json16
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/wwwroot/service-worker.js4
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/wwwroot/service-worker.published.js59
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/EmptyComponentsWebAssembly-CSharp.sln64
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Server/Program.cs33
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Server/Properties/launchSettings.json44
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Server/appsettings.Development.json8
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Server/appsettings.json9
-rw-r--r--src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Shared/SharedClass.cs1
-rw-r--r--src/ProjectTemplates/test/Templates.Blazor.Server.Tests/.gitattributes (renamed from src/ProjectTemplates/test/Templates.Blazor.Tests/.gitattributes)0
-rw-r--r--src/ProjectTemplates/test/Templates.Blazor.Server.Tests/BlazorServerTemplateTest.cs (renamed from src/ProjectTemplates/test/Templates.Blazor.Tests/BlazorServerTemplateTest.cs)0
-rw-r--r--src/ProjectTemplates/test/Templates.Blazor.Server.Tests/EmptyBlazorServerTemplateTest.cs31
-rw-r--r--src/ProjectTemplates/test/Templates.Blazor.Server.Tests/Templates.Blazor.Server.Tests.csproj (renamed from src/ProjectTemplates/test/Templates.Blazor.Tests/Templates.Blazor.Tests.csproj)2
-rw-r--r--src/ProjectTemplates/test/Templates.Blazor.WebAssembly.Tests/BlazorWasmTemplateTest.cs (renamed from src/ProjectTemplates/test/Templates.Blazor.Tests/BlazorWasmTemplateTest.cs)0
-rw-r--r--src/ProjectTemplates/test/Templates.Blazor.WebAssembly.Tests/EmptyBlazorWasmTemplateTest.cs114
-rw-r--r--src/ProjectTemplates/test/Templates.Blazor.WebAssembly.Tests/Templates.Blazor.WebAssembly.Tests.csproj68
-rw-r--r--src/ProjectTemplates/test/Templates.Tests/WorkerTemplateTest.cs2
-rw-r--r--src/ProjectTemplates/test/Templates.Tests/template-baselines.json459
-rw-r--r--src/ProjectTemplates/test/Templates.Tests/yarn.lock46
-rw-r--r--src/Security/Authentication/Core/src/AuthenticationConfigurationProviderExtensions.cs2
-rw-r--r--src/Security/Authentication/Negotiate/src/Internal/INegotiateStateFactory.cs3
-rw-r--r--src/Security/Authentication/Negotiate/src/Internal/NegotiateState.cs88
-rw-r--r--src/Security/Authentication/Negotiate/src/Internal/NegotiateStateFactory.cs12
-rw-r--r--src/Security/Authentication/Negotiate/src/Internal/ReflectedNegotiateState.cs210
-rw-r--r--src/Security/Authentication/Negotiate/src/Internal/ReflectedNegotiateStateFactory.cs15
-rw-r--r--src/Security/Authentication/Negotiate/src/Internal/SecurityStatusPalErrorCode.cs53
-rw-r--r--src/Security/Authentication/Negotiate/src/NegotiateExtensions.cs5
-rw-r--r--src/Security/Authentication/Negotiate/src/NegotiateHandler.cs2
-rw-r--r--src/Security/Authentication/Negotiate/src/NegotiateOptions.cs2
-rw-r--r--src/Security/Authentication/test/AuthenticationMiddlewareTests.cs10
-rw-r--r--src/Servers/HttpSys/test/FunctionalTests/Http2Tests.cs2
-rw-r--r--src/Servers/IIS/AspNetCoreModuleV2/CommonLibTests/CommonLibTests.vcxproj8
-rw-r--r--src/Servers/IIS/build/Build.Common.Settings4
-rw-r--r--src/Servers/Kestrel/Transport.Quic/test/QuicConnectionListenerTests.cs10
-rw-r--r--src/Servers/Kestrel/test/Interop.FunctionalTests/Http3/Http3RequestTests.cs1
-rw-r--r--src/Servers/Kestrel/test/Interop.FunctionalTests/Http3/Http3TlsTests.cs17
-rw-r--r--src/Servers/test/FunctionalTests/HelloWorldTest.cs1
-rw-r--r--src/Shared/E2ETesting/selenium-config.json2
-rw-r--r--src/Shared/TaskToApm.cs2
-rw-r--r--src/SignalR/clients/ts/FunctionalTests/yarn.lock13
-rw-r--r--src/Tools/Tools.slnf1
-rw-r--r--src/Tools/dotnet-user-jwts/src/Commands/ClearCommand.cs2
-rw-r--r--src/Tools/dotnet-user-jwts/src/Helpers/DevJwtCliHelpers.cs23
-rw-r--r--src/Tools/dotnet-user-jwts/src/Helpers/JwtAuthenticationSchemeSettings.cs17
-rw-r--r--src/Tools/dotnet-user-jwts/test/UserJwtsTestFixture.cs38
-rw-r--r--src/Tools/dotnet-user-jwts/test/UserJwtsTests.cs107
m---------src/submodules/googletest0
307 files changed, 16289 insertions, 895 deletions
diff --git a/.azure/pipelines/jobs/default-build.yml b/.azure/pipelines/jobs/default-build.yml
index 7e2b785576..663b5abd87 100644
--- a/.azure/pipelines/jobs/default-build.yml
+++ b/.azure/pipelines/jobs/default-build.yml
@@ -120,11 +120,11 @@ jobs:
${{ if eq(parameters.agentOs, 'Windows') }}:
${{ if eq(variables['System.TeamProject'], 'public') }}:
name: NetCore1ESPool-Public
- demands: ImageOverride -equals 1es-windows-2019-open
+ demands: ImageOverride -equals 1es-windows-2022-open
${{ if eq(variables['System.TeamProject'], 'internal') }}:
name: NetCore1ESPool-Internal
# Visual Studio Enterprise - contains some stuff, like SQL Server and IIS Express, that we use for testing
- demands: ImageOverride -equals Build.Server.Amd64.VS2019
+ demands: ImageOverride -equals 1es-windows-2022
${{ if ne(parameters.container, '') }}:
container: ${{ parameters.container }}
${{ if ne(parameters.disableComponentGovernance, '') }}:
diff --git a/.github/fabricbot.json b/.github/fabricbot.json
index 3a8346df58..25a78c9e9f 100644
--- a/.github/fabricbot.json
+++ b/.github/fabricbot.json
@@ -2562,7 +2562,7 @@
{
"name": "addMilestone",
"parameters": {
- "milestoneName": "7.0-preview6"
+ "milestoneName": "7.0-preview7"
}
}
],
@@ -3375,4 +3375,4 @@
}
],
"userGroups": []
-} \ No newline at end of file
+}
diff --git a/.vsconfig b/.vsconfig
index 3dff770c10..e0fa83eab9 100644
--- a/.vsconfig
+++ b/.vsconfig
@@ -12,7 +12,7 @@
"Microsoft.VisualStudio.Component.VC.14.29.16.11.x86.x64",
"Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL.ARM64",
"Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL",
- "Microsoft.VisualStudio.Component.Windows10SDK.18362",
+ "Microsoft.VisualStudio.Component.Windows10SDK.19041",
"Microsoft.VisualStudio.Workload.ManagedDesktop",
"Microsoft.VisualStudio.Workload.NativeDesktop",
"Microsoft.VisualStudio.Workload.NetWeb",
diff --git a/AspNetCore.sln b/AspNetCore.sln
index 3aff0dd693..28b0be916a 100644
--- a/AspNetCore.sln
+++ b/AspNetCore.sln
@@ -1654,6 +1654,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Compon
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.SdkAnalyzers.Tests", "src\Tools\SDK-Analyzers\Components\test\Microsoft.AspNetCore.Components.SdkAnalyzers.Tests.csproj", "{DC349A25-0DBF-4468-99E1-B95C22D3A7EF}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "OutputCaching", "OutputCaching", "{AA5ABFBC-177C-421E-B743-005E0FD1248B}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.OutputCaching", "src\Middleware\OutputCaching\src\Microsoft.AspNetCore.OutputCaching.csproj", "{5D5A3B60-A014-447C-9126-B1FA6C821C8D}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{B5AC1D8B-9D43-4261-AE0F-6B7574656F2C}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OutputCachingSample", "src\Middleware\OutputCaching\samples\OutputCachingSample\OutputCachingSample.csproj", "{C3FFA4E4-0E7E-4866-A15F-034245BFD800}"
+EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "RequestDecompression", "RequestDecompression", "{5465F96F-33D5-454E-9C40-494E58AEEE5D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.RequestDecompression.Tests", "src\Middleware\RequestDecompression\test\Microsoft.AspNetCore.RequestDecompression.Tests.csproj", "{97996D39-7722-4AFC-A41A-AD61CA7A413D}"
@@ -1700,6 +1708,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BuildAfterTargetingPack", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BuildAfterTargetingPack", "src\BuildAfterTargetingPack\BuildAfterTargetingPack.csproj", "{8FED7E65-A7DD-4F13-8980-BF03E77B6C85}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.OutputCaching.Tests", "src\Middleware\OutputCaching\test\Microsoft.AspNetCore.OutputCaching.Tests.csproj", "{046F43BC-BEE4-48B7-8C09-ED0A1054A2D7}"
+EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ResultsOfTGenerator", "src\Http\Http.Results\tools\ResultsOfTGenerator\ResultsOfTGenerator.csproj", "{9716D0D0-2251-44DD-8596-67D253EEF41C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "OpenApi", "OpenApi", "{2299CCD8-8F9C-4F2B-A633-9BF4DA81022B}"
@@ -1738,10 +1748,16 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestInfrastructure", "TestI
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Templates.Mvc.Tests", "src\ProjectTemplates\test\Templates.Mvc.Tests\Templates.Mvc.Tests.csproj", "{AA7445F5-BD28-400C-8507-E2E0D3CF7D7E}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Templates.Blazor.Tests", "src\ProjectTemplates\test\Templates.Blazor.Tests\Templates.Blazor.Tests.csproj", "{281BF9DB-7B8A-446B-9611-10A60903F125}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Templates.Blazor.Server.Tests", "src\ProjectTemplates\test\Templates.Blazor.Server.Tests\Templates.Blazor.Server.Tests.csproj", "{281BF9DB-7B8A-446B-9611-10A60903F125}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "stress", "stress", "{A5946454-4788-4871-8F23-A9471D55F115}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.CustomElements", "src\Components\CustomElements\src\Microsoft.AspNetCore.Components.CustomElements.csproj", "{76C3E22D-092B-4E8A-81F0-DCF071BFF4CD}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CustomElements", "CustomElements", "{0BB58FB6-8B66-4C6D-BA8A-DF3AFAF9AB8F}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Templates.Blazor.WebAssembly.Tests", "src\ProjectTemplates\test\Templates.Blazor.WebAssembly.Tests\Templates.Blazor.WebAssembly.Tests.csproj", "{7CA0A9AF-9088-471C-B0B6-EBF43F21D3B9}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -9991,6 +10007,38 @@ Global
{DC349A25-0DBF-4468-99E1-B95C22D3A7EF}.Release|x64.Build.0 = Release|Any CPU
{DC349A25-0DBF-4468-99E1-B95C22D3A7EF}.Release|x86.ActiveCfg = Release|Any CPU
{DC349A25-0DBF-4468-99E1-B95C22D3A7EF}.Release|x86.Build.0 = Release|Any CPU
+ {5D5A3B60-A014-447C-9126-B1FA6C821C8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5D5A3B60-A014-447C-9126-B1FA6C821C8D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5D5A3B60-A014-447C-9126-B1FA6C821C8D}.Debug|arm64.ActiveCfg = Debug|Any CPU
+ {5D5A3B60-A014-447C-9126-B1FA6C821C8D}.Debug|arm64.Build.0 = Debug|Any CPU
+ {5D5A3B60-A014-447C-9126-B1FA6C821C8D}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {5D5A3B60-A014-447C-9126-B1FA6C821C8D}.Debug|x64.Build.0 = Debug|Any CPU
+ {5D5A3B60-A014-447C-9126-B1FA6C821C8D}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {5D5A3B60-A014-447C-9126-B1FA6C821C8D}.Debug|x86.Build.0 = Debug|Any CPU
+ {5D5A3B60-A014-447C-9126-B1FA6C821C8D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5D5A3B60-A014-447C-9126-B1FA6C821C8D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5D5A3B60-A014-447C-9126-B1FA6C821C8D}.Release|arm64.ActiveCfg = Release|Any CPU
+ {5D5A3B60-A014-447C-9126-B1FA6C821C8D}.Release|arm64.Build.0 = Release|Any CPU
+ {5D5A3B60-A014-447C-9126-B1FA6C821C8D}.Release|x64.ActiveCfg = Release|Any CPU
+ {5D5A3B60-A014-447C-9126-B1FA6C821C8D}.Release|x64.Build.0 = Release|Any CPU
+ {5D5A3B60-A014-447C-9126-B1FA6C821C8D}.Release|x86.ActiveCfg = Release|Any CPU
+ {5D5A3B60-A014-447C-9126-B1FA6C821C8D}.Release|x86.Build.0 = Release|Any CPU
+ {C3FFA4E4-0E7E-4866-A15F-034245BFD800}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C3FFA4E4-0E7E-4866-A15F-034245BFD800}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C3FFA4E4-0E7E-4866-A15F-034245BFD800}.Debug|arm64.ActiveCfg = Debug|Any CPU
+ {C3FFA4E4-0E7E-4866-A15F-034245BFD800}.Debug|arm64.Build.0 = Debug|Any CPU
+ {C3FFA4E4-0E7E-4866-A15F-034245BFD800}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {C3FFA4E4-0E7E-4866-A15F-034245BFD800}.Debug|x64.Build.0 = Debug|Any CPU
+ {C3FFA4E4-0E7E-4866-A15F-034245BFD800}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {C3FFA4E4-0E7E-4866-A15F-034245BFD800}.Debug|x86.Build.0 = Debug|Any CPU
+ {C3FFA4E4-0E7E-4866-A15F-034245BFD800}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C3FFA4E4-0E7E-4866-A15F-034245BFD800}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C3FFA4E4-0E7E-4866-A15F-034245BFD800}.Release|arm64.ActiveCfg = Release|Any CPU
+ {C3FFA4E4-0E7E-4866-A15F-034245BFD800}.Release|arm64.Build.0 = Release|Any CPU
+ {C3FFA4E4-0E7E-4866-A15F-034245BFD800}.Release|x64.ActiveCfg = Release|Any CPU
+ {C3FFA4E4-0E7E-4866-A15F-034245BFD800}.Release|x64.Build.0 = Release|Any CPU
+ {C3FFA4E4-0E7E-4866-A15F-034245BFD800}.Release|x86.ActiveCfg = Release|Any CPU
+ {C3FFA4E4-0E7E-4866-A15F-034245BFD800}.Release|x86.Build.0 = Release|Any CPU
{97996D39-7722-4AFC-A41A-AD61CA7A413D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{97996D39-7722-4AFC-A41A-AD61CA7A413D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{97996D39-7722-4AFC-A41A-AD61CA7A413D}.Debug|arm64.ActiveCfg = Debug|Any CPU
@@ -10215,6 +10263,22 @@ Global
{8FED7E65-A7DD-4F13-8980-BF03E77B6C85}.Release|x64.Build.0 = Release|Any CPU
{8FED7E65-A7DD-4F13-8980-BF03E77B6C85}.Release|x86.ActiveCfg = Release|Any CPU
{8FED7E65-A7DD-4F13-8980-BF03E77B6C85}.Release|x86.Build.0 = Release|Any CPU
+ {046F43BC-BEE4-48B7-8C09-ED0A1054A2D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {046F43BC-BEE4-48B7-8C09-ED0A1054A2D7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {046F43BC-BEE4-48B7-8C09-ED0A1054A2D7}.Debug|arm64.ActiveCfg = Debug|Any CPU
+ {046F43BC-BEE4-48B7-8C09-ED0A1054A2D7}.Debug|arm64.Build.0 = Debug|Any CPU
+ {046F43BC-BEE4-48B7-8C09-ED0A1054A2D7}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {046F43BC-BEE4-48B7-8C09-ED0A1054A2D7}.Debug|x64.Build.0 = Debug|Any CPU
+ {046F43BC-BEE4-48B7-8C09-ED0A1054A2D7}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {046F43BC-BEE4-48B7-8C09-ED0A1054A2D7}.Debug|x86.Build.0 = Debug|Any CPU
+ {046F43BC-BEE4-48B7-8C09-ED0A1054A2D7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {046F43BC-BEE4-48B7-8C09-ED0A1054A2D7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {046F43BC-BEE4-48B7-8C09-ED0A1054A2D7}.Release|arm64.ActiveCfg = Release|Any CPU
+ {046F43BC-BEE4-48B7-8C09-ED0A1054A2D7}.Release|arm64.Build.0 = Release|Any CPU
+ {046F43BC-BEE4-48B7-8C09-ED0A1054A2D7}.Release|x64.ActiveCfg = Release|Any CPU
+ {046F43BC-BEE4-48B7-8C09-ED0A1054A2D7}.Release|x64.Build.0 = Release|Any CPU
+ {046F43BC-BEE4-48B7-8C09-ED0A1054A2D7}.Release|x86.ActiveCfg = Release|Any CPU
+ {046F43BC-BEE4-48B7-8C09-ED0A1054A2D7}.Release|x86.Build.0 = Release|Any CPU
{9716D0D0-2251-44DD-8596-67D253EEF41C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9716D0D0-2251-44DD-8596-67D253EEF41C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9716D0D0-2251-44DD-8596-67D253EEF41C}.Debug|arm64.ActiveCfg = Debug|Any CPU
@@ -10423,6 +10487,38 @@ Global
{281BF9DB-7B8A-446B-9611-10A60903F125}.Release|x64.Build.0 = Release|Any CPU
{281BF9DB-7B8A-446B-9611-10A60903F125}.Release|x86.ActiveCfg = Release|Any CPU
{281BF9DB-7B8A-446B-9611-10A60903F125}.Release|x86.Build.0 = Release|Any CPU
+ {76C3E22D-092B-4E8A-81F0-DCF071BFF4CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {76C3E22D-092B-4E8A-81F0-DCF071BFF4CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {76C3E22D-092B-4E8A-81F0-DCF071BFF4CD}.Debug|arm64.ActiveCfg = Debug|Any CPU
+ {76C3E22D-092B-4E8A-81F0-DCF071BFF4CD}.Debug|arm64.Build.0 = Debug|Any CPU
+ {76C3E22D-092B-4E8A-81F0-DCF071BFF4CD}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {76C3E22D-092B-4E8A-81F0-DCF071BFF4CD}.Debug|x64.Build.0 = Debug|Any CPU
+ {76C3E22D-092B-4E8A-81F0-DCF071BFF4CD}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {76C3E22D-092B-4E8A-81F0-DCF071BFF4CD}.Debug|x86.Build.0 = Debug|Any CPU
+ {76C3E22D-092B-4E8A-81F0-DCF071BFF4CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {76C3E22D-092B-4E8A-81F0-DCF071BFF4CD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {76C3E22D-092B-4E8A-81F0-DCF071BFF4CD}.Release|arm64.ActiveCfg = Release|Any CPU
+ {76C3E22D-092B-4E8A-81F0-DCF071BFF4CD}.Release|arm64.Build.0 = Release|Any CPU
+ {76C3E22D-092B-4E8A-81F0-DCF071BFF4CD}.Release|x64.ActiveCfg = Release|Any CPU
+ {76C3E22D-092B-4E8A-81F0-DCF071BFF4CD}.Release|x64.Build.0 = Release|Any CPU
+ {76C3E22D-092B-4E8A-81F0-DCF071BFF4CD}.Release|x86.ActiveCfg = Release|Any CPU
+ {76C3E22D-092B-4E8A-81F0-DCF071BFF4CD}.Release|x86.Build.0 = Release|Any CPU
+ {7CA0A9AF-9088-471C-B0B6-EBF43F21D3B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7CA0A9AF-9088-471C-B0B6-EBF43F21D3B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7CA0A9AF-9088-471C-B0B6-EBF43F21D3B9}.Debug|arm64.ActiveCfg = Debug|Any CPU
+ {7CA0A9AF-9088-471C-B0B6-EBF43F21D3B9}.Debug|arm64.Build.0 = Debug|Any CPU
+ {7CA0A9AF-9088-471C-B0B6-EBF43F21D3B9}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {7CA0A9AF-9088-471C-B0B6-EBF43F21D3B9}.Debug|x64.Build.0 = Debug|Any CPU
+ {7CA0A9AF-9088-471C-B0B6-EBF43F21D3B9}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {7CA0A9AF-9088-471C-B0B6-EBF43F21D3B9}.Debug|x86.Build.0 = Debug|Any CPU
+ {7CA0A9AF-9088-471C-B0B6-EBF43F21D3B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7CA0A9AF-9088-471C-B0B6-EBF43F21D3B9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7CA0A9AF-9088-471C-B0B6-EBF43F21D3B9}.Release|arm64.ActiveCfg = Release|Any CPU
+ {7CA0A9AF-9088-471C-B0B6-EBF43F21D3B9}.Release|arm64.Build.0 = Release|Any CPU
+ {7CA0A9AF-9088-471C-B0B6-EBF43F21D3B9}.Release|x64.ActiveCfg = Release|Any CPU
+ {7CA0A9AF-9088-471C-B0B6-EBF43F21D3B9}.Release|x64.Build.0 = Release|Any CPU
+ {7CA0A9AF-9088-471C-B0B6-EBF43F21D3B9}.Release|x86.ActiveCfg = Release|Any CPU
+ {7CA0A9AF-9088-471C-B0B6-EBF43F21D3B9}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -11242,6 +11338,10 @@ Global
{CC45FA2D-128B-485D-BA6D-DFD9735CB3C3} = {6C06163A-80E9-49C1-817C-B391852BA563}
{825BCF97-67A9-4834-B3A8-C3DC97A90E41} = {CC45FA2D-128B-485D-BA6D-DFD9735CB3C3}
{DC349A25-0DBF-4468-99E1-B95C22D3A7EF} = {CC45FA2D-128B-485D-BA6D-DFD9735CB3C3}
+ {AA5ABFBC-177C-421E-B743-005E0FD1248B} = {E5963C9F-20A6-4385-B364-814D2581FADF}
+ {5D5A3B60-A014-447C-9126-B1FA6C821C8D} = {AA5ABFBC-177C-421E-B743-005E0FD1248B}
+ {B5AC1D8B-9D43-4261-AE0F-6B7574656F2C} = {AA5ABFBC-177C-421E-B743-005E0FD1248B}
+ {C3FFA4E4-0E7E-4866-A15F-034245BFD800} = {B5AC1D8B-9D43-4261-AE0F-6B7574656F2C}
{5465F96F-33D5-454E-9C40-494E58AEEE5D} = {E5963C9F-20A6-4385-B364-814D2581FADF}
{97996D39-7722-4AFC-A41A-AD61CA7A413D} = {5465F96F-33D5-454E-9C40-494E58AEEE5D}
{37144E52-611B-40E8-807C-2821F5A814CB} = {5465F96F-33D5-454E-9C40-494E58AEEE5D}
@@ -11265,6 +11365,7 @@ Global
{B7DAA48B-8E5E-4A5D-9FEB-E6D49AE76A04} = {41BB7BA4-AC08-4E9A-83EA-6D587A5B951C}
{489020F2-80D9-4468-A5D3-07E785837A5D} = {017429CC-C5FB-48B4-9C46-034E29EE2F06}
{8FED7E65-A7DD-4F13-8980-BF03E77B6C85} = {489020F2-80D9-4468-A5D3-07E785837A5D}
+ {046F43BC-BEE4-48B7-8C09-ED0A1054A2D7} = {AA5ABFBC-177C-421E-B743-005E0FD1248B}
{9716D0D0-2251-44DD-8596-67D253EEF41C} = {323C3EB6-1D15-4B3D-918D-699D7F64DED9}
{2299CCD8-8F9C-4F2B-A633-9BF4DA81022B} = {017429CC-C5FB-48B4-9C46-034E29EE2F06}
{3AEFB466-6310-4F3F-923F-9154224E3629} = {2299CCD8-8F9C-4F2B-A633-9BF4DA81022B}
@@ -11283,6 +11384,9 @@ Global
{AA7445F5-BD28-400C-8507-E2E0D3CF7D7E} = {08D53E58-4AAE-40C4-8497-63EC8664F304}
{281BF9DB-7B8A-446B-9611-10A60903F125} = {08D53E58-4AAE-40C4-8497-63EC8664F304}
{A5946454-4788-4871-8F23-A9471D55F115} = {4FDDC525-4E60-4CAF-83A3-261C5B43721F}
+ {76C3E22D-092B-4E8A-81F0-DCF071BFF4CD} = {0BB58FB6-8B66-4C6D-BA8A-DF3AFAF9AB8F}
+ {0BB58FB6-8B66-4C6D-BA8A-DF3AFAF9AB8F} = {60D51C98-2CC0-40DF-B338-44154EFEE2FF}
+ {7CA0A9AF-9088-471C-B0B6-EBF43F21D3B9} = {08D53E58-4AAE-40C4-8497-63EC8664F304}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F}
diff --git a/eng/ProjectReferences.props b/eng/ProjectReferences.props
index c0842d685c..e502668e67 100644
--- a/eng/ProjectReferences.props
+++ b/eng/ProjectReferences.props
@@ -89,6 +89,7 @@
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Localization.Routing" ProjectPath="$(RepoRoot)src\Middleware\Localization.Routing\src\Microsoft.AspNetCore.Localization.Routing.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Localization" ProjectPath="$(RepoRoot)src\Middleware\Localization\src\Microsoft.AspNetCore.Localization.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.MiddlewareAnalysis" ProjectPath="$(RepoRoot)src\Middleware\MiddlewareAnalysis\src\Microsoft.AspNetCore.MiddlewareAnalysis.csproj" />
+ <ProjectReferenceProvider Include="Microsoft.AspNetCore.OutputCaching" ProjectPath="$(RepoRoot)src\Middleware\OutputCaching\src\Microsoft.AspNetCore.OutputCaching.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.RateLimiting" ProjectPath="$(RepoRoot)src\Middleware\RateLimiting\src\Microsoft.AspNetCore.RateLimiting.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.RequestDecompression" ProjectPath="$(RepoRoot)src\Middleware\RequestDecompression\src\Microsoft.AspNetCore.RequestDecompression.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.ResponseCaching.Abstractions" ProjectPath="$(RepoRoot)src\Middleware\ResponseCaching.Abstractions\src\Microsoft.AspNetCore.ResponseCaching.Abstractions.csproj" />
@@ -139,6 +140,7 @@
<ProjectReferenceProvider Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" ProjectPath="$(RepoRoot)src\SignalR\server\StackExchangeRedis\src\Microsoft.AspNetCore.SignalR.StackExchangeRedis.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.Authorization" ProjectPath="$(RepoRoot)src\Components\Authorization\src\Microsoft.AspNetCore.Components.Authorization.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components" ProjectPath="$(RepoRoot)src\Components\Components\src\Microsoft.AspNetCore.Components.csproj" />
+ <ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.CustomElements" ProjectPath="$(RepoRoot)src\Components\CustomElements\src\Microsoft.AspNetCore.Components.CustomElements.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.Forms" ProjectPath="$(RepoRoot)src\Components\Forms\src\Microsoft.AspNetCore.Components.Forms.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Components.Server" ProjectPath="$(RepoRoot)src\Components\Server\src\Microsoft.AspNetCore.Components.Server.csproj" />
<ProjectReferenceProvider Include="Microsoft.Authentication.WebAssembly.Msal" ProjectPath="$(RepoRoot)src\Components\WebAssembly\Authentication.Msal\src\Microsoft.Authentication.WebAssembly.Msal.csproj" />
diff --git a/eng/SharedFramework.Local.props b/eng/SharedFramework.Local.props
index b727b8df0f..df28fe453b 100644
--- a/eng/SharedFramework.Local.props
+++ b/eng/SharedFramework.Local.props
@@ -77,6 +77,7 @@
<AspNetCoreAppReference Include="Microsoft.AspNetCore.HttpsPolicy" />
<AspNetCoreAppReference Include="Microsoft.AspNetCore.Localization.Routing" />
<AspNetCoreAppReference Include="Microsoft.AspNetCore.Localization" />
+ <AspNetCoreAppReference Include="Microsoft.AspNetCore.OutputCaching" />
<AspNetCoreAppReference Include="Microsoft.AspNetCore.RequestDecompression" />
<AspNetCoreAppReference Include="Microsoft.AspNetCore.ResponseCaching.Abstractions" />
<AspNetCoreAppReference Include="Microsoft.AspNetCore.ResponseCaching" />
diff --git a/eng/TrimmableProjects.props b/eng/TrimmableProjects.props
index 5f86c590f4..f87e23bba2 100644
--- a/eng/TrimmableProjects.props
+++ b/eng/TrimmableProjects.props
@@ -66,7 +66,9 @@
<TrimmableProject Include="Microsoft.AspNetCore.HttpsPolicy" />
<TrimmableProject Include="Microsoft.AspNetCore.Localization.Routing" />
<TrimmableProject Include="Microsoft.AspNetCore.Localization" />
+ <TrimmableProject Include="Microsoft.AspNetCore.OutputCaching" />
<TrimmableProject Include="Microsoft.AspNetCore.RateLimiting" />
+ <TrimmableProject Include="Microsoft.AspNetCore.RequestDecompression" />
<TrimmableProject Include="Microsoft.AspNetCore.ResponseCaching.Abstractions" />
<TrimmableProject Include="Microsoft.AspNetCore.ResponseCaching" />
<TrimmableProject Include="Microsoft.AspNetCore.ResponseCompression" />
@@ -78,6 +80,7 @@
<TrimmableProject Include="Microsoft.AspNetCore.WebSockets" />
<TrimmableProject Include="Microsoft.AspNetCore.Components.Authorization" />
<TrimmableProject Include="Microsoft.AspNetCore.Components" />
+ <TrimmableProject Include="Microsoft.AspNetCore.Components.CustomElements" />
<TrimmableProject Include="Microsoft.AspNetCore.Components.Forms" />
<TrimmableProject Include="Microsoft.Authentication.WebAssembly.Msal" />
<TrimmableProject Include="Microsoft.JSInterop.WebAssembly" />
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index a1574a6a97..dced255f01 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -9,305 +9,305 @@
-->
<Dependencies>
<ProductDependencies>
- <Dependency Name="dotnet-ef" Version="7.0.0-preview.6.22321.1">
+ <Dependency Name="dotnet-ef" Version="7.0.0-preview.7.22323.2">
<Uri>https://github.com/dotnet/efcore</Uri>
- <Sha>e257d1d635fa6b04357342fe390ad3093b7ad08c</Sha>
+ <Sha>4129cdcca7ec4e9b152ed76955d65bc7214484f4</Sha>
</Dependency>
- <Dependency Name="Microsoft.EntityFrameworkCore.InMemory" Version="7.0.0-preview.6.22321.1">
+ <Dependency Name="Microsoft.EntityFrameworkCore.InMemory" Version="7.0.0-preview.7.22323.2">
<Uri>https://github.com/dotnet/efcore</Uri>
- <Sha>e257d1d635fa6b04357342fe390ad3093b7ad08c</Sha>
+ <Sha>4129cdcca7ec4e9b152ed76955d65bc7214484f4</Sha>
</Dependency>
- <Dependency Name="Microsoft.EntityFrameworkCore.Relational" Version="7.0.0-preview.6.22321.1">
+ <Dependency Name="Microsoft.EntityFrameworkCore.Relational" Version="7.0.0-preview.7.22323.2">
<Uri>https://github.com/dotnet/efcore</Uri>
- <Sha>e257d1d635fa6b04357342fe390ad3093b7ad08c</Sha>
+ <Sha>4129cdcca7ec4e9b152ed76955d65bc7214484f4</Sha>
</Dependency>
- <Dependency Name="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.0-preview.6.22321.1">
+ <Dependency Name="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.0-preview.7.22323.2">
<Uri>https://github.com/dotnet/efcore</Uri>
- <Sha>e257d1d635fa6b04357342fe390ad3093b7ad08c</Sha>
+ <Sha>4129cdcca7ec4e9b152ed76955d65bc7214484f4</Sha>
</Dependency>
- <Dependency Name="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.0-preview.6.22321.1">
+ <Dependency Name="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.0-preview.7.22323.2">
<Uri>https://github.com/dotnet/efcore</Uri>
- <Sha>e257d1d635fa6b04357342fe390ad3093b7ad08c</Sha>
+ <Sha>4129cdcca7ec4e9b152ed76955d65bc7214484f4</Sha>
</Dependency>
- <Dependency Name="Microsoft.EntityFrameworkCore.Tools" Version="7.0.0-preview.6.22321.1">
+ <Dependency Name="Microsoft.EntityFrameworkCore.Tools" Version="7.0.0-preview.7.22323.2">
<Uri>https://github.com/dotnet/efcore</Uri>
- <Sha>e257d1d635fa6b04357342fe390ad3093b7ad08c</Sha>
+ <Sha>4129cdcca7ec4e9b152ed76955d65bc7214484f4</Sha>
</Dependency>
- <Dependency Name="Microsoft.EntityFrameworkCore" Version="7.0.0-preview.6.22321.1">
+ <Dependency Name="Microsoft.EntityFrameworkCore" Version="7.0.0-preview.7.22323.2">
<Uri>https://github.com/dotnet/efcore</Uri>
- <Sha>e257d1d635fa6b04357342fe390ad3093b7ad08c</Sha>
+ <Sha>4129cdcca7ec4e9b152ed76955d65bc7214484f4</Sha>
</Dependency>
- <Dependency Name="Microsoft.EntityFrameworkCore.Design" Version="7.0.0-preview.6.22321.1">
+ <Dependency Name="Microsoft.EntityFrameworkCore.Design" Version="7.0.0-preview.7.22323.2">
<Uri>https://github.com/dotnet/efcore</Uri>
- <Sha>e257d1d635fa6b04357342fe390ad3093b7ad08c</Sha>
+ <Sha>4129cdcca7ec4e9b152ed76955d65bc7214484f4</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.Caching.Abstractions" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.Caching.Abstractions" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.Caching.Memory" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.Caching.Memory" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.Configuration.Binder" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.Configuration.Binder" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.Configuration.CommandLine" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.Configuration.CommandLine" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.Configuration.FileExtensions" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.Configuration.FileExtensions" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.Configuration.Ini" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.Configuration.Ini" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.Configuration.Json" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.Configuration.Json" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.Configuration.UserSecrets" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.Configuration.UserSecrets" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.Configuration.Xml" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.Configuration.Xml" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.Configuration" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.Configuration" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.DependencyInjection" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.DependencyInjection" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.FileProviders.Abstractions" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.FileProviders.Abstractions" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.FileProviders.Composite" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.FileProviders.Composite" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.FileProviders.Physical" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.FileProviders.Physical" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.FileSystemGlobbing" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.FileSystemGlobbing" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.HostFactoryResolver.Sources" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.HostFactoryResolver.Sources" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.Hosting.Abstractions" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.Hosting.Abstractions" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.Hosting" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.Hosting" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.Http" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.Http" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.Logging.Configuration" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.Logging.Configuration" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.Logging.Console" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.Logging.Console" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.Logging.Debug" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.Logging.Debug" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.Logging.EventSource" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.Logging.EventSource" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.Logging.EventLog" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.Logging.EventLog" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.Logging.TraceSource" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.Logging.TraceSource" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.Logging" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.Logging" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.Options.ConfigurationExtensions" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.Options.DataAnnotations" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.Options.DataAnnotations" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.Options" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.Options" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.Primitives" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.Primitives" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Internal.Runtime.AspNetCore.Transport" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Internal.Runtime.AspNetCore.Transport" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="System.Configuration.ConfigurationManager" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="System.Configuration.ConfigurationManager" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="System.Diagnostics.DiagnosticSource" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="System.Diagnostics.DiagnosticSource" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="System.Diagnostics.EventLog" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="System.Diagnostics.EventLog" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="System.DirectoryServices.Protocols" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="System.DirectoryServices.Protocols" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="System.IO.Pipelines" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="System.IO.Pipelines" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="System.Net.Http.Json" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="System.Net.Http.Json" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="System.Net.Http.WinHttpHandler" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="System.Net.Http.WinHttpHandler" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="System.Reflection.Metadata" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="System.Reflection.Metadata" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="System.Resources.Extensions" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="System.Resources.Extensions" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="System.Security.Cryptography.Pkcs" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="System.Security.Cryptography.Pkcs" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="System.Security.Cryptography.Xml" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="System.Security.Cryptography.Xml" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="System.Security.Permissions" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="System.Security.Permissions" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="System.ServiceProcess.ServiceController" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="System.ServiceProcess.ServiceController" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="System.Text.Encodings.Web" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="System.Text.Encodings.Web" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="System.Text.Json" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="System.Text.Json" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="System.Threading.AccessControl" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="System.Threading.AccessControl" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="System.Threading.Channels" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="System.Threading.Channels" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="System.Threading.RateLimiting" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="System.Threading.RateLimiting" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.Extensions.DependencyModel" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.Extensions.DependencyModel" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.NETCore.App.Ref" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.NETCore.App.Ref" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.NET.Runtime.MonoAOTCompiler.Task" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.NET.Runtime.MonoAOTCompiler.Task" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.NET.Runtime.WebAssembly.Sdk" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.NET.Runtime.WebAssembly.Sdk" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
<!--
Win-x64 is used here because we have picked an arbitrary runtime identifier to flow the version of the latest NETCore.App runtime.
All Runtime.$rid packages should have the same version.
-->
- <Dependency Name="Microsoft.NETCore.App.Runtime.win-x64" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.NETCore.App.Runtime.win-x64" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.NETCore.App.Runtime.AOT.win-x64.Cross.browser-wasm" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.NETCore.App.Runtime.AOT.win-x64.Cross.browser-wasm" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.NETCore.BrowserDebugHost.Transport" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.NETCore.BrowserDebugHost.Transport" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
</ProductDependencies>
<ToolsetDependencies>
<!-- Listed explicitly to workaround https://github.com/dotnet/cli/issues/10528 -->
- <Dependency Name="Microsoft.NETCore.Platforms" Version="7.0.0-preview.6.22320.7">
+ <Dependency Name="Microsoft.NETCore.Platforms" Version="7.0.0-preview.6.22327.5">
<Uri>https://github.com/dotnet/runtime</Uri>
- <Sha>7ab7f83fb1bdfed781ceb8da066986184ac782a1</Sha>
+ <Sha>812f8f56f4ce09d91ef6d436ccf88f4ccabc9687</Sha>
</Dependency>
- <Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="7.0.0-beta.22316.2">
+ <Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="7.0.0-beta.22327.1">
<Uri>https://github.com/dotnet/arcade</Uri>
- <Sha>ccfe6da198c5f05534863bbb1bff66e830e0c6ab</Sha>
+ <Sha>640c1cc2a140b322c4f30f6d6b85f35f0c4c7313</Sha>
<SourceBuild RepoName="arcade" ManagedOnly="true" />
</Dependency>
- <Dependency Name="Microsoft.DotNet.Build.Tasks.Installers" Version="7.0.0-beta.22316.2">
+ <Dependency Name="Microsoft.DotNet.Build.Tasks.Installers" Version="7.0.0-beta.22327.1">
<Uri>https://github.com/dotnet/arcade</Uri>
- <Sha>ccfe6da198c5f05534863bbb1bff66e830e0c6ab</Sha>
+ <Sha>640c1cc2a140b322c4f30f6d6b85f35f0c4c7313</Sha>
</Dependency>
- <Dependency Name="Microsoft.DotNet.Build.Tasks.Templating" Version="7.0.0-beta.22316.2">
+ <Dependency Name="Microsoft.DotNet.Build.Tasks.Templating" Version="7.0.0-beta.22327.1">
<Uri>https://github.com/dotnet/arcade</Uri>
- <Sha>ccfe6da198c5f05534863bbb1bff66e830e0c6ab</Sha>
+ <Sha>640c1cc2a140b322c4f30f6d6b85f35f0c4c7313</Sha>
</Dependency>
- <Dependency Name="Microsoft.DotNet.Helix.Sdk" Version="7.0.0-beta.22316.2">
+ <Dependency Name="Microsoft.DotNet.Helix.Sdk" Version="7.0.0-beta.22327.1">
<Uri>https://github.com/dotnet/arcade</Uri>
- <Sha>ccfe6da198c5f05534863bbb1bff66e830e0c6ab</Sha>
+ <Sha>640c1cc2a140b322c4f30f6d6b85f35f0c4c7313</Sha>
</Dependency>
</ToolsetDependencies>
</Dependencies>
diff --git a/eng/Versions.props b/eng/Versions.props
index 0b54decb89..4e21db12a3 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -9,7 +9,7 @@
<AspNetCoreMajorVersion>7</AspNetCoreMajorVersion>
<AspNetCoreMinorVersion>0</AspNetCoreMinorVersion>
<AspNetCorePatchVersion>0</AspNetCorePatchVersion>
- <PreReleaseVersionIteration>6</PreReleaseVersionIteration>
+ <PreReleaseVersionIteration>7</PreReleaseVersionIteration>
<ValidateBaseline>true</ValidateBaseline>
<!--
When StabilizePackageVersion is set to 'true', this branch will produce stable outputs for 'Shipping' packages
@@ -63,80 +63,80 @@
-->
<PropertyGroup Label="Automated">
<!-- Packages from dotnet/runtime -->
- <MicrosoftExtensionsDependencyModelVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsDependencyModelVersion>
- <MicrosoftNETCoreAppRefVersion>7.0.0-preview.6.22320.7</MicrosoftNETCoreAppRefVersion>
- <MicrosoftNETCoreAppRuntimewinx64Version>7.0.0-preview.6.22320.7</MicrosoftNETCoreAppRuntimewinx64Version>
- <MicrosoftNETRuntimeMonoAOTCompilerTaskVersion>7.0.0-preview.6.22320.7</MicrosoftNETRuntimeMonoAOTCompilerTaskVersion>
- <MicrosoftNETRuntimeWebAssemblySdkVersion>7.0.0-preview.6.22320.7</MicrosoftNETRuntimeWebAssemblySdkVersion>
- <MicrosoftNETCoreAppRuntimeAOTwinx64CrossbrowserwasmVersion>7.0.0-preview.6.22320.7</MicrosoftNETCoreAppRuntimeAOTwinx64CrossbrowserwasmVersion>
- <MicrosoftNETCoreBrowserDebugHostTransportVersion>7.0.0-preview.6.22320.7</MicrosoftNETCoreBrowserDebugHostTransportVersion>
- <MicrosoftExtensionsCachingAbstractionsVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsCachingAbstractionsVersion>
- <MicrosoftExtensionsCachingMemoryVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsCachingMemoryVersion>
- <MicrosoftExtensionsConfigurationAbstractionsVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsConfigurationAbstractionsVersion>
- <MicrosoftExtensionsConfigurationBinderVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsConfigurationBinderVersion>
- <MicrosoftExtensionsConfigurationCommandLineVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsConfigurationCommandLineVersion>
- <MicrosoftExtensionsConfigurationEnvironmentVariablesVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsConfigurationEnvironmentVariablesVersion>
- <MicrosoftExtensionsConfigurationFileExtensionsVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsConfigurationFileExtensionsVersion>
- <MicrosoftExtensionsConfigurationIniVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsConfigurationIniVersion>
- <MicrosoftExtensionsConfigurationJsonVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsConfigurationJsonVersion>
- <MicrosoftExtensionsConfigurationVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsConfigurationVersion>
- <MicrosoftExtensionsConfigurationUserSecretsVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsConfigurationUserSecretsVersion>
- <MicrosoftExtensionsConfigurationXmlVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsConfigurationXmlVersion>
- <MicrosoftExtensionsDependencyInjectionAbstractionsVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsDependencyInjectionAbstractionsVersion>
- <MicrosoftExtensionsDependencyInjectionVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsDependencyInjectionVersion>
- <MicrosoftExtensionsFileProvidersAbstractionsVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsFileProvidersAbstractionsVersion>
- <MicrosoftExtensionsFileProvidersCompositeVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsFileProvidersCompositeVersion>
- <MicrosoftExtensionsFileProvidersPhysicalVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsFileProvidersPhysicalVersion>
- <MicrosoftExtensionsFileSystemGlobbingVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsFileSystemGlobbingVersion>
- <MicrosoftExtensionsHostFactoryResolverSourcesVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsHostFactoryResolverSourcesVersion>
- <MicrosoftExtensionsHostingAbstractionsVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsHostingAbstractionsVersion>
- <MicrosoftExtensionsHostingVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsHostingVersion>
- <MicrosoftExtensionsHttpVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsHttpVersion>
- <MicrosoftExtensionsLoggingAbstractionsVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsLoggingAbstractionsVersion>
- <MicrosoftExtensionsLoggingConfigurationVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsLoggingConfigurationVersion>
- <MicrosoftExtensionsLoggingConsoleVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsLoggingConsoleVersion>
- <MicrosoftExtensionsLoggingDebugVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsLoggingDebugVersion>
- <MicrosoftExtensionsLoggingEventSourceVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsLoggingEventSourceVersion>
- <MicrosoftExtensionsLoggingEventLogVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsLoggingEventLogVersion>
- <MicrosoftExtensionsLoggingVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsLoggingVersion>
- <MicrosoftExtensionsLoggingTraceSourceVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsLoggingTraceSourceVersion>
- <MicrosoftExtensionsOptionsConfigurationExtensionsVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsOptionsConfigurationExtensionsVersion>
- <MicrosoftExtensionsOptionsDataAnnotationsVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsOptionsDataAnnotationsVersion>
- <MicrosoftExtensionsOptionsVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsOptionsVersion>
- <MicrosoftExtensionsPrimitivesVersion>7.0.0-preview.6.22320.7</MicrosoftExtensionsPrimitivesVersion>
- <MicrosoftInternalRuntimeAspNetCoreTransportVersion>7.0.0-preview.6.22320.7</MicrosoftInternalRuntimeAspNetCoreTransportVersion>
- <SystemConfigurationConfigurationManagerVersion>7.0.0-preview.6.22320.7</SystemConfigurationConfigurationManagerVersion>
- <SystemDiagnosticsDiagnosticSourceVersion>7.0.0-preview.6.22320.7</SystemDiagnosticsDiagnosticSourceVersion>
- <SystemDiagnosticsEventLogVersion>7.0.0-preview.6.22320.7</SystemDiagnosticsEventLogVersion>
- <SystemDirectoryServicesProtocolsVersion>7.0.0-preview.6.22320.7</SystemDirectoryServicesProtocolsVersion>
- <SystemIOPipelinesVersion>7.0.0-preview.6.22320.7</SystemIOPipelinesVersion>
- <SystemNetHttpJsonVersion>7.0.0-preview.6.22320.7</SystemNetHttpJsonVersion>
- <SystemNetHttpWinHttpHandlerVersion>7.0.0-preview.6.22320.7</SystemNetHttpWinHttpHandlerVersion>
- <SystemReflectionMetadataVersion>7.0.0-preview.6.22320.7</SystemReflectionMetadataVersion>
- <SystemResourcesExtensionsVersion>7.0.0-preview.6.22320.7</SystemResourcesExtensionsVersion>
- <SystemSecurityCryptographyPkcsVersion>7.0.0-preview.6.22320.7</SystemSecurityCryptographyPkcsVersion>
- <SystemSecurityCryptographyXmlVersion>7.0.0-preview.6.22320.7</SystemSecurityCryptographyXmlVersion>
- <SystemSecurityPermissionsVersion>7.0.0-preview.6.22320.7</SystemSecurityPermissionsVersion>
- <SystemServiceProcessServiceControllerVersion>7.0.0-preview.6.22320.7</SystemServiceProcessServiceControllerVersion>
- <SystemTextEncodingsWebVersion>7.0.0-preview.6.22320.7</SystemTextEncodingsWebVersion>
- <SystemTextJsonVersion>7.0.0-preview.6.22320.7</SystemTextJsonVersion>
- <SystemThreadingAccessControlVersion>7.0.0-preview.6.22320.7</SystemThreadingAccessControlVersion>
- <SystemThreadingChannelsVersion>7.0.0-preview.6.22320.7</SystemThreadingChannelsVersion>
- <SystemThreadingRateLimitingVersion>7.0.0-preview.6.22320.7</SystemThreadingRateLimitingVersion>
+ <MicrosoftExtensionsDependencyModelVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsDependencyModelVersion>
+ <MicrosoftNETCoreAppRefVersion>7.0.0-preview.6.22327.5</MicrosoftNETCoreAppRefVersion>
+ <MicrosoftNETCoreAppRuntimewinx64Version>7.0.0-preview.6.22327.5</MicrosoftNETCoreAppRuntimewinx64Version>
+ <MicrosoftNETRuntimeMonoAOTCompilerTaskVersion>7.0.0-preview.6.22327.5</MicrosoftNETRuntimeMonoAOTCompilerTaskVersion>
+ <MicrosoftNETRuntimeWebAssemblySdkVersion>7.0.0-preview.6.22327.5</MicrosoftNETRuntimeWebAssemblySdkVersion>
+ <MicrosoftNETCoreAppRuntimeAOTwinx64CrossbrowserwasmVersion>7.0.0-preview.6.22327.5</MicrosoftNETCoreAppRuntimeAOTwinx64CrossbrowserwasmVersion>
+ <MicrosoftNETCoreBrowserDebugHostTransportVersion>7.0.0-preview.6.22327.5</MicrosoftNETCoreBrowserDebugHostTransportVersion>
+ <MicrosoftExtensionsCachingAbstractionsVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsCachingAbstractionsVersion>
+ <MicrosoftExtensionsCachingMemoryVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsCachingMemoryVersion>
+ <MicrosoftExtensionsConfigurationAbstractionsVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsConfigurationAbstractionsVersion>
+ <MicrosoftExtensionsConfigurationBinderVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsConfigurationBinderVersion>
+ <MicrosoftExtensionsConfigurationCommandLineVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsConfigurationCommandLineVersion>
+ <MicrosoftExtensionsConfigurationEnvironmentVariablesVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsConfigurationEnvironmentVariablesVersion>
+ <MicrosoftExtensionsConfigurationFileExtensionsVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsConfigurationFileExtensionsVersion>
+ <MicrosoftExtensionsConfigurationIniVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsConfigurationIniVersion>
+ <MicrosoftExtensionsConfigurationJsonVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsConfigurationJsonVersion>
+ <MicrosoftExtensionsConfigurationVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsConfigurationVersion>
+ <MicrosoftExtensionsConfigurationUserSecretsVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsConfigurationUserSecretsVersion>
+ <MicrosoftExtensionsConfigurationXmlVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsConfigurationXmlVersion>
+ <MicrosoftExtensionsDependencyInjectionAbstractionsVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsDependencyInjectionAbstractionsVersion>
+ <MicrosoftExtensionsDependencyInjectionVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsDependencyInjectionVersion>
+ <MicrosoftExtensionsFileProvidersAbstractionsVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsFileProvidersAbstractionsVersion>
+ <MicrosoftExtensionsFileProvidersCompositeVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsFileProvidersCompositeVersion>
+ <MicrosoftExtensionsFileProvidersPhysicalVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsFileProvidersPhysicalVersion>
+ <MicrosoftExtensionsFileSystemGlobbingVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsFileSystemGlobbingVersion>
+ <MicrosoftExtensionsHostFactoryResolverSourcesVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsHostFactoryResolverSourcesVersion>
+ <MicrosoftExtensionsHostingAbstractionsVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsHostingAbstractionsVersion>
+ <MicrosoftExtensionsHostingVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsHostingVersion>
+ <MicrosoftExtensionsHttpVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsHttpVersion>
+ <MicrosoftExtensionsLoggingAbstractionsVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsLoggingAbstractionsVersion>
+ <MicrosoftExtensionsLoggingConfigurationVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsLoggingConfigurationVersion>
+ <MicrosoftExtensionsLoggingConsoleVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsLoggingConsoleVersion>
+ <MicrosoftExtensionsLoggingDebugVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsLoggingDebugVersion>
+ <MicrosoftExtensionsLoggingEventSourceVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsLoggingEventSourceVersion>
+ <MicrosoftExtensionsLoggingEventLogVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsLoggingEventLogVersion>
+ <MicrosoftExtensionsLoggingVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsLoggingVersion>
+ <MicrosoftExtensionsLoggingTraceSourceVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsLoggingTraceSourceVersion>
+ <MicrosoftExtensionsOptionsConfigurationExtensionsVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsOptionsConfigurationExtensionsVersion>
+ <MicrosoftExtensionsOptionsDataAnnotationsVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsOptionsDataAnnotationsVersion>
+ <MicrosoftExtensionsOptionsVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsOptionsVersion>
+ <MicrosoftExtensionsPrimitivesVersion>7.0.0-preview.6.22327.5</MicrosoftExtensionsPrimitivesVersion>
+ <MicrosoftInternalRuntimeAspNetCoreTransportVersion>7.0.0-preview.6.22327.5</MicrosoftInternalRuntimeAspNetCoreTransportVersion>
+ <SystemConfigurationConfigurationManagerVersion>7.0.0-preview.6.22327.5</SystemConfigurationConfigurationManagerVersion>
+ <SystemDiagnosticsDiagnosticSourceVersion>7.0.0-preview.6.22327.5</SystemDiagnosticsDiagnosticSourceVersion>
+ <SystemDiagnosticsEventLogVersion>7.0.0-preview.6.22327.5</SystemDiagnosticsEventLogVersion>
+ <SystemDirectoryServicesProtocolsVersion>7.0.0-preview.6.22327.5</SystemDirectoryServicesProtocolsVersion>
+ <SystemIOPipelinesVersion>7.0.0-preview.6.22327.5</SystemIOPipelinesVersion>
+ <SystemNetHttpJsonVersion>7.0.0-preview.6.22327.5</SystemNetHttpJsonVersion>
+ <SystemNetHttpWinHttpHandlerVersion>7.0.0-preview.6.22327.5</SystemNetHttpWinHttpHandlerVersion>
+ <SystemReflectionMetadataVersion>7.0.0-preview.6.22327.5</SystemReflectionMetadataVersion>
+ <SystemResourcesExtensionsVersion>7.0.0-preview.6.22327.5</SystemResourcesExtensionsVersion>
+ <SystemSecurityCryptographyPkcsVersion>7.0.0-preview.6.22327.5</SystemSecurityCryptographyPkcsVersion>
+ <SystemSecurityCryptographyXmlVersion>7.0.0-preview.6.22327.5</SystemSecurityCryptographyXmlVersion>
+ <SystemSecurityPermissionsVersion>7.0.0-preview.6.22327.5</SystemSecurityPermissionsVersion>
+ <SystemServiceProcessServiceControllerVersion>7.0.0-preview.6.22327.5</SystemServiceProcessServiceControllerVersion>
+ <SystemTextEncodingsWebVersion>7.0.0-preview.6.22327.5</SystemTextEncodingsWebVersion>
+ <SystemTextJsonVersion>7.0.0-preview.6.22327.5</SystemTextJsonVersion>
+ <SystemThreadingAccessControlVersion>7.0.0-preview.6.22327.5</SystemThreadingAccessControlVersion>
+ <SystemThreadingChannelsVersion>7.0.0-preview.6.22327.5</SystemThreadingChannelsVersion>
+ <SystemThreadingRateLimitingVersion>7.0.0-preview.6.22327.5</SystemThreadingRateLimitingVersion>
<!-- Only listed explicitly to workaround https://github.com/dotnet/cli/issues/10528 -->
- <MicrosoftNETCorePlatformsVersion>7.0.0-preview.6.22320.7</MicrosoftNETCorePlatformsVersion>
+ <MicrosoftNETCorePlatformsVersion>7.0.0-preview.6.22327.5</MicrosoftNETCorePlatformsVersion>
<!-- Packages from dotnet/efcore -->
- <dotnetefVersion>7.0.0-preview.6.22321.1</dotnetefVersion>
- <MicrosoftEntityFrameworkCoreInMemoryVersion>7.0.0-preview.6.22321.1</MicrosoftEntityFrameworkCoreInMemoryVersion>
- <MicrosoftEntityFrameworkCoreRelationalVersion>7.0.0-preview.6.22321.1</MicrosoftEntityFrameworkCoreRelationalVersion>
- <MicrosoftEntityFrameworkCoreSqliteVersion>7.0.0-preview.6.22321.1</MicrosoftEntityFrameworkCoreSqliteVersion>
- <MicrosoftEntityFrameworkCoreSqlServerVersion>7.0.0-preview.6.22321.1</MicrosoftEntityFrameworkCoreSqlServerVersion>
- <MicrosoftEntityFrameworkCoreToolsVersion>7.0.0-preview.6.22321.1</MicrosoftEntityFrameworkCoreToolsVersion>
- <MicrosoftEntityFrameworkCoreVersion>7.0.0-preview.6.22321.1</MicrosoftEntityFrameworkCoreVersion>
- <MicrosoftEntityFrameworkCoreDesignVersion>7.0.0-preview.6.22321.1</MicrosoftEntityFrameworkCoreDesignVersion>
+ <dotnetefVersion>7.0.0-preview.7.22323.2</dotnetefVersion>
+ <MicrosoftEntityFrameworkCoreInMemoryVersion>7.0.0-preview.7.22323.2</MicrosoftEntityFrameworkCoreInMemoryVersion>
+ <MicrosoftEntityFrameworkCoreRelationalVersion>7.0.0-preview.7.22323.2</MicrosoftEntityFrameworkCoreRelationalVersion>
+ <MicrosoftEntityFrameworkCoreSqliteVersion>7.0.0-preview.7.22323.2</MicrosoftEntityFrameworkCoreSqliteVersion>
+ <MicrosoftEntityFrameworkCoreSqlServerVersion>7.0.0-preview.7.22323.2</MicrosoftEntityFrameworkCoreSqlServerVersion>
+ <MicrosoftEntityFrameworkCoreToolsVersion>7.0.0-preview.7.22323.2</MicrosoftEntityFrameworkCoreToolsVersion>
+ <MicrosoftEntityFrameworkCoreVersion>7.0.0-preview.7.22323.2</MicrosoftEntityFrameworkCoreVersion>
+ <MicrosoftEntityFrameworkCoreDesignVersion>7.0.0-preview.7.22323.2</MicrosoftEntityFrameworkCoreDesignVersion>
<!-- Packages from dotnet/arcade -->
- <MicrosoftDotNetBuildTasksInstallersVersion>7.0.0-beta.22316.2</MicrosoftDotNetBuildTasksInstallersVersion>
- <MicrosoftDotNetBuildTasksTemplatingVersion>7.0.0-beta.22316.2</MicrosoftDotNetBuildTasksTemplatingVersion>
+ <MicrosoftDotNetBuildTasksInstallersVersion>7.0.0-beta.22327.1</MicrosoftDotNetBuildTasksInstallersVersion>
+ <MicrosoftDotNetBuildTasksTemplatingVersion>7.0.0-beta.22327.1</MicrosoftDotNetBuildTasksTemplatingVersion>
</PropertyGroup>
<!--
@@ -217,9 +217,9 @@
<MicrosoftWebAdministrationVersion>11.1.0</MicrosoftWebAdministrationVersion>
<MicrosoftWebXdtVersion>1.4.0</MicrosoftWebXdtVersion>
<SystemIdentityModelTokensJwtVersion>6.15.1</SystemIdentityModelTokensJwtVersion>
- <NuGetPackagingVersion>5.11.0</NuGetPackagingVersion>
- <NuGetVersioningVersion>5.11.0</NuGetVersioningVersion>
- <NuGetFrameworksVersion>5.11.0</NuGetFrameworksVersion>
+ <NuGetPackagingVersion>6.2.0</NuGetPackagingVersion>
+ <NuGetVersioningVersion>6.2.0</NuGetVersioningVersion>
+ <NuGetFrameworksVersion>6.2.0</NuGetFrameworksVersion>
<SystemComponentModelAnnotationsVersion>5.0.0</SystemComponentModelAnnotationsVersion>
<SystemNetExperimentalMsQuicVersion>5.0.0-alpha.20560.6</SystemNetExperimentalMsQuicVersion>
<SystemSecurityPrincipalWindowsVersion>5.0.0</SystemSecurityPrincipalWindowsVersion>
diff --git a/eng/common/templates/steps/send-to-helix.yml b/eng/common/templates/steps/send-to-helix.yml
index 09a223989f..3eb7e2d5f8 100644
--- a/eng/common/templates/steps/send-to-helix.yml
+++ b/eng/common/templates/steps/send-to-helix.yml
@@ -3,7 +3,7 @@ parameters:
HelixSource: 'pr/default' # required -- sources must start with pr/, official/, prodcon/, or agent/
HelixType: 'tests/default/' # required -- Helix telemetry which identifies what type of data this is; should include "test" for clarity and must end in '/'
HelixBuild: $(Build.BuildNumber) # required -- the build number Helix will use to identify this -- automatically set to the AzDO build number
- HelixTargetQueues: '' # required -- semicolon delimited list of Helix queues to test on; see https://helix.dot.net/ for a list of queues
+ HelixTargetQueues: '' # required -- semicolon-delimited list of Helix queues to test on; see https://helix.dot.net/ for a list of queues
HelixAccessToken: '' # required -- access token to make Helix API requests; should be provided by the appropriate variable group
HelixConfiguration: '' # optional -- additional property attached to a job
HelixPreCommands: '' # optional -- commands to run before Helix work item execution
@@ -12,7 +12,7 @@ parameters:
WorkItemCommand: '' # optional -- a command to execute on the payload; requires WorkItemDirectory; incompatible with XUnitProjects
WorkItemTimeout: '' # optional -- a timeout in TimeSpan.Parse-ready value (e.g. 00:02:00) for the work item command; requires WorkItemDirectory; incompatible with XUnitProjects
CorrelationPayloadDirectory: '' # optional -- a directory to zip up and send to Helix as a correlation payload
- XUnitProjects: '' # optional -- semicolon delimited list of XUnitProjects to parse and send to Helix; requires XUnitRuntimeTargetFramework, XUnitPublishTargetFramework, XUnitRunnerVersion, and IncludeDotNetCli=true
+ XUnitProjects: '' # optional -- semicolon-delimited list of XUnitProjects to parse and send to Helix; requires XUnitRuntimeTargetFramework, XUnitPublishTargetFramework, XUnitRunnerVersion, and IncludeDotNetCli=true
XUnitWorkItemTimeout: '' # optional -- the workitem timeout in seconds for all workitems created from the xUnit projects specified by XUnitProjects
XUnitPublishTargetFramework: '' # optional -- framework to use to publish your xUnit projects
XUnitRuntimeTargetFramework: '' # optional -- framework to use for the xUnit console runner
@@ -22,14 +22,14 @@ parameters:
DotNetCliVersion: '' # optional -- version of the CLI to send to Helix; based on this: https://raw.githubusercontent.com/dotnet/core/main/release-notes/releases-index.json
WaitForWorkItemCompletion: true # optional -- true will make the task wait until work items have been completed and fail the build if work items fail. False is "fire and forget."
IsExternal: false # [DEPRECATED] -- doesn't do anything, jobs are external if HelixAccessToken is empty and Creator is set
- HelixBaseUri: 'https://helix.dot.net/' # optional -- sets the Helix API base URI (allows targeting int)
+ HelixBaseUri: 'https://helix.dot.net/' # optional -- sets the Helix API base URI (allows targeting https://helix.int-dot.net )
Creator: '' # optional -- if the build is external, use this to specify who is sending the job
DisplayNamePrefix: 'Run Tests' # optional -- rename the beginning of the displayName of the steps in AzDO
condition: succeeded() # optional -- condition for step to execute; defaults to succeeded()
continueOnError: false # optional -- determines whether to continue the build if the step errors; defaults to false
steps:
- - powershell: 'powershell "$env:BUILD_SOURCESDIRECTORY\eng\common\msbuild.ps1 $env:BUILD_SOURCESDIRECTORY\eng\common\helixpublish.proj /restore /t:Test /bl:$env:BUILD_SOURCESDIRECTORY\artifacts\log\$env:BuildConfig\SendToHelix.binlog"'
+ - powershell: 'powershell "$env:BUILD_SOURCESDIRECTORY\eng\common\msbuild.ps1 $env:BUILD_SOURCESDIRECTORY\eng\common\helixpublish.proj /restore /p:TreatWarningsAsErrors=false /t:Test /bl:$env:BUILD_SOURCESDIRECTORY\artifacts\log\$env:BuildConfig\SendToHelix.binlog"'
displayName: ${{ parameters.DisplayNamePrefix }} (Windows)
env:
BuildConfig: $(_BuildConfig)
@@ -59,7 +59,7 @@ steps:
SYSTEM_ACCESSTOKEN: $(System.AccessToken)
condition: and(${{ parameters.condition }}, eq(variables['Agent.Os'], 'Windows_NT'))
continueOnError: ${{ parameters.continueOnError }}
- - script: $BUILD_SOURCESDIRECTORY/eng/common/msbuild.sh $BUILD_SOURCESDIRECTORY/eng/common/helixpublish.proj /restore /t:Test /bl:$BUILD_SOURCESDIRECTORY/artifacts/log/$BuildConfig/SendToHelix.binlog
+ - script: $BUILD_SOURCESDIRECTORY/eng/common/msbuild.sh $BUILD_SOURCESDIRECTORY/eng/common/helixpublish.proj /restore /p:TreatWarningsAsErrors=false /t:Test /bl:$BUILD_SOURCESDIRECTORY/artifacts/log/$BuildConfig/SendToHelix.binlog
displayName: ${{ parameters.DisplayNamePrefix }} (Unix)
env:
BuildConfig: $(_BuildConfig)
diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1
index 423bd962e9..395b43eebb 100644
--- a/eng/common/tools.ps1
+++ b/eng/common/tools.ps1
@@ -573,7 +573,7 @@ function InitializeBuildTool() {
ExitWithExitCode 1
}
$dotnetPath = Join-Path $dotnetRoot (GetExecutableFileName 'dotnet')
- $buildTool = @{ Path = $dotnetPath; Command = 'msbuild'; Tool = 'dotnet'; Framework = 'netcoreapp3.1' }
+ $buildTool = @{ Path = $dotnetPath; Command = 'msbuild'; Tool = 'dotnet'; Framework = 'net7.0' }
} elseif ($msbuildEngine -eq "vs") {
try {
$msbuildPath = InitializeVisualStudioMSBuild -install:$restore
diff --git a/eng/common/tools.sh b/eng/common/tools.sh
index 17f0a36580..c110d0ed41 100755
--- a/eng/common/tools.sh
+++ b/eng/common/tools.sh
@@ -312,7 +312,7 @@ function InitializeBuildTool {
# return values
_InitializeBuildTool="$_InitializeDotNetCli/dotnet"
_InitializeBuildToolCommand="msbuild"
- _InitializeBuildToolFramework="netcoreapp3.1"
+ _InitializeBuildToolFramework="net7.0"
}
# Set RestoreNoCache as a workaround for https://github.com/NuGet/Home/issues/3116
diff --git a/eng/scripts/vs.17.buildtools.intpreview.json b/eng/scripts/vs.17.buildtools.intpreview.json
index e1fe46fad9..8a7c5ac445 100644
--- a/eng/scripts/vs.17.buildtools.intpreview.json
+++ b/eng/scripts/vs.17.buildtools.intpreview.json
@@ -20,7 +20,7 @@
"Microsoft.VisualStudio.Component.VC.14.29.16.11.x86.x64",
"Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL.ARM64",
"Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL",
- "Microsoft.VisualStudio.Component.Windows10SDK.18362",
+ "Microsoft.VisualStudio.Component.Windows10SDK.19041",
"Microsoft.VisualStudio.Workload.ManagedDesktopBuildTools",
"Microsoft.VisualStudio.Workload.MSBuildTools",
"Microsoft.VisualStudio.Workload.NetCoreBuildTools",
diff --git a/eng/scripts/vs.17.buildtools.json b/eng/scripts/vs.17.buildtools.json
index ead9add6d6..3a125c9d35 100644
--- a/eng/scripts/vs.17.buildtools.json
+++ b/eng/scripts/vs.17.buildtools.json
@@ -20,7 +20,7 @@
"Microsoft.VisualStudio.Component.VC.14.29.16.11.x86.x64",
"Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL.ARM64",
"Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL",
- "Microsoft.VisualStudio.Component.Windows10SDK.18362",
+ "Microsoft.VisualStudio.Component.Windows10SDK.19041",
"Microsoft.VisualStudio.Workload.ManagedDesktopBuildTools",
"Microsoft.VisualStudio.Workload.MSBuildTools",
"Microsoft.VisualStudio.Workload.NetCoreBuildTools",
diff --git a/eng/scripts/vs.17.buildtools.preview.json b/eng/scripts/vs.17.buildtools.preview.json
index c200518a15..571a499781 100644
--- a/eng/scripts/vs.17.buildtools.preview.json
+++ b/eng/scripts/vs.17.buildtools.preview.json
@@ -20,7 +20,7 @@
"Microsoft.VisualStudio.Component.VC.14.29.16.11.x86.x64",
"Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL.ARM64",
"Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL",
- "Microsoft.VisualStudio.Component.Windows10SDK.18362",
+ "Microsoft.VisualStudio.Component.Windows10SDK.19041",
"Microsoft.VisualStudio.Workload.ManagedDesktopBuildTools",
"Microsoft.VisualStudio.Workload.MSBuildTools",
"Microsoft.VisualStudio.Workload.NetCoreBuildTools",
diff --git a/eng/scripts/vs.17.intpreview.json b/eng/scripts/vs.17.intpreview.json
index bdf5c862b9..67b7a1f8e9 100644
--- a/eng/scripts/vs.17.intpreview.json
+++ b/eng/scripts/vs.17.intpreview.json
@@ -17,7 +17,7 @@
"Microsoft.VisualStudio.Component.VC.14.29.16.11.x86.x64",
"Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL.ARM64",
"Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL",
- "Microsoft.VisualStudio.Component.Windows10SDK.18362",
+ "Microsoft.VisualStudio.Component.Windows10SDK.19041",
"Microsoft.VisualStudio.Workload.ManagedDesktop",
"Microsoft.VisualStudio.Workload.NativeDesktop",
"Microsoft.VisualStudio.Workload.NetWeb",
diff --git a/eng/scripts/vs.17.json b/eng/scripts/vs.17.json
index a626179c32..86acfd28fd 100644
--- a/eng/scripts/vs.17.json
+++ b/eng/scripts/vs.17.json
@@ -17,7 +17,7 @@
"Microsoft.VisualStudio.Component.VC.14.29.16.11.x86.x64",
"Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL.ARM64",
"Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL",
- "Microsoft.VisualStudio.Component.Windows10SDK.18362",
+ "Microsoft.VisualStudio.Component.Windows10SDK.19041",
"Microsoft.VisualStudio.Workload.ManagedDesktop",
"Microsoft.VisualStudio.Workload.NativeDesktop",
"Microsoft.VisualStudio.Workload.NetWeb",
diff --git a/eng/scripts/vs.17.preview.json b/eng/scripts/vs.17.preview.json
index 943b5697c1..39f8297f1f 100644
--- a/eng/scripts/vs.17.preview.json
+++ b/eng/scripts/vs.17.preview.json
@@ -17,7 +17,7 @@
"Microsoft.VisualStudio.Component.VC.14.29.16.11.x86.x64",
"Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL.ARM64",
"Microsoft.VisualStudio.Component.VC.14.29.16.11.ATL",
- "Microsoft.VisualStudio.Component.Windows10SDK.18362",
+ "Microsoft.VisualStudio.Component.Windows10SDK.19041",
"Microsoft.VisualStudio.Workload.ManagedDesktop",
"Microsoft.VisualStudio.Workload.NativeDesktop",
"Microsoft.VisualStudio.Workload.NetWeb",
diff --git a/eng/targets/Cpp.Common.props b/eng/targets/Cpp.Common.props
index b11c00f03e..c824b87caa 100644
--- a/eng/targets/Cpp.Common.props
+++ b/eng/targets/Cpp.Common.props
@@ -15,6 +15,17 @@
<TestProjectSkipReason>You cannot test native ARM64 projects on a x86/x64 machine</TestProjectSkipReason>
</PropertyGroup>
+ <PropertyGroup Label="Configuration">
+ <Configuration Condition="'$(Configuration)'==''">Debug</Configuration>
+ <VCProjectVersion>15.0</VCProjectVersion>
+ <Keyword>Win32Proj</Keyword>
+ <Platform Condition="'$(Platform)' == ''">x64</Platform>
+ <PlatformToolsetVersion>v143</PlatformToolsetVersion>
+ <PlatformToolset>$(PlatformToolsetVersion)</PlatformToolset>
+ <!-- If the following line is updated ensure that the /eng/scripts/vs.17.*.json files are updated to include the same version component too. -->
+ <WindowsTargetPlatformVersion Condition=" '$(WindowsTargetPlatformVersion)' == '' ">10.0.19041.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+
<Import Project="MicroBuild.Plugin.props" Condition="'$(MicroBuildSentinelFile)' == ''" />
<Import Project="$(MicroBuildPluginDirectory)\MicroBuild.Plugins.*\**\build\MicroBuild.Plugins.*.props" Condition=" '$(MicroBuildPluginDirectory)' != ''" />
diff --git a/global.json b/global.json
index 5dcfaf7e25..3c98cec4de 100644
--- a/global.json
+++ b/global.json
@@ -1,9 +1,9 @@
{
"sdk": {
- "version": "7.0.100-preview.5.22263.22"
+ "version": "7.0.100-preview.7.22327.3"
},
"tools": {
- "dotnet": "7.0.100-preview.5.22263.22",
+ "dotnet": "7.0.100-preview.7.22327.3",
"runtimes": {
"dotnet/x86": [
"$(MicrosoftNETCoreBrowserDebugHostTransportVersion)"
@@ -15,7 +15,7 @@
"Git": "2.22.0",
"jdk": "11.0.3",
"vs": {
- "version": "16.8",
+ "version": "17.2",
"components": [
"Microsoft.VisualStudio.Component.VC.ATL",
"Microsoft.VisualStudio.Component.VC.ATL.ARM64",
@@ -23,11 +23,11 @@
"Microsoft.VisualStudio.Component.VC.Tools.x86.x64"
]
},
- "xcopy-msbuild": "16.5.0-alpha"
+ "xcopy-msbuild": "17.1.0"
},
"msbuild-sdks": {
"Yarn.MSBuild": "1.22.10",
- "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.22316.2",
- "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.22316.2"
+ "Microsoft.DotNet.Arcade.Sdk": "7.0.0-beta.22327.1",
+ "Microsoft.DotNet.Helix.Sdk": "7.0.0-beta.22327.1"
}
}
diff --git a/src/Components/Authorization/src/AuthorizeRouteView.cs b/src/Components/Authorization/src/AuthorizeRouteView.cs
index 05a7749225..137b999e4d 100644
--- a/src/Components/Authorization/src/AuthorizeRouteView.cs
+++ b/src/Components/Authorization/src/AuthorizeRouteView.cs
@@ -99,6 +99,8 @@ public sealed class AuthorizeRouteView : RouteView
Justification = "OpenComponent already has the right set of attributes")]
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2110:RequiresUnreferencedCode",
Justification = "OpenComponent already has the right set of attributes")]
+ [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2118:RequiresUnreferencedCode",
+ Justification = "OpenComponent already has the right set of attributes")]
private void RenderContentInDefaultLayout(RenderTreeBuilder builder, RenderFragment content)
{
builder.OpenComponent<LayoutView>(0);
diff --git a/src/Components/Components.slnf b/src/Components/Components.slnf
index d867f6dc47..04fdac617c 100644
--- a/src/Components/Components.slnf
+++ b/src/Components/Components.slnf
@@ -11,6 +11,7 @@
"src\\Components\\Components\\perf\\Microsoft.AspNetCore.Components.Performance.csproj",
"src\\Components\\Components\\src\\Microsoft.AspNetCore.Components.csproj",
"src\\Components\\Components\\test\\Microsoft.AspNetCore.Components.Tests.csproj",
+ "src\\Components\\CustomElements\\src\\Microsoft.AspNetCore.Components.CustomElements.csproj",
"src\\Components\\Forms\\src\\Microsoft.AspNetCore.Components.Forms.csproj",
"src\\Components\\Forms\\test\\Microsoft.AspNetCore.Components.Forms.Tests.csproj",
"src\\Components\\Samples\\BlazorServerApp\\BlazorServerApp.csproj",
@@ -140,4 +141,4 @@
"src\\WebEncoders\\src\\Microsoft.Extensions.WebEncoders.csproj"
]
}
-} \ No newline at end of file
+}
diff --git a/src/Components/Components/src/RouteView.cs b/src/Components/Components/src/RouteView.cs
index 3fb7d853a7..349060c1e6 100644
--- a/src/Components/Components/src/RouteView.cs
+++ b/src/Components/Components/src/RouteView.cs
@@ -74,6 +74,7 @@ public class RouteView : IComponent
/// </summary>
/// <param name="builder">The <see cref="RenderTreeBuilder"/>.</param>
[UnconditionalSuppressMessage("Trimming", "IL2111", Justification = "Layout components are preserved because the LayoutAttribute constructor parameter is correctly annotated.")]
+ [UnconditionalSuppressMessage("Trimming", "IL2118", Justification = "Layout components are preserved because the LayoutAttribute constructor parameter is correctly annotated.")]
protected virtual void Render(RenderTreeBuilder builder)
{
var pageLayoutType = RouteData.PageType.GetCustomAttribute<LayoutAttribute>()?.LayoutType
diff --git a/src/Components/Components/src/Routing/RouteTableFactory.cs b/src/Components/Components/src/Routing/RouteTableFactory.cs
index de6d8f2bb9..ace100b649 100644
--- a/src/Components/Components/src/Routing/RouteTableFactory.cs
+++ b/src/Components/Components/src/Routing/RouteTableFactory.cs
@@ -88,6 +88,7 @@ internal static class RouteTableFactory
return Create(templatesByHandler);
}
+ [UnconditionalSuppressMessage("Trimming", "IL2067", Justification = "Application code does not get trimmed, and the framework does not define routable components.")]
internal static RouteTable Create(Dictionary<Type, string[]> templatesByHandler)
{
var routes = new List<RouteEntry>();
diff --git a/src/Components/CustomElements/src/JSComponentConfigurationExtensions.cs b/src/Components/CustomElements/src/JSComponentConfigurationExtensions.cs
new file mode 100644
index 0000000000..8ff0f184af
--- /dev/null
+++ b/src/Components/CustomElements/src/JSComponentConfigurationExtensions.cs
@@ -0,0 +1,21 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics.CodeAnalysis;
+
+namespace Microsoft.AspNetCore.Components.Web;
+
+/// <summary>
+/// Extension methods for registering custom elements from an <see cref="IJSComponentConfiguration"/>.
+/// </summary>
+public static class CustomElementsJSComponentConfigurationExtensions
+{
+ /// <summary>
+ /// Marks the specified component type as allowed for use as a custom element.
+ /// </summary>
+ /// <typeparam name="TComponent">The component type.</typeparam>
+ /// <param name="configuration">The <see cref="IJSComponentConfiguration"/>.</param>
+ /// <param name="identifier">A unique name for the custom element. This must conform to custom element naming rules, so it must contain a dash character.</param>
+ public static void RegisterCustomElement<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TComponent>(this IJSComponentConfiguration configuration, string identifier) where TComponent : IComponent
+ => configuration.RegisterForJavaScript<TComponent>(identifier, "registerBlazorCustomElement");
+}
diff --git a/src/Components/CustomElements/src/Microsoft.AspNetCore.Components.CustomElements.csproj b/src/Components/CustomElements/src/Microsoft.AspNetCore.Components.CustomElements.csproj
new file mode 100644
index 0000000000..95c2b5ab32
--- /dev/null
+++ b/src/Components/CustomElements/src/Microsoft.AspNetCore.Components.CustomElements.csproj
@@ -0,0 +1,86 @@
+<Project Sdk="Microsoft.NET.Sdk.Razor">
+
+ <Import Project="Sdk.props" Sdk="Yarn.MSBuild" Condition=" '$(DotNetBuildFromSource)' != 'true'" />
+
+ <PropertyGroup>
+ <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
+ <Description>Provides a mechanism for using Blazor components as custom HTML elements.</Description>
+ <IsTrimmable>true</IsTrimmable>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Reference Include="Microsoft.AspNetCore.Components.Web" />
+ </ItemGroup>
+
+ <PropertyGroup>
+ <YarnWorkingDir>$(MSBuildThisFileDirectory)js\</YarnWorkingDir>
+ <ResolveStaticWebAssetsInputsDependsOn>
+ CompileJs;
+ IncludeCompileJsOutput;
+ $(ResolveStaticWebAssetsInputsDependsOn)
+ </ResolveStaticWebAssetsInputsDependsOn>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <YarnInputs Include="$(YarnWorkingDir)**" Exclude="$(YarnWorkingDir)node_modules\**;$(YarnWorkingDir)*.d.ts;$(YarnWorkingDir)dist\**" />
+ <YarnOutputs Include="$(YarnWorkingDir)dist\$(Configuration)\BlazorCustomElements.js" />
+
+ <Content Remove="$(YarnWorkingDir)**" />
+ <None Include="$(YarnWorkingDir)*" Exclude="$(YarnWorkingDir)node_modules\**" />
+
+ <UpToDateCheckInput Include="@(YarnInputs)" Set="StaticWebassets" />
+ <UpToDateCheckInput Include="@(YarnOutputs)" Set="StaticWebassets" />
+ </ItemGroup>
+
+ <Target Name="_CreateJsHash" BeforeTargets="CompileJs" Condition="'$(BuildNodeJS)' != 'false' AND '$(DesignTimeBuild)' != 'true'">
+
+ <PropertyGroup>
+ <JsCompilationCacheFile>$(IntermediateOutputPath)js.cache</JsCompilationCacheFile>
+ </PropertyGroup>
+
+ <Hash ItemsToHash="@(YarnInputs)">
+ <Output TaskParameter="HashResult" PropertyName="_YarnInputsHash" />
+ </Hash>
+
+ <WriteLinesToFile Lines="$(_YarnInputsHash)" File="$(JsCompilationCacheFile)" Overwrite="True" WriteOnlyWhenDifferent="True" />
+
+ <ItemGroup>
+ <FileWrites Include="$(JsCompilationCacheFile)" />
+ </ItemGroup>
+
+ </Target>
+
+ <Target Name="CompileJs" Condition="'$(BuildNodeJS)' != 'false' AND '$(DesignTimeBuild)' != 'true'" Inputs="$(JsCompilationCacheFile)" Outputs="@(YarnOutputs)">
+ <Yarn Command="install --mutex network --frozen-lockfile" WorkingDirectory="$(YarnWorkingDir)" IgnoreStandardErrorWarningFormat="$(IgnoreYarnWarnings)" />
+ <Yarn Command="run build:production" WorkingDirectory="$(YarnWorkingDir)" Condition="'$(Configuration)' == 'Release'" IgnoreStandardErrorWarningFormat="$(IgnoreYarnWarnings)" />
+ <Yarn Command="run build:debug" WorkingDirectory="$(YarnWorkingDir)" Condition="'$(Configuration)' == 'Debug'" IgnoreStandardErrorWarningFormat="$(IgnoreYarnWarnings)" />
+
+ <Message Importance="high" Text="@(_JsBuildOutput->'Emitted %(FullPath)')" />
+
+ </Target>
+
+ <Target Name="IncludeCompileJsOutput">
+ <ItemGroup>
+ <_JsBuildOutput Include="$(YarnWorkingDir)dist\$(Configuration)\**" Exclude="$(YarnWorkingDir)dist\.gitignore" />
+ </ItemGroup>
+
+ <DefineStaticWebAssets Condition="'@(_JsBuildOutput)' != ''"
+ SourceType="Computed"
+ SourceId="$(PackageId)"
+ ContentRoot="$(YarnWorkingDir)dist\$(Configuration)\"
+ BasePath="_content\$(PackageId)"
+ CandidateAssets="@(_JsBuildOutput)"
+ RelativePathFilter="**.js"
+ >
+ <Output TaskParameter="Assets" ItemName="StaticWebAsset" />
+ </DefineStaticWebAssets>
+
+ <ItemGroup>
+ <_JsBuildOutput Include="$(YarnWorkingDir)dist\$(Configuration)\**" Exclude="$(YarnWorkingDir)dist\.gitignore" />
+ <FileWrites Include="$(_JsBuildOutput)" />
+ </ItemGroup>
+ </Target>
+
+ <Import Project="Sdk.targets" Sdk="Yarn.MSBuild" Condition=" '$(DotNetBuildFromSource)' != 'true'" />
+
+</Project>
diff --git a/src/Components/CustomElements/src/PublicAPI.Shipped.txt b/src/Components/CustomElements/src/PublicAPI.Shipped.txt
new file mode 100644
index 0000000000..ab058de62d
--- /dev/null
+++ b/src/Components/CustomElements/src/PublicAPI.Shipped.txt
@@ -0,0 +1 @@
+#nullable enable
diff --git a/src/Components/CustomElements/src/PublicAPI.Unshipped.txt b/src/Components/CustomElements/src/PublicAPI.Unshipped.txt
new file mode 100644
index 0000000000..d4fcfd03d8
--- /dev/null
+++ b/src/Components/CustomElements/src/PublicAPI.Unshipped.txt
@@ -0,0 +1,3 @@
+#nullable enable
+Microsoft.AspNetCore.Components.Web.CustomElementsJSComponentConfigurationExtensions
+static Microsoft.AspNetCore.Components.Web.CustomElementsJSComponentConfigurationExtensions.RegisterCustomElement<TComponent>(this Microsoft.AspNetCore.Components.Web.IJSComponentConfiguration! configuration, string! identifier) -> void
diff --git a/src/Components/CustomElements/src/js/BlazorCustomElements.ts b/src/Components/CustomElements/src/js/BlazorCustomElements.ts
new file mode 100644
index 0000000000..b963cc0274
--- /dev/null
+++ b/src/Components/CustomElements/src/js/BlazorCustomElements.ts
@@ -0,0 +1,156 @@
+declare const Blazor: any;
+
+// This function is called by the framework because RegisterAsCustomElement sets it as the initializer function
+(window as any).registerBlazorCustomElement = function defaultRegisterCustomElement(elementName: string, parameters: JSComponentParameter[]): void {
+ customElements.define(elementName, class ConfiguredBlazorCustomElement extends BlazorCustomElement {
+ static get observedAttributes() {
+ return BlazorCustomElement.getObservedAttributes(parameters);
+ }
+
+ constructor() {
+ super(parameters);
+ }
+ });
+}
+
+export class BlazorCustomElement extends HTMLElement {
+ private _attributeMappings: { [attributeName: string]: JSComponentParameter };
+ private _parameterValues: { [dotNetName: string]: any } = {};
+ private _addRootComponentPromise: Promise<any>;
+ private _hasPendingSetParameters = true; // The constructor will call setParameters, so it starts true
+ private _isDisposed = false;
+ private _disposalTimeoutHandle: any;
+
+ public renderIntoElement = this;
+
+ // Subclasses will need to call this if they want to retain the built-in behavior for knowing which
+ // attribute names to observe, since they have to return it from a static function
+ static getObservedAttributes(parameters: JSComponentParameter[]): string[] {
+ return parameters.map(p => dasherize(p.name));
+ }
+
+ constructor(parameters: JSComponentParameter[]) {
+ super();
+
+ // Keep track of how we'll map the attributes to parameters
+ this._attributeMappings = {};
+ parameters.forEach(parameter => {
+ const attributeName = dasherize(parameter.name);
+ this._attributeMappings[attributeName] = parameter;
+ });
+
+ // Defer until end of execution cycle so that (1) we know the heap is unlocked, and (2) the initial parameter
+ // values will be populated from the initial attributes before we send them to .NET
+ this._addRootComponentPromise = Promise.resolve().then(() => {
+ this._hasPendingSetParameters = false;
+ return Blazor.rootComponents.add(this.renderIntoElement, this.localName, this._parameterValues);
+ });
+
+ // Also allow assignment of parameters via properties. This is the only way to set complex-typed values.
+ for (const [attributeName, parameterInfo] of Object.entries(this._attributeMappings)) {
+ const dotNetName = parameterInfo.name;
+ Object.defineProperty(this, camelCase(dotNetName), {
+ get: () => this._parameterValues[dotNetName],
+ set: newValue => {
+ if (this.hasAttribute(attributeName)) {
+ // It's nice to keep the DOM in sync with the properties. This set a string representation
+ // of the value, but this will get overwritten with the original typed value before we send it to .NET
+ this.setAttribute(attributeName, newValue);
+ }
+
+ this._parameterValues[dotNetName] = newValue;
+ this._supplyUpdatedParameters();
+ }
+ });
+ }
+ }
+
+ connectedCallback() {
+ if (this._isDisposed) {
+ throw new Error(`Cannot connect component ${this.localName} to the document after it has been disposed.`);
+ }
+
+ clearTimeout(this._disposalTimeoutHandle);
+ }
+
+ disconnectedCallback() {
+ this._disposalTimeoutHandle = setTimeout(async () => {
+ this._isDisposed = true;
+ const rootComponent = await this._addRootComponentPromise;
+ rootComponent.dispose();
+ }, 1000);
+ }
+
+ attributeChangedCallback(name: string, oldValue: string, newValue: string) {
+ const parameterInfo = this._attributeMappings[name];
+ if (parameterInfo) {
+ this._parameterValues[parameterInfo.name] = BlazorCustomElement.parseAttributeValue(newValue, parameterInfo.type, parameterInfo.name);
+ this._supplyUpdatedParameters();
+ }
+ }
+
+ private async _supplyUpdatedParameters() {
+ if (!this._hasPendingSetParameters) {
+ this._hasPendingSetParameters = true;
+
+ // Continuation from here will always be async, so at the earliest it will be at
+ // the end of the current JS execution cycle
+ const rootComponent = await this._addRootComponentPromise;
+ if (!this._isDisposed) {
+ const setParametersPromise = rootComponent.setParameters(this._parameterValues);
+ this._hasPendingSetParameters = false; // We just snapshotted _parameterValues, so we need to start allowing new calls in case it changes further
+ await setParametersPromise;
+ }
+ }
+ }
+
+ static parseAttributeValue(attributeValue: string, type: JSComponentParameterType, parameterName: string): any {
+ switch (type) {
+ case 'string':
+ return attributeValue;
+ case 'boolean':
+ switch (attributeValue) {
+ case 'true':
+ case 'True':
+ return true;
+ case 'false':
+ case 'False':
+ return false;
+ default:
+ throw new Error(`Invalid boolean value '${attributeValue}' for parameter '${parameterName}'`);
+ }
+ case 'number':
+ const number = Number(attributeValue);
+ if (Number.isNaN(number)) {
+ throw new Error(`Invalid number value '${attributeValue}' for parameter '${parameterName}'`);
+ } else {
+ return number;
+ }
+ case 'boolean?':
+ return attributeValue ? BlazorCustomElement.parseAttributeValue(attributeValue, 'boolean', parameterName) : null;
+ case 'number?':
+ return attributeValue ? BlazorCustomElement.parseAttributeValue(attributeValue, 'number', parameterName) : null;
+ case 'object':
+ throw new Error(`The parameter '${parameterName}' accepts a complex-typed object so it cannot be set using an attribute. Try setting it as a element property instead.`);
+ default:
+ throw new Error(`Unknown type '${type}' for parameter '${parameterName}'`);
+ }
+ }
+}
+
+function dasherize(value: string): string {
+ return camelCase(value).replace(/([A-Z])/g, "-$1").toLowerCase();
+}
+
+function camelCase(value: string): string {
+ return value[0].toLowerCase() + value.substring(1);
+}
+
+interface JSComponentParameter {
+ name: string;
+ type: JSComponentParameterType;
+}
+
+// JSON-primitive types, plus for those whose .NET equivalent isn't nullable, a '?' to indicate nullability
+// This allows custom element authors to coerce attribute strings into the appropriate type
+type JSComponentParameterType = 'string' | 'boolean' | 'boolean?' | 'number' | 'number?' | 'object';
diff --git a/src/Components/CustomElements/src/js/babel.config.json b/src/Components/CustomElements/src/js/babel.config.json
new file mode 100644
index 0000000000..d48662a9cf
--- /dev/null
+++ b/src/Components/CustomElements/src/js/babel.config.json
@@ -0,0 +1,3 @@
+{
+ "presets": [ [ "@babel/preset-env", { "targets": { "node": true } } ] ]
+}
diff --git a/src/Components/CustomElements/src/js/dist/.gitignore b/src/Components/CustomElements/src/js/dist/.gitignore
new file mode 100644
index 0000000000..bd25200e2b
--- /dev/null
+++ b/src/Components/CustomElements/src/js/dist/.gitignore
@@ -0,0 +1,3 @@
+**/*.js
+**/*.js.map
+**/*.txt
diff --git a/src/Components/CustomElements/src/js/package.json b/src/Components/CustomElements/src/js/package.json
new file mode 100644
index 0000000000..a04c52e083
--- /dev/null
+++ b/src/Components/CustomElements/src/js/package.json
@@ -0,0 +1,29 @@
+{
+ "private": true,
+ "scripts": {
+ "preclean": "yarn install --mutex network --frozen-lockfile",
+ "clean": "node node_modules/rimraf/bin.js ./dist/Debug ./dist/Release",
+ "prebuild": "yarn run clean && yarn install --mutex network --frozen-lockfile",
+ "build": "yarn run build:debug && yarn run build:production",
+ "build:debug": "node node_modules/webpack-cli/bin/cli.js --mode development --config ./webpack.config.js",
+ "build:production": "node node_modules/webpack-cli/bin/cli.js --mode production --config ./webpack.config.js"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.15.0",
+ "@babel/preset-env": "^7.15.0",
+ "@typescript-eslint/eslint-plugin": "^5.26.0",
+ "@typescript-eslint/parser": "^5.26.0",
+ "eslint": "^8.16.0",
+ "inspectpack": "^4.7.1",
+ "rimraf": "^3.0.2",
+ "terser": "^5.13.1",
+ "ts-loader": "^9.2.5",
+ "typescript": "^4.4.2",
+ "webpack": "^5.72.1",
+ "webpack-cli": "^4.9.2"
+ },
+ "resolutions": {
+ "ansi-regex": "5.0.1",
+ "minimist": ">=1.2.6"
+ }
+}
diff --git a/src/Components/CustomElements/src/js/tsconfig.json b/src/Components/CustomElements/src/js/tsconfig.json
new file mode 100644
index 0000000000..13316f2a9e
--- /dev/null
+++ b/src/Components/CustomElements/src/js/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "compilerOptions": {
+ "target": "ES2019",
+ "module": "commonjs",
+ "lib": [ "DOM", "ES2019" ],
+ "sourceMap": true,
+ "strict": true,
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true
+ }
+}
diff --git a/src/Components/CustomElements/src/js/webpack.config.js b/src/Components/CustomElements/src/js/webpack.config.js
new file mode 100644
index 0000000000..5dacdcf860
--- /dev/null
+++ b/src/Components/CustomElements/src/js/webpack.config.js
@@ -0,0 +1,70 @@
+const path = require('path');
+const webpack = require('webpack');
+const TerserJsPlugin = require("terser-webpack-plugin");
+const { DuplicatesPlugin } = require("inspectpack/plugin");
+
+module.exports = (env, args) => ({
+ resolve: {
+ extensions: ['.ts', '.js'],
+ },
+ devtool: false, // Source maps configured below
+ module: {
+ rules: [{ test: /\.ts?$/, loader: 'ts-loader' }]
+ },
+ entry: {
+ 'BlazorCustomElements': './BlazorCustomElements.ts',
+ },
+ output: { path: path.join(__dirname, 'dist', args.mode == 'development' ? 'Debug' : 'Release'), filename: '[name].js' },
+ performance: {
+ maxAssetSize: 122880,
+ },
+ optimization: {
+ sideEffects: true,
+ concatenateModules: true,
+ providedExports: true,
+ usedExports: true,
+ innerGraph: true,
+ minimize: true,
+ minimizer: [new TerserJsPlugin({
+ terserOptions: {
+ ecma: 2019,
+ compress: {
+ passes: 3
+ },
+ mangle: {
+ },
+ module: false,
+ format: {
+ ecma: 2019
+ },
+ keep_classnames: false,
+ keep_fnames: false,
+ toplevel: true
+ }
+ })]
+ },
+ plugins: Array.prototype.concat.apply([
+ new webpack.DefinePlugin({
+ 'process.env.NODE_DEBUG': false,
+ 'Platform.isNode': false
+ }),
+ new DuplicatesPlugin({
+ emitErrors: false,
+ emitHandler: undefined,
+ ignoredPackages: undefined,
+ verbose: false
+ }),
+ ], args.mode !== 'development' ? [] : [
+ // In most cases we want to use external source map files
+ new webpack.SourceMapDevToolPlugin({
+ filename: '[name].js.map',
+ exclude: 'blazor.webview.js',
+ })
+ ]),
+ stats: {
+ warnings: true,
+ errors: true,
+ performance: true,
+ optimizationBailout: true
+ }
+});
diff --git a/src/Components/CustomElements/src/js/yarn.lock b/src/Components/CustomElements/src/js/yarn.lock
new file mode 100644
index 0000000000..ef949be751
--- /dev/null
+++ b/src/Components/CustomElements/src/js/yarn.lock
@@ -0,0 +1,2797 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@ampproject/remapping@^2.1.0":
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d"
+ integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==
+ dependencies:
+ "@jridgewell/gen-mapping" "^0.1.0"
+ "@jridgewell/trace-mapping" "^0.3.9"
+
+"@babel/code-frame@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789"
+ integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg==
+ dependencies:
+ "@babel/highlight" "^7.16.7"
+
+"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.17.10":
+ version "7.18.5"
+ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.5.tgz#acac0c839e317038c73137fbb6ef71a1d6238471"
+ integrity sha512-BxhE40PVCBxVEJsSBhB6UWyAuqJRxGsAw8BdHMJ3AKGydcwuWW4kOO3HmqBQAdcq/OP+/DlTVxLvsCzRTnZuGg==
+
+"@babel/core@^7.15.0":
+ version "7.18.5"
+ resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.5.tgz#c597fa680e58d571c28dda9827669c78cdd7f000"
+ integrity sha512-MGY8vg3DxMnctw0LdvSEojOsumc70g0t18gNyUdAZqB1Rpd1Bqo/svHGvt+UJ6JcGX+DIekGFDxxIWofBxLCnQ==
+ dependencies:
+ "@ampproject/remapping" "^2.1.0"
+ "@babel/code-frame" "^7.16.7"
+ "@babel/generator" "^7.18.2"
+ "@babel/helper-compilation-targets" "^7.18.2"
+ "@babel/helper-module-transforms" "^7.18.0"
+ "@babel/helpers" "^7.18.2"
+ "@babel/parser" "^7.18.5"
+ "@babel/template" "^7.16.7"
+ "@babel/traverse" "^7.18.5"
+ "@babel/types" "^7.18.4"
+ convert-source-map "^1.7.0"
+ debug "^4.1.0"
+ gensync "^1.0.0-beta.2"
+ json5 "^2.2.1"
+ semver "^6.3.0"
+
+"@babel/generator@^7.18.2":
+ version "7.18.2"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.2.tgz#33873d6f89b21efe2da63fe554460f3df1c5880d"
+ integrity sha512-W1lG5vUwFvfMd8HVXqdfbuG7RuaSrTCCD8cl8fP8wOivdbtbIg2Db3IWUcgvfxKbbn6ZBGYRW/Zk1MIwK49mgw==
+ dependencies:
+ "@babel/types" "^7.18.2"
+ "@jridgewell/gen-mapping" "^0.3.0"
+ jsesc "^2.5.1"
+
+"@babel/helper-annotate-as-pure@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862"
+ integrity sha512-s6t2w/IPQVTAET1HitoowRGXooX8mCgtuP5195wD/QJPV6wYjpujCGF7JuMODVX2ZAJOf1GT6DT9MHEZvLOFSw==
+ dependencies:
+ "@babel/types" "^7.16.7"
+
+"@babel/helper-builder-binary-assignment-operator-visitor@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz#38d138561ea207f0f69eb1626a418e4f7e6a580b"
+ integrity sha512-C6FdbRaxYjwVu/geKW4ZeQ0Q31AftgRcdSnZ5/jsH6BzCJbtvXvhpfkbkThYSuutZA7nCXpPR6AD9zd1dprMkA==
+ dependencies:
+ "@babel/helper-explode-assignable-expression" "^7.16.7"
+ "@babel/types" "^7.16.7"
+
+"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7", "@babel/helper-compilation-targets@^7.17.10", "@babel/helper-compilation-targets@^7.18.2":
+ version "7.18.2"
+ resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.2.tgz#67a85a10cbd5fc7f1457fec2e7f45441dc6c754b"
+ integrity sha512-s1jnPotJS9uQnzFtiZVBUxe67CuBa679oWFHpxYYnTpRL/1ffhyX44R9uYiXoa/pLXcY9H2moJta0iaanlk/rQ==
+ dependencies:
+ "@babel/compat-data" "^7.17.10"
+ "@babel/helper-validator-option" "^7.16.7"
+ browserslist "^4.20.2"
+ semver "^6.3.0"
+
+"@babel/helper-create-class-features-plugin@^7.17.12", "@babel/helper-create-class-features-plugin@^7.18.0":
+ version "7.18.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.0.tgz#fac430912606331cb075ea8d82f9a4c145a4da19"
+ integrity sha512-Kh8zTGR9de3J63e5nS0rQUdRs/kbtwoeQQ0sriS0lItjC96u8XXZN6lKpuyWd2coKSU13py/y+LTmThLuVX0Pg==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.16.7"
+ "@babel/helper-environment-visitor" "^7.16.7"
+ "@babel/helper-function-name" "^7.17.9"
+ "@babel/helper-member-expression-to-functions" "^7.17.7"
+ "@babel/helper-optimise-call-expression" "^7.16.7"
+ "@babel/helper-replace-supers" "^7.16.7"
+ "@babel/helper-split-export-declaration" "^7.16.7"
+
+"@babel/helper-create-regexp-features-plugin@^7.16.7", "@babel/helper-create-regexp-features-plugin@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.12.tgz#bb37ca467f9694bbe55b884ae7a5cc1e0084e4fd"
+ integrity sha512-b2aZrV4zvutr9AIa6/gA3wsZKRwTKYoDxYiFKcESS3Ug2GTXzwBEvMuuFLhCQpEnRXs1zng4ISAXSUxxKBIcxw==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.16.7"
+ regexpu-core "^5.0.1"
+
+"@babel/helper-define-polyfill-provider@^0.3.1":
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz#52411b445bdb2e676869e5a74960d2d3826d2665"
+ integrity sha512-J9hGMpJQmtWmj46B3kBHmL38UhJGhYX7eqkcq+2gsstyYt341HmPeWspihX43yVRA0mS+8GGk2Gckc7bY/HCmA==
+ dependencies:
+ "@babel/helper-compilation-targets" "^7.13.0"
+ "@babel/helper-module-imports" "^7.12.13"
+ "@babel/helper-plugin-utils" "^7.13.0"
+ "@babel/traverse" "^7.13.0"
+ debug "^4.1.1"
+ lodash.debounce "^4.0.8"
+ resolve "^1.14.2"
+ semver "^6.1.2"
+
+"@babel/helper-environment-visitor@^7.16.7", "@babel/helper-environment-visitor@^7.18.2":
+ version "7.18.2"
+ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.2.tgz#8a6d2dedb53f6bf248e31b4baf38739ee4a637bd"
+ integrity sha512-14GQKWkX9oJzPiQQ7/J36FTXcD4kSp8egKjO9nINlSKiHITRA9q/R74qu8S9xlc/b/yjsJItQUeeh3xnGN0voQ==
+
+"@babel/helper-explode-assignable-expression@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz#12a6d8522fdd834f194e868af6354e8650242b7a"
+ integrity sha512-KyUenhWMC8VrxzkGP0Jizjo4/Zx+1nNZhgocs+gLzyZyB8SHidhoq9KK/8Ato4anhwsivfkBLftky7gvzbZMtQ==
+ dependencies:
+ "@babel/types" "^7.16.7"
+
+"@babel/helper-function-name@^7.16.7", "@babel/helper-function-name@^7.17.9":
+ version "7.17.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.17.9.tgz#136fcd54bc1da82fcb47565cf16fd8e444b1ff12"
+ integrity sha512-7cRisGlVtiVqZ0MW0/yFB4atgpGLWEHUVYnb448hZK4x+vih0YO5UoS11XIYtZYqHd0dIPMdUSv8q5K4LdMnIg==
+ dependencies:
+ "@babel/template" "^7.16.7"
+ "@babel/types" "^7.17.0"
+
+"@babel/helper-hoist-variables@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246"
+ integrity sha512-m04d/0Op34H5v7pbZw6pSKP7weA6lsMvfiIAMeIvkY/R4xQtBSMFEigu9QTZ2qB/9l22vsxtM8a+Q8CzD255fg==
+ dependencies:
+ "@babel/types" "^7.16.7"
+
+"@babel/helper-member-expression-to-functions@^7.17.7":
+ version "7.17.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.17.7.tgz#a34013b57d8542a8c4ff8ba3f747c02452a4d8c4"
+ integrity sha512-thxXgnQ8qQ11W2wVUObIqDL4p148VMxkt5T/qpN5k2fboRyzFGFmKsTGViquyM5QHKUy48OZoca8kw4ajaDPyw==
+ dependencies:
+ "@babel/types" "^7.17.0"
+
+"@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437"
+ integrity sha512-LVtS6TqjJHFc+nYeITRo6VLXve70xmq7wPhWTqDJusJEgGmkAACWwMiTNrvfoQo6hEhFwAIixNkvB0jPXDL8Wg==
+ dependencies:
+ "@babel/types" "^7.16.7"
+
+"@babel/helper-module-transforms@^7.18.0":
+ version "7.18.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.0.tgz#baf05dec7a5875fb9235bd34ca18bad4e21221cd"
+ integrity sha512-kclUYSUBIjlvnzN2++K9f2qzYKFgjmnmjwL4zlmU5f8ZtzgWe8s0rUPSTGy2HmK4P8T52MQsS+HTQAgZd3dMEA==
+ dependencies:
+ "@babel/helper-environment-visitor" "^7.16.7"
+ "@babel/helper-module-imports" "^7.16.7"
+ "@babel/helper-simple-access" "^7.17.7"
+ "@babel/helper-split-export-declaration" "^7.16.7"
+ "@babel/helper-validator-identifier" "^7.16.7"
+ "@babel/template" "^7.16.7"
+ "@babel/traverse" "^7.18.0"
+ "@babel/types" "^7.18.0"
+
+"@babel/helper-optimise-call-expression@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz#a34e3560605abbd31a18546bd2aad3e6d9a174f2"
+ integrity sha512-EtgBhg7rd/JcnpZFXpBy0ze1YRfdm7BnBX4uKMBd3ixa3RGAE002JZB66FJyNH7g0F38U05pXmA5P8cBh7z+1w==
+ dependencies:
+ "@babel/types" "^7.16.7"
+
+"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.17.12", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.17.12.tgz#86c2347da5acbf5583ba0a10aed4c9bf9da9cf96"
+ integrity sha512-JDkf04mqtN3y4iAbO1hv9U2ARpPyPL1zqyWs/2WG1pgSq9llHFjStX5jdxb84himgJm+8Ng+x0oiWF/nw/XQKA==
+
+"@babel/helper-remap-async-to-generator@^7.16.8":
+ version "7.16.8"
+ resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz#29ffaade68a367e2ed09c90901986918d25e57e3"
+ integrity sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.16.7"
+ "@babel/helper-wrap-function" "^7.16.8"
+ "@babel/types" "^7.16.8"
+
+"@babel/helper-replace-supers@^7.16.7", "@babel/helper-replace-supers@^7.18.2":
+ version "7.18.2"
+ resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.18.2.tgz#41fdfcc9abaf900e18ba6e5931816d9062a7b2e0"
+ integrity sha512-XzAIyxx+vFnrOxiQrToSUOzUOn0e1J2Li40ntddek1Y69AXUTXoDJ40/D5RdjFu7s7qHiaeoTiempZcbuVXh2Q==
+ dependencies:
+ "@babel/helper-environment-visitor" "^7.18.2"
+ "@babel/helper-member-expression-to-functions" "^7.17.7"
+ "@babel/helper-optimise-call-expression" "^7.16.7"
+ "@babel/traverse" "^7.18.2"
+ "@babel/types" "^7.18.2"
+
+"@babel/helper-simple-access@^7.17.7", "@babel/helper-simple-access@^7.18.2":
+ version "7.18.2"
+ resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.2.tgz#4dc473c2169ac3a1c9f4a51cfcd091d1c36fcff9"
+ integrity sha512-7LIrjYzndorDY88MycupkpQLKS1AFfsVRm2k/9PtKScSy5tZq0McZTj+DiMRynboZfIqOKvo03pmhTaUgiD6fQ==
+ dependencies:
+ "@babel/types" "^7.18.2"
+
+"@babel/helper-skip-transparent-expression-wrappers@^7.16.0":
+ version "7.16.0"
+ resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz#0ee3388070147c3ae051e487eca3ebb0e2e8bb09"
+ integrity sha512-+il1gTy0oHwUsBQZyJvukbB4vPMdcYBrFHa0Uc4AizLxbq6BOYC51Rv4tWocX9BLBDLZ4kc6qUFpQ6HRgL+3zw==
+ dependencies:
+ "@babel/types" "^7.16.0"
+
+"@babel/helper-split-export-declaration@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b"
+ integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==
+ dependencies:
+ "@babel/types" "^7.16.7"
+
+"@babel/helper-validator-identifier@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad"
+ integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==
+
+"@babel/helper-validator-option@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23"
+ integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==
+
+"@babel/helper-wrap-function@^7.16.8":
+ version "7.16.8"
+ resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz#58afda087c4cd235de92f7ceedebca2c41274200"
+ integrity sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==
+ dependencies:
+ "@babel/helper-function-name" "^7.16.7"
+ "@babel/template" "^7.16.7"
+ "@babel/traverse" "^7.16.8"
+ "@babel/types" "^7.16.8"
+
+"@babel/helpers@^7.18.2":
+ version "7.18.2"
+ resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.2.tgz#970d74f0deadc3f5a938bfa250738eb4ac889384"
+ integrity sha512-j+d+u5xT5utcQSzrh9p+PaJX94h++KN+ng9b9WEJq7pkUPAd61FGqhjuUEdfknb3E/uDBb7ruwEeKkIxNJPIrg==
+ dependencies:
+ "@babel/template" "^7.16.7"
+ "@babel/traverse" "^7.18.2"
+ "@babel/types" "^7.18.2"
+
+"@babel/highlight@^7.16.7":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.17.12.tgz#257de56ee5afbd20451ac0a75686b6b404257351"
+ integrity sha512-7yykMVF3hfZY2jsHZEEgLc+3x4o1O+fYyULu11GynEUQNwB6lua+IIQn1FiJxNucd5UlyJryrwsOh8PL9Sn8Qg==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.16.7"
+ chalk "^2.0.0"
+ js-tokens "^4.0.0"
+
+"@babel/parser@^7.16.7", "@babel/parser@^7.18.5":
+ version "7.18.5"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.5.tgz#337062363436a893a2d22faa60be5bb37091c83c"
+ integrity sha512-YZWVaglMiplo7v8f1oMQ5ZPQr0vn7HPeZXxXWsxXJRjGVrzUFn9OxFQl1sb5wzfootjA/yChhW84BV+383FSOw==
+
+"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.17.12.tgz#1dca338caaefca368639c9ffb095afbd4d420b1e"
+ integrity sha512-xCJQXl4EeQ3J9C4yOmpTrtVGmzpm2iSzyxbkZHw7UCnZBftHpF/hpII80uWVyVrc40ytIClHjgWGTG1g/yB+aw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.17.12.tgz#0d498ec8f0374b1e2eb54b9cb2c4c78714c77753"
+ integrity sha512-/vt0hpIw0x4b6BLKUkwlvEoiGZYYLNZ96CzyHYPbtG2jZGz6LBe7/V+drYrc/d+ovrF9NBi0pmtvmNb/FsWtRQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0"
+ "@babel/plugin-proposal-optional-chaining" "^7.17.12"
+
+"@babel/plugin-proposal-async-generator-functions@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.17.12.tgz#094a417e31ce7e692d84bab06c8e2a607cbeef03"
+ integrity sha512-RWVvqD1ooLKP6IqWTA5GyFVX2isGEgC5iFxKzfYOIy/QEFdxYyCybBDtIGjipHpb9bDWHzcqGqFakf+mVmBTdQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/helper-remap-async-to-generator" "^7.16.8"
+ "@babel/plugin-syntax-async-generators" "^7.8.4"
+
+"@babel/plugin-proposal-class-properties@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.17.12.tgz#84f65c0cc247d46f40a6da99aadd6438315d80a4"
+ integrity sha512-U0mI9q8pW5Q9EaTHFPwSVusPMV/DV9Mm8p7csqROFLtIE9rBF5piLqyrBGigftALrBcsBGu4m38JneAe7ZDLXw==
+ dependencies:
+ "@babel/helper-create-class-features-plugin" "^7.17.12"
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-proposal-class-static-block@^7.18.0":
+ version "7.18.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.0.tgz#7d02253156e3c3793bdb9f2faac3a1c05f0ba710"
+ integrity sha512-t+8LsRMMDE74c6sV7KShIw13sqbqd58tlqNrsWoWBTIMw7SVQ0cZ905wLNS/FBCy/3PyooRHLFFlfrUNyyz5lA==
+ dependencies:
+ "@babel/helper-create-class-features-plugin" "^7.18.0"
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/plugin-syntax-class-static-block" "^7.14.5"
+
+"@babel/plugin-proposal-dynamic-import@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz#c19c897eaa46b27634a00fee9fb7d829158704b2"
+ integrity sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.16.7"
+ "@babel/plugin-syntax-dynamic-import" "^7.8.3"
+
+"@babel/plugin-proposal-export-namespace-from@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.17.12.tgz#b22864ccd662db9606edb2287ea5fd1709f05378"
+ integrity sha512-j7Ye5EWdwoXOpRmo5QmRyHPsDIe6+u70ZYZrd7uz+ebPYFKfRcLcNu3Ro0vOlJ5zuv8rU7xa+GttNiRzX56snQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/plugin-syntax-export-namespace-from" "^7.8.3"
+
+"@babel/plugin-proposal-json-strings@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.17.12.tgz#f4642951792437233216d8c1af370bb0fbff4664"
+ integrity sha512-rKJ+rKBoXwLnIn7n6o6fulViHMrOThz99ybH+hKHcOZbnN14VuMnH9fo2eHE69C8pO4uX1Q7t2HYYIDmv8VYkg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/plugin-syntax-json-strings" "^7.8.3"
+
+"@babel/plugin-proposal-logical-assignment-operators@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.17.12.tgz#c64a1bcb2b0a6d0ed2ff674fd120f90ee4b88a23"
+ integrity sha512-EqFo2s1Z5yy+JeJu7SFfbIUtToJTVlC61/C7WLKDntSw4Sz6JNAIfL7zQ74VvirxpjB5kz/kIx0gCcb+5OEo2Q==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
+
+"@babel/plugin-proposal-nullish-coalescing-operator@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.17.12.tgz#1e93079bbc2cbc756f6db6a1925157c4a92b94be"
+ integrity sha512-ws/g3FSGVzv+VH86+QvgtuJL/kR67xaEIF2x0iPqdDfYW6ra6JF3lKVBkWynRLcNtIC1oCTfDRVxmm2mKzy+ag==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
+
+"@babel/plugin-proposal-numeric-separator@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz#d6b69f4af63fb38b6ca2558442a7fb191236eba9"
+ integrity sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.16.7"
+ "@babel/plugin-syntax-numeric-separator" "^7.10.4"
+
+"@babel/plugin-proposal-object-rest-spread@^7.18.0":
+ version "7.18.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.0.tgz#79f2390c892ba2a68ec112eb0d895cfbd11155e8"
+ integrity sha512-nbTv371eTrFabDfHLElkn9oyf9VG+VKK6WMzhY2o4eHKaG19BToD9947zzGMO6I/Irstx9d8CwX6njPNIAR/yw==
+ dependencies:
+ "@babel/compat-data" "^7.17.10"
+ "@babel/helper-compilation-targets" "^7.17.10"
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/plugin-syntax-object-rest-spread" "^7.8.3"
+ "@babel/plugin-transform-parameters" "^7.17.12"
+
+"@babel/plugin-proposal-optional-catch-binding@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz#c623a430674ffc4ab732fd0a0ae7722b67cb74cf"
+ integrity sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.16.7"
+ "@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
+
+"@babel/plugin-proposal-optional-chaining@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.17.12.tgz#f96949e9bacace3a9066323a5cf90cfb9de67174"
+ integrity sha512-7wigcOs/Z4YWlK7xxjkvaIw84vGhDv/P1dFGQap0nHkc8gFKY/r+hXc8Qzf5k1gY7CvGIcHqAnOagVKJJ1wVOQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0"
+ "@babel/plugin-syntax-optional-chaining" "^7.8.3"
+
+"@babel/plugin-proposal-private-methods@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.17.12.tgz#c2ca3a80beb7539289938da005ad525a038a819c"
+ integrity sha512-SllXoxo19HmxhDWm3luPz+cPhtoTSKLJE9PXshsfrOzBqs60QP0r8OaJItrPhAj0d7mZMnNF0Y1UUggCDgMz1A==
+ dependencies:
+ "@babel/helper-create-class-features-plugin" "^7.17.12"
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-proposal-private-property-in-object@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.17.12.tgz#b02efb7f106d544667d91ae97405a9fd8c93952d"
+ integrity sha512-/6BtVi57CJfrtDNKfK5b66ydK2J5pXUKBKSPD2G1whamMuEnZWgoOIfO8Vf9F/DoD4izBLD/Au4NMQfruzzykg==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.16.7"
+ "@babel/helper-create-class-features-plugin" "^7.17.12"
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/plugin-syntax-private-property-in-object" "^7.14.5"
+
+"@babel/plugin-proposal-unicode-property-regex@^7.17.12", "@babel/plugin-proposal-unicode-property-regex@^7.4.4":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.17.12.tgz#3dbd7a67bd7f94c8238b394da112d86aaf32ad4d"
+ integrity sha512-Wb9qLjXf3ZazqXA7IvI7ozqRIXIGPtSo+L5coFmEkhTQK18ao4UDDD0zdTGAarmbLj2urpRwrc6893cu5Bfh0A==
+ dependencies:
+ "@babel/helper-create-regexp-features-plugin" "^7.17.12"
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-syntax-async-generators@^7.8.4":
+ version "7.8.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d"
+ integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-class-properties@^7.12.13":
+ version "7.12.13"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10"
+ integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.12.13"
+
+"@babel/plugin-syntax-class-static-block@^7.14.5":
+ version "7.14.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406"
+ integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.14.5"
+
+"@babel/plugin-syntax-dynamic-import@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3"
+ integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-export-namespace-from@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a"
+ integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.3"
+
+"@babel/plugin-syntax-import-assertions@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.17.12.tgz#58096a92b11b2e4e54b24c6a0cc0e5e607abcedd"
+ integrity sha512-n/loy2zkq9ZEM8tEOwON9wTQSTNDTDEz6NujPtJGLU7qObzT1N4c4YZZf8E6ATB2AjNQg/Ib2AIpO03EZaCehw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-syntax-json-strings@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a"
+ integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-logical-assignment-operators@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699"
+ integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9"
+ integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-numeric-separator@^7.10.4":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97"
+ integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-syntax-object-rest-spread@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871"
+ integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-optional-catch-binding@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1"
+ integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-optional-chaining@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a"
+ integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-private-property-in-object@^7.14.5":
+ version "7.14.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad"
+ integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.14.5"
+
+"@babel/plugin-syntax-top-level-await@^7.14.5":
+ version "7.14.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c"
+ integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.14.5"
+
+"@babel/plugin-transform-arrow-functions@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.17.12.tgz#dddd783b473b1b1537ef46423e3944ff24898c45"
+ integrity sha512-PHln3CNi/49V+mza4xMwrg+WGYevSF1oaiXaC2EQfdp4HWlSjRsrDXWJiQBKpP7749u6vQ9mcry2uuFOv5CXvA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-transform-async-to-generator@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.17.12.tgz#dbe5511e6b01eee1496c944e35cdfe3f58050832"
+ integrity sha512-J8dbrWIOO3orDzir57NRsjg4uxucvhby0L/KZuGsWDj0g7twWK3g7JhJhOrXtuXiw8MeiSdJ3E0OW9H8LYEzLQ==
+ dependencies:
+ "@babel/helper-module-imports" "^7.16.7"
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/helper-remap-async-to-generator" "^7.16.8"
+
+"@babel/plugin-transform-block-scoped-functions@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz#4d0d57d9632ef6062cdf354bb717102ee042a620"
+ integrity sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.16.7"
+
+"@babel/plugin-transform-block-scoping@^7.17.12":
+ version "7.18.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.4.tgz#7988627b3e9186a13e4d7735dc9c34a056613fb9"
+ integrity sha512-+Hq10ye+jlvLEogSOtq4mKvtk7qwcUQ1f0Mrueai866C82f844Yom2cttfJdMdqRLTxWpsbfbkIkOIfovyUQXw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-transform-classes@^7.17.12":
+ version "7.18.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.18.4.tgz#51310b812a090b846c784e47087fa6457baef814"
+ integrity sha512-e42NSG2mlKWgxKUAD9EJJSkZxR67+wZqzNxLSpc51T8tRU5SLFHsPmgYR5yr7sdgX4u+iHA1C5VafJ6AyImV3A==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.16.7"
+ "@babel/helper-environment-visitor" "^7.18.2"
+ "@babel/helper-function-name" "^7.17.9"
+ "@babel/helper-optimise-call-expression" "^7.16.7"
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/helper-replace-supers" "^7.18.2"
+ "@babel/helper-split-export-declaration" "^7.16.7"
+ globals "^11.1.0"
+
+"@babel/plugin-transform-computed-properties@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.17.12.tgz#bca616a83679698f3258e892ed422546e531387f"
+ integrity sha512-a7XINeplB5cQUWMg1E/GI1tFz3LfK021IjV1rj1ypE+R7jHm+pIHmHl25VNkZxtx9uuYp7ThGk8fur1HHG7PgQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-transform-destructuring@^7.18.0":
+ version "7.18.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.0.tgz#dc4f92587e291b4daa78aa20cc2d7a63aa11e858"
+ integrity sha512-Mo69klS79z6KEfrLg/1WkmVnB8javh75HX4pi2btjvlIoasuxilEyjtsQW6XPrubNd7AQy0MMaNIaQE4e7+PQw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-transform-dotall-regex@^7.16.7", "@babel/plugin-transform-dotall-regex@^7.4.4":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz#6b2d67686fab15fb6a7fd4bd895d5982cfc81241"
+ integrity sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==
+ dependencies:
+ "@babel/helper-create-regexp-features-plugin" "^7.16.7"
+ "@babel/helper-plugin-utils" "^7.16.7"
+
+"@babel/plugin-transform-duplicate-keys@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.17.12.tgz#a09aa709a3310013f8e48e0e23bc7ace0f21477c"
+ integrity sha512-EA5eYFUG6xeerdabina/xIoB95jJ17mAkR8ivx6ZSu9frKShBjpOGZPn511MTDTkiCO+zXnzNczvUM69YSf3Zw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-transform-exponentiation-operator@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz#efa9862ef97e9e9e5f653f6ddc7b665e8536fe9b"
+ integrity sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==
+ dependencies:
+ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.16.7"
+ "@babel/helper-plugin-utils" "^7.16.7"
+
+"@babel/plugin-transform-for-of@^7.18.1":
+ version "7.18.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.1.tgz#ed14b657e162b72afbbb2b4cdad277bf2bb32036"
+ integrity sha512-+TTB5XwvJ5hZbO8xvl2H4XaMDOAK57zF4miuC9qQJgysPNEAZZ9Z69rdF5LJkozGdZrjBIUAIyKUWRMmebI7vg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-transform-function-name@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz#5ab34375c64d61d083d7d2f05c38d90b97ec65cf"
+ integrity sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==
+ dependencies:
+ "@babel/helper-compilation-targets" "^7.16.7"
+ "@babel/helper-function-name" "^7.16.7"
+ "@babel/helper-plugin-utils" "^7.16.7"
+
+"@babel/plugin-transform-literals@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.17.12.tgz#97131fbc6bbb261487105b4b3edbf9ebf9c830ae"
+ integrity sha512-8iRkvaTjJciWycPIZ9k9duu663FT7VrBdNqNgxnVXEFwOIp55JWcZd23VBRySYbnS3PwQ3rGiabJBBBGj5APmQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-transform-member-expression-literals@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz#6e5dcf906ef8a098e630149d14c867dd28f92384"
+ integrity sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.16.7"
+
+"@babel/plugin-transform-modules-amd@^7.18.0":
+ version "7.18.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.0.tgz#7ef1002e67e36da3155edc8bf1ac9398064c02ed"
+ integrity sha512-h8FjOlYmdZwl7Xm2Ug4iX2j7Qy63NANI+NQVWQzv6r25fqgg7k2dZl03p95kvqNclglHs4FZ+isv4p1uXMA+QA==
+ dependencies:
+ "@babel/helper-module-transforms" "^7.18.0"
+ "@babel/helper-plugin-utils" "^7.17.12"
+ babel-plugin-dynamic-import-node "^2.3.3"
+
+"@babel/plugin-transform-modules-commonjs@^7.18.2":
+ version "7.18.2"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.2.tgz#1aa8efa2e2a6e818b6a7f2235fceaf09bdb31e9e"
+ integrity sha512-f5A865gFPAJAEE0K7F/+nm5CmAE3y8AWlMBG9unu5j9+tk50UQVK0QS8RNxSp7MJf0wh97uYyLWt3Zvu71zyOQ==
+ dependencies:
+ "@babel/helper-module-transforms" "^7.18.0"
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/helper-simple-access" "^7.18.2"
+ babel-plugin-dynamic-import-node "^2.3.3"
+
+"@babel/plugin-transform-modules-systemjs@^7.18.0":
+ version "7.18.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.5.tgz#87f11c44fbfd3657be000d4897e192d9cb535996"
+ integrity sha512-SEewrhPpcqMF1V7DhnEbhVJLrC+nnYfe1E0piZMZXBpxi9WvZqWGwpsk7JYP7wPWeqaBh4gyKlBhHJu3uz5g4Q==
+ dependencies:
+ "@babel/helper-hoist-variables" "^7.16.7"
+ "@babel/helper-module-transforms" "^7.18.0"
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/helper-validator-identifier" "^7.16.7"
+ babel-plugin-dynamic-import-node "^2.3.3"
+
+"@babel/plugin-transform-modules-umd@^7.18.0":
+ version "7.18.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.0.tgz#56aac64a2c2a1922341129a4597d1fd5c3ff020f"
+ integrity sha512-d/zZ8I3BWli1tmROLxXLc9A6YXvGK8egMxHp+E/rRwMh1Kip0AP77VwZae3snEJ33iiWwvNv2+UIIhfalqhzZA==
+ dependencies:
+ "@babel/helper-module-transforms" "^7.18.0"
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-transform-named-capturing-groups-regex@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.17.12.tgz#9c4a5a5966e0434d515f2675c227fd8cc8606931"
+ integrity sha512-vWoWFM5CKaTeHrdUJ/3SIOTRV+MBVGybOC9mhJkaprGNt5demMymDW24yC74avb915/mIRe3TgNb/d8idvnCRA==
+ dependencies:
+ "@babel/helper-create-regexp-features-plugin" "^7.17.12"
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-transform-new-target@^7.17.12":
+ version "7.18.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.5.tgz#8c228c4a07501dd12c95c5f23d1622131cc23931"
+ integrity sha512-TuRL5uGW4KXU6OsRj+mLp9BM7pO8e7SGNTEokQRRxHFkXYMFiy2jlKSZPFtI/mKORDzciH+hneskcSOp0gU8hg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-transform-object-super@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz#ac359cf8d32cf4354d27a46867999490b6c32a94"
+ integrity sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.16.7"
+ "@babel/helper-replace-supers" "^7.16.7"
+
+"@babel/plugin-transform-parameters@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.17.12.tgz#eb467cd9586ff5ff115a9880d6fdbd4a846b7766"
+ integrity sha512-6qW4rWo1cyCdq1FkYri7AHpauchbGLXpdwnYsfxFb+KtddHENfsY5JZb35xUwkK5opOLcJ3BNd2l7PhRYGlwIA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-transform-property-literals@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz#2dadac85155436f22c696c4827730e0fe1057a55"
+ integrity sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.16.7"
+
+"@babel/plugin-transform-regenerator@^7.18.0":
+ version "7.18.0"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.0.tgz#44274d655eb3f1af3f3a574ba819d3f48caf99d5"
+ integrity sha512-C8YdRw9uzx25HSIzwA7EM7YP0FhCe5wNvJbZzjVNHHPGVcDJ3Aie+qGYYdS1oVQgn+B3eAIJbWFLrJ4Jipv7nw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+ regenerator-transform "^0.15.0"
+
+"@babel/plugin-transform-reserved-words@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.17.12.tgz#7dbd349f3cdffba751e817cf40ca1386732f652f"
+ integrity sha512-1KYqwbJV3Co03NIi14uEHW8P50Md6KqFgt0FfpHdK6oyAHQVTosgPuPSiWud1HX0oYJ1hGRRlk0fP87jFpqXZA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-transform-shorthand-properties@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz#e8549ae4afcf8382f711794c0c7b6b934c5fbd2a"
+ integrity sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.16.7"
+
+"@babel/plugin-transform-spread@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.17.12.tgz#c112cad3064299f03ea32afed1d659223935d1f5"
+ integrity sha512-9pgmuQAtFi3lpNUstvG9nGfk9DkrdmWNp9KeKPFmuZCpEnxRzYlS8JgwPjYj+1AWDOSvoGN0H30p1cBOmT/Svg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0"
+
+"@babel/plugin-transform-sticky-regex@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz#c84741d4f4a38072b9a1e2e3fd56d359552e8660"
+ integrity sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.16.7"
+
+"@babel/plugin-transform-template-literals@^7.18.2":
+ version "7.18.2"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.2.tgz#31ed6915721864847c48b656281d0098ea1add28"
+ integrity sha512-/cmuBVw9sZBGZVOMkpAEaVLwm4JmK2GZ1dFKOGGpMzEHWFmyZZ59lUU0PdRr8YNYeQdNzTDwuxP2X2gzydTc9g==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-transform-typeof-symbol@^7.17.12":
+ version "7.17.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.17.12.tgz#0f12f57ac35e98b35b4ed34829948d42bd0e6889"
+ integrity sha512-Q8y+Jp7ZdtSPXCThB6zjQ74N3lj0f6TDh1Hnf5B+sYlzQ8i5Pjp8gW0My79iekSpT4WnI06blqP6DT0OmaXXmw==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.17.12"
+
+"@babel/plugin-transform-unicode-escapes@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz#da8717de7b3287a2c6d659750c964f302b31ece3"
+ integrity sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.16.7"
+
+"@babel/plugin-transform-unicode-regex@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz#0f7aa4a501198976e25e82702574c34cfebe9ef2"
+ integrity sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==
+ dependencies:
+ "@babel/helper-create-regexp-features-plugin" "^7.16.7"
+ "@babel/helper-plugin-utils" "^7.16.7"
+
+"@babel/preset-env@^7.15.0":
+ version "7.18.2"
+ resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.18.2.tgz#f47d3000a098617926e674c945d95a28cb90977a"
+ integrity sha512-PfpdxotV6afmXMU47S08F9ZKIm2bJIQ0YbAAtDfIENX7G1NUAXigLREh69CWDjtgUy7dYn7bsMzkgdtAlmS68Q==
+ dependencies:
+ "@babel/compat-data" "^7.17.10"
+ "@babel/helper-compilation-targets" "^7.18.2"
+ "@babel/helper-plugin-utils" "^7.17.12"
+ "@babel/helper-validator-option" "^7.16.7"
+ "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.17.12"
+ "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.17.12"
+ "@babel/plugin-proposal-async-generator-functions" "^7.17.12"
+ "@babel/plugin-proposal-class-properties" "^7.17.12"
+ "@babel/plugin-proposal-class-static-block" "^7.18.0"
+ "@babel/plugin-proposal-dynamic-import" "^7.16.7"
+ "@babel/plugin-proposal-export-namespace-from" "^7.17.12"
+ "@babel/plugin-proposal-json-strings" "^7.17.12"
+ "@babel/plugin-proposal-logical-assignment-operators" "^7.17.12"
+ "@babel/plugin-proposal-nullish-coalescing-operator" "^7.17.12"
+ "@babel/plugin-proposal-numeric-separator" "^7.16.7"
+ "@babel/plugin-proposal-object-rest-spread" "^7.18.0"
+ "@babel/plugin-proposal-optional-catch-binding" "^7.16.7"
+ "@babel/plugin-proposal-optional-chaining" "^7.17.12"
+ "@babel/plugin-proposal-private-methods" "^7.17.12"
+ "@babel/plugin-proposal-private-property-in-object" "^7.17.12"
+ "@babel/plugin-proposal-unicode-property-regex" "^7.17.12"
+ "@babel/plugin-syntax-async-generators" "^7.8.4"
+ "@babel/plugin-syntax-class-properties" "^7.12.13"
+ "@babel/plugin-syntax-class-static-block" "^7.14.5"
+ "@babel/plugin-syntax-dynamic-import" "^7.8.3"
+ "@babel/plugin-syntax-export-namespace-from" "^7.8.3"
+ "@babel/plugin-syntax-import-assertions" "^7.17.12"
+ "@babel/plugin-syntax-json-strings" "^7.8.3"
+ "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
+ "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
+ "@babel/plugin-syntax-numeric-separator" "^7.10.4"
+ "@babel/plugin-syntax-object-rest-spread" "^7.8.3"
+ "@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
+ "@babel/plugin-syntax-optional-chaining" "^7.8.3"
+ "@babel/plugin-syntax-private-property-in-object" "^7.14.5"
+ "@babel/plugin-syntax-top-level-await" "^7.14.5"
+ "@babel/plugin-transform-arrow-functions" "^7.17.12"
+ "@babel/plugin-transform-async-to-generator" "^7.17.12"
+ "@babel/plugin-transform-block-scoped-functions" "^7.16.7"
+ "@babel/plugin-transform-block-scoping" "^7.17.12"
+ "@babel/plugin-transform-classes" "^7.17.12"
+ "@babel/plugin-transform-computed-properties" "^7.17.12"
+ "@babel/plugin-transform-destructuring" "^7.18.0"
+ "@babel/plugin-transform-dotall-regex" "^7.16.7"
+ "@babel/plugin-transform-duplicate-keys" "^7.17.12"
+ "@babel/plugin-transform-exponentiation-operator" "^7.16.7"
+ "@babel/plugin-transform-for-of" "^7.18.1"
+ "@babel/plugin-transform-function-name" "^7.16.7"
+ "@babel/plugin-transform-literals" "^7.17.12"
+ "@babel/plugin-transform-member-expression-literals" "^7.16.7"
+ "@babel/plugin-transform-modules-amd" "^7.18.0"
+ "@babel/plugin-transform-modules-commonjs" "^7.18.2"
+ "@babel/plugin-transform-modules-systemjs" "^7.18.0"
+ "@babel/plugin-transform-modules-umd" "^7.18.0"
+ "@babel/plugin-transform-named-capturing-groups-regex" "^7.17.12"
+ "@babel/plugin-transform-new-target" "^7.17.12"
+ "@babel/plugin-transform-object-super" "^7.16.7"
+ "@babel/plugin-transform-parameters" "^7.17.12"
+ "@babel/plugin-transform-property-literals" "^7.16.7"
+ "@babel/plugin-transform-regenerator" "^7.18.0"
+ "@babel/plugin-transform-reserved-words" "^7.17.12"
+ "@babel/plugin-transform-shorthand-properties" "^7.16.7"
+ "@babel/plugin-transform-spread" "^7.17.12"
+ "@babel/plugin-transform-sticky-regex" "^7.16.7"
+ "@babel/plugin-transform-template-literals" "^7.18.2"
+ "@babel/plugin-transform-typeof-symbol" "^7.17.12"
+ "@babel/plugin-transform-unicode-escapes" "^7.16.7"
+ "@babel/plugin-transform-unicode-regex" "^7.16.7"
+ "@babel/preset-modules" "^0.1.5"
+ "@babel/types" "^7.18.2"
+ babel-plugin-polyfill-corejs2 "^0.3.0"
+ babel-plugin-polyfill-corejs3 "^0.5.0"
+ babel-plugin-polyfill-regenerator "^0.3.0"
+ core-js-compat "^3.22.1"
+ semver "^6.3.0"
+
+"@babel/preset-modules@^0.1.5":
+ version "0.1.5"
+ resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9"
+ integrity sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@babel/plugin-proposal-unicode-property-regex" "^7.4.4"
+ "@babel/plugin-transform-dotall-regex" "^7.4.4"
+ "@babel/types" "^7.4.4"
+ esutils "^2.0.2"
+
+"@babel/runtime@^7.8.4":
+ version "7.18.3"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.3.tgz#c7b654b57f6f63cf7f8b418ac9ca04408c4579f4"
+ integrity sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug==
+ dependencies:
+ regenerator-runtime "^0.13.4"
+
+"@babel/template@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155"
+ integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==
+ dependencies:
+ "@babel/code-frame" "^7.16.7"
+ "@babel/parser" "^7.16.7"
+ "@babel/types" "^7.16.7"
+
+"@babel/traverse@^7.13.0", "@babel/traverse@^7.16.8", "@babel/traverse@^7.18.0", "@babel/traverse@^7.18.2", "@babel/traverse@^7.18.5":
+ version "7.18.5"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.5.tgz#94a8195ad9642801837988ab77f36e992d9a20cd"
+ integrity sha512-aKXj1KT66sBj0vVzk6rEeAO6Z9aiiQ68wfDgge3nHhA/my6xMM/7HGQUNumKZaoa2qUPQ5whJG9aAifsxUKfLA==
+ dependencies:
+ "@babel/code-frame" "^7.16.7"
+ "@babel/generator" "^7.18.2"
+ "@babel/helper-environment-visitor" "^7.18.2"
+ "@babel/helper-function-name" "^7.17.9"
+ "@babel/helper-hoist-variables" "^7.16.7"
+ "@babel/helper-split-export-declaration" "^7.16.7"
+ "@babel/parser" "^7.18.5"
+ "@babel/types" "^7.18.4"
+ debug "^4.1.0"
+ globals "^11.1.0"
+
+"@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.17.0", "@babel/types@^7.18.0", "@babel/types@^7.18.2", "@babel/types@^7.18.4", "@babel/types@^7.4.4":
+ version "7.18.4"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.4.tgz#27eae9b9fd18e9dccc3f9d6ad051336f307be354"
+ integrity sha512-ThN1mBcMq5pG/Vm2IcBmPPfyPXbd8S02rS+OBIDENdufvqC7Z/jHPCv9IcP01277aKtDI8g/2XysBN4hA8niiw==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.16.7"
+ to-fast-properties "^2.0.0"
+
+"@discoveryjs/json-ext@^0.5.0":
+ version "0.5.7"
+ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70"
+ integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==
+
+"@eslint/eslintrc@^1.3.0":
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f"
+ integrity sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==
+ dependencies:
+ ajv "^6.12.4"
+ debug "^4.3.2"
+ espree "^9.3.2"
+ globals "^13.15.0"
+ ignore "^5.2.0"
+ import-fresh "^3.2.1"
+ js-yaml "^4.1.0"
+ minimatch "^3.1.2"
+ strip-json-comments "^3.1.1"
+
+"@humanwhocodes/config-array@^0.9.2":
+ version "0.9.5"
+ resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.5.tgz#2cbaf9a89460da24b5ca6531b8bbfc23e1df50c7"
+ integrity sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==
+ dependencies:
+ "@humanwhocodes/object-schema" "^1.2.1"
+ debug "^4.1.1"
+ minimatch "^3.0.4"
+
+"@humanwhocodes/object-schema@^1.2.1":
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
+ integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
+
+"@jridgewell/gen-mapping@^0.1.0":
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996"
+ integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==
+ dependencies:
+ "@jridgewell/set-array" "^1.0.0"
+ "@jridgewell/sourcemap-codec" "^1.4.10"
+
+"@jridgewell/gen-mapping@^0.3.0":
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.1.tgz#cf92a983c83466b8c0ce9124fadeaf09f7c66ea9"
+ integrity sha512-GcHwniMlA2z+WFPWuY8lp3fsza0I8xPFMWL5+n8LYyP6PSvPrXf4+n8stDHZY2DM0zy9sVkRDy1jDI4XGzYVqg==
+ dependencies:
+ "@jridgewell/set-array" "^1.0.0"
+ "@jridgewell/sourcemap-codec" "^1.4.10"
+ "@jridgewell/trace-mapping" "^0.3.9"
+
+"@jridgewell/resolve-uri@^3.0.3":
+ version "3.0.7"
+ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz#30cd49820a962aff48c8fffc5cd760151fca61fe"
+ integrity sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA==
+
+"@jridgewell/set-array@^1.0.0":
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.1.tgz#36a6acc93987adcf0ba50c66908bd0b70de8afea"
+ integrity sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ==
+
+"@jridgewell/source-map@^0.3.2":
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb"
+ integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==
+ dependencies:
+ "@jridgewell/gen-mapping" "^0.3.0"
+ "@jridgewell/trace-mapping" "^0.3.9"
+
+"@jridgewell/sourcemap-codec@^1.4.10":
+ version "1.4.13"
+ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz#b6461fb0c2964356c469e115f504c95ad97ab88c"
+ integrity sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w==
+
+"@jridgewell/trace-mapping@^0.3.7", "@jridgewell/trace-mapping@^0.3.9":
+ version "0.3.13"
+ resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz#dcfe3e95f224c8fe97a87a5235defec999aa92ea"
+ integrity sha512-o1xbKhp9qnIAoHJSWd6KlCZfqslL4valSF81H8ImioOAxluWYWOpWkpyktY2vnt4tbrX9XYaxovq6cgowaJp2w==
+ dependencies:
+ "@jridgewell/resolve-uri" "^3.0.3"
+ "@jridgewell/sourcemap-codec" "^1.4.10"
+
+"@nodelib/fs.scandir@2.1.5":
+ version "2.1.5"
+ resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
+ integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==
+ dependencies:
+ "@nodelib/fs.stat" "2.0.5"
+ run-parallel "^1.1.9"
+
+"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
+ integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
+
+"@nodelib/fs.walk@^1.2.3":
+ version "1.2.8"
+ resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
+ integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
+ dependencies:
+ "@nodelib/fs.scandir" "2.1.5"
+ fastq "^1.6.0"
+
+"@types/eslint-scope@^3.7.3":
+ version "3.7.3"
+ resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.3.tgz#125b88504b61e3c8bc6f870882003253005c3224"
+ integrity sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==
+ dependencies:
+ "@types/eslint" "*"
+ "@types/estree" "*"
+
+"@types/eslint@*":
+ version "8.4.3"
+ resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.4.3.tgz#5c92815a3838b1985c90034cd85f26f59d9d0ece"
+ integrity sha512-YP1S7YJRMPs+7KZKDb9G63n8YejIwW9BALq7a5j2+H4yl6iOv9CB29edho+cuFRrvmJbbaH2yiVChKLJVysDGw==
+ dependencies:
+ "@types/estree" "*"
+ "@types/json-schema" "*"
+
+"@types/estree@*", "@types/estree@^0.0.51":
+ version "0.0.51"
+ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40"
+ integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==
+
+"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
+ version "7.0.11"
+ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
+ integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
+
+"@types/node@*":
+ version "18.0.0"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.0.tgz#67c7b724e1bcdd7a8821ce0d5ee184d3b4dd525a"
+ integrity sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA==
+
+"@typescript-eslint/eslint-plugin@^5.26.0":
+ version "5.29.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.29.0.tgz#c67794d2b0fd0b4a47f50266088acdc52a08aab6"
+ integrity sha512-kgTsISt9pM53yRFQmLZ4npj99yGl3x3Pl7z4eA66OuTzAGC4bQB5H5fuLwPnqTKU3yyrrg4MIhjF17UYnL4c0w==
+ dependencies:
+ "@typescript-eslint/scope-manager" "5.29.0"
+ "@typescript-eslint/type-utils" "5.29.0"
+ "@typescript-eslint/utils" "5.29.0"
+ debug "^4.3.4"
+ functional-red-black-tree "^1.0.1"
+ ignore "^5.2.0"
+ regexpp "^3.2.0"
+ semver "^7.3.7"
+ tsutils "^3.21.0"
+
+"@typescript-eslint/parser@^5.26.0":
+ version "5.29.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.29.0.tgz#41314b195b34d44ff38220caa55f3f93cfca43cf"
+ integrity sha512-ruKWTv+x0OOxbzIw9nW5oWlUopvP/IQDjB5ZqmTglLIoDTctLlAJpAQFpNPJP/ZI7hTT9sARBosEfaKbcFuECw==
+ dependencies:
+ "@typescript-eslint/scope-manager" "5.29.0"
+ "@typescript-eslint/types" "5.29.0"
+ "@typescript-eslint/typescript-estree" "5.29.0"
+ debug "^4.3.4"
+
+"@typescript-eslint/scope-manager@5.29.0":
+ version "5.29.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.29.0.tgz#2a6a32e3416cb133e9af8dcf54bf077a916aeed3"
+ integrity sha512-etbXUT0FygFi2ihcxDZjz21LtC+Eps9V2xVx09zFoN44RRHPrkMflidGMI+2dUs821zR1tDS6Oc9IXxIjOUZwA==
+ dependencies:
+ "@typescript-eslint/types" "5.29.0"
+ "@typescript-eslint/visitor-keys" "5.29.0"
+
+"@typescript-eslint/type-utils@5.29.0":
+ version "5.29.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.29.0.tgz#241918001d164044020b37d26d5b9f4e37cc3d5d"
+ integrity sha512-JK6bAaaiJozbox3K220VRfCzLa9n0ib/J+FHIwnaV3Enw/TO267qe0pM1b1QrrEuy6xun374XEAsRlA86JJnyg==
+ dependencies:
+ "@typescript-eslint/utils" "5.29.0"
+ debug "^4.3.4"
+ tsutils "^3.21.0"
+
+"@typescript-eslint/types@5.29.0":
+ version "5.29.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.29.0.tgz#7861d3d288c031703b2d97bc113696b4d8c19aab"
+ integrity sha512-X99VbqvAXOMdVyfFmksMy3u8p8yoRGITgU1joBJPzeYa0rhdf5ok9S56/itRoUSh99fiDoMtarSIJXo7H/SnOg==
+
+"@typescript-eslint/typescript-estree@5.29.0":
+ version "5.29.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.29.0.tgz#e83d19aa7fd2e74616aab2f25dfbe4de4f0b5577"
+ integrity sha512-mQvSUJ/JjGBdvo+1LwC+GY2XmSYjK1nAaVw2emp/E61wEVYEyibRHCqm1I1vEKbXCpUKuW4G7u9ZCaZhJbLoNQ==
+ dependencies:
+ "@typescript-eslint/types" "5.29.0"
+ "@typescript-eslint/visitor-keys" "5.29.0"
+ debug "^4.3.4"
+ globby "^11.1.0"
+ is-glob "^4.0.3"
+ semver "^7.3.7"
+ tsutils "^3.21.0"
+
+"@typescript-eslint/utils@5.29.0":
+ version "5.29.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.29.0.tgz#775046effd5019667bd086bcf326acbe32cd0082"
+ integrity sha512-3Eos6uP1nyLOBayc/VUdKZikV90HahXE5Dx9L5YlSd/7ylQPXhLk1BYb29SDgnBnTp+jmSZUU0QxUiyHgW4p7A==
+ dependencies:
+ "@types/json-schema" "^7.0.9"
+ "@typescript-eslint/scope-manager" "5.29.0"
+ "@typescript-eslint/types" "5.29.0"
+ "@typescript-eslint/typescript-estree" "5.29.0"
+ eslint-scope "^5.1.1"
+ eslint-utils "^3.0.0"
+
+"@typescript-eslint/visitor-keys@5.29.0":
+ version "5.29.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.29.0.tgz#7a4749fa7ef5160c44a451bf060ac1dc6dfb77ee"
+ integrity sha512-Hpb/mCWsjILvikMQoZIE3voc9wtQcS0A9FUw3h8bhr9UxBdtI/tw1ZDZUOXHXLOVMedKCH5NxyzATwnU78bWCQ==
+ dependencies:
+ "@typescript-eslint/types" "5.29.0"
+ eslint-visitor-keys "^3.3.0"
+
+"@webassemblyjs/ast@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7"
+ integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==
+ dependencies:
+ "@webassemblyjs/helper-numbers" "1.11.1"
+ "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
+
+"@webassemblyjs/floating-point-hex-parser@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f"
+ integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==
+
+"@webassemblyjs/helper-api-error@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16"
+ integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==
+
+"@webassemblyjs/helper-buffer@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5"
+ integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==
+
+"@webassemblyjs/helper-numbers@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae"
+ integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==
+ dependencies:
+ "@webassemblyjs/floating-point-hex-parser" "1.11.1"
+ "@webassemblyjs/helper-api-error" "1.11.1"
+ "@xtuc/long" "4.2.2"
+
+"@webassemblyjs/helper-wasm-bytecode@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1"
+ integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==
+
+"@webassemblyjs/helper-wasm-section@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a"
+ integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==
+ dependencies:
+ "@webassemblyjs/ast" "1.11.1"
+ "@webassemblyjs/helper-buffer" "1.11.1"
+ "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
+ "@webassemblyjs/wasm-gen" "1.11.1"
+
+"@webassemblyjs/ieee754@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614"
+ integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==
+ dependencies:
+ "@xtuc/ieee754" "^1.2.0"
+
+"@webassemblyjs/leb128@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5"
+ integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==
+ dependencies:
+ "@xtuc/long" "4.2.2"
+
+"@webassemblyjs/utf8@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff"
+ integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==
+
+"@webassemblyjs/wasm-edit@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6"
+ integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==
+ dependencies:
+ "@webassemblyjs/ast" "1.11.1"
+ "@webassemblyjs/helper-buffer" "1.11.1"
+ "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
+ "@webassemblyjs/helper-wasm-section" "1.11.1"
+ "@webassemblyjs/wasm-gen" "1.11.1"
+ "@webassemblyjs/wasm-opt" "1.11.1"
+ "@webassemblyjs/wasm-parser" "1.11.1"
+ "@webassemblyjs/wast-printer" "1.11.1"
+
+"@webassemblyjs/wasm-gen@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76"
+ integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==
+ dependencies:
+ "@webassemblyjs/ast" "1.11.1"
+ "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
+ "@webassemblyjs/ieee754" "1.11.1"
+ "@webassemblyjs/leb128" "1.11.1"
+ "@webassemblyjs/utf8" "1.11.1"
+
+"@webassemblyjs/wasm-opt@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2"
+ integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==
+ dependencies:
+ "@webassemblyjs/ast" "1.11.1"
+ "@webassemblyjs/helper-buffer" "1.11.1"
+ "@webassemblyjs/wasm-gen" "1.11.1"
+ "@webassemblyjs/wasm-parser" "1.11.1"
+
+"@webassemblyjs/wasm-parser@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199"
+ integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==
+ dependencies:
+ "@webassemblyjs/ast" "1.11.1"
+ "@webassemblyjs/helper-api-error" "1.11.1"
+ "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
+ "@webassemblyjs/ieee754" "1.11.1"
+ "@webassemblyjs/leb128" "1.11.1"
+ "@webassemblyjs/utf8" "1.11.1"
+
+"@webassemblyjs/wast-printer@1.11.1":
+ version "1.11.1"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0"
+ integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==
+ dependencies:
+ "@webassemblyjs/ast" "1.11.1"
+ "@xtuc/long" "4.2.2"
+
+"@webpack-cli/configtest@^1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.2.0.tgz#7b20ce1c12533912c3b217ea68262365fa29a6f5"
+ integrity sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==
+
+"@webpack-cli/info@^1.5.0":
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.5.0.tgz#6c78c13c5874852d6e2dd17f08a41f3fe4c261b1"
+ integrity sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==
+ dependencies:
+ envinfo "^7.7.3"
+
+"@webpack-cli/serve@^1.7.0":
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.7.0.tgz#e1993689ac42d2b16e9194376cfb6753f6254db1"
+ integrity sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==
+
+"@xtuc/ieee754@^1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
+ integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==
+
+"@xtuc/long@4.2.2":
+ version "4.2.2"
+ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d"
+ integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==
+
+acorn-import-assertions@^1.7.6:
+ version "1.8.0"
+ resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9"
+ integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==
+
+acorn-jsx@^5.3.2:
+ version "5.3.2"
+ resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
+ integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
+
+acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.1:
+ version "8.7.1"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30"
+ integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==
+
+ajv-keywords@^3.5.2:
+ version "3.5.2"
+ resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
+ integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
+
+ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5:
+ version "6.12.6"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
+ integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
+ dependencies:
+ fast-deep-equal "^3.1.1"
+ fast-json-stable-stringify "^2.0.0"
+ json-schema-traverse "^0.4.1"
+ uri-js "^4.2.2"
+
+ansi-regex@5.0.1, ansi-regex@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
+ integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
+
+ansi-styles@^3.2.1:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
+ integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
+ dependencies:
+ color-convert "^1.9.0"
+
+ansi-styles@^4.0.0, ansi-styles@^4.1.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+ integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+ dependencies:
+ color-convert "^2.0.1"
+
+argparse@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
+ integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
+
+array-union@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
+ integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
+
+babel-plugin-dynamic-import-node@^2.3.3:
+ version "2.3.3"
+ resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3"
+ integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==
+ dependencies:
+ object.assign "^4.1.0"
+
+babel-plugin-polyfill-corejs2@^0.3.0:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.1.tgz#440f1b70ccfaabc6b676d196239b138f8a2cfba5"
+ integrity sha512-v7/T6EQcNfVLfcN2X8Lulb7DjprieyLWJK/zOWH5DUYcAgex9sP3h25Q+DLsX9TloXe3y1O8l2q2Jv9q8UVB9w==
+ dependencies:
+ "@babel/compat-data" "^7.13.11"
+ "@babel/helper-define-polyfill-provider" "^0.3.1"
+ semver "^6.1.1"
+
+babel-plugin-polyfill-corejs3@^0.5.0:
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz#aabe4b2fa04a6e038b688c5e55d44e78cd3a5f72"
+ integrity sha512-G3uJih0XWiID451fpeFaYGVuxHEjzKTHtc9uGFEjR6hHrvNzeS/PX+LLLcetJcytsB5m4j+K3o/EpXJNb/5IEQ==
+ dependencies:
+ "@babel/helper-define-polyfill-provider" "^0.3.1"
+ core-js-compat "^3.21.0"
+
+babel-plugin-polyfill-regenerator@^0.3.0:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz#2c0678ea47c75c8cc2fbb1852278d8fb68233990"
+ integrity sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A==
+ dependencies:
+ "@babel/helper-define-polyfill-provider" "^0.3.1"
+
+balanced-match@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
+ integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
+
+brace-expansion@^1.1.7:
+ version "1.1.11"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+ integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+ dependencies:
+ balanced-match "^1.0.0"
+ concat-map "0.0.1"
+
+braces@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
+ integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
+ dependencies:
+ fill-range "^7.0.1"
+
+browserslist@^4.14.5, browserslist@^4.20.2, browserslist@^4.20.4:
+ version "4.21.0"
+ resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.0.tgz#7ab19572361a140ecd1e023e2c1ed95edda0cefe"
+ integrity sha512-UQxE0DIhRB5z/zDz9iA03BOfxaN2+GQdBYH/2WrSIWEUrnpzTPJbhqt+umq6r3acaPRTW1FNTkrcp0PXgtFkvA==
+ dependencies:
+ caniuse-lite "^1.0.30001358"
+ electron-to-chromium "^1.4.164"
+ node-releases "^2.0.5"
+ update-browserslist-db "^1.0.0"
+
+buffer-from@^1.0.0:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
+ integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
+
+call-bind@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
+ integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==
+ dependencies:
+ function-bind "^1.1.1"
+ get-intrinsic "^1.0.2"
+
+callsites@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
+ integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
+
+caniuse-lite@^1.0.30001358:
+ version "1.0.30001358"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001358.tgz#473d35dabf5e448b463095cab7924e96ccfb8c00"
+ integrity sha512-hvp8PSRymk85R20bsDra7ZTCpSVGN/PAz9pSAjPSjKC+rNmnUk5vCRgJwiTT/O4feQ/yu/drvZYpKxxhbFuChw==
+
+chalk@^2.0.0:
+ version "2.4.2"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
+ integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
+ dependencies:
+ ansi-styles "^3.2.1"
+ escape-string-regexp "^1.0.5"
+ supports-color "^5.3.0"
+
+chalk@^4.0.0, chalk@^4.1.0:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
+ integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
+ dependencies:
+ ansi-styles "^4.1.0"
+ supports-color "^7.1.0"
+
+chrome-trace-event@^1.0.2:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac"
+ integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==
+
+cliui@^7.0.2:
+ version "7.0.4"
+ resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"
+ integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==
+ dependencies:
+ string-width "^4.2.0"
+ strip-ansi "^6.0.0"
+ wrap-ansi "^7.0.0"
+
+clone-deep@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387"
+ integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==
+ dependencies:
+ is-plain-object "^2.0.4"
+ kind-of "^6.0.2"
+ shallow-clone "^3.0.0"
+
+color-convert@^1.9.0:
+ version "1.9.3"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
+ integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
+ dependencies:
+ color-name "1.1.3"
+
+color-convert@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+ integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+ dependencies:
+ color-name "~1.1.4"
+
+color-name@1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
+ integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
+
+color-name@~1.1.4:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+ integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+colorette@^2.0.14:
+ version "2.0.19"
+ resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798"
+ integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==
+
+commander@^2.20.0:
+ version "2.20.3"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
+ integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
+
+commander@^7.0.0:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
+ integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
+
+concat-map@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+ integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
+
+convert-source-map@^1.7.0:
+ version "1.8.0"
+ resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369"
+ integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==
+ dependencies:
+ safe-buffer "~5.1.1"
+
+core-js-compat@^3.21.0, core-js-compat@^3.22.1:
+ version "3.23.2"
+ resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.23.2.tgz#5cbf8a9c8812d665392845b85ae91b5bcc7b615c"
+ integrity sha512-lrgZvxFwbQp9v7E8mX0rJ+JX7Bvh4eGULZXA1IAyjlsnWvCdw6TF8Tg6xtaSUSJMrSrMaLdpmk+V54LM1dvfOA==
+ dependencies:
+ browserslist "^4.20.4"
+ semver "7.0.0"
+
+cross-spawn@^7.0.2, cross-spawn@^7.0.3:
+ version "7.0.3"
+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
+ integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
+ dependencies:
+ path-key "^3.1.0"
+ shebang-command "^2.0.0"
+ which "^2.0.1"
+
+debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4:
+ version "4.3.4"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
+ integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
+ dependencies:
+ ms "2.1.2"
+
+deep-is@^0.1.3:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
+ integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
+
+define-properties@^1.1.3:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1"
+ integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==
+ dependencies:
+ has-property-descriptors "^1.0.0"
+ object-keys "^1.1.1"
+
+dir-glob@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
+ integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==
+ dependencies:
+ path-type "^4.0.0"
+
+doctrine@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
+ integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
+ dependencies:
+ esutils "^2.0.2"
+
+electron-to-chromium@^1.4.164:
+ version "1.4.165"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.165.tgz#a1ae079a4412b0c2d3bf6908e8db54511fb0bbac"
+ integrity sha512-DKQW1lqUSAYQvn9dnpK7mWaDpWbNOXQLXhfCi7Iwx0BKxdZOxkKcCyKw1l3ihWWW5iWSxKKbhEUoNRoHvl/hbA==
+
+emoji-regex@^8.0.0:
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
+ integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
+
+enhanced-resolve@^5.0.0, enhanced-resolve@^5.9.3:
+ version "5.9.3"
+ resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz#44a342c012cbc473254af5cc6ae20ebd0aae5d88"
+ integrity sha512-Bq9VSor+kjvW3f9/MiiR4eE3XYgOl7/rS8lnSxbRbF3kS0B2r+Y9w5krBWxZgDxASVZbdYrn5wT4j/Wb0J9qow==
+ dependencies:
+ graceful-fs "^4.2.4"
+ tapable "^2.2.0"
+
+envinfo@^7.7.3:
+ version "7.8.1"
+ resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475"
+ integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==
+
+es-module-lexer@^0.9.0:
+ version "0.9.3"
+ resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19"
+ integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==
+
+escalade@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
+ integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
+
+escape-string-regexp@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+ integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
+
+escape-string-regexp@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
+ integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
+
+eslint-scope@5.1.1, eslint-scope@^5.1.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
+ integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
+ dependencies:
+ esrecurse "^4.3.0"
+ estraverse "^4.1.1"
+
+eslint-scope@^7.1.1:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642"
+ integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==
+ dependencies:
+ esrecurse "^4.3.0"
+ estraverse "^5.2.0"
+
+eslint-utils@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672"
+ integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==
+ dependencies:
+ eslint-visitor-keys "^2.0.0"
+
+eslint-visitor-keys@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303"
+ integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==
+
+eslint-visitor-keys@^3.3.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826"
+ integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
+
+eslint@^8.16.0:
+ version "8.18.0"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.18.0.tgz#78d565d16c993d0b73968c523c0446b13da784fd"
+ integrity sha512-As1EfFMVk7Xc6/CvhssHUjsAQSkpfXvUGMFC3ce8JDe6WvqCgRrLOBQbVpsBFr1X1V+RACOadnzVvcUS5ni2bA==
+ dependencies:
+ "@eslint/eslintrc" "^1.3.0"
+ "@humanwhocodes/config-array" "^0.9.2"
+ ajv "^6.10.0"
+ chalk "^4.0.0"
+ cross-spawn "^7.0.2"
+ debug "^4.3.2"
+ doctrine "^3.0.0"
+ escape-string-regexp "^4.0.0"
+ eslint-scope "^7.1.1"
+ eslint-utils "^3.0.0"
+ eslint-visitor-keys "^3.3.0"
+ espree "^9.3.2"
+ esquery "^1.4.0"
+ esutils "^2.0.2"
+ fast-deep-equal "^3.1.3"
+ file-entry-cache "^6.0.1"
+ functional-red-black-tree "^1.0.1"
+ glob-parent "^6.0.1"
+ globals "^13.15.0"
+ ignore "^5.2.0"
+ import-fresh "^3.0.0"
+ imurmurhash "^0.1.4"
+ is-glob "^4.0.0"
+ js-yaml "^4.1.0"
+ json-stable-stringify-without-jsonify "^1.0.1"
+ levn "^0.4.1"
+ lodash.merge "^4.6.2"
+ minimatch "^3.1.2"
+ natural-compare "^1.4.0"
+ optionator "^0.9.1"
+ regexpp "^3.2.0"
+ strip-ansi "^6.0.1"
+ strip-json-comments "^3.1.0"
+ text-table "^0.2.0"
+ v8-compile-cache "^2.0.3"
+
+espree@^9.3.2:
+ version "9.3.2"
+ resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.2.tgz#f58f77bd334731182801ced3380a8cc859091596"
+ integrity sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==
+ dependencies:
+ acorn "^8.7.1"
+ acorn-jsx "^5.3.2"
+ eslint-visitor-keys "^3.3.0"
+
+esquery@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5"
+ integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==
+ dependencies:
+ estraverse "^5.1.0"
+
+esrecurse@^4.3.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
+ integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
+ dependencies:
+ estraverse "^5.2.0"
+
+estraverse@^4.1.1:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
+ integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
+
+estraverse@^5.1.0, estraverse@^5.2.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
+ integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
+
+esutils@^2.0.2:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
+ integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+
+events@^3.2.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
+ integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
+
+fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
+ integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
+
+fast-glob@^3.2.9:
+ version "3.2.11"
+ resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9"
+ integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==
+ dependencies:
+ "@nodelib/fs.stat" "^2.0.2"
+ "@nodelib/fs.walk" "^1.2.3"
+ glob-parent "^5.1.2"
+ merge2 "^1.3.0"
+ micromatch "^4.0.4"
+
+fast-json-stable-stringify@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
+ integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
+
+fast-levenshtein@^2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+ integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
+
+fastest-levenshtein@^1.0.12:
+ version "1.0.12"
+ resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz#9990f7d3a88cc5a9ffd1f1745745251700d497e2"
+ integrity sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==
+
+fastq@^1.6.0:
+ version "1.13.0"
+ resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c"
+ integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==
+ dependencies:
+ reusify "^1.0.4"
+
+file-entry-cache@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
+ integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==
+ dependencies:
+ flat-cache "^3.0.4"
+
+fill-range@^7.0.1:
+ version "7.0.1"
+ resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
+ integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
+ dependencies:
+ to-regex-range "^5.0.1"
+
+find-up@^4.0.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
+ integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
+ dependencies:
+ locate-path "^5.0.0"
+ path-exists "^4.0.0"
+
+flat-cache@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11"
+ integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==
+ dependencies:
+ flatted "^3.1.0"
+ rimraf "^3.0.2"
+
+flatted@^3.1.0:
+ version "3.2.5"
+ resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3"
+ integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==
+
+fp-ts@^2.6.1:
+ version "2.12.1"
+ resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-2.12.1.tgz#e389488bfd1507af06bd5965e97367edcd4fabad"
+ integrity sha512-oxvgqUYR6O9VkKXrxkJ0NOyU0FrE705MeqgBUMEPWyTu6Pwn768cJbHChw2XOBlgFLKfIHxjr2OOBFpv2mUGZw==
+
+fs.realpath@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+ integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
+
+function-bind@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
+ integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
+
+functional-red-black-tree@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
+ integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==
+
+gensync@^1.0.0-beta.2:
+ version "1.0.0-beta.2"
+ resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
+ integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
+
+get-caller-file@^2.0.5:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
+ integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
+
+get-intrinsic@^1.0.2, get-intrinsic@^1.1.1:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598"
+ integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==
+ dependencies:
+ function-bind "^1.1.1"
+ has "^1.0.3"
+ has-symbols "^1.0.3"
+
+glob-parent@^5.1.2:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
+ integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
+ dependencies:
+ is-glob "^4.0.1"
+
+glob-parent@^6.0.1:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3"
+ integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==
+ dependencies:
+ is-glob "^4.0.3"
+
+glob-to-regexp@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
+ integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
+
+glob@^7.1.3:
+ version "7.2.3"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
+ integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.1.1"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+globals@^11.1.0:
+ version "11.12.0"
+ resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
+ integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
+
+globals@^13.15.0:
+ version "13.15.0"
+ resolved "https://registry.yarnpkg.com/globals/-/globals-13.15.0.tgz#38113218c907d2f7e98658af246cef8b77e90bac"
+ integrity sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==
+ dependencies:
+ type-fest "^0.20.2"
+
+globby@^11.1.0:
+ version "11.1.0"
+ resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b"
+ integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==
+ dependencies:
+ array-union "^2.1.0"
+ dir-glob "^3.0.1"
+ fast-glob "^3.2.9"
+ ignore "^5.2.0"
+ merge2 "^1.4.1"
+ slash "^3.0.0"
+
+graceful-fs@^4.1.2, graceful-fs@^4.2.4, graceful-fs@^4.2.9:
+ version "4.2.10"
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
+ integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
+
+has-flag@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
+ integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==
+
+has-flag@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+ integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
+has-property-descriptors@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861"
+ integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==
+ dependencies:
+ get-intrinsic "^1.1.1"
+
+has-symbols@^1.0.1, has-symbols@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
+ integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
+
+has@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
+ integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
+ dependencies:
+ function-bind "^1.1.1"
+
+ignore@^5.2.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
+ integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==
+
+import-fresh@^3.0.0, import-fresh@^3.2.1:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
+ integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
+ dependencies:
+ parent-module "^1.0.0"
+ resolve-from "^4.0.0"
+
+import-local@^3.0.2:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4"
+ integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==
+ dependencies:
+ pkg-dir "^4.2.0"
+ resolve-cwd "^3.0.0"
+
+imurmurhash@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
+ integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
+
+inflight@^1.0.4:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+ integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
+ dependencies:
+ once "^1.3.0"
+ wrappy "1"
+
+inherits@2:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+ integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+inspectpack@^4.7.1:
+ version "4.7.1"
+ resolved "https://registry.yarnpkg.com/inspectpack/-/inspectpack-4.7.1.tgz#baf081bae0b5b31bbedd534424e658f32d60f97a"
+ integrity sha512-XoDJbKSM9I2KA+8+OLFJHm8m4NM2pMEgsDD2hze6swVfynEed9ngCx36mRR+otzOsskwnxIZWXjI23FTW1uHqA==
+ dependencies:
+ chalk "^4.1.0"
+ fp-ts "^2.6.1"
+ io-ts "^2.2.13"
+ io-ts-reporters "^1.2.2"
+ pify "^5.0.0"
+ semver-compare "^1.0.0"
+ yargs "^16.2.0"
+
+interpret@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9"
+ integrity sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==
+
+io-ts-reporters@^1.2.2:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/io-ts-reporters/-/io-ts-reporters-1.2.2.tgz#4d3219777ea5219c7d8f6ffac01fd68e72426dd1"
+ integrity sha512-igASwWWkDY757OutNcM6zTtdJf/eTZYkoe2ymsX2qpm5bKZLo74FJYjsCtMQOEdY7dRHLLEulCyFQwdN69GBCg==
+
+io-ts@^2.2.13:
+ version "2.2.16"
+ resolved "https://registry.yarnpkg.com/io-ts/-/io-ts-2.2.16.tgz#597dffa03db1913fc318c9c6df6931cb4ed808b2"
+ integrity sha512-y5TTSa6VP6le0hhmIyN0dqEXkrZeJLeC5KApJq6VLci3UEKF80lZ+KuoUs02RhBxNWlrqSNxzfI7otLX1Euv8Q==
+
+is-core-module@^2.9.0:
+ version "2.9.0"
+ resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69"
+ integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==
+ dependencies:
+ has "^1.0.3"
+
+is-extglob@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+ integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
+
+is-fullwidth-code-point@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
+ integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
+
+is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3:
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
+ integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
+ dependencies:
+ is-extglob "^2.1.1"
+
+is-number@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
+ integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
+
+is-plain-object@^2.0.4:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
+ integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==
+ dependencies:
+ isobject "^3.0.1"
+
+isexe@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+ integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
+
+isobject@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
+ integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==
+
+jest-worker@^27.4.5:
+ version "27.5.1"
+ resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0"
+ integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==
+ dependencies:
+ "@types/node" "*"
+ merge-stream "^2.0.0"
+ supports-color "^8.0.0"
+
+js-tokens@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+ integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
+js-yaml@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
+ integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
+ dependencies:
+ argparse "^2.0.1"
+
+jsesc@^2.5.1:
+ version "2.5.2"
+ resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
+ integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
+
+jsesc@~0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
+ integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==
+
+json-parse-even-better-errors@^2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
+ integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
+
+json-schema-traverse@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+ integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+
+json-stable-stringify-without-jsonify@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
+ integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
+
+json5@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c"
+ integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==
+
+kind-of@^6.0.2:
+ version "6.0.3"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
+ integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
+
+levn@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
+ integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==
+ dependencies:
+ prelude-ls "^1.2.1"
+ type-check "~0.4.0"
+
+loader-runner@^4.2.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1"
+ integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==
+
+locate-path@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
+ integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
+ dependencies:
+ p-locate "^4.1.0"
+
+lodash.debounce@^4.0.8:
+ version "4.0.8"
+ resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
+ integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
+
+lodash.merge@^4.6.2:
+ version "4.6.2"
+ resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
+ integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
+
+lru-cache@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
+ integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
+ dependencies:
+ yallist "^4.0.0"
+
+merge-stream@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
+ integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
+
+merge2@^1.3.0, merge2@^1.4.1:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
+ integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
+
+micromatch@^4.0.0, micromatch@^4.0.4:
+ version "4.0.5"
+ resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
+ integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
+ dependencies:
+ braces "^3.0.2"
+ picomatch "^2.3.1"
+
+mime-db@1.52.0:
+ version "1.52.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
+ integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
+
+mime-types@^2.1.27:
+ version "2.1.35"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
+ integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
+ dependencies:
+ mime-db "1.52.0"
+
+minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
+ integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
+ dependencies:
+ brace-expansion "^1.1.7"
+
+minimist@>=1.2.6:
+ version "1.2.6"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
+ integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
+
+ms@2.1.2:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
+ integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
+
+natural-compare@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
+ integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
+
+neo-async@^2.6.2:
+ version "2.6.2"
+ resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f"
+ integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==
+
+node-releases@^2.0.5:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.5.tgz#280ed5bc3eba0d96ce44897d8aee478bfb3d9666"
+ integrity sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q==
+
+object-keys@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
+ integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
+
+object.assign@^4.1.0:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940"
+ integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==
+ dependencies:
+ call-bind "^1.0.0"
+ define-properties "^1.1.3"
+ has-symbols "^1.0.1"
+ object-keys "^1.1.1"
+
+once@^1.3.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+ integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
+ dependencies:
+ wrappy "1"
+
+optionator@^0.9.1:
+ version "0.9.1"
+ resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499"
+ integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==
+ dependencies:
+ deep-is "^0.1.3"
+ fast-levenshtein "^2.0.6"
+ levn "^0.4.1"
+ prelude-ls "^1.2.1"
+ type-check "^0.4.0"
+ word-wrap "^1.2.3"
+
+p-limit@^2.2.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
+ integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
+ dependencies:
+ p-try "^2.0.0"
+
+p-locate@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
+ integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
+ dependencies:
+ p-limit "^2.2.0"
+
+p-try@^2.0.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
+ integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
+
+parent-module@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
+ integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
+ dependencies:
+ callsites "^3.0.0"
+
+path-exists@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
+ integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
+
+path-is-absolute@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+ integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
+
+path-key@^3.1.0:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
+ integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
+
+path-parse@^1.0.7:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
+ integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
+
+path-type@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
+ integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
+
+picocolors@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
+ integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
+
+picomatch@^2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
+ integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+
+pify@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/pify/-/pify-5.0.0.tgz#1f5eca3f5e87ebec28cc6d54a0e4aaf00acc127f"
+ integrity sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==
+
+pkg-dir@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3"
+ integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==
+ dependencies:
+ find-up "^4.0.0"
+
+prelude-ls@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
+ integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
+
+punycode@^2.1.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
+ integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
+
+queue-microtask@^1.2.2:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
+ integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
+
+randombytes@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
+ integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
+ dependencies:
+ safe-buffer "^5.1.0"
+
+rechoir@^0.7.0:
+ version "0.7.1"
+ resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686"
+ integrity sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==
+ dependencies:
+ resolve "^1.9.0"
+
+regenerate-unicode-properties@^10.0.1:
+ version "10.0.1"
+ resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.0.1.tgz#7f442732aa7934a3740c779bb9b3340dccc1fb56"
+ integrity sha512-vn5DU6yg6h8hP/2OkQo3K7uVILvY4iu0oI4t3HFa81UPkhGJwkRwM10JEc3upjdhHjs/k8GJY1sRBhk5sr69Bw==
+ dependencies:
+ regenerate "^1.4.2"
+
+regenerate@^1.4.2:
+ version "1.4.2"
+ resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a"
+ integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==
+
+regenerator-runtime@^0.13.4:
+ version "0.13.9"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
+ integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
+
+regenerator-transform@^0.15.0:
+ version "0.15.0"
+ resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.0.tgz#cbd9ead5d77fae1a48d957cf889ad0586adb6537"
+ integrity sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==
+ dependencies:
+ "@babel/runtime" "^7.8.4"
+
+regexpp@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
+ integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==
+
+regexpu-core@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.0.1.tgz#c531122a7840de743dcf9c83e923b5560323ced3"
+ integrity sha512-CriEZlrKK9VJw/xQGJpQM5rY88BtuL8DM+AEwvcThHilbxiTAy8vq4iJnd2tqq8wLmjbGZzP7ZcKFjbGkmEFrw==
+ dependencies:
+ regenerate "^1.4.2"
+ regenerate-unicode-properties "^10.0.1"
+ regjsgen "^0.6.0"
+ regjsparser "^0.8.2"
+ unicode-match-property-ecmascript "^2.0.0"
+ unicode-match-property-value-ecmascript "^2.0.0"
+
+regjsgen@^0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.6.0.tgz#83414c5354afd7d6627b16af5f10f41c4e71808d"
+ integrity sha512-ozE883Uigtqj3bx7OhL1KNbCzGyW2NQZPl6Hs09WTvCuZD5sTI4JY58bkbQWa/Y9hxIsvJ3M8Nbf7j54IqeZbA==
+
+regjsparser@^0.8.2:
+ version "0.8.4"
+ resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.8.4.tgz#8a14285ffcc5de78c5b95d62bbf413b6bc132d5f"
+ integrity sha512-J3LABycON/VNEu3abOviqGHuB/LOtOQj8SKmfP9anY5GfAVw/SPjwzSjxGjbZXIxbGfqTHtJw58C2Li/WkStmA==
+ dependencies:
+ jsesc "~0.5.0"
+
+require-directory@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
+ integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
+
+resolve-cwd@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
+ integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==
+ dependencies:
+ resolve-from "^5.0.0"
+
+resolve-from@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
+ integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+
+resolve-from@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
+ integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
+
+resolve@^1.14.2, resolve@^1.9.0:
+ version "1.22.1"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
+ integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
+ dependencies:
+ is-core-module "^2.9.0"
+ path-parse "^1.0.7"
+ supports-preserve-symlinks-flag "^1.0.0"
+
+reusify@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
+ integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
+
+rimraf@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
+ integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
+ dependencies:
+ glob "^7.1.3"
+
+run-parallel@^1.1.9:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
+ integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
+ dependencies:
+ queue-microtask "^1.2.2"
+
+safe-buffer@^5.1.0:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
+ integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
+
+safe-buffer@~5.1.1:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
+ integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
+
+schema-utils@^3.1.0, schema-utils@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281"
+ integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==
+ dependencies:
+ "@types/json-schema" "^7.0.8"
+ ajv "^6.12.5"
+ ajv-keywords "^3.5.2"
+
+semver-compare@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
+ integrity sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==
+
+semver@7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
+ integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
+
+semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
+ version "6.3.0"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
+ integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
+
+semver@^7.3.4, semver@^7.3.7:
+ version "7.3.7"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
+ integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==
+ dependencies:
+ lru-cache "^6.0.0"
+
+serialize-javascript@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8"
+ integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==
+ dependencies:
+ randombytes "^2.1.0"
+
+shallow-clone@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3"
+ integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==
+ dependencies:
+ kind-of "^6.0.2"
+
+shebang-command@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
+ integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
+ dependencies:
+ shebang-regex "^3.0.0"
+
+shebang-regex@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
+ integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
+
+slash@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
+ integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
+
+source-map-support@~0.5.20:
+ version "0.5.21"
+ resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
+ integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
+ dependencies:
+ buffer-from "^1.0.0"
+ source-map "^0.6.0"
+
+source-map@^0.6.0:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+ integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
+string-width@^4.1.0, string-width@^4.2.0:
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+ integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+ dependencies:
+ emoji-regex "^8.0.0"
+ is-fullwidth-code-point "^3.0.0"
+ strip-ansi "^6.0.1"
+
+strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+ integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+ dependencies:
+ ansi-regex "^5.0.1"
+
+strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
+ integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
+
+supports-color@^5.3.0:
+ version "5.5.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
+ integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
+ dependencies:
+ has-flag "^3.0.0"
+
+supports-color@^7.1.0:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+ integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+ dependencies:
+ has-flag "^4.0.0"
+
+supports-color@^8.0.0:
+ version "8.1.1"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
+ integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
+ dependencies:
+ has-flag "^4.0.0"
+
+supports-preserve-symlinks-flag@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
+ integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
+
+tapable@^2.1.1, tapable@^2.2.0:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0"
+ integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==
+
+terser-webpack-plugin@^5.1.3:
+ version "5.3.3"
+ resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz#8033db876dd5875487213e87c627bca323e5ed90"
+ integrity sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ==
+ dependencies:
+ "@jridgewell/trace-mapping" "^0.3.7"
+ jest-worker "^27.4.5"
+ schema-utils "^3.1.1"
+ serialize-javascript "^6.0.0"
+ terser "^5.7.2"
+
+terser@^5.13.1, terser@^5.7.2:
+ version "5.14.1"
+ resolved "https://registry.yarnpkg.com/terser/-/terser-5.14.1.tgz#7c95eec36436cb11cf1902cc79ac564741d19eca"
+ integrity sha512-+ahUAE+iheqBTDxXhTisdA8hgvbEG1hHOQ9xmNjeUJSoi6DU/gMrKNcfZjHkyY6Alnuyc+ikYJaxxfHkT3+WuQ==
+ dependencies:
+ "@jridgewell/source-map" "^0.3.2"
+ acorn "^8.5.0"
+ commander "^2.20.0"
+ source-map-support "~0.5.20"
+
+text-table@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+ integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
+
+to-fast-properties@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
+ integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==
+
+to-regex-range@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
+ integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
+ dependencies:
+ is-number "^7.0.0"
+
+ts-loader@^9.2.5:
+ version "9.3.1"
+ resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.3.1.tgz#fe25cca56e3e71c1087fe48dc67f4df8c59b22d4"
+ integrity sha512-OkyShkcZTsTwyS3Kt7a4rsT/t2qvEVQuKCTg4LJmpj9fhFR7ukGdZwV6Qq3tRUkqcXtfGpPR7+hFKHCG/0d3Lw==
+ dependencies:
+ chalk "^4.1.0"
+ enhanced-resolve "^5.0.0"
+ micromatch "^4.0.0"
+ semver "^7.3.4"
+
+tslib@^1.8.1:
+ version "1.14.1"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
+ integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
+
+tsutils@^3.21.0:
+ version "3.21.0"
+ resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623"
+ integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==
+ dependencies:
+ tslib "^1.8.1"
+
+type-check@^0.4.0, type-check@~0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
+ integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==
+ dependencies:
+ prelude-ls "^1.2.1"
+
+type-fest@^0.20.2:
+ version "0.20.2"
+ resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
+ integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
+
+typescript@^4.4.2:
+ version "4.7.4"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235"
+ integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==
+
+unicode-canonical-property-names-ecmascript@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc"
+ integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==
+
+unicode-match-property-ecmascript@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3"
+ integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==
+ dependencies:
+ unicode-canonical-property-names-ecmascript "^2.0.0"
+ unicode-property-aliases-ecmascript "^2.0.0"
+
+unicode-match-property-value-ecmascript@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714"
+ integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw==
+
+unicode-property-aliases-ecmascript@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8"
+ integrity sha512-5Zfuy9q/DFr4tfO7ZPeVXb1aPoeQSdeFMLpYuFebehDAhbuevLs5yxSZmIFN1tP5F9Wl4IpJrYojg85/zgyZHQ==
+
+update-browserslist-db@^1.0.0:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.3.tgz#6c47cb996f34afb363e924748e2f6e4d983c6fc1"
+ integrity sha512-ufSazemeh9Gty0qiWtoRpJ9F5Q5W3xdIPm1UZQqYQv/q0Nyb9EMHUB2lu+O9x1re9WsorpMAUu4Y6Lxcs5n+XQ==
+ dependencies:
+ escalade "^3.1.1"
+ picocolors "^1.0.0"
+
+uri-js@^4.2.2:
+ version "4.4.1"
+ resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
+ integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
+ dependencies:
+ punycode "^2.1.0"
+
+v8-compile-cache@^2.0.3:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
+ integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
+
+watchpack@^2.3.1:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d"
+ integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==
+ dependencies:
+ glob-to-regexp "^0.4.1"
+ graceful-fs "^4.1.2"
+
+webpack-cli@^4.9.2:
+ version "4.10.0"
+ resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.10.0.tgz#37c1d69c8d85214c5a65e589378f53aec64dab31"
+ integrity sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==
+ dependencies:
+ "@discoveryjs/json-ext" "^0.5.0"
+ "@webpack-cli/configtest" "^1.2.0"
+ "@webpack-cli/info" "^1.5.0"
+ "@webpack-cli/serve" "^1.7.0"
+ colorette "^2.0.14"
+ commander "^7.0.0"
+ cross-spawn "^7.0.3"
+ fastest-levenshtein "^1.0.12"
+ import-local "^3.0.2"
+ interpret "^2.2.0"
+ rechoir "^0.7.0"
+ webpack-merge "^5.7.3"
+
+webpack-merge@^5.7.3:
+ version "5.8.0"
+ resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.8.0.tgz#2b39dbf22af87776ad744c390223731d30a68f61"
+ integrity sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==
+ dependencies:
+ clone-deep "^4.0.1"
+ wildcard "^2.0.0"
+
+webpack-sources@^3.2.3:
+ version "3.2.3"
+ resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde"
+ integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
+
+webpack@^5.72.1:
+ version "5.73.0"
+ resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.73.0.tgz#bbd17738f8a53ee5760ea2f59dce7f3431d35d38"
+ integrity sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA==
+ dependencies:
+ "@types/eslint-scope" "^3.7.3"
+ "@types/estree" "^0.0.51"
+ "@webassemblyjs/ast" "1.11.1"
+ "@webassemblyjs/wasm-edit" "1.11.1"
+ "@webassemblyjs/wasm-parser" "1.11.1"
+ acorn "^8.4.1"
+ acorn-import-assertions "^1.7.6"
+ browserslist "^4.14.5"
+ chrome-trace-event "^1.0.2"
+ enhanced-resolve "^5.9.3"
+ es-module-lexer "^0.9.0"
+ eslint-scope "5.1.1"
+ events "^3.2.0"
+ glob-to-regexp "^0.4.1"
+ graceful-fs "^4.2.9"
+ json-parse-even-better-errors "^2.3.1"
+ loader-runner "^4.2.0"
+ mime-types "^2.1.27"
+ neo-async "^2.6.2"
+ schema-utils "^3.1.0"
+ tapable "^2.1.1"
+ terser-webpack-plugin "^5.1.3"
+ watchpack "^2.3.1"
+ webpack-sources "^3.2.3"
+
+which@^2.0.1:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
+ integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
+ dependencies:
+ isexe "^2.0.0"
+
+wildcard@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec"
+ integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==
+
+word-wrap@^1.2.3:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
+ integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
+
+wrap-ansi@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+ integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+ dependencies:
+ ansi-styles "^4.0.0"
+ string-width "^4.1.0"
+ strip-ansi "^6.0.0"
+
+wrappy@1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+ integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
+
+y18n@^5.0.5:
+ version "5.0.8"
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
+ integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
+
+yallist@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
+ integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
+
+yargs-parser@^20.2.2:
+ version "20.2.9"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
+ integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
+
+yargs@^16.2.0:
+ version "16.2.0"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
+ integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==
+ dependencies:
+ cliui "^7.0.2"
+ escalade "^3.1.1"
+ get-caller-file "^2.0.5"
+ require-directory "^2.1.1"
+ string-width "^4.2.0"
+ y18n "^5.0.5"
+ yargs-parser "^20.2.2"
diff --git a/src/Components/Web.JS/dist/Release/blazor.server.js b/src/Components/Web.JS/dist/Release/blazor.server.js
index 40e7ed9511..c6266f1eed 100644
--- a/src/Components/Web.JS/dist/Release/blazor.server.js
+++ b/src/Components/Web.JS/dist/Release/blazor.server.js
@@ -1 +1 @@
-(()=>{"use strict";var e,t,n,r={};r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),function(e){window.DotNet=e;const t=[],n=new Map,r=new Map,o="__jsObjectId",i="__byte[]";class s{constructor(e){this._jsObject=e,this._cachedFunctions=new Map}findFunction(e){const t=this._cachedFunctions.get(e);if(t)return t;let n,r=this._jsObject;if(e.split(".").forEach((t=>{if(!(t in r))throw new Error(`Could not find '${e}' ('${t}' was undefined).`);n=r,r=r[t]})),r instanceof Function)return r=r.bind(n),this._cachedFunctions.set(e,r),r;throw new Error(`The value '${e}' is not a function.`)}getWrappedObject(){return this._jsObject}}const a={},c={0:new s(window)};c[0]._cachedFunctions.set("import",(e=>("string"==typeof e&&e.startsWith("./")&&(e=document.baseURI+e.substr(2)),import(e))));let l,h=1,u=1,d=null;function p(e){t.push(e)}function f(e){if(e&&"object"==typeof e){c[u]=new s(e);const t={[o]:u};return u++,t}throw new Error(`Cannot create a JSObjectReference from the value '${e}'.`)}function g(e){let t=-1;if(e instanceof ArrayBuffer&&(e=new Uint8Array(e)),e instanceof Blob)t=e.size;else{if(!(e.buffer instanceof ArrayBuffer))throw new Error("Supplied value is not a typed array or blob.");if(void 0===e.byteLength)throw new Error(`Cannot create a JSStreamReference from the value '${e}' as it doesn't have a byteLength.`);t=e.byteLength}const n={__jsStreamReferenceLength:t};try{const t=f(e);n.__jsObjectId=t.__jsObjectId}catch(t){throw new Error(`Cannot create a JSStreamReference from the value '${e}'.`)}return n}function m(e){return e?JSON.parse(e,((e,n)=>t.reduce(((t,n)=>n(e,t)),n))):null}function y(e,t,n,r){const o=v();if(o.invokeDotNetFromJS){const i=D(r),s=o.invokeDotNetFromJS(e,t,n,i);return s?m(s):null}throw new Error("The current dispatcher does not support synchronous calls from JS to .NET. Use invokeMethodAsync instead.")}function w(e,t,n,r){if(e&&n)throw new Error(`For instance method calls, assemblyName should be null. Received '${e}'.`);const o=h++,i=new Promise(((e,t)=>{a[o]={resolve:e,reject:t}}));try{const i=D(r);v().beginInvokeDotNetFromJS(o,e,t,n,i)}catch(e){b(o,!1,e)}return i}function v(){if(null!==d)return d;throw new Error("No .NET call dispatcher has been set.")}function b(e,t,n){if(!a.hasOwnProperty(e))throw new Error(`There is no pending async call with ID ${e}.`);const r=a[e];delete a[e],t?r.resolve(n):r.reject(n)}function _(e){return e instanceof Error?`${e.message}\n${e.stack}`:e?e.toString():"null"}function E(e,t){const n=c[t];if(n)return n.findFunction(e);throw new Error(`JS object instance with ID ${t} does not exist (has it been disposed?).`)}function S(e){delete c[e]}e.attachDispatcher=function(e){d=e},e.attachReviver=p,e.invokeMethod=function(e,t,...n){return y(e,t,null,n)},e.invokeMethodAsync=function(e,t,...n){return w(e,t,null,n)},e.createJSObjectReference=f,e.createJSStreamReference=g,e.disposeJSObjectReference=function(e){const t=e&&e.__jsObjectId;"number"==typeof t&&S(t)},function(e){e[e.Default=0]="Default",e[e.JSObjectReference=1]="JSObjectReference",e[e.JSStreamReference=2]="JSStreamReference",e[e.JSVoidResult=3]="JSVoidResult"}(l=e.JSCallResultType||(e.JSCallResultType={})),e.jsCallDispatcher={findJSFunction:E,disposeJSObjectReferenceById:S,invokeJSFromDotNet:(e,t,n,r)=>{const o=T(E(e,r).apply(null,m(t)),n);return null==o?null:D(o)},beginInvokeJSFromDotNet:(e,t,n,r,o)=>{const i=new Promise((e=>{e(E(t,o).apply(null,m(n)))}));e&&i.then((t=>D([e,!0,T(t,r)]))).then((t=>v().endInvokeJSFromDotNet(e,!0,t)),(t=>v().endInvokeJSFromDotNet(e,!1,JSON.stringify([e,!1,_(t)]))))},endInvokeDotNetFromJS:(e,t,n)=>{const r=t?m(n):new Error(n);b(parseInt(e,10),t,r)},receiveByteArray:(e,t)=>{n.set(e,t)},supplyDotNetStream:(e,t)=>{if(r.has(e)){const n=r.get(e);r.delete(e),n.resolve(t)}else{const n=new k;n.resolve(t),r.set(e,n)}}};class C{constructor(e){this._id=e}invokeMethod(e,...t){return y(null,e,this._id,t)}invokeMethodAsync(e,...t){return w(null,e,this._id,t)}dispose(){w(null,"__Dispose",this._id,null).catch((e=>console.error(e)))}serializeAsArg(){return{__dotNetObject:this._id}}}e.DotNetObject=C,p((function(e,t){if(t&&"object"==typeof t){if(t.hasOwnProperty("__dotNetObject"))return new C(t.__dotNetObject);if(t.hasOwnProperty(o)){const e=t.__jsObjectId,n=c[e];if(n)return n.getWrappedObject();throw new Error(`JS object instance with Id '${e}' does not exist. It may have been disposed.`)}if(t.hasOwnProperty(i)){const e=t["__byte[]"],r=n.get(e);if(void 0===r)throw new Error(`Byte array index '${e}' does not exist.`);return n.delete(e),r}if(t.hasOwnProperty("__dotNetStream"))return new I(t.__dotNetStream)}return t}));class I{constructor(e){if(r.has(e))this._streamPromise=r.get(e).streamPromise,r.delete(e);else{const t=new k;r.set(e,t),this._streamPromise=t.streamPromise}}stream(){return this._streamPromise}async arrayBuffer(){return new Response(await this.stream()).arrayBuffer()}}class k{constructor(){this.streamPromise=new Promise(((e,t)=>{this.resolve=e,this.reject=t}))}}function T(e,t){switch(t){case l.Default:return e;case l.JSObjectReference:return f(e);case l.JSStreamReference:return g(e);case l.JSVoidResult:return null;default:throw new Error(`Invalid JS call result type '${t}'.`)}}let x=0;function D(e){return x=0,JSON.stringify(e,R)}function R(e,t){if(t instanceof C)return t.serializeAsArg();if(t instanceof Uint8Array){d.sendByteArray(x,t);const e={[i]:x};return x++,e}return t}}(e||(e={})),function(e){e[e.prependFrame=1]="prependFrame",e[e.removeFrame=2]="removeFrame",e[e.setAttribute=3]="setAttribute",e[e.removeAttribute=4]="removeAttribute",e[e.updateText=5]="updateText",e[e.stepIn=6]="stepIn",e[e.stepOut=7]="stepOut",e[e.updateMarkup=8]="updateMarkup",e[e.permutationListEntry=9]="permutationListEntry",e[e.permutationListEnd=10]="permutationListEnd"}(t||(t={})),function(e){e[e.element=1]="element",e[e.text=2]="text",e[e.attribute=3]="attribute",e[e.component=4]="component",e[e.region=5]="region",e[e.elementReferenceCapture=6]="elementReferenceCapture",e[e.markup=8]="markup"}(n||(n={}));class o{constructor(e,t){this.componentId=e,this.fieldValue=t}static fromEvent(e,t){const n=t.target;if(n instanceof Element){const t=function(e){return e instanceof HTMLInputElement?e.type&&"checkbox"===e.type.toLowerCase()?{value:e.checked}:{value:e.value}:e instanceof HTMLSelectElement||e instanceof HTMLTextAreaElement?{value:e.value}:null}(n);if(t)return new o(e,t.value)}return null}}const i=new Map,s=new Map,a=[];function c(e){return i.get(e)}function l(e){const t=i.get(e);return(null==t?void 0:t.browserEventName)||e}function h(e,t){e.forEach((e=>i.set(e,t)))}function u(e){const t=[];for(let n=0;n<e.length;n++){const r=e[n];t.push({identifier:r.identifier,clientX:r.clientX,clientY:r.clientY,screenX:r.screenX,screenY:r.screenY,pageX:r.pageX,pageY:r.pageY})}return t}function d(e){return{detail:e.detail,screenX:e.screenX,screenY:e.screenY,clientX:e.clientX,clientY:e.clientY,offsetX:e.offsetX,offsetY:e.offsetY,pageX:e.pageX,pageY:e.pageY,button:e.button,buttons:e.buttons,ctrlKey:e.ctrlKey,shiftKey:e.shiftKey,altKey:e.altKey,metaKey:e.metaKey,type:e.type}}h(["input","change"],{createEventArgs:function(e){const t=e.target;if(function(e){return-1!==p.indexOf(e.getAttribute("type"))}(t)){const e=function(e){const t=e.value,n=e.type;switch(n){case"date":case"month":case"week":return t;case"datetime-local":return 16===t.length?t+":00":t;case"time":return 5===t.length?t+":00":t}throw new Error(`Invalid element type '${n}'.`)}(t);return{value:e}}if(function(e){return e instanceof HTMLSelectElement&&"select-multiple"===e.type}(t)){const e=t;return{value:Array.from(e.options).filter((e=>e.selected)).map((e=>e.value))}}{const e=function(e){return!!e&&"INPUT"===e.tagName&&"checkbox"===e.getAttribute("type")}(t);return{value:e?!!t.checked:t.value}}}}),h(["copy","cut","paste"],{createEventArgs:e=>({type:e.type})}),h(["drag","dragend","dragenter","dragleave","dragover","dragstart","drop"],{createEventArgs:e=>{return{...d(t=e),dataTransfer:t.dataTransfer?{dropEffect:t.dataTransfer.dropEffect,effectAllowed:t.dataTransfer.effectAllowed,files:Array.from(t.dataTransfer.files).map((e=>e.name)),items:Array.from(t.dataTransfer.items).map((e=>({kind:e.kind,type:e.type}))),types:t.dataTransfer.types}:null};var t}}),h(["focus","blur","focusin","focusout"],{createEventArgs:e=>({type:e.type})}),h(["keydown","keyup","keypress"],{createEventArgs:e=>{return{key:(t=e).key,code:t.code,location:t.location,repeat:t.repeat,ctrlKey:t.ctrlKey,shiftKey:t.shiftKey,altKey:t.altKey,metaKey:t.metaKey,type:t.type};var t}}),h(["contextmenu","click","mouseover","mouseout","mousemove","mousedown","mouseup","mouseleave","mouseenter","dblclick"],{createEventArgs:e=>d(e)}),h(["error"],{createEventArgs:e=>{return{message:(t=e).message,filename:t.filename,lineno:t.lineno,colno:t.colno,type:t.type};var t}}),h(["loadstart","timeout","abort","load","loadend","progress"],{createEventArgs:e=>{return{lengthComputable:(t=e).lengthComputable,loaded:t.loaded,total:t.total,type:t.type};var t}}),h(["touchcancel","touchend","touchmove","touchenter","touchleave","touchstart"],{createEventArgs:e=>{return{detail:(t=e).detail,touches:u(t.touches),targetTouches:u(t.targetTouches),changedTouches:u(t.changedTouches),ctrlKey:t.ctrlKey,shiftKey:t.shiftKey,altKey:t.altKey,metaKey:t.metaKey,type:t.type};var t}}),h(["gotpointercapture","lostpointercapture","pointercancel","pointerdown","pointerenter","pointerleave","pointermove","pointerout","pointerover","pointerup"],{createEventArgs:e=>{return{...d(t=e),pointerId:t.pointerId,width:t.width,height:t.height,pressure:t.pressure,tiltX:t.tiltX,tiltY:t.tiltY,pointerType:t.pointerType,isPrimary:t.isPrimary};var t}}),h(["wheel","mousewheel"],{createEventArgs:e=>{return{...d(t=e),deltaX:t.deltaX,deltaY:t.deltaY,deltaZ:t.deltaZ,deltaMode:t.deltaMode};var t}}),h(["toggle"],{createEventArgs:()=>({})});const p=["date","datetime-local","month","time","week"],f=new Map;let g,m,y=0;const w={async add(e,t,n){if(!n)throw new Error("initialParameters must be an object, even if empty.");const r="__bl-dynamic-root:"+(++y).toString();f.set(r,e);const o=await _().invokeMethodAsync("AddRootComponent",t,r),i=new b(o,m[t]);return await i.setParameters(n),i}};class v{invoke(e){return this._callback(e)}setCallback(t){this._selfJSObjectReference||(this._selfJSObjectReference=e.createJSObjectReference(this)),this._callback=t}getJSObjectReference(){return this._selfJSObjectReference}dispose(){this._selfJSObjectReference&&e.disposeJSObjectReference(this._selfJSObjectReference)}}class b{constructor(e,t){this._jsEventCallbackWrappers=new Map,this._componentId=e;for(const e of t)"eventcallback"===e.type&&this._jsEventCallbackWrappers.set(e.name.toLowerCase(),new v)}setParameters(e){const t={},n=Object.entries(e||{}),r=n.length;for(const[e,r]of n){const n=this._jsEventCallbackWrappers.get(e.toLowerCase());n&&r?(n.setCallback(r),t[e]=n.getJSObjectReference()):t[e]=r}return _().invokeMethodAsync("SetRootComponentParameters",this._componentId,r,t)}async dispose(){if(null!==this._componentId){await _().invokeMethodAsync("RemoveRootComponent",this._componentId),this._componentId=null;for(const e of this._jsEventCallbackWrappers.values())e.dispose()}}}function _(){if(!g)throw new Error("Dynamic root components have not been enabled in this application.");return g}const E=new Map;let S;const C=new Promise((e=>{S=e}));function I(e,t,n){return T(e,t.eventHandlerId,(()=>k(e).invokeMethodAsync("DispatchEventAsync",t,n)))}function k(e){const t=E.get(e);if(!t)throw new Error(`No interop methods are registered for renderer ${e}`);return t}let T=(e,t,n)=>n();const x=N(["abort","blur","canplay","canplaythrough","change","cuechange","durationchange","emptied","ended","error","focus","load","loadeddata","loadedmetadata","loadend","loadstart","mouseenter","mouseleave","pause","play","playing","progress","ratechange","reset","scroll","seeked","seeking","stalled","submit","suspend","timeupdate","toggle","unload","volumechange","waiting","DOMNodeInsertedIntoDocument","DOMNodeRemovedFromDocument"]),D={submit:!0},R=N(["click","dblclick","mousedown","mousemove","mouseup"]);class P{constructor(e){this.browserRendererId=e,this.afterClickCallbacks=[];const t=++P.nextEventDelegatorId;this.eventsCollectionKey=`_blazorEvents_${t}`,this.eventInfoStore=new U(this.onGlobalEvent.bind(this))}setListener(e,t,n,r){const o=this.getEventHandlerInfosForElement(e,!0),i=o.getHandler(t);if(i)this.eventInfoStore.update(i.eventHandlerId,n);else{const i={element:e,eventName:t,eventHandlerId:n,renderingComponentId:r};this.eventInfoStore.add(i),o.setHandler(t,i)}}getHandler(e){return this.eventInfoStore.get(e)}removeListener(e){const t=this.eventInfoStore.remove(e);if(t){const e=t.element,n=this.getEventHandlerInfosForElement(e,!1);n&&n.removeHandler(t.eventName)}}notifyAfterClick(e){this.afterClickCallbacks.push(e),this.eventInfoStore.addGlobalListener("click")}setStopPropagation(e,t,n){this.getEventHandlerInfosForElement(e,!0).stopPropagation(t,n)}setPreventDefault(e,t,n){this.getEventHandlerInfosForElement(e,!0).preventDefault(t,n)}onGlobalEvent(e){if(!(e.target instanceof Element))return;this.dispatchGlobalEventToAllElements(e.type,e);const t=(n=e.type,s.get(n));var n;t&&t.forEach((t=>this.dispatchGlobalEventToAllElements(t,e))),"click"===e.type&&this.afterClickCallbacks.forEach((t=>t(e)))}dispatchGlobalEventToAllElements(e,t){const n=t.composedPath();let r=n.shift(),i=null,s=!1;const a=Object.prototype.hasOwnProperty.call(x,e);let l=!1;for(;r;){const d=r,p=this.getEventHandlerInfosForElement(d,!1);if(p){const n=p.getHandler(e);if(n&&(h=d,u=t.type,!((h instanceof HTMLButtonElement||h instanceof HTMLInputElement||h instanceof HTMLTextAreaElement||h instanceof HTMLSelectElement)&&Object.prototype.hasOwnProperty.call(R,u)&&h.disabled))){if(!s){const n=c(e);i=(null==n?void 0:n.createEventArgs)?n.createEventArgs(t):{},s=!0}Object.prototype.hasOwnProperty.call(D,t.type)&&t.preventDefault(),I(this.browserRendererId,{eventHandlerId:n.eventHandlerId,eventName:e,eventFieldInfo:o.fromEvent(n.renderingComponentId,t)},i)}p.stopPropagation(e)&&(l=!0),p.preventDefault(e)&&t.preventDefault()}r=a||l?void 0:n.shift()}var h,u}getEventHandlerInfosForElement(e,t){return Object.prototype.hasOwnProperty.call(e,this.eventsCollectionKey)?e[this.eventsCollectionKey]:t?e[this.eventsCollectionKey]=new A:null}}P.nextEventDelegatorId=0;class U{constructor(e){this.globalListener=e,this.infosByEventHandlerId={},this.countByEventName={},a.push(this.handleEventNameAliasAdded.bind(this))}add(e){if(this.infosByEventHandlerId[e.eventHandlerId])throw new Error(`Event ${e.eventHandlerId} is already tracked`);this.infosByEventHandlerId[e.eventHandlerId]=e,this.addGlobalListener(e.eventName)}get(e){return this.infosByEventHandlerId[e]}addGlobalListener(e){if(e=l(e),Object.prototype.hasOwnProperty.call(this.countByEventName,e))this.countByEventName[e]++;else{this.countByEventName[e]=1;const t=Object.prototype.hasOwnProperty.call(x,e);document.addEventListener(e,this.globalListener,t)}}update(e,t){if(Object.prototype.hasOwnProperty.call(this.infosByEventHandlerId,t))throw new Error(`Event ${t} is already tracked`);const n=this.infosByEventHandlerId[e];delete this.infosByEventHandlerId[e],n.eventHandlerId=t,this.infosByEventHandlerId[t]=n}remove(e){const t=this.infosByEventHandlerId[e];if(t){delete this.infosByEventHandlerId[e];const n=l(t.eventName);0==--this.countByEventName[n]&&(delete this.countByEventName[n],document.removeEventListener(n,this.globalListener))}return t}handleEventNameAliasAdded(e,t){if(Object.prototype.hasOwnProperty.call(this.countByEventName,e)){const n=this.countByEventName[e];delete this.countByEventName[e],document.removeEventListener(e,this.globalListener),this.addGlobalListener(t),this.countByEventName[t]+=n-1}}}class A{constructor(){this.handlers={},this.preventDefaultFlags=null,this.stopPropagationFlags=null}getHandler(e){return Object.prototype.hasOwnProperty.call(this.handlers,e)?this.handlers[e]:null}setHandler(e,t){this.handlers[e]=t}removeHandler(e){delete this.handlers[e]}preventDefault(e,t){return void 0!==t&&(this.preventDefaultFlags=this.preventDefaultFlags||{},this.preventDefaultFlags[e]=t),!!this.preventDefaultFlags&&this.preventDefaultFlags[e]}stopPropagation(e,t){return void 0!==t&&(this.stopPropagationFlags=this.stopPropagationFlags||{},this.stopPropagationFlags[e]=t),!!this.stopPropagationFlags&&this.stopPropagationFlags[e]}}function N(e){const t={};return e.forEach((e=>{t[e]=!0})),t}const $=G("_blazorLogicalChildren"),B=G("_blazorLogicalParent"),L=G("_blazorLogicalEnd");function M(e,t){if(e.childNodes.length>0&&!t)throw new Error("New logical elements must start empty, or allowExistingContents must be true");return $ in e||(e[$]=[]),e}function O(e,t){const n=document.createComment("!");return F(n,e,t),n}function F(e,t,n){const r=e;if(e instanceof Comment&&J(r)&&J(r).length>0)throw new Error("Not implemented: inserting non-empty logical container");if(H(r))throw new Error("Not implemented: moving existing logical children");const o=J(t);if(n<o.length){const t=o[n];t.parentNode.insertBefore(e,t),o.splice(n,0,r)}else X(e,t),o.push(r);r[B]=t,$ in r||(r[$]=[])}function j(e,t){const n=J(e).splice(t,1)[0];if(n instanceof Comment){const e=J(n);if(e)for(;e.length>0;)j(n,0)}const r=n;r.parentNode.removeChild(r)}function H(e){return e[B]||null}function W(e,t){return J(e)[t]}function z(e){const t=V(e);return"http://www.w3.org/2000/svg"===t.namespaceURI&&"foreignObject"!==t.tagName}function J(e){return e[$]}function q(e,t){const n=J(e);t.forEach((e=>{e.moveRangeStart=n[e.fromSiblingIndex],e.moveRangeEnd=Y(e.moveRangeStart)})),t.forEach((t=>{const r=document.createComment("marker");t.moveToBeforeMarker=r;const o=n[t.toSiblingIndex+1];o?o.parentNode.insertBefore(r,o):X(r,e)})),t.forEach((e=>{const t=e.moveToBeforeMarker,n=t.parentNode,r=e.moveRangeStart,o=e.moveRangeEnd;let i=r;for(;i;){const e=i.nextSibling;if(n.insertBefore(i,t),i===o)break;i=e}n.removeChild(t)})),t.forEach((e=>{n[e.toSiblingIndex]=e.moveRangeStart}))}function V(e){if(e instanceof Element||e instanceof DocumentFragment)return e;if(e instanceof Comment)return e.parentNode;throw new Error("Not a valid logical element")}function K(e){const t=J(H(e));return t[Array.prototype.indexOf.call(t,e)+1]||null}function X(e,t){if(t instanceof Element||t instanceof DocumentFragment)t.appendChild(e);else{if(!(t instanceof Comment))throw new Error(`Cannot append node because the parent is not a valid logical element. Parent: ${t}`);{const n=K(t);n?n.parentNode.insertBefore(e,n):X(e,H(t))}}}function Y(e){if(e instanceof Element||e instanceof DocumentFragment)return e;const t=K(e);if(t)return t.previousSibling;{const t=H(e);return t instanceof Element||t instanceof DocumentFragment?t.lastChild:Y(t)}}function G(e){return"function"==typeof Symbol?Symbol():e}function Q(e){return`_bl_${e}`}e.attachReviver(((e,t)=>t&&"object"==typeof t&&Object.prototype.hasOwnProperty.call(t,"__internalId")&&"string"==typeof t.__internalId?function(e){const t=`[${Q(e)}]`;return document.querySelector(t)}(t.__internalId):t));const Z="_blazorDeferredValue",ee=document.createElement("template"),te=document.createElementNS("http://www.w3.org/2000/svg","g"),ne={},re="__internal_",oe="preventDefault_",ie="stopPropagation_";class se{constructor(e){this.rootComponentIds=new Set,this.childComponentLocations={},this.eventDelegator=new P(e),this.eventDelegator.notifyAfterClick((e=>{if(!fe)return;if(0!==e.button||function(e){return e.ctrlKey||e.shiftKey||e.altKey||e.metaKey}(e))return;if(e.defaultPrevented)return;const t=function(e){const t=!window._blazorDisableComposedPath&&e.composedPath&&e.composedPath();if(t){for(let e=0;e<t.length;e++){const n=t[e];if(n instanceof Element&&"A"===n.tagName)return n}return null}return Se(e.target,"A")}(e);if(t&&function(e){const t=e.getAttribute("target");return(!t||"_self"===t)&&e.hasAttribute("href")&&!e.hasAttribute("download")}(t)){const n=Ee(t.getAttribute("href"));Ce(n)&&(e.preventDefault(),ve(n,!0,!1))}}))}attachRootComponentToLogicalElement(e,t,n){this.attachComponentToElement(e,t),this.rootComponentIds.add(e),n||(ne[e]=t)}updateComponent(e,t,n,r){var o;const i=this.childComponentLocations[t];if(!i)throw new Error(`No element is currently associated with component ${t}`);const s=ne[t];if(s){const e=function(e){return e[L]||null}(s);delete ne[t],e?function(e,t){const n=H(e);if(!n)throw new Error("Can't clear between nodes. The start node does not have a logical parent.");const r=J(n),o=r.indexOf(e)+1,i=r.indexOf(t);for(let e=o;e<=i;e++)j(n,o);e.textContent="!"}(s,e):function(e){let t;for(;t=e.firstChild;)e.removeChild(t)}(s)}const a=null===(o=V(i))||void 0===o?void 0:o.getRootNode(),c=a&&a.activeElement;this.applyEdits(e,t,i,0,n,r),c instanceof HTMLElement&&a&&a.activeElement!==c&&c.focus()}disposeComponent(e){this.rootComponentIds.delete(e)&&function(e){const t=J(e);for(;t.length;)j(e,0)}(this.childComponentLocations[e]),delete this.childComponentLocations[e]}disposeEventHandler(e){this.eventDelegator.removeListener(e)}attachComponentToElement(e,t){this.childComponentLocations[e]=t}applyEdits(e,n,r,o,i,s){let a,c=0,l=o;const h=e.arrayBuilderSegmentReader,u=e.editReader,d=e.frameReader,p=h.values(i),f=h.offset(i),g=f+h.count(i);for(let i=f;i<g;i++){const h=e.diffReader.editsEntry(p,i),f=u.editType(h);switch(f){case t.prependFrame:{const t=u.newTreeIndex(h),o=e.referenceFramesEntry(s,t),i=u.siblingIndex(h);this.insertFrame(e,n,r,l+i,s,o,t);break}case t.removeFrame:j(r,l+u.siblingIndex(h));break;case t.setAttribute:{const t=u.newTreeIndex(h),o=e.referenceFramesEntry(s,t),i=W(r,l+u.siblingIndex(h));if(!(i instanceof Element))throw new Error("Cannot set attribute on non-element child");this.applyAttribute(e,n,i,o);break}case t.removeAttribute:{const t=W(r,l+u.siblingIndex(h));if(!(t instanceof HTMLElement))throw new Error("Cannot remove attribute from non-element child");{const n=u.removedAttributeName(h);this.tryApplySpecialProperty(e,t,n,null)||t.removeAttribute(n)}break}case t.updateText:{const t=u.newTreeIndex(h),n=e.referenceFramesEntry(s,t),o=W(r,l+u.siblingIndex(h));if(!(o instanceof Text))throw new Error("Cannot set text content on non-text child");o.textContent=d.textContent(n);break}case t.updateMarkup:{const t=u.newTreeIndex(h),n=e.referenceFramesEntry(s,t),o=u.siblingIndex(h);j(r,l+o),this.insertMarkup(e,r,l+o,n);break}case t.stepIn:r=W(r,l+u.siblingIndex(h)),c++,l=0;break;case t.stepOut:r=H(r),c--,l=0===c?o:0;break;case t.permutationListEntry:a=a||[],a.push({fromSiblingIndex:l+u.siblingIndex(h),toSiblingIndex:l+u.moveToSiblingIndex(h)});break;case t.permutationListEnd:q(r,a),a=void 0;break;default:throw new Error(`Unknown edit type: ${f}`)}}}insertFrame(e,t,r,o,i,s,a){const c=e.frameReader,l=c.frameType(s);switch(l){case n.element:return this.insertElement(e,t,r,o,i,s,a),1;case n.text:return this.insertText(e,r,o,s),1;case n.attribute:throw new Error("Attribute frames should only be present as leading children of element frames.");case n.component:return this.insertComponent(e,r,o,s),1;case n.region:return this.insertFrameRange(e,t,r,o,i,a+1,a+c.subtreeLength(s));case n.elementReferenceCapture:if(r instanceof Element)return h=r,u=c.elementReferenceCaptureId(s),h.setAttribute(Q(u),""),0;throw new Error("Reference capture frames can only be children of element frames.");case n.markup:return this.insertMarkup(e,r,o,s),1;default:throw new Error(`Unknown frame type: ${l}`)}var h,u}insertElement(e,t,r,o,i,s,a){const c=e.frameReader,l=c.elementName(s),h="svg"===l||z(r)?document.createElementNS("http://www.w3.org/2000/svg",l):document.createElement(l),u=M(h);let d=!1;const p=a+c.subtreeLength(s);for(let s=a+1;s<p;s++){const a=e.referenceFramesEntry(i,s);if(c.frameType(a)!==n.attribute){F(h,r,o),d=!0,this.insertFrameRange(e,t,u,0,i,s,p);break}this.applyAttribute(e,t,h,a)}d||F(h,r,o),h instanceof HTMLOptionElement?this.trySetSelectValueFromOptionElement(h):Z in h&&ue(h,h._blazorDeferredValue)}trySetSelectValueFromOptionElement(e){const t=this.findClosestAncestorSelectElement(e);if(!function(e){return!!e&&Z in e}(t))return!1;if(le(t))e.selected=-1!==t._blazorDeferredValue.indexOf(e.value);else{if(t._blazorDeferredValue!==e.value)return!1;he(t,e.value),delete t._blazorDeferredValue}return!0}insertComponent(e,t,n,r){const o=O(t,n),i=e.frameReader.componentId(r);this.attachComponentToElement(i,o)}insertText(e,t,n,r){const o=e.frameReader.textContent(r);F(document.createTextNode(o),t,n)}insertMarkup(e,t,n,r){const o=O(t,n),i=(s=e.frameReader.markupContent(r),z(t)?(te.innerHTML=s||" ",te):(ee.innerHTML=s||" ",ee.content));var s;let a=0;for(;i.firstChild;)F(i.firstChild,o,a++)}applyAttribute(e,t,n,r){const o=e.frameReader,i=o.attributeName(r),s=o.attributeEventHandlerId(r);if(s){const e=ce(i);this.eventDelegator.setListener(n,e,s,t)}else this.tryApplySpecialProperty(e,n,i,r)||n.setAttribute(i,o.attributeValue(r))}tryApplySpecialProperty(e,t,n,r){switch(n){case"value":return this.tryApplyValueProperty(e,t,r);case"checked":return this.tryApplyCheckedProperty(e,t,r);default:return!!n.startsWith(re)&&(this.applyInternalAttribute(e,t,n.substring(re.length),r),!0)}}applyInternalAttribute(e,t,n,r){const o=r?e.frameReader.attributeValue(r):null;if(n.startsWith(ie)){const e=ce(n.substring(ie.length));this.eventDelegator.setStopPropagation(t,e,null!==o)}else{if(!n.startsWith(oe))throw new Error(`Unsupported internal attribute '${n}'`);{const e=ce(n.substring(oe.length));this.eventDelegator.setPreventDefault(t,e,null!==o)}}}tryApplyValueProperty(e,t,n){const r=e.frameReader;let o=n?r.attributeValue(n):null;switch(o&&"INPUT"===t.tagName&&(o=function(e,t){switch(t.getAttribute("type")){case"time":return 8!==e.length||!e.endsWith("00")&&t.hasAttribute("step")?e:e.substring(0,5);case"datetime-local":return 19!==e.length||!e.endsWith("00")&&t.hasAttribute("step")?e:e.substring(0,16);default:return e}}(o,t)),t.tagName){case"INPUT":case"SELECT":case"TEXTAREA":return o&&t instanceof HTMLSelectElement&&le(t)&&(o=JSON.parse(o)),ue(t,o),t._blazorDeferredValue=o,!0;case"OPTION":return o||""===o?t.setAttribute("value",o):t.removeAttribute("value"),this.trySetSelectValueFromOptionElement(t),!0;default:return!1}}tryApplyCheckedProperty(e,t,n){if("INPUT"===t.tagName){const r=n?e.frameReader.attributeValue(n):null;return t.checked=null!==r,!0}return!1}findClosestAncestorSelectElement(e){for(;e;){if(e instanceof HTMLSelectElement)return e;e=e.parentElement}return null}insertFrameRange(e,t,n,r,o,i,s){const a=r;for(let a=i;a<s;a++){const i=e.referenceFramesEntry(o,a);r+=this.insertFrame(e,t,n,r,o,i,a),a+=ae(e,i)}return r-a}}function ae(e,t){const r=e.frameReader;switch(r.frameType(t)){case n.component:case n.element:case n.region:return r.subtreeLength(t)-1;default:return 0}}function ce(e){if(e.startsWith("on"))return e.substring(2);throw new Error(`Attribute should be an event name, but doesn't start with 'on'. Value: '${e}'`)}function le(e){return"select-multiple"===e.type}function he(e,t){e.value=t||""}function ue(e,t){e instanceof HTMLSelectElement?le(e)?function(e,t){t||(t=[]);for(let n=0;n<e.options.length;n++)e.options[n].selected=-1!==t.indexOf(e.options[n].value)}(e,t):he(e,t):e.value=t}const de={};let pe=!1,fe=!1,ge=!1,me=null;const ye={listenForNavigationEvents:function(e){me=e,ge||(ge=!0,window.addEventListener("popstate",(()=>be(!1))))},enableNavigationInterception:function(){fe=!0},navigateTo:we,getBaseURI:()=>document.baseURI,getLocationHref:()=>location.href};function we(e,t,n=!1){const r=Ee(e),o=t instanceof Object?t:{forceLoad:t,replaceHistoryEntry:n};!o.forceLoad&&Ce(r)?ve(r,!1,o.replaceHistoryEntry):function(e,t){if(location.href===e){const t=e+"?";history.replaceState(null,"",t),location.replace(e)}else t?location.replace(e):location.href=e}(e,o.replaceHistoryEntry)}function ve(e,t,n){pe=!0,n?history.replaceState(null,"",e):history.pushState(null,"",e),be(t)}async function be(e){me&&await me(location.href,e)}let _e;function Ee(e){return _e=_e||document.createElement("a"),_e.href=e,_e.href}function Se(e,t){return e?e.tagName===t?e:Se(e.parentElement,t):null}function Ce(e){const t=(n=document.baseURI).substring(0,n.lastIndexOf("/"));var n;const r=e.charAt(t.length);return e.startsWith(t)&&(""===r||"/"===r||"?"===r||"#"===r)}const Ie={focus:function(e,t){if(e instanceof HTMLElement)e.focus({preventScroll:t});else{if(!(e instanceof SVGElement))throw new Error("Unable to focus an invalid element.");if(!e.hasAttribute("tabindex"))throw new Error("Unable to focus an SVG element that does not have a tabindex.");e.focus({preventScroll:t})}},focusBySelector:function(e){const t=document.querySelector(e);t&&(t.hasAttribute("tabindex")||(t.tabIndex=-1),t.focus())}},ke={init:function(e,t,n,r=50){const o=xe(t);(o||document.documentElement).style.overflowAnchor="none";const i=document.createRange();h(n.parentElement)&&(t.style.display="table-row",n.style.display="table-row");const s=new IntersectionObserver((function(r){r.forEach((r=>{var o;if(!r.isIntersecting)return;i.setStartAfter(t),i.setEndBefore(n);const s=i.getBoundingClientRect().height,a=null===(o=r.rootBounds)||void 0===o?void 0:o.height;r.target===t?e.invokeMethodAsync("OnSpacerBeforeVisible",r.intersectionRect.top-r.boundingClientRect.top,s,a):r.target===n&&n.offsetHeight>0&&e.invokeMethodAsync("OnSpacerAfterVisible",r.boundingClientRect.bottom-r.intersectionRect.bottom,s,a)}))}),{root:o,rootMargin:`${r}px`});s.observe(t),s.observe(n);const a=l(t),c=l(n);function l(e){const t={attributes:!0},n=new MutationObserver(((n,r)=>{h(e.parentElement)&&(r.disconnect(),e.style.display="table-row",r.observe(e,t)),s.unobserve(e),s.observe(e)}));return n.observe(e,t),n}function h(e){return null!==e&&(e instanceof HTMLTableElement&&""===e.style.display||"table"===e.style.display||e instanceof HTMLTableSectionElement&&""===e.style.display||"table-row-group"===e.style.display)}Te[e._id]={intersectionObserver:s,mutationObserverBefore:a,mutationObserverAfter:c}},dispose:function(e){const t=Te[e._id];t&&(t.intersectionObserver.disconnect(),t.mutationObserverBefore.disconnect(),t.mutationObserverAfter.disconnect(),e.dispose(),delete Te[e._id])}},Te={};function xe(e){return e?"visible"!==getComputedStyle(e).overflowY?e:xe(e.parentElement):null}const De={getAndRemoveExistingTitle:function(){var e;const t=document.head?document.head.getElementsByTagName("title"):[];if(0===t.length)return null;let n=null;for(let r=t.length-1;r>=0;r--){const o=t[r],i=o.previousSibling;i instanceof Comment&&null!==H(i)||(null===n&&(n=o.textContent),null===(e=o.parentNode)||void 0===e||e.removeChild(o))}return n}},Re={init:function(e,t){t._blazorInputFileNextFileId=0,t.addEventListener("click",(function(){t.value=""})),t.addEventListener("change",(function(){t._blazorFilesById={};const n=Array.prototype.map.call(t.files,(function(e){const n={id:++t._blazorInputFileNextFileId,lastModified:new Date(e.lastModified).toISOString(),name:e.name,size:e.size,contentType:e.type,readPromise:void 0,arrayBuffer:void 0,blob:e};return t._blazorFilesById[n.id]=n,n}));e.invokeMethodAsync("NotifyChange",n)}))},toImageFile:async function(e,t,n,r,o){const i=Pe(e,t),s=await new Promise((function(e){const t=new Image;t.onload=function(){URL.revokeObjectURL(t.src),e(t)},t.onerror=function(){t.onerror=null,URL.revokeObjectURL(t.src)},t.src=URL.createObjectURL(i.blob)})),a=await new Promise((function(e){var t;const i=Math.min(1,r/s.width),a=Math.min(1,o/s.height),c=Math.min(i,a),l=document.createElement("canvas");l.width=Math.round(s.width*c),l.height=Math.round(s.height*c),null===(t=l.getContext("2d"))||void 0===t||t.drawImage(s,0,0,l.width,l.height),l.toBlob(e,n)})),c={id:++e._blazorInputFileNextFileId,lastModified:i.lastModified,name:i.name,size:(null==a?void 0:a.size)||0,contentType:n,blob:a||i.blob};return e._blazorFilesById[c.id]=c,c},readFileData:async function(e,t){return Pe(e,t).blob}};function Pe(e,t){const n=e._blazorFilesById[t];if(!n)throw new Error(`There is no file with ID ${t}. The file list may have changed. See https://aka.ms/aspnet/blazor-input-file-multiple-selections.`);return n}async function Ue(e,t,n){return e instanceof Blob?await async function(e,t,n){const r=e.slice(t,t+n),o=await r.arrayBuffer();return new Uint8Array(o)}(e,t,n):function(e,t,n){return new Uint8Array(e.buffer,e.byteOffset+t,n)}(e,t,n)}const Ae=new Map,Ne={navigateTo:we,registerCustomEventType:function(e,t){if(!t)throw new Error("The options parameter is required.");if(i.has(e))throw new Error(`The event '${e}' is already registered.`);if(t.browserEventName){const n=s.get(t.browserEventName);n?n.push(e):s.set(t.browserEventName,[e]),a.forEach((n=>n(e,t.browserEventName)))}i.set(e,t)},rootComponents:w,_internal:{navigationManager:ye,domWrapper:Ie,Virtualize:ke,PageTitle:De,InputFile:Re,getJSDataStreamChunk:Ue,receiveDotNetDataStream:function(t,n,r,o){let i=Ae.get(t);if(!i){const n=new ReadableStream({start(e){Ae.set(t,e),i=e}});e.jsCallDispatcher.supplyDotNetStream(t,n)}o?(i.error(o),Ae.delete(t)):0===r?(i.close(),Ae.delete(t)):i.enqueue(n.length===r?n:n.subarray(0,r))},attachWebRendererInterop:function(t,n,r,o){if(E.has(t))throw new Error(`Interop methods are already registered for renderer ${t}`);E.set(t,n),Object.keys(r).length>0&&function(t,n,r){if(g)throw new Error("Dynamic root components have already been enabled.");g=t,m=n;for(const[t,o]of Object.entries(r)){const r=e.jsCallDispatcher.findJSFunction(t,0);for(const e of o)r(e,n[e])}}(k(t),r,o),S()}}};window.Blazor=Ne;const $e=[0,2e3,1e4,3e4,null];class Be{constructor(e){this._retryDelays=void 0!==e?[...e,null]:$e}nextRetryDelayInMilliseconds(e){return this._retryDelays[e.previousRetryCount]}}class Le extends Error{constructor(e,t){const n=new.target.prototype;super(`${e}: Status code '${t}'`),this.statusCode=t,this.__proto__=n}}class Me extends Error{constructor(e="A timeout occurred."){const t=new.target.prototype;super(e),this.__proto__=t}}class Oe extends Error{constructor(e="An abort occurred."){const t=new.target.prototype;super(e),this.__proto__=t}}class Fe extends Error{constructor(e,t){const n=new.target.prototype;super(e),this.transport=t,this.errorType="UnsupportedTransportError",this.__proto__=n}}class je extends Error{constructor(e,t){const n=new.target.prototype;super(e),this.transport=t,this.errorType="DisabledTransportError",this.__proto__=n}}class He extends Error{constructor(e,t){const n=new.target.prototype;super(e),this.transport=t,this.errorType="FailedToStartTransportError",this.__proto__=n}}class We extends Error{constructor(e){const t=new.target.prototype;super(e),this.errorType="FailedToNegotiateWithServerError",this.__proto__=t}}class ze extends Error{constructor(e,t){const n=new.target.prototype;super(e),this.innerErrors=t,this.__proto__=n}}class Je{constructor(e,t,n){this.statusCode=e,this.statusText=t,this.content=n}}class qe{get(e,t){return this.send({...t,method:"GET",url:e})}post(e,t){return this.send({...t,method:"POST",url:e})}delete(e,t){return this.send({...t,method:"DELETE",url:e})}getCookieString(e){return""}}var Ve,Ke,Xe,Ye,Ge;!function(e){e[e.Trace=0]="Trace",e[e.Debug=1]="Debug",e[e.Information=2]="Information",e[e.Warning=3]="Warning",e[e.Error=4]="Error",e[e.Critical=5]="Critical",e[e.None=6]="None"}(Ve||(Ve={}));class Qe{constructor(){}log(e,t){}}Qe.instance=new Qe;class Ze{static isRequired(e,t){if(null==e)throw new Error(`The '${t}' argument is required.`)}static isNotEmpty(e,t){if(!e||e.match(/^\s*$/))throw new Error(`The '${t}' argument should not be empty.`)}static isIn(e,t,n){if(!(e in t))throw new Error(`Unknown ${n} value: ${e}.`)}}class et{static get isBrowser(){return"object"==typeof window&&"object"==typeof window.document}static get isWebWorker(){return"object"==typeof self&&"importScripts"in self}static get isReactNative(){return"object"==typeof window&&void 0===window.document}static get isNode(){return!this.isBrowser&&!this.isWebWorker&&!this.isReactNative}}function tt(e,t){let n="";return nt(e)?(n=`Binary data of length ${e.byteLength}`,t&&(n+=`. Content: '${function(e){const t=new Uint8Array(e);let n="";return t.forEach((e=>{n+=`0x${e<16?"0":""}${e.toString(16)} `})),n.substr(0,n.length-1)}(e)}'`)):"string"==typeof e&&(n=`String data of length ${e.length}`,t&&(n+=`. Content: '${e}'`)),n}function nt(e){return e&&"undefined"!=typeof ArrayBuffer&&(e instanceof ArrayBuffer||e.constructor&&"ArrayBuffer"===e.constructor.name)}async function rt(e,t,n,r,o,i,s){let a={};if(o){const e=await o();e&&(a={Authorization:`Bearer ${e}`})}const[c,l]=st();a[c]=l,e.log(Ve.Trace,`(${t} transport) sending data. ${tt(i,s.logMessageContent)}.`);const h=nt(i)?"arraybuffer":"text",u=await n.post(r,{content:i,headers:{...a,...s.headers},responseType:h,timeout:s.timeout,withCredentials:s.withCredentials});e.log(Ve.Trace,`(${t} transport) request complete. Response status: ${u.statusCode}.`)}class ot{constructor(e,t){this._subject=e,this._observer=t}dispose(){const e=this._subject.observers.indexOf(this._observer);e>-1&&this._subject.observers.splice(e,1),0===this._subject.observers.length&&this._subject.cancelCallback&&this._subject.cancelCallback().catch((e=>{}))}}class it{constructor(e){this._minLevel=e,this.out=console}log(e,t){if(e>=this._minLevel){const n=`[${(new Date).toISOString()}] ${Ve[e]}: ${t}`;switch(e){case Ve.Critical:case Ve.Error:this.out.error(n);break;case Ve.Warning:this.out.warn(n);break;case Ve.Information:this.out.info(n);break;default:this.out.log(n)}}}}function st(){let e="X-SignalR-User-Agent";return et.isNode&&(e="User-Agent"),[e,at("0.0.0-DEV_BUILD",ct(),et.isNode?"NodeJS":"Browser",lt())]}function at(e,t,n,r){let o="Microsoft SignalR/";const i=e.split(".");return o+=`${i[0]}.${i[1]}`,o+=` (${e}; `,o+=t&&""!==t?`${t}; `:"Unknown OS; ",o+=`${n}`,o+=r?`; ${r}`:"; Unknown Runtime Version",o+=")",o}function ct(){if(!et.isNode)return"";switch(process.platform){case"win32":return"Windows NT";case"darwin":return"macOS";case"linux":return"Linux";default:return process.platform}}function lt(){if(et.isNode)return process.versions.node}function ht(e){return e.stack?e.stack:e.message?e.message:`${e}`}class ut extends qe{constructor(e){if(super(),this._logger=e,"undefined"==typeof fetch){const e=require;this._jar=new(e("tough-cookie").CookieJar),this._fetchType=e("node-fetch"),this._fetchType=e("fetch-cookie")(this._fetchType,this._jar)}else this._fetchType=fetch.bind(function(){if("undefined"!=typeof globalThis)return globalThis;if("undefined"!=typeof self)return self;if("undefined"!=typeof window)return window;if(void 0!==r.g)return r.g;throw new Error("could not find global")}());if("undefined"==typeof AbortController){const e=require;this._abortControllerType=e("abort-controller")}else this._abortControllerType=AbortController}async send(e){if(e.abortSignal&&e.abortSignal.aborted)throw new Oe;if(!e.method)throw new Error("No method defined.");if(!e.url)throw new Error("No url defined.");const t=new this._abortControllerType;let n;e.abortSignal&&(e.abortSignal.onabort=()=>{t.abort(),n=new Oe});let r,o=null;if(e.timeout){const r=e.timeout;o=setTimeout((()=>{t.abort(),this._logger.log(Ve.Warning,"Timeout from HTTP request."),n=new Me}),r)}""===e.content&&(e.content=void 0),e.content&&(e.headers=e.headers||{},nt(e.content)?e.headers["Content-Type"]="application/octet-stream":e.headers["Content-Type"]="text/plain;charset=UTF-8");try{r=await this._fetchType(e.url,{body:e.content,cache:"no-cache",credentials:!0===e.withCredentials?"include":"same-origin",headers:{"X-Requested-With":"XMLHttpRequest",...e.headers},method:e.method,mode:"cors",redirect:"follow",signal:t.signal})}catch(e){if(n)throw n;throw this._logger.log(Ve.Warning,`Error from HTTP request. ${e}.`),e}finally{o&&clearTimeout(o),e.abortSignal&&(e.abortSignal.onabort=null)}if(!r.ok){const e=await dt(r,"text");throw new Le(e||r.statusText,r.status)}const i=dt(r,e.responseType),s=await i;return new Je(r.status,r.statusText,s)}getCookieString(e){return""}}function dt(e,t){let n;switch(t){case"arraybuffer":n=e.arrayBuffer();break;case"text":default:n=e.text();break;case"blob":case"document":case"json":throw new Error(`${t} is not supported.`)}return n}class pt extends qe{constructor(e){super(),this._logger=e}send(e){return e.abortSignal&&e.abortSignal.aborted?Promise.reject(new Oe):e.method?e.url?new Promise(((t,n)=>{const r=new XMLHttpRequest;r.open(e.method,e.url,!0),r.withCredentials=void 0===e.withCredentials||e.withCredentials,r.setRequestHeader("X-Requested-With","XMLHttpRequest"),""===e.content&&(e.content=void 0),e.content&&(nt(e.content)?r.setRequestHeader("Content-Type","application/octet-stream"):r.setRequestHeader("Content-Type","text/plain;charset=UTF-8"));const o=e.headers;o&&Object.keys(o).forEach((e=>{r.setRequestHeader(e,o[e])})),e.responseType&&(r.responseType=e.responseType),e.abortSignal&&(e.abortSignal.onabort=()=>{r.abort(),n(new Oe)}),e.timeout&&(r.timeout=e.timeout),r.onload=()=>{e.abortSignal&&(e.abortSignal.onabort=null),r.status>=200&&r.status<300?t(new Je(r.status,r.statusText,r.response||r.responseText)):n(new Le(r.response||r.responseText||r.statusText,r.status))},r.onerror=()=>{this._logger.log(Ve.Warning,`Error from HTTP request. ${r.status}: ${r.statusText}.`),n(new Le(r.statusText,r.status))},r.ontimeout=()=>{this._logger.log(Ve.Warning,"Timeout from HTTP request."),n(new Me)},r.send(e.content)})):Promise.reject(new Error("No url defined.")):Promise.reject(new Error("No method defined."))}}class ft extends qe{constructor(e){if(super(),"undefined"!=typeof fetch)this._httpClient=new ut(e);else{if("undefined"==typeof XMLHttpRequest)throw new Error("No usable HttpClient found.");this._httpClient=new pt(e)}}send(e){return e.abortSignal&&e.abortSignal.aborted?Promise.reject(new Oe):e.method?e.url?this._httpClient.send(e):Promise.reject(new Error("No url defined.")):Promise.reject(new Error("No method defined."))}getCookieString(e){return this._httpClient.getCookieString(e)}}class gt{}gt.Authorization="Authorization",gt.Cookie="Cookie",function(e){e[e.None=0]="None",e[e.WebSockets=1]="WebSockets",e[e.ServerSentEvents=2]="ServerSentEvents",e[e.LongPolling=4]="LongPolling"}(Ke||(Ke={})),function(e){e[e.Text=1]="Text",e[e.Binary=2]="Binary"}(Xe||(Xe={}));class mt{constructor(){this._isAborted=!1,this.onabort=null}abort(){this._isAborted||(this._isAborted=!0,this.onabort&&this.onabort())}get signal(){return this}get aborted(){return this._isAborted}}class yt{constructor(e,t,n,r){this._httpClient=e,this._accessTokenFactory=t,this._logger=n,this._pollAbort=new mt,this._options=r,this._running=!1,this.onreceive=null,this.onclose=null}get pollAborted(){return this._pollAbort.aborted}async connect(e,t){if(Ze.isRequired(e,"url"),Ze.isRequired(t,"transferFormat"),Ze.isIn(t,Xe,"transferFormat"),this._url=e,this._logger.log(Ve.Trace,"(LongPolling transport) Connecting."),t===Xe.Binary&&"undefined"!=typeof XMLHttpRequest&&"string"!=typeof(new XMLHttpRequest).responseType)throw new Error("Binary protocols over XmlHttpRequest not implementing advanced features are not supported.");const[n,r]=st(),o={[n]:r,...this._options.headers},i={abortSignal:this._pollAbort.signal,headers:o,timeout:1e5,withCredentials:this._options.withCredentials};t===Xe.Binary&&(i.responseType="arraybuffer"),await this._updateHeaderToken(i);const s=`${e}&_=${Date.now()}`;this._logger.log(Ve.Trace,`(LongPolling transport) polling: ${s}.`);const a=await this._httpClient.get(s,i);200!==a.statusCode?(this._logger.log(Ve.Error,`(LongPolling transport) Unexpected response code: ${a.statusCode}.`),this._closeError=new Le(a.statusText||"",a.statusCode),this._running=!1):this._running=!0,this._receiving=this._poll(this._url,i)}async _updateHeaderToken(e){if(e.headers||(e.headers={}),this._accessTokenFactory){const t=await this._accessTokenFactory();t?e.headers[gt.Authorization]=`Bearer ${t}`:e.headers[gt.Authorization]&&delete e.headers[gt.Authorization]}}async _poll(e,t){try{for(;this._running;){await this._updateHeaderToken(t);try{const n=`${e}&_=${Date.now()}`;this._logger.log(Ve.Trace,`(LongPolling transport) polling: ${n}.`);const r=await this._httpClient.get(n,t);204===r.statusCode?(this._logger.log(Ve.Information,"(LongPolling transport) Poll terminated by server."),this._running=!1):200!==r.statusCode?(this._logger.log(Ve.Error,`(LongPolling transport) Unexpected response code: ${r.statusCode}.`),this._closeError=new Le(r.statusText||"",r.statusCode),this._running=!1):r.content?(this._logger.log(Ve.Trace,`(LongPolling transport) data received. ${tt(r.content,this._options.logMessageContent)}.`),this.onreceive&&this.onreceive(r.content)):this._logger.log(Ve.Trace,"(LongPolling transport) Poll timed out, reissuing.")}catch(e){this._running?e instanceof Me?this._logger.log(Ve.Trace,"(LongPolling transport) Poll timed out, reissuing."):(this._closeError=e,this._running=!1):this._logger.log(Ve.Trace,`(LongPolling transport) Poll errored after shutdown: ${e.message}`)}}}finally{this._logger.log(Ve.Trace,"(LongPolling transport) Polling complete."),this.pollAborted||this._raiseOnClose()}}async send(e){return this._running?rt(this._logger,"LongPolling",this._httpClient,this._url,this._accessTokenFactory,e,this._options):Promise.reject(new Error("Cannot send until the transport is connected"))}async stop(){this._logger.log(Ve.Trace,"(LongPolling transport) Stopping polling."),this._running=!1,this._pollAbort.abort();try{await this._receiving,this._logger.log(Ve.Trace,`(LongPolling transport) sending DELETE request to ${this._url}.`);const e={},[t,n]=st();e[t]=n;const r={headers:{...e,...this._options.headers},timeout:this._options.timeout,withCredentials:this._options.withCredentials};await this._updateHeaderToken(r),await this._httpClient.delete(this._url,r),this._logger.log(Ve.Trace,"(LongPolling transport) DELETE request sent.")}finally{this._logger.log(Ve.Trace,"(LongPolling transport) Stop finished."),this._raiseOnClose()}}_raiseOnClose(){if(this.onclose){let e="(LongPolling transport) Firing onclose event.";this._closeError&&(e+=" Error: "+this._closeError),this._logger.log(Ve.Trace,e),this.onclose(this._closeError)}}}class wt{constructor(e,t,n,r){this._httpClient=e,this._accessTokenFactory=t,this._logger=n,this._options=r,this.onreceive=null,this.onclose=null}async connect(e,t){if(Ze.isRequired(e,"url"),Ze.isRequired(t,"transferFormat"),Ze.isIn(t,Xe,"transferFormat"),this._logger.log(Ve.Trace,"(SSE transport) Connecting."),this._url=e,this._accessTokenFactory){const t=await this._accessTokenFactory();t&&(e+=(e.indexOf("?")<0?"?":"&")+`access_token=${encodeURIComponent(t)}`)}return new Promise(((n,r)=>{let o,i=!1;if(t===Xe.Text){if(et.isBrowser||et.isWebWorker)o=new this._options.EventSource(e,{withCredentials:this._options.withCredentials});else{const t=this._httpClient.getCookieString(e),n={};n.Cookie=t;const[r,i]=st();n[r]=i,o=new this._options.EventSource(e,{withCredentials:this._options.withCredentials,headers:{...n,...this._options.headers}})}try{o.onmessage=e=>{if(this.onreceive)try{this._logger.log(Ve.Trace,`(SSE transport) data received. ${tt(e.data,this._options.logMessageContent)}.`),this.onreceive(e.data)}catch(e){return void this._close(e)}},o.onerror=e=>{i?this._close():r(new Error("EventSource failed to connect. The connection could not be found on the server, either the connection ID is not present on the server, or a proxy is refusing/buffering the connection. If you have multiple servers check that sticky sessions are enabled."))},o.onopen=()=>{this._logger.log(Ve.Information,`SSE connected to ${this._url}`),this._eventSource=o,i=!0,n()}}catch(e){return void r(e)}}else r(new Error("The Server-Sent Events transport only supports the 'Text' transfer format"))}))}async send(e){return this._eventSource?rt(this._logger,"SSE",this._httpClient,this._url,this._accessTokenFactory,e,this._options):Promise.reject(new Error("Cannot send until the transport is connected"))}stop(){return this._close(),Promise.resolve()}_close(e){this._eventSource&&(this._eventSource.close(),this._eventSource=void 0,this.onclose&&this.onclose(e))}}class vt{constructor(e,t,n,r,o,i){this._logger=n,this._accessTokenFactory=t,this._logMessageContent=r,this._webSocketConstructor=o,this._httpClient=e,this.onreceive=null,this.onclose=null,this._headers=i}async connect(e,t){let n;return Ze.isRequired(e,"url"),Ze.isRequired(t,"transferFormat"),Ze.isIn(t,Xe,"transferFormat"),this._logger.log(Ve.Trace,"(WebSockets transport) Connecting."),this._accessTokenFactory&&(n=await this._accessTokenFactory()),new Promise(((r,o)=>{let i;e=e.replace(/^http/,"ws");const s=this._httpClient.getCookieString(e);let a=!1;if(et.isReactNative){const t={},[r,o]=st();t[r]=o,n&&(t[gt.Authorization]=`Bearer ${n}`),s&&(t[gt.Cookie]=s),i=new this._webSocketConstructor(e,void 0,{headers:{...t,...this._headers}})}else n&&(e+=(e.indexOf("?")<0?"?":"&")+`access_token=${encodeURIComponent(n)}`);i||(i=new this._webSocketConstructor(e)),t===Xe.Binary&&(i.binaryType="arraybuffer"),i.onopen=t=>{this._logger.log(Ve.Information,`WebSocket connected to ${e}.`),this._webSocket=i,a=!0,r()},i.onerror=e=>{let t=null;t="undefined"!=typeof ErrorEvent&&e instanceof ErrorEvent?e.error:"There was an error with the transport",this._logger.log(Ve.Information,`(WebSockets transport) ${t}.`)},i.onmessage=e=>{if(this._logger.log(Ve.Trace,`(WebSockets transport) data received. ${tt(e.data,this._logMessageContent)}.`),this.onreceive)try{this.onreceive(e.data)}catch(e){return void this._close(e)}},i.onclose=e=>{if(a)this._close(e);else{let t=null;t="undefined"!=typeof ErrorEvent&&e instanceof ErrorEvent?e.error:"WebSocket failed to connect. The connection could not be found on the server, either the endpoint may not be a SignalR endpoint, the connection ID is not present on the server, or there is a proxy blocking WebSockets. If you have multiple servers check that sticky sessions are enabled.",o(new Error(t))}}}))}send(e){return this._webSocket&&this._webSocket.readyState===this._webSocketConstructor.OPEN?(this._logger.log(Ve.Trace,`(WebSockets transport) sending data. ${tt(e,this._logMessageContent)}.`),this._webSocket.send(e),Promise.resolve()):Promise.reject("WebSocket is not in the OPEN state")}stop(){return this._webSocket&&this._close(void 0),Promise.resolve()}_close(e){this._webSocket&&(this._webSocket.onclose=()=>{},this._webSocket.onmessage=()=>{},this._webSocket.onerror=()=>{},this._webSocket.close(),this._webSocket=void 0),this._logger.log(Ve.Trace,"(WebSockets transport) socket closed."),this.onclose&&(!this._isCloseEvent(e)||!1!==e.wasClean&&1e3===e.code?e instanceof Error?this.onclose(e):this.onclose():this.onclose(new Error(`WebSocket closed with status code: ${e.code} (${e.reason||"no reason given"}).`)))}_isCloseEvent(e){return e&&"boolean"==typeof e.wasClean&&"number"==typeof e.code}}class bt{constructor(e,t={}){var n;if(this._stopPromiseResolver=()=>{},this.features={},this._negotiateVersion=1,Ze.isRequired(e,"url"),this._logger=void 0===(n=t.logger)?new it(Ve.Information):null===n?Qe.instance:void 0!==n.log?n:new it(n),this.baseUrl=this._resolveUrl(e),(t=t||{}).logMessageContent=void 0!==t.logMessageContent&&t.logMessageContent,"boolean"!=typeof t.withCredentials&&void 0!==t.withCredentials)throw new Error("withCredentials option was not a 'boolean' or 'undefined' value");t.withCredentials=void 0===t.withCredentials||t.withCredentials,t.timeout=void 0===t.timeout?1e5:t.timeout,"undefined"==typeof WebSocket||t.WebSocket||(t.WebSocket=WebSocket),"undefined"==typeof EventSource||t.EventSource||(t.EventSource=EventSource),this._httpClient=t.httpClient||new ft(this._logger),this._connectionState="Disconnected",this._connectionStarted=!1,this._options=t,this.onreceive=null,this.onclose=null}async start(e){if(e=e||Xe.Binary,Ze.isIn(e,Xe,"transferFormat"),this._logger.log(Ve.Debug,`Starting connection with transfer format '${Xe[e]}'.`),"Disconnected"!==this._connectionState)return Promise.reject(new Error("Cannot start an HttpConnection that is not in the 'Disconnected' state."));if(this._connectionState="Connecting",this._startInternalPromise=this._startInternal(e),await this._startInternalPromise,"Disconnecting"===this._connectionState){const e="Failed to start the HttpConnection before stop() was called.";return this._logger.log(Ve.Error,e),await this._stopPromise,Promise.reject(new Oe(e))}if("Connected"!==this._connectionState){const e="HttpConnection.startInternal completed gracefully but didn't enter the connection into the connected state!";return this._logger.log(Ve.Error,e),Promise.reject(new Oe(e))}this._connectionStarted=!0}send(e){return"Connected"!==this._connectionState?Promise.reject(new Error("Cannot send data if the connection is not in the 'Connected' State.")):(this._sendQueue||(this._sendQueue=new _t(this.transport)),this._sendQueue.send(e))}async stop(e){return"Disconnected"===this._connectionState?(this._logger.log(Ve.Debug,`Call to HttpConnection.stop(${e}) ignored because the connection is already in the disconnected state.`),Promise.resolve()):"Disconnecting"===this._connectionState?(this._logger.log(Ve.Debug,`Call to HttpConnection.stop(${e}) ignored because the connection is already in the disconnecting state.`),this._stopPromise):(this._connectionState="Disconnecting",this._stopPromise=new Promise((e=>{this._stopPromiseResolver=e})),await this._stopInternal(e),void await this._stopPromise)}async _stopInternal(e){this._stopError=e;try{await this._startInternalPromise}catch(e){}if(this.transport){try{await this.transport.stop()}catch(e){this._logger.log(Ve.Error,`HttpConnection.transport.stop() threw error '${e}'.`),this._stopConnection()}this.transport=void 0}else this._logger.log(Ve.Debug,"HttpConnection.transport is undefined in HttpConnection.stop() because start() failed.")}async _startInternal(e){let t=this.baseUrl;this._accessTokenFactory=this._options.accessTokenFactory;try{if(this._options.skipNegotiation){if(this._options.transport!==Ke.WebSockets)throw new Error("Negotiation can only be skipped when using the WebSocket transport directly.");this.transport=this._constructTransport(Ke.WebSockets),await this._startTransport(t,e)}else{let n=null,r=0;do{if(n=await this._getNegotiationResponse(t),"Disconnecting"===this._connectionState||"Disconnected"===this._connectionState)throw new Oe("The connection was stopped during negotiation.");if(n.error)throw new Error(n.error);if(n.ProtocolVersion)throw new Error("Detected a connection attempt to an ASP.NET SignalR Server. This client only supports connecting to an ASP.NET Core SignalR Server. See https://aka.ms/signalr-core-differences for details.");if(n.url&&(t=n.url),n.accessToken){const e=n.accessToken;this._accessTokenFactory=()=>e}r++}while(n.url&&r<100);if(100===r&&n.url)throw new Error("Negotiate redirection limit exceeded.");await this._createTransport(t,this._options.transport,n,e)}this.transport instanceof yt&&(this.features.inherentKeepAlive=!0),"Connecting"===this._connectionState&&(this._logger.log(Ve.Debug,"The HttpConnection connected successfully."),this._connectionState="Connected")}catch(e){return this._logger.log(Ve.Error,"Failed to start the connection: "+e),this._connectionState="Disconnected",this.transport=void 0,this._stopPromiseResolver(),Promise.reject(e)}}async _getNegotiationResponse(e){const t={};if(this._accessTokenFactory){const e=await this._accessTokenFactory();e&&(t[gt.Authorization]=`Bearer ${e}`)}const[n,r]=st();t[n]=r;const o=this._resolveNegotiateUrl(e);this._logger.log(Ve.Debug,`Sending negotiation request: ${o}.`);try{const e=await this._httpClient.post(o,{content:"",headers:{...t,...this._options.headers},timeout:this._options.timeout,withCredentials:this._options.withCredentials});if(200!==e.statusCode)return Promise.reject(new Error(`Unexpected status code returned from negotiate '${e.statusCode}'`));const n=JSON.parse(e.content);return(!n.negotiateVersion||n.negotiateVersion<1)&&(n.connectionToken=n.connectionId),n}catch(e){let t="Failed to complete negotiation with the server: "+e;return e instanceof Le&&404===e.statusCode&&(t+=" Either this is not a SignalR endpoint or there is a proxy blocking the connection."),this._logger.log(Ve.Error,t),Promise.reject(new We(t))}}_createConnectUrl(e,t){return t?e+(-1===e.indexOf("?")?"?":"&")+`id=${t}`:e}async _createTransport(e,t,n,r){let o=this._createConnectUrl(e,n.connectionToken);if(this._isITransport(t))return this._logger.log(Ve.Debug,"Connection was provided an instance of ITransport, using that directly."),this.transport=t,await this._startTransport(o,r),void(this.connectionId=n.connectionId);const i=[],s=n.availableTransports||[];let a=n;for(const n of s){const s=this._resolveTransportOrError(n,t,r);if(s instanceof Error)i.push(`${n.transport} failed:`),i.push(s);else if(this._isITransport(s)){if(this.transport=s,!a){try{a=await this._getNegotiationResponse(e)}catch(e){return Promise.reject(e)}o=this._createConnectUrl(e,a.connectionToken)}try{return await this._startTransport(o,r),void(this.connectionId=a.connectionId)}catch(e){if(this._logger.log(Ve.Error,`Failed to start the transport '${n.transport}': ${e}`),a=void 0,i.push(new He(`${n.transport} failed: ${e}`,Ke[n.transport])),"Connecting"!==this._connectionState){const e="Failed to select transport before stop() was called.";return this._logger.log(Ve.Debug,e),Promise.reject(new Oe(e))}}}}return i.length>0?Promise.reject(new ze(`Unable to connect to the server with any of the available transports. ${i.join(" ")}`,i)):Promise.reject(new Error("None of the transports supported by the client are supported by the server."))}_constructTransport(e){switch(e){case Ke.WebSockets:if(!this._options.WebSocket)throw new Error("'WebSocket' is not supported in your environment.");return new vt(this._httpClient,this._accessTokenFactory,this._logger,this._options.logMessageContent,this._options.WebSocket,this._options.headers||{});case Ke.ServerSentEvents:if(!this._options.EventSource)throw new Error("'EventSource' is not supported in your environment.");return new wt(this._httpClient,this._accessTokenFactory,this._logger,this._options);case Ke.LongPolling:return new yt(this._httpClient,this._accessTokenFactory,this._logger,this._options);default:throw new Error(`Unknown transport: ${e}.`)}}_startTransport(e,t){return this.transport.onreceive=this.onreceive,this.transport.onclose=e=>this._stopConnection(e),this.transport.connect(e,t)}_resolveTransportOrError(e,t,n){const r=Ke[e.transport];if(null==r)return this._logger.log(Ve.Debug,`Skipping transport '${e.transport}' because it is not supported by this client.`),new Error(`Skipping transport '${e.transport}' because it is not supported by this client.`);if(!function(e,t){return!e||0!=(t&e)}(t,r))return this._logger.log(Ve.Debug,`Skipping transport '${Ke[r]}' because it was disabled by the client.`),new je(`'${Ke[r]}' is disabled by the client.`,r);if(!(e.transferFormats.map((e=>Xe[e])).indexOf(n)>=0))return this._logger.log(Ve.Debug,`Skipping transport '${Ke[r]}' because it does not support the requested transfer format '${Xe[n]}'.`),new Error(`'${Ke[r]}' does not support ${Xe[n]}.`);if(r===Ke.WebSockets&&!this._options.WebSocket||r===Ke.ServerSentEvents&&!this._options.EventSource)return this._logger.log(Ve.Debug,`Skipping transport '${Ke[r]}' because it is not supported in your environment.'`),new Fe(`'${Ke[r]}' is not supported in your environment.`,r);this._logger.log(Ve.Debug,`Selecting transport '${Ke[r]}'.`);try{return this._constructTransport(r)}catch(e){return e}}_isITransport(e){return e&&"object"==typeof e&&"connect"in e}_stopConnection(e){if(this._logger.log(Ve.Debug,`HttpConnection.stopConnection(${e}) called while in state ${this._connectionState}.`),this.transport=void 0,e=this._stopError||e,this._stopError=void 0,"Disconnected"!==this._connectionState){if("Connecting"===this._connectionState)throw this._logger.log(Ve.Warning,`Call to HttpConnection.stopConnection(${e}) was ignored because the connection is still in the connecting state.`),new Error(`HttpConnection.stopConnection(${e}) was called while the connection is still in the connecting state.`);if("Disconnecting"===this._connectionState&&this._stopPromiseResolver(),e?this._logger.log(Ve.Error,`Connection disconnected with error '${e}'.`):this._logger.log(Ve.Information,"Connection disconnected."),this._sendQueue&&(this._sendQueue.stop().catch((e=>{this._logger.log(Ve.Error,`TransportSendQueue.stop() threw error '${e}'.`)})),this._sendQueue=void 0),this.connectionId=void 0,this._connectionState="Disconnected",this._connectionStarted){this._connectionStarted=!1;try{this.onclose&&this.onclose(e)}catch(t){this._logger.log(Ve.Error,`HttpConnection.onclose(${e}) threw error '${t}'.`)}}}else this._logger.log(Ve.Debug,`Call to HttpConnection.stopConnection(${e}) was ignored because the connection is already in the disconnected state.`)}_resolveUrl(e){if(0===e.lastIndexOf("https://",0)||0===e.lastIndexOf("http://",0))return e;if(!et.isBrowser)throw new Error(`Cannot resolve '${e}'.`);const t=window.document.createElement("a");return t.href=e,this._logger.log(Ve.Information,`Normalizing '${e}' to '${t.href}'.`),t.href}_resolveNegotiateUrl(e){const t=e.indexOf("?");let n=e.substring(0,-1===t?e.length:t);return"/"!==n[n.length-1]&&(n+="/"),n+="negotiate",n+=-1===t?"":e.substring(t),-1===n.indexOf("negotiateVersion")&&(n+=-1===t?"?":"&",n+="negotiateVersion="+this._negotiateVersion),n}}class _t{constructor(e){this._transport=e,this._buffer=[],this._executing=!0,this._sendBufferedData=new Et,this._transportResult=new Et,this._sendLoopPromise=this._sendLoop()}send(e){return this._bufferData(e),this._transportResult||(this._transportResult=new Et),this._transportResult.promise}stop(){return this._executing=!1,this._sendBufferedData.resolve(),this._sendLoopPromise}_bufferData(e){if(this._buffer.length&&typeof this._buffer[0]!=typeof e)throw new Error(`Expected data to be of type ${typeof this._buffer} but was of type ${typeof e}`);this._buffer.push(e),this._sendBufferedData.resolve()}async _sendLoop(){for(;;){if(await this._sendBufferedData.promise,!this._executing){this._transportResult&&this._transportResult.reject("Connection stopped.");break}this._sendBufferedData=new Et;const e=this._transportResult;this._transportResult=void 0;const t="string"==typeof this._buffer[0]?this._buffer.join(""):_t._concatBuffers(this._buffer);this._buffer.length=0;try{await this._transport.send(t),e.resolve()}catch(t){e.reject(t)}}}static _concatBuffers(e){const t=e.map((e=>e.byteLength)).reduce(((e,t)=>e+t)),n=new Uint8Array(t);let r=0;for(const t of e)n.set(new Uint8Array(t),r),r+=t.byteLength;return n.buffer}}class Et{constructor(){this.promise=new Promise(((e,t)=>[this._resolver,this._rejecter]=[e,t]))}resolve(){this._resolver()}reject(e){this._rejecter(e)}}class St{static write(e){return`${e}${St.RecordSeparator}`}static parse(e){if(e[e.length-1]!==St.RecordSeparator)throw new Error("Message is incomplete.");const t=e.split(St.RecordSeparator);return t.pop(),t}}St.RecordSeparatorCode=30,St.RecordSeparator=String.fromCharCode(St.RecordSeparatorCode);class Ct{writeHandshakeRequest(e){return St.write(JSON.stringify(e))}parseHandshakeResponse(e){let t,n;if(nt(e)){const r=new Uint8Array(e),o=r.indexOf(St.RecordSeparatorCode);if(-1===o)throw new Error("Message is incomplete.");const i=o+1;t=String.fromCharCode.apply(null,Array.prototype.slice.call(r.slice(0,i))),n=r.byteLength>i?r.slice(i).buffer:null}else{const r=e,o=r.indexOf(St.RecordSeparator);if(-1===o)throw new Error("Message is incomplete.");const i=o+1;t=r.substring(0,i),n=r.length>i?r.substring(i):null}const r=St.parse(t),o=JSON.parse(r[0]);if(o.type)throw new Error("Expected a handshake response from the server.");return[n,o]}}!function(e){e[e.Invocation=1]="Invocation",e[e.StreamItem=2]="StreamItem",e[e.Completion=3]="Completion",e[e.StreamInvocation=4]="StreamInvocation",e[e.CancelInvocation=5]="CancelInvocation",e[e.Ping=6]="Ping",e[e.Close=7]="Close"}(Ye||(Ye={}));class It{constructor(){this.observers=[]}next(e){for(const t of this.observers)t.next(e)}error(e){for(const t of this.observers)t.error&&t.error(e)}complete(){for(const e of this.observers)e.complete&&e.complete()}subscribe(e){return this.observers.push(e),new ot(this,e)}}!function(e){e.Disconnected="Disconnected",e.Connecting="Connecting",e.Connected="Connected",e.Disconnecting="Disconnecting",e.Reconnecting="Reconnecting"}(Ge||(Ge={}));class kt{constructor(e,t,n,r){this._nextKeepAlive=0,this._freezeEventListener=()=>{this._logger.log(Ve.Warning,"The page is being frozen, this will likely lead to the connection being closed and messages being lost. For more information see the docs at https://docs.microsoft.com/aspnet/core/signalr/javascript-client#bsleep")},Ze.isRequired(e,"connection"),Ze.isRequired(t,"logger"),Ze.isRequired(n,"protocol"),this.serverTimeoutInMilliseconds=3e4,this.keepAliveIntervalInMilliseconds=15e3,this._logger=t,this._protocol=n,this.connection=e,this._reconnectPolicy=r,this._handshakeProtocol=new Ct,this.connection.onreceive=e=>this._processIncomingData(e),this.connection.onclose=e=>this._connectionClosed(e),this._callbacks={},this._methods={},this._closedCallbacks=[],this._reconnectingCallbacks=[],this._reconnectedCallbacks=[],this._invocationId=0,this._receivedHandshakeResponse=!1,this._connectionState=Ge.Disconnected,this._connectionStarted=!1,this._cachedPingMessage=this._protocol.writeMessage({type:Ye.Ping})}static create(e,t,n,r){return new kt(e,t,n,r)}get state(){return this._connectionState}get connectionId(){return this.connection&&this.connection.connectionId||null}get baseUrl(){return this.connection.baseUrl||""}set baseUrl(e){if(this._connectionState!==Ge.Disconnected&&this._connectionState!==Ge.Reconnecting)throw new Error("The HubConnection must be in the Disconnected or Reconnecting state to change the url.");if(!e)throw new Error("The HubConnection url must be a valid url.");this.connection.baseUrl=e}start(){return this._startPromise=this._startWithStateTransitions(),this._startPromise}async _startWithStateTransitions(){if(this._connectionState!==Ge.Disconnected)return Promise.reject(new Error("Cannot start a HubConnection that is not in the 'Disconnected' state."));this._connectionState=Ge.Connecting,this._logger.log(Ve.Debug,"Starting HubConnection.");try{await this._startInternal(),et.isBrowser&&window.document.addEventListener("freeze",this._freezeEventListener),this._connectionState=Ge.Connected,this._connectionStarted=!0,this._logger.log(Ve.Debug,"HubConnection connected successfully.")}catch(e){return this._connectionState=Ge.Disconnected,this._logger.log(Ve.Debug,`HubConnection failed to start successfully because of error '${e}'.`),Promise.reject(e)}}async _startInternal(){this._stopDuringStartError=void 0,this._receivedHandshakeResponse=!1;const e=new Promise(((e,t)=>{this._handshakeResolver=e,this._handshakeRejecter=t}));await this.connection.start(this._protocol.transferFormat);try{const t={protocol:this._protocol.name,version:this._protocol.version};if(this._logger.log(Ve.Debug,"Sending handshake request."),await this._sendMessage(this._handshakeProtocol.writeHandshakeRequest(t)),this._logger.log(Ve.Information,`Using HubProtocol '${this._protocol.name}'.`),this._cleanupTimeout(),this._resetTimeoutPeriod(),this._resetKeepAliveInterval(),await e,this._stopDuringStartError)throw this._stopDuringStartError}catch(e){throw this._logger.log(Ve.Debug,`Hub handshake failed with error '${e}' during start(). Stopping HubConnection.`),this._cleanupTimeout(),this._cleanupPingTimer(),await this.connection.stop(e),e}}async stop(){const e=this._startPromise;this._stopPromise=this._stopInternal(),await this._stopPromise;try{await e}catch(e){}}_stopInternal(e){return this._connectionState===Ge.Disconnected?(this._logger.log(Ve.Debug,`Call to HubConnection.stop(${e}) ignored because it is already in the disconnected state.`),Promise.resolve()):this._connectionState===Ge.Disconnecting?(this._logger.log(Ve.Debug,`Call to HttpConnection.stop(${e}) ignored because the connection is already in the disconnecting state.`),this._stopPromise):(this._connectionState=Ge.Disconnecting,this._logger.log(Ve.Debug,"Stopping HubConnection."),this._reconnectDelayHandle?(this._logger.log(Ve.Debug,"Connection stopped during reconnect delay. Done reconnecting."),clearTimeout(this._reconnectDelayHandle),this._reconnectDelayHandle=void 0,this._completeClose(),Promise.resolve()):(this._cleanupTimeout(),this._cleanupPingTimer(),this._stopDuringStartError=e||new Oe("The connection was stopped before the hub handshake could complete."),this.connection.stop(e)))}stream(e,...t){const[n,r]=this._replaceStreamingParams(t),o=this._createStreamInvocation(e,t,r);let i;const s=new It;return s.cancelCallback=()=>{const e=this._createCancelInvocation(o.invocationId);return delete this._callbacks[o.invocationId],i.then((()=>this._sendWithProtocol(e)))},this._callbacks[o.invocationId]=(e,t)=>{t?s.error(t):e&&(e.type===Ye.Completion?e.error?s.error(new Error(e.error)):s.complete():s.next(e.item))},i=this._sendWithProtocol(o).catch((e=>{s.error(e),delete this._callbacks[o.invocationId]})),this._launchStreams(n,i),s}_sendMessage(e){return this._resetKeepAliveInterval(),this.connection.send(e)}_sendWithProtocol(e){return this._sendMessage(this._protocol.writeMessage(e))}send(e,...t){const[n,r]=this._replaceStreamingParams(t),o=this._sendWithProtocol(this._createInvocation(e,t,!0,r));return this._launchStreams(n,o),o}invoke(e,...t){const[n,r]=this._replaceStreamingParams(t),o=this._createInvocation(e,t,!1,r);return new Promise(((e,t)=>{this._callbacks[o.invocationId]=(n,r)=>{r?t(r):n&&(n.type===Ye.Completion?n.error?t(new Error(n.error)):e(n.result):t(new Error(`Unexpected message type: ${n.type}`)))};const r=this._sendWithProtocol(o).catch((e=>{t(e),delete this._callbacks[o.invocationId]}));this._launchStreams(n,r)}))}on(e,t){e&&t&&(e=e.toLowerCase(),this._methods[e]||(this._methods[e]=[]),-1===this._methods[e].indexOf(t)&&this._methods[e].push(t))}off(e,t){if(!e)return;e=e.toLowerCase();const n=this._methods[e];if(n)if(t){const r=n.indexOf(t);-1!==r&&(n.splice(r,1),0===n.length&&delete this._methods[e])}else delete this._methods[e]}onclose(e){e&&this._closedCallbacks.push(e)}onreconnecting(e){e&&this._reconnectingCallbacks.push(e)}onreconnected(e){e&&this._reconnectedCallbacks.push(e)}_processIncomingData(e){if(this._cleanupTimeout(),this._receivedHandshakeResponse||(e=this._processHandshakeResponse(e),this._receivedHandshakeResponse=!0),e){const t=this._protocol.parseMessages(e,this._logger);for(const e of t)switch(e.type){case Ye.Invocation:this._invokeClientMethod(e);break;case Ye.StreamItem:case Ye.Completion:{const t=this._callbacks[e.invocationId];if(t){e.type===Ye.Completion&&delete this._callbacks[e.invocationId];try{t(e)}catch(e){this._logger.log(Ve.Error,`Stream callback threw error: ${ht(e)}`)}}break}case Ye.Ping:break;case Ye.Close:{this._logger.log(Ve.Information,"Close message received from server.");const t=e.error?new Error("Server returned an error on close: "+e.error):void 0;!0===e.allowReconnect?this.connection.stop(t):this._stopPromise=this._stopInternal(t);break}default:this._logger.log(Ve.Warning,`Invalid message type: ${e.type}.`)}}this._resetTimeoutPeriod()}_processHandshakeResponse(e){let t,n;try{[n,t]=this._handshakeProtocol.parseHandshakeResponse(e)}catch(e){const t="Error parsing handshake response: "+e;this._logger.log(Ve.Error,t);const n=new Error(t);throw this._handshakeRejecter(n),n}if(t.error){const e="Server returned handshake error: "+t.error;this._logger.log(Ve.Error,e);const n=new Error(e);throw this._handshakeRejecter(n),n}return this._logger.log(Ve.Debug,"Server handshake complete."),this._handshakeResolver(),n}_resetKeepAliveInterval(){this.connection.features.inherentKeepAlive||(this._nextKeepAlive=(new Date).getTime()+this.keepAliveIntervalInMilliseconds,this._cleanupPingTimer())}_resetTimeoutPeriod(){if(!(this.connection.features&&this.connection.features.inherentKeepAlive||(this._timeoutHandle=setTimeout((()=>this.serverTimeout()),this.serverTimeoutInMilliseconds),void 0!==this._pingServerHandle))){let e=this._nextKeepAlive-(new Date).getTime();e<0&&(e=0),this._pingServerHandle=setTimeout((async()=>{if(this._connectionState===Ge.Connected)try{await this._sendMessage(this._cachedPingMessage)}catch{this._cleanupPingTimer()}}),e)}}serverTimeout(){this.connection.stop(new Error("Server timeout elapsed without receiving a message from the server."))}async _invokeClientMethod(e){const t=e.target.toLowerCase(),n=this._methods[t];if(!n)return this._logger.log(Ve.Warning,`No client method with the name '${t}' found.`),void(e.invocationId&&(this._logger.log(Ve.Warning,`No result given for '${t}' method and invocation ID '${e.invocationId}'.`),await this._sendWithProtocol(this._createCompletionMessage(e.invocationId,"Client didn't provide a result.",null))));const r=n.slice(),o=!!e.invocationId;let i,s,a;for(const n of r)try{const r=i;i=await n.apply(this,e.arguments),o&&i&&r&&(this._logger.log(Ve.Error,`Multiple results provided for '${t}'. Sending error to server.`),a=this._createCompletionMessage(e.invocationId,"Client provided multiple results.",null)),s=void 0}catch(e){s=e,this._logger.log(Ve.Error,`A callback for the method '${t}' threw error '${e}'.`)}a?await this._sendWithProtocol(a):o?(s?a=this._createCompletionMessage(e.invocationId,`${s}`,null):void 0!==i?a=this._createCompletionMessage(e.invocationId,null,i):(this._logger.log(Ve.Warning,`No result given for '${t}' method and invocation ID '${e.invocationId}'.`),a=this._createCompletionMessage(e.invocationId,"Client didn't provide a result.",null)),await this._sendWithProtocol(a)):i&&this._logger.log(Ve.Error,`Result given for '${t}' method but server is not expecting a result.`)}_connectionClosed(e){this._logger.log(Ve.Debug,`HubConnection.connectionClosed(${e}) called while in state ${this._connectionState}.`),this._stopDuringStartError=this._stopDuringStartError||e||new Oe("The underlying connection was closed before the hub handshake could complete."),this._handshakeResolver&&this._handshakeResolver(),this._cancelCallbacksWithError(e||new Error("Invocation canceled due to the underlying connection being closed.")),this._cleanupTimeout(),this._cleanupPingTimer(),this._connectionState===Ge.Disconnecting?this._completeClose(e):this._connectionState===Ge.Connected&&this._reconnectPolicy?this._reconnect(e):this._connectionState===Ge.Connected&&this._completeClose(e)}_completeClose(e){if(this._connectionStarted){this._connectionState=Ge.Disconnected,this._connectionStarted=!1,et.isBrowser&&window.document.removeEventListener("freeze",this._freezeEventListener);try{this._closedCallbacks.forEach((t=>t.apply(this,[e])))}catch(t){this._logger.log(Ve.Error,`An onclose callback called with error '${e}' threw error '${t}'.`)}}}async _reconnect(e){const t=Date.now();let n=0,r=void 0!==e?e:new Error("Attempting to reconnect due to a unknown error."),o=this._getNextRetryDelay(n++,0,r);if(null===o)return this._logger.log(Ve.Debug,"Connection not reconnecting because the IRetryPolicy returned null on the first reconnect attempt."),void this._completeClose(e);if(this._connectionState=Ge.Reconnecting,e?this._logger.log(Ve.Information,`Connection reconnecting because of error '${e}'.`):this._logger.log(Ve.Information,"Connection reconnecting."),0!==this._reconnectingCallbacks.length){try{this._reconnectingCallbacks.forEach((t=>t.apply(this,[e])))}catch(t){this._logger.log(Ve.Error,`An onreconnecting callback called with error '${e}' threw error '${t}'.`)}if(this._connectionState!==Ge.Reconnecting)return void this._logger.log(Ve.Debug,"Connection left the reconnecting state in onreconnecting callback. Done reconnecting.")}for(;null!==o;){if(this._logger.log(Ve.Information,`Reconnect attempt number ${n} will start in ${o} ms.`),await new Promise((e=>{this._reconnectDelayHandle=setTimeout(e,o)})),this._reconnectDelayHandle=void 0,this._connectionState!==Ge.Reconnecting)return void this._logger.log(Ve.Debug,"Connection left the reconnecting state during reconnect delay. Done reconnecting.");try{if(await this._startInternal(),this._connectionState=Ge.Connected,this._logger.log(Ve.Information,"HubConnection reconnected successfully."),0!==this._reconnectedCallbacks.length)try{this._reconnectedCallbacks.forEach((e=>e.apply(this,[this.connection.connectionId])))}catch(e){this._logger.log(Ve.Error,`An onreconnected callback called with connectionId '${this.connection.connectionId}; threw error '${e}'.`)}return}catch(e){if(this._logger.log(Ve.Information,`Reconnect attempt failed because of error '${e}'.`),this._connectionState!==Ge.Reconnecting)return this._logger.log(Ve.Debug,`Connection moved to the '${this._connectionState}' from the reconnecting state during reconnect attempt. Done reconnecting.`),void(this._connectionState===Ge.Disconnecting&&this._completeClose());r=e instanceof Error?e:new Error(e.toString()),o=this._getNextRetryDelay(n++,Date.now()-t,r)}}this._logger.log(Ve.Information,`Reconnect retries have been exhausted after ${Date.now()-t} ms and ${n} failed attempts. Connection disconnecting.`),this._completeClose()}_getNextRetryDelay(e,t,n){try{return this._reconnectPolicy.nextRetryDelayInMilliseconds({elapsedMilliseconds:t,previousRetryCount:e,retryReason:n})}catch(n){return this._logger.log(Ve.Error,`IRetryPolicy.nextRetryDelayInMilliseconds(${e}, ${t}) threw error '${n}'.`),null}}_cancelCallbacksWithError(e){const t=this._callbacks;this._callbacks={},Object.keys(t).forEach((n=>{const r=t[n];try{r(null,e)}catch(t){this._logger.log(Ve.Error,`Stream 'error' callback called with '${e}' threw error: ${ht(t)}`)}}))}_cleanupPingTimer(){this._pingServerHandle&&(clearTimeout(this._pingServerHandle),this._pingServerHandle=void 0)}_cleanupTimeout(){this._timeoutHandle&&clearTimeout(this._timeoutHandle)}_createInvocation(e,t,n,r){if(n)return 0!==r.length?{arguments:t,streamIds:r,target:e,type:Ye.Invocation}:{arguments:t,target:e,type:Ye.Invocation};{const n=this._invocationId;return this._invocationId++,0!==r.length?{arguments:t,invocationId:n.toString(),streamIds:r,target:e,type:Ye.Invocation}:{arguments:t,invocationId:n.toString(),target:e,type:Ye.Invocation}}}_launchStreams(e,t){if(0!==e.length){t||(t=Promise.resolve());for(const n in e)e[n].subscribe({complete:()=>{t=t.then((()=>this._sendWithProtocol(this._createCompletionMessage(n))))},error:e=>{let r;r=e instanceof Error?e.message:e&&e.toString?e.toString():"Unknown error",t=t.then((()=>this._sendWithProtocol(this._createCompletionMessage(n,r))))},next:e=>{t=t.then((()=>this._sendWithProtocol(this._createStreamItemMessage(n,e))))}})}}_replaceStreamingParams(e){const t=[],n=[];for(let r=0;r<e.length;r++){const o=e[r];if(this._isObservable(o)){const i=this._invocationId;this._invocationId++,t[i]=o,n.push(i.toString()),e.splice(r,1)}}return[t,n]}_isObservable(e){return e&&e.subscribe&&"function"==typeof e.subscribe}_createStreamInvocation(e,t,n){const r=this._invocationId;return this._invocationId++,0!==n.length?{arguments:t,invocationId:r.toString(),streamIds:n,target:e,type:Ye.StreamInvocation}:{arguments:t,invocationId:r.toString(),target:e,type:Ye.StreamInvocation}}_createCancelInvocation(e){return{invocationId:e,type:Ye.CancelInvocation}}_createStreamItemMessage(e,t){return{invocationId:e,item:t,type:Ye.StreamItem}}_createCompletionMessage(e,t,n){return t?{error:t,invocationId:e,type:Ye.Completion}:{invocationId:e,result:n,type:Ye.Completion}}}class Tt{constructor(){this.name="json",this.version=1,this.transferFormat=Xe.Text}parseMessages(e,t){if("string"!=typeof e)throw new Error("Invalid input for JSON hub protocol. Expected a string.");if(!e)return[];null===t&&(t=Qe.instance);const n=St.parse(e),r=[];for(const e of n){const n=JSON.parse(e);if("number"!=typeof n.type)throw new Error("Invalid payload.");switch(n.type){case Ye.Invocation:this._isInvocationMessage(n);break;case Ye.StreamItem:this._isStreamItemMessage(n);break;case Ye.Completion:this._isCompletionMessage(n);break;case Ye.Ping:case Ye.Close:break;default:t.log(Ve.Information,"Unknown message type '"+n.type+"' ignored.");continue}r.push(n)}return r}writeMessage(e){return St.write(JSON.stringify(e))}_isInvocationMessage(e){this._assertNotEmptyString(e.target,"Invalid payload for Invocation message."),void 0!==e.invocationId&&this._assertNotEmptyString(e.invocationId,"Invalid payload for Invocation message.")}_isStreamItemMessage(e){if(this._assertNotEmptyString(e.invocationId,"Invalid payload for StreamItem message."),void 0===e.item)throw new Error("Invalid payload for StreamItem message.")}_isCompletionMessage(e){if(e.result&&e.error)throw new Error("Invalid payload for Completion message.");!e.result&&e.error&&this._assertNotEmptyString(e.error,"Invalid payload for Completion message."),this._assertNotEmptyString(e.invocationId,"Invalid payload for Completion message.")}_assertNotEmptyString(e,t){if("string"!=typeof e||""===e)throw new Error(t)}}const xt={trace:Ve.Trace,debug:Ve.Debug,info:Ve.Information,information:Ve.Information,warn:Ve.Warning,warning:Ve.Warning,error:Ve.Error,critical:Ve.Critical,none:Ve.None};class Dt{configureLogging(e){if(Ze.isRequired(e,"logging"),void 0!==e.log)this.logger=e;else if("string"==typeof e){const t=function(e){const t=xt[e.toLowerCase()];if(void 0!==t)return t;throw new Error(`Unknown log level: ${e}`)}(e);this.logger=new it(t)}else this.logger=new it(e);return this}withUrl(e,t){return Ze.isRequired(e,"url"),Ze.isNotEmpty(e,"url"),this.url=e,this.httpConnectionOptions="object"==typeof t?{...this.httpConnectionOptions,...t}:{...this.httpConnectionOptions,transport:t},this}withHubProtocol(e){return Ze.isRequired(e,"protocol"),this.protocol=e,this}withAutomaticReconnect(e){if(this.reconnectPolicy)throw new Error("A reconnectPolicy has already been set.");return e?Array.isArray(e)?this.reconnectPolicy=new Be(e):this.reconnectPolicy=e:this.reconnectPolicy=new Be,this}build(){const e=this.httpConnectionOptions||{};if(void 0===e.logger&&(e.logger=this.logger),!this.url)throw new Error("The 'HubConnectionBuilder.withUrl' method must be called before building the connection.");const t=new bt(this.url,e);return kt.create(t,this.logger||Qe.instance,this.protocol||new Tt,this.reconnectPolicy)}}var Rt=4294967295;function Pt(e,t,n){var r=Math.floor(n/4294967296),o=n;e.setUint32(t,r),e.setUint32(t+4,o)}function Ut(e,t){return 4294967296*e.getInt32(t)+e.getUint32(t+4)}var At=("undefined"==typeof process||"never"!==process.env.TEXT_ENCODING)&&"undefined"!=typeof TextEncoder&&"undefined"!=typeof TextDecoder;function Nt(e){for(var t=e.length,n=0,r=0;r<t;){var o=e.charCodeAt(r++);if(0!=(4294967168&o))if(0==(4294965248&o))n+=2;else{if(o>=55296&&o<=56319&&r<t){var i=e.charCodeAt(r);56320==(64512&i)&&(++r,o=((1023&o)<<10)+(1023&i)+65536)}n+=0==(4294901760&o)?3:4}else n++}return n}var $t=At?new TextEncoder:void 0,Bt=At?"undefined"!=typeof process&&"force"!==process.env.TEXT_ENCODING?200:0:Rt,Lt=(null==$t?void 0:$t.encodeInto)?function(e,t,n){$t.encodeInto(e,t.subarray(n))}:function(e,t,n){t.set($t.encode(e),n)};function Mt(e,t,n){for(var r=t,o=r+n,i=[],s="";r<o;){var a=e[r++];if(0==(128&a))i.push(a);else if(192==(224&a)){var c=63&e[r++];i.push((31&a)<<6|c)}else if(224==(240&a)){c=63&e[r++];var l=63&e[r++];i.push((31&a)<<12|c<<6|l)}else if(240==(248&a)){var h=(7&a)<<18|(c=63&e[r++])<<12|(l=63&e[r++])<<6|63&e[r++];h>65535&&(h-=65536,i.push(h>>>10&1023|55296),h=56320|1023&h),i.push(h)}else i.push(a);i.length>=4096&&(s+=String.fromCharCode.apply(String,i),i.length=0)}return i.length>0&&(s+=String.fromCharCode.apply(String,i)),s}var Ot,Ft=At?new TextDecoder:null,jt=At?"undefined"!=typeof process&&"force"!==process.env.TEXT_DECODER?200:0:Rt,Ht=function(e,t){this.type=e,this.data=t},Wt=(Ot=function(e,t){return Ot=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},Ot(e,t)},function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function n(){this.constructor=e}Ot(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),zt=function(e){function t(n){var r=e.call(this,n)||this,o=Object.create(t.prototype);return Object.setPrototypeOf(r,o),Object.defineProperty(r,"name",{configurable:!0,enumerable:!1,value:t.name}),r}return Wt(t,e),t}(Error),Jt={type:-1,encode:function(e){var t,n,r,o;return e instanceof Date?function(e){var t,n=e.sec,r=e.nsec;if(n>=0&&r>=0&&n<=17179869183){if(0===r&&n<=4294967295){var o=new Uint8Array(4);return(t=new DataView(o.buffer)).setUint32(0,n),o}var i=n/4294967296,s=4294967295&n;return o=new Uint8Array(8),(t=new DataView(o.buffer)).setUint32(0,r<<2|3&i),t.setUint32(4,s),o}return o=new Uint8Array(12),(t=new DataView(o.buffer)).setUint32(0,r),Pt(t,4,n),o}((r=1e6*((t=e.getTime())-1e3*(n=Math.floor(t/1e3))),{sec:n+(o=Math.floor(r/1e9)),nsec:r-1e9*o})):null},decode:function(e){var t=function(e){var t=new DataView(e.buffer,e.byteOffset,e.byteLength);switch(e.byteLength){case 4:return{sec:t.getUint32(0),nsec:0};case 8:var n=t.getUint32(0);return{sec:4294967296*(3&n)+t.getUint32(4),nsec:n>>>2};case 12:return{sec:Ut(t,4),nsec:t.getUint32(0)};default:throw new zt("Unrecognized data size for timestamp (expected 4, 8, or 12): "+e.length)}}(e);return new Date(1e3*t.sec+t.nsec/1e6)}},qt=function(){function e(){this.builtInEncoders=[],this.builtInDecoders=[],this.encoders=[],this.decoders=[],this.register(Jt)}return e.prototype.register=function(e){var t=e.type,n=e.encode,r=e.decode;if(t>=0)this.encoders[t]=n,this.decoders[t]=r;else{var o=1+t;this.builtInEncoders[o]=n,this.builtInDecoders[o]=r}},e.prototype.tryToEncode=function(e,t){for(var n=0;n<this.builtInEncoders.length;n++)if(null!=(r=this.builtInEncoders[n])&&null!=(o=r(e,t)))return new Ht(-1-n,o);for(n=0;n<this.encoders.length;n++){var r,o;if(null!=(r=this.encoders[n])&&null!=(o=r(e,t)))return new Ht(n,o)}return e instanceof Ht?e:null},e.prototype.decode=function(e,t,n){var r=t<0?this.builtInDecoders[-1-t]:this.decoders[t];return r?r(e,t,n):new Ht(t,e)},e.defaultCodec=new e,e}();function Vt(e){return e instanceof Uint8Array?e:ArrayBuffer.isView(e)?new Uint8Array(e.buffer,e.byteOffset,e.byteLength):e instanceof ArrayBuffer?new Uint8Array(e):Uint8Array.from(e)}var Kt=function(){function e(e,t,n,r,o,i,s,a){void 0===e&&(e=qt.defaultCodec),void 0===t&&(t=void 0),void 0===n&&(n=100),void 0===r&&(r=2048),void 0===o&&(o=!1),void 0===i&&(i=!1),void 0===s&&(s=!1),void 0===a&&(a=!1),this.extensionCodec=e,this.context=t,this.maxDepth=n,this.initialBufferSize=r,this.sortKeys=o,this.forceFloat32=i,this.ignoreUndefined=s,this.forceIntegerToFloat=a,this.pos=0,this.view=new DataView(new ArrayBuffer(this.initialBufferSize)),this.bytes=new Uint8Array(this.view.buffer)}return e.prototype.getUint8Array=function(){return this.bytes.subarray(0,this.pos)},e.prototype.reinitializeState=function(){this.pos=0},e.prototype.encode=function(e){return this.reinitializeState(),this.doEncode(e,1),this.getUint8Array()},e.prototype.doEncode=function(e,t){if(t>this.maxDepth)throw new Error("Too deep objects in depth "+t);null==e?this.encodeNil():"boolean"==typeof e?this.encodeBoolean(e):"number"==typeof e?this.encodeNumber(e):"string"==typeof e?this.encodeString(e):this.encodeObject(e,t)},e.prototype.ensureBufferSizeToWrite=function(e){var t=this.pos+e;this.view.byteLength<t&&this.resizeBuffer(2*t)},e.prototype.resizeBuffer=function(e){var t=new ArrayBuffer(e),n=new Uint8Array(t),r=new DataView(t);n.set(this.bytes),this.view=r,this.bytes=n},e.prototype.encodeNil=function(){this.writeU8(192)},e.prototype.encodeBoolean=function(e){!1===e?this.writeU8(194):this.writeU8(195)},e.prototype.encodeNumber=function(e){Number.isSafeInteger(e)&&!this.forceIntegerToFloat?e>=0?e<128?this.writeU8(e):e<256?(this.writeU8(204),this.writeU8(e)):e<65536?(this.writeU8(205),this.writeU16(e)):e<4294967296?(this.writeU8(206),this.writeU32(e)):(this.writeU8(207),this.writeU64(e)):e>=-32?this.writeU8(224|e+32):e>=-128?(this.writeU8(208),this.writeI8(e)):e>=-32768?(this.writeU8(209),this.writeI16(e)):e>=-2147483648?(this.writeU8(210),this.writeI32(e)):(this.writeU8(211),this.writeI64(e)):this.forceFloat32?(this.writeU8(202),this.writeF32(e)):(this.writeU8(203),this.writeF64(e))},e.prototype.writeStringHeader=function(e){if(e<32)this.writeU8(160+e);else if(e<256)this.writeU8(217),this.writeU8(e);else if(e<65536)this.writeU8(218),this.writeU16(e);else{if(!(e<4294967296))throw new Error("Too long string: "+e+" bytes in UTF-8");this.writeU8(219),this.writeU32(e)}},e.prototype.encodeString=function(e){if(e.length>Bt){var t=Nt(e);this.ensureBufferSizeToWrite(5+t),this.writeStringHeader(t),Lt(e,this.bytes,this.pos),this.pos+=t}else t=Nt(e),this.ensureBufferSizeToWrite(5+t),this.writeStringHeader(t),function(e,t,n){for(var r=e.length,o=n,i=0;i<r;){var s=e.charCodeAt(i++);if(0!=(4294967168&s)){if(0==(4294965248&s))t[o++]=s>>6&31|192;else{if(s>=55296&&s<=56319&&i<r){var a=e.charCodeAt(i);56320==(64512&a)&&(++i,s=((1023&s)<<10)+(1023&a)+65536)}0==(4294901760&s)?(t[o++]=s>>12&15|224,t[o++]=s>>6&63|128):(t[o++]=s>>18&7|240,t[o++]=s>>12&63|128,t[o++]=s>>6&63|128)}t[o++]=63&s|128}else t[o++]=s}}(e,this.bytes,this.pos),this.pos+=t},e.prototype.encodeObject=function(e,t){var n=this.extensionCodec.tryToEncode(e,this.context);if(null!=n)this.encodeExtension(n);else if(Array.isArray(e))this.encodeArray(e,t);else if(ArrayBuffer.isView(e))this.encodeBinary(e);else{if("object"!=typeof e)throw new Error("Unrecognized object: "+Object.prototype.toString.apply(e));this.encodeMap(e,t)}},e.prototype.encodeBinary=function(e){var t=e.byteLength;if(t<256)this.writeU8(196),this.writeU8(t);else if(t<65536)this.writeU8(197),this.writeU16(t);else{if(!(t<4294967296))throw new Error("Too large binary: "+t);this.writeU8(198),this.writeU32(t)}var n=Vt(e);this.writeU8a(n)},e.prototype.encodeArray=function(e,t){var n=e.length;if(n<16)this.writeU8(144+n);else if(n<65536)this.writeU8(220),this.writeU16(n);else{if(!(n<4294967296))throw new Error("Too large array: "+n);this.writeU8(221),this.writeU32(n)}for(var r=0,o=e;r<o.length;r++){var i=o[r];this.doEncode(i,t+1)}},e.prototype.countWithoutUndefined=function(e,t){for(var n=0,r=0,o=t;r<o.length;r++)void 0!==e[o[r]]&&n++;return n},e.prototype.encodeMap=function(e,t){var n=Object.keys(e);this.sortKeys&&n.sort();var r=this.ignoreUndefined?this.countWithoutUndefined(e,n):n.length;if(r<16)this.writeU8(128+r);else if(r<65536)this.writeU8(222),this.writeU16(r);else{if(!(r<4294967296))throw new Error("Too large map object: "+r);this.writeU8(223),this.writeU32(r)}for(var o=0,i=n;o<i.length;o++){var s=i[o],a=e[s];this.ignoreUndefined&&void 0===a||(this.encodeString(s),this.doEncode(a,t+1))}},e.prototype.encodeExtension=function(e){var t=e.data.length;if(1===t)this.writeU8(212);else if(2===t)this.writeU8(213);else if(4===t)this.writeU8(214);else if(8===t)this.writeU8(215);else if(16===t)this.writeU8(216);else if(t<256)this.writeU8(199),this.writeU8(t);else if(t<65536)this.writeU8(200),this.writeU16(t);else{if(!(t<4294967296))throw new Error("Too large extension object: "+t);this.writeU8(201),this.writeU32(t)}this.writeI8(e.type),this.writeU8a(e.data)},e.prototype.writeU8=function(e){this.ensureBufferSizeToWrite(1),this.view.setUint8(this.pos,e),this.pos++},e.prototype.writeU8a=function(e){var t=e.length;this.ensureBufferSizeToWrite(t),this.bytes.set(e,this.pos),this.pos+=t},e.prototype.writeI8=function(e){this.ensureBufferSizeToWrite(1),this.view.setInt8(this.pos,e),this.pos++},e.prototype.writeU16=function(e){this.ensureBufferSizeToWrite(2),this.view.setUint16(this.pos,e),this.pos+=2},e.prototype.writeI16=function(e){this.ensureBufferSizeToWrite(2),this.view.setInt16(this.pos,e),this.pos+=2},e.prototype.writeU32=function(e){this.ensureBufferSizeToWrite(4),this.view.setUint32(this.pos,e),this.pos+=4},e.prototype.writeI32=function(e){this.ensureBufferSizeToWrite(4),this.view.setInt32(this.pos,e),this.pos+=4},e.prototype.writeF32=function(e){this.ensureBufferSizeToWrite(4),this.view.setFloat32(this.pos,e),this.pos+=4},e.prototype.writeF64=function(e){this.ensureBufferSizeToWrite(8),this.view.setFloat64(this.pos,e),this.pos+=8},e.prototype.writeU64=function(e){this.ensureBufferSizeToWrite(8),function(e,t,n){var r=n/4294967296,o=n;e.setUint32(t,r),e.setUint32(t+4,o)}(this.view,this.pos,e),this.pos+=8},e.prototype.writeI64=function(e){this.ensureBufferSizeToWrite(8),Pt(this.view,this.pos,e),this.pos+=8},e}();function Xt(e){return(e<0?"-":"")+"0x"+Math.abs(e).toString(16).padStart(2,"0")}var Yt=function(){function e(e,t){void 0===e&&(e=16),void 0===t&&(t=16),this.maxKeyLength=e,this.maxLengthPerKey=t,this.hit=0,this.miss=0,this.caches=[];for(var n=0;n<this.maxKeyLength;n++)this.caches.push([])}return e.prototype.canBeCached=function(e){return e>0&&e<=this.maxKeyLength},e.prototype.find=function(e,t,n){e:for(var r=0,o=this.caches[n-1];r<o.length;r++){for(var i=o[r],s=i.bytes,a=0;a<n;a++)if(s[a]!==e[t+a])continue e;return i.str}return null},e.prototype.store=function(e,t){var n=this.caches[e.length-1],r={bytes:e,str:t};n.length>=this.maxLengthPerKey?n[Math.random()*n.length|0]=r:n.push(r)},e.prototype.decode=function(e,t,n){var r=this.find(e,t,n);if(null!=r)return this.hit++,r;this.miss++;var o=Mt(e,t,n),i=Uint8Array.prototype.slice.call(e,t,t+n);return this.store(i,o),o},e}(),Gt=function(e,t){var n,r,o,i,s={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function a(i){return function(a){return function(i){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(o=2&i[0]?r.return:i[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,i[1])).done)return o;switch(r=0,o&&(i=[2&i[0],o.value]),i[0]){case 0:case 1:o=i;break;case 4:return s.label++,{value:i[1],done:!1};case 5:s.label++,r=i[1],i=[0];continue;case 7:i=s.ops.pop(),s.trys.pop();continue;default:if(!((o=(o=s.trys).length>0&&o[o.length-1])||6!==i[0]&&2!==i[0])){s=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]<o[3])){s.label=i[1];break}if(6===i[0]&&s.label<o[1]){s.label=o[1],o=i;break}if(o&&s.label<o[2]){s.label=o[2],s.ops.push(i);break}o[2]&&s.ops.pop(),s.trys.pop();continue}i=t.call(e,s)}catch(e){i=[6,e],r=0}finally{n=o=0}if(5&i[0])throw i[1];return{value:i[0]?i[1]:void 0,done:!0}}([i,a])}}},Qt=function(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t,n=e[Symbol.asyncIterator];return n?n.call(e):(e="function"==typeof __values?__values(e):e[Symbol.iterator](),t={},r("next"),r("throw"),r("return"),t[Symbol.asyncIterator]=function(){return this},t);function r(n){t[n]=e[n]&&function(t){return new Promise((function(r,o){!function(e,t,n,r){Promise.resolve(r).then((function(t){e({value:t,done:n})}),t)}(r,o,(t=e[n](t)).done,t.value)}))}}},Zt=function(e){return this instanceof Zt?(this.v=e,this):new Zt(e)},en=function(e,t,n){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var r,o=n.apply(e,t||[]),i=[];return r={},s("next"),s("throw"),s("return"),r[Symbol.asyncIterator]=function(){return this},r;function s(e){o[e]&&(r[e]=function(t){return new Promise((function(n,r){i.push([e,t,n,r])>1||a(e,t)}))})}function a(e,t){try{(n=o[e](t)).value instanceof Zt?Promise.resolve(n.value.v).then(c,l):h(i[0][2],n)}catch(e){h(i[0][3],e)}var n}function c(e){a("next",e)}function l(e){a("throw",e)}function h(e,t){e(t),i.shift(),i.length&&a(i[0][0],i[0][1])}},tn=new DataView(new ArrayBuffer(0)),nn=new Uint8Array(tn.buffer),rn=function(){try{tn.getInt8(0)}catch(e){return e.constructor}throw new Error("never reached")}(),on=new rn("Insufficient data"),sn=new Yt,an=function(){function e(e,t,n,r,o,i,s,a){void 0===e&&(e=qt.defaultCodec),void 0===t&&(t=void 0),void 0===n&&(n=Rt),void 0===r&&(r=Rt),void 0===o&&(o=Rt),void 0===i&&(i=Rt),void 0===s&&(s=Rt),void 0===a&&(a=sn),this.extensionCodec=e,this.context=t,this.maxStrLength=n,this.maxBinLength=r,this.maxArrayLength=o,this.maxMapLength=i,this.maxExtLength=s,this.keyDecoder=a,this.totalPos=0,this.pos=0,this.view=tn,this.bytes=nn,this.headByte=-1,this.stack=[]}return e.prototype.reinitializeState=function(){this.totalPos=0,this.headByte=-1,this.stack.length=0},e.prototype.setBuffer=function(e){this.bytes=Vt(e),this.view=function(e){if(e instanceof ArrayBuffer)return new DataView(e);var t=Vt(e);return new DataView(t.buffer,t.byteOffset,t.byteLength)}(this.bytes),this.pos=0},e.prototype.appendBuffer=function(e){if(-1!==this.headByte||this.hasRemaining(1)){var t=this.bytes.subarray(this.pos),n=Vt(e),r=new Uint8Array(t.length+n.length);r.set(t),r.set(n,t.length),this.setBuffer(r)}else this.setBuffer(e)},e.prototype.hasRemaining=function(e){return this.view.byteLength-this.pos>=e},e.prototype.createExtraByteError=function(e){var t=this.view,n=this.pos;return new RangeError("Extra "+(t.byteLength-n)+" of "+t.byteLength+" byte(s) found at buffer["+e+"]")},e.prototype.decode=function(e){this.reinitializeState(),this.setBuffer(e);var t=this.doDecodeSync();if(this.hasRemaining(1))throw this.createExtraByteError(this.pos);return t},e.prototype.decodeMulti=function(e){return Gt(this,(function(t){switch(t.label){case 0:this.reinitializeState(),this.setBuffer(e),t.label=1;case 1:return this.hasRemaining(1)?[4,this.doDecodeSync()]:[3,3];case 2:return t.sent(),[3,1];case 3:return[2]}}))},e.prototype.decodeAsync=function(e){var t,n,r,o,i,s,a;return i=this,void 0,a=function(){var i,s,a,c,l,h,u,d;return Gt(this,(function(p){switch(p.label){case 0:i=!1,p.label=1;case 1:p.trys.push([1,6,7,12]),t=Qt(e),p.label=2;case 2:return[4,t.next()];case 3:if((n=p.sent()).done)return[3,5];if(a=n.value,i)throw this.createExtraByteError(this.totalPos);this.appendBuffer(a);try{s=this.doDecodeSync(),i=!0}catch(e){if(!(e instanceof rn))throw e}this.totalPos+=this.pos,p.label=4;case 4:return[3,2];case 5:return[3,12];case 6:return c=p.sent(),r={error:c},[3,12];case 7:return p.trys.push([7,,10,11]),n&&!n.done&&(o=t.return)?[4,o.call(t)]:[3,9];case 8:p.sent(),p.label=9;case 9:return[3,11];case 10:if(r)throw r.error;return[7];case 11:return[7];case 12:if(i){if(this.hasRemaining(1))throw this.createExtraByteError(this.totalPos);return[2,s]}throw h=(l=this).headByte,u=l.pos,d=l.totalPos,new RangeError("Insufficient data in parsing "+Xt(h)+" at "+d+" ("+u+" in the current buffer)")}}))},new((s=void 0)||(s=Promise))((function(e,t){function n(e){try{o(a.next(e))}catch(e){t(e)}}function r(e){try{o(a.throw(e))}catch(e){t(e)}}function o(t){var o;t.done?e(t.value):(o=t.value,o instanceof s?o:new s((function(e){e(o)}))).then(n,r)}o((a=a.apply(i,[])).next())}))},e.prototype.decodeArrayStream=function(e){return this.decodeMultiAsync(e,!0)},e.prototype.decodeStream=function(e){return this.decodeMultiAsync(e,!1)},e.prototype.decodeMultiAsync=function(e,t){return en(this,arguments,(function(){var n,r,o,i,s,a,c,l,h;return Gt(this,(function(u){switch(u.label){case 0:n=t,r=-1,u.label=1;case 1:u.trys.push([1,13,14,19]),o=Qt(e),u.label=2;case 2:return[4,Zt(o.next())];case 3:if((i=u.sent()).done)return[3,12];if(s=i.value,t&&0===r)throw this.createExtraByteError(this.totalPos);this.appendBuffer(s),n&&(r=this.readArraySize(),n=!1,this.complete()),u.label=4;case 4:u.trys.push([4,9,,10]),u.label=5;case 5:return[4,Zt(this.doDecodeSync())];case 6:return[4,u.sent()];case 7:return u.sent(),0==--r?[3,8]:[3,5];case 8:return[3,10];case 9:if(!((a=u.sent())instanceof rn))throw a;return[3,10];case 10:this.totalPos+=this.pos,u.label=11;case 11:return[3,2];case 12:return[3,19];case 13:return c=u.sent(),l={error:c},[3,19];case 14:return u.trys.push([14,,17,18]),i&&!i.done&&(h=o.return)?[4,Zt(h.call(o))]:[3,16];case 15:u.sent(),u.label=16;case 16:return[3,18];case 17:if(l)throw l.error;return[7];case 18:return[7];case 19:return[2]}}))}))},e.prototype.doDecodeSync=function(){e:for(;;){var e=this.readHeadByte(),t=void 0;if(e>=224)t=e-256;else if(e<192)if(e<128)t=e;else if(e<144){if(0!=(r=e-128)){this.pushMapState(r),this.complete();continue e}t={}}else if(e<160){if(0!=(r=e-144)){this.pushArrayState(r),this.complete();continue e}t=[]}else{var n=e-160;t=this.decodeUtf8String(n,0)}else if(192===e)t=null;else if(194===e)t=!1;else if(195===e)t=!0;else if(202===e)t=this.readF32();else if(203===e)t=this.readF64();else if(204===e)t=this.readU8();else if(205===e)t=this.readU16();else if(206===e)t=this.readU32();else if(207===e)t=this.readU64();else if(208===e)t=this.readI8();else if(209===e)t=this.readI16();else if(210===e)t=this.readI32();else if(211===e)t=this.readI64();else if(217===e)n=this.lookU8(),t=this.decodeUtf8String(n,1);else if(218===e)n=this.lookU16(),t=this.decodeUtf8String(n,2);else if(219===e)n=this.lookU32(),t=this.decodeUtf8String(n,4);else if(220===e){if(0!==(r=this.readU16())){this.pushArrayState(r),this.complete();continue e}t=[]}else if(221===e){if(0!==(r=this.readU32())){this.pushArrayState(r),this.complete();continue e}t=[]}else if(222===e){if(0!==(r=this.readU16())){this.pushMapState(r),this.complete();continue e}t={}}else if(223===e){if(0!==(r=this.readU32())){this.pushMapState(r),this.complete();continue e}t={}}else if(196===e){var r=this.lookU8();t=this.decodeBinary(r,1)}else if(197===e)r=this.lookU16(),t=this.decodeBinary(r,2);else if(198===e)r=this.lookU32(),t=this.decodeBinary(r,4);else if(212===e)t=this.decodeExtension(1,0);else if(213===e)t=this.decodeExtension(2,0);else if(214===e)t=this.decodeExtension(4,0);else if(215===e)t=this.decodeExtension(8,0);else if(216===e)t=this.decodeExtension(16,0);else if(199===e)r=this.lookU8(),t=this.decodeExtension(r,1);else if(200===e)r=this.lookU16(),t=this.decodeExtension(r,2);else{if(201!==e)throw new zt("Unrecognized type byte: "+Xt(e));r=this.lookU32(),t=this.decodeExtension(r,4)}this.complete();for(var o=this.stack;o.length>0;){var i=o[o.length-1];if(0===i.type){if(i.array[i.position]=t,i.position++,i.position!==i.size)continue e;o.pop(),t=i.array}else{if(1===i.type){if("string"!=(s=typeof t)&&"number"!==s)throw new zt("The type of key must be string or number but "+typeof t);if("__proto__"===t)throw new zt("The key __proto__ is not allowed");i.key=t,i.type=2;continue e}if(i.map[i.key]=t,i.readCount++,i.readCount!==i.size){i.key=null,i.type=1;continue e}o.pop(),t=i.map}}return t}var s},e.prototype.readHeadByte=function(){return-1===this.headByte&&(this.headByte=this.readU8()),this.headByte},e.prototype.complete=function(){this.headByte=-1},e.prototype.readArraySize=function(){var e=this.readHeadByte();switch(e){case 220:return this.readU16();case 221:return this.readU32();default:if(e<160)return e-144;throw new zt("Unrecognized array type byte: "+Xt(e))}},e.prototype.pushMapState=function(e){if(e>this.maxMapLength)throw new zt("Max length exceeded: map length ("+e+") > maxMapLengthLength ("+this.maxMapLength+")");this.stack.push({type:1,size:e,key:null,readCount:0,map:{}})},e.prototype.pushArrayState=function(e){if(e>this.maxArrayLength)throw new zt("Max length exceeded: array length ("+e+") > maxArrayLength ("+this.maxArrayLength+")");this.stack.push({type:0,size:e,array:new Array(e),position:0})},e.prototype.decodeUtf8String=function(e,t){var n;if(e>this.maxStrLength)throw new zt("Max length exceeded: UTF-8 byte length ("+e+") > maxStrLength ("+this.maxStrLength+")");if(this.bytes.byteLength<this.pos+t+e)throw on;var r,o=this.pos+t;return r=this.stateIsMapKey()&&(null===(n=this.keyDecoder)||void 0===n?void 0:n.canBeCached(e))?this.keyDecoder.decode(this.bytes,o,e):e>jt?function(e,t,n){var r=e.subarray(t,t+n);return Ft.decode(r)}(this.bytes,o,e):Mt(this.bytes,o,e),this.pos+=t+e,r},e.prototype.stateIsMapKey=function(){return this.stack.length>0&&1===this.stack[this.stack.length-1].type},e.prototype.decodeBinary=function(e,t){if(e>this.maxBinLength)throw new zt("Max length exceeded: bin length ("+e+") > maxBinLength ("+this.maxBinLength+")");if(!this.hasRemaining(e+t))throw on;var n=this.pos+t,r=this.bytes.subarray(n,n+e);return this.pos+=t+e,r},e.prototype.decodeExtension=function(e,t){if(e>this.maxExtLength)throw new zt("Max length exceeded: ext length ("+e+") > maxExtLength ("+this.maxExtLength+")");var n=this.view.getInt8(this.pos+t),r=this.decodeBinary(e,t+1);return this.extensionCodec.decode(r,n,this.context)},e.prototype.lookU8=function(){return this.view.getUint8(this.pos)},e.prototype.lookU16=function(){return this.view.getUint16(this.pos)},e.prototype.lookU32=function(){return this.view.getUint32(this.pos)},e.prototype.readU8=function(){var e=this.view.getUint8(this.pos);return this.pos++,e},e.prototype.readI8=function(){var e=this.view.getInt8(this.pos);return this.pos++,e},e.prototype.readU16=function(){var e=this.view.getUint16(this.pos);return this.pos+=2,e},e.prototype.readI16=function(){var e=this.view.getInt16(this.pos);return this.pos+=2,e},e.prototype.readU32=function(){var e=this.view.getUint32(this.pos);return this.pos+=4,e},e.prototype.readI32=function(){var e=this.view.getInt32(this.pos);return this.pos+=4,e},e.prototype.readU64=function(){var e,t,n=(e=this.view,t=this.pos,4294967296*e.getUint32(t)+e.getUint32(t+4));return this.pos+=8,n},e.prototype.readI64=function(){var e=Ut(this.view,this.pos);return this.pos+=8,e},e.prototype.readF32=function(){var e=this.view.getFloat32(this.pos);return this.pos+=4,e},e.prototype.readF64=function(){var e=this.view.getFloat64(this.pos);return this.pos+=8,e},e}();class cn{static write(e){let t=e.byteLength||e.length;const n=[];do{let e=127&t;t>>=7,t>0&&(e|=128),n.push(e)}while(t>0);t=e.byteLength||e.length;const r=new Uint8Array(n.length+t);return r.set(n,0),r.set(e,n.length),r.buffer}static parse(e){const t=[],n=new Uint8Array(e),r=[0,7,14,21,28];for(let o=0;o<e.byteLength;){let i,s=0,a=0;do{i=n[o+s],a|=(127&i)<<r[s],s++}while(s<Math.min(5,e.byteLength-o)&&0!=(128&i));if(0!=(128&i)&&s<5)throw new Error("Cannot read message size.");if(5===s&&i>7)throw new Error("Messages bigger than 2GB are not supported.");if(!(n.byteLength>=o+s+a))throw new Error("Incomplete message.");t.push(n.slice?n.slice(o+s,o+s+a):n.subarray(o+s,o+s+a)),o=o+s+a}return t}}const ln=new Uint8Array([145,Ye.Ping]);class hn{constructor(e){this.name="messagepack",this.version=1,this.transferFormat=Xe.Binary,this._errorResult=1,this._voidResult=2,this._nonVoidResult=3,e=e||{},this._encoder=new Kt(e.extensionCodec,e.context,e.maxDepth,e.initialBufferSize,e.sortKeys,e.forceFloat32,e.ignoreUndefined,e.forceIntegerToFloat),this._decoder=new an(e.extensionCodec,e.context,e.maxStrLength,e.maxBinLength,e.maxArrayLength,e.maxMapLength,e.maxExtLength)}parseMessages(e,t){if(!(n=e)||"undefined"==typeof ArrayBuffer||!(n instanceof ArrayBuffer||n.constructor&&"ArrayBuffer"===n.constructor.name))throw new Error("Invalid input for MessagePack hub protocol. Expected an ArrayBuffer.");var n;null===t&&(t=Qe.instance);const r=cn.parse(e),o=[];for(const e of r){const n=this._parseMessage(e,t);n&&o.push(n)}return o}writeMessage(e){switch(e.type){case Ye.Invocation:return this._writeInvocation(e);case Ye.StreamInvocation:return this._writeStreamInvocation(e);case Ye.StreamItem:return this._writeStreamItem(e);case Ye.Completion:return this._writeCompletion(e);case Ye.Ping:return cn.write(ln);case Ye.CancelInvocation:return this._writeCancelInvocation(e);default:throw new Error("Invalid message type.")}}_parseMessage(e,t){if(0===e.length)throw new Error("Invalid payload.");const n=this._decoder.decode(e);if(0===n.length||!(n instanceof Array))throw new Error("Invalid payload.");const r=n[0];switch(r){case Ye.Invocation:return this._createInvocationMessage(this._readHeaders(n),n);case Ye.StreamItem:return this._createStreamItemMessage(this._readHeaders(n),n);case Ye.Completion:return this._createCompletionMessage(this._readHeaders(n),n);case Ye.Ping:return this._createPingMessage(n);case Ye.Close:return this._createCloseMessage(n);default:return t.log(Ve.Information,"Unknown message type '"+r+"' ignored."),null}}_createCloseMessage(e){if(e.length<2)throw new Error("Invalid payload for Close message.");return{allowReconnect:e.length>=3?e[2]:void 0,error:e[1],type:Ye.Close}}_createPingMessage(e){if(e.length<1)throw new Error("Invalid payload for Ping message.");return{type:Ye.Ping}}_createInvocationMessage(e,t){if(t.length<5)throw new Error("Invalid payload for Invocation message.");const n=t[2];return n?{arguments:t[4],headers:e,invocationId:n,streamIds:[],target:t[3],type:Ye.Invocation}:{arguments:t[4],headers:e,streamIds:[],target:t[3],type:Ye.Invocation}}_createStreamItemMessage(e,t){if(t.length<4)throw new Error("Invalid payload for StreamItem message.");return{headers:e,invocationId:t[2],item:t[3],type:Ye.StreamItem}}_createCompletionMessage(e,t){if(t.length<4)throw new Error("Invalid payload for Completion message.");const n=t[3];if(n!==this._voidResult&&t.length<5)throw new Error("Invalid payload for Completion message.");let r,o;switch(n){case this._errorResult:r=t[4];break;case this._nonVoidResult:o=t[4]}return{error:r,headers:e,invocationId:t[2],result:o,type:Ye.Completion}}_writeInvocation(e){let t;return t=e.streamIds?this._encoder.encode([Ye.Invocation,e.headers||{},e.invocationId||null,e.target,e.arguments,e.streamIds]):this._encoder.encode([Ye.Invocation,e.headers||{},e.invocationId||null,e.target,e.arguments]),cn.write(t.slice())}_writeStreamInvocation(e){let t;return t=e.streamIds?this._encoder.encode([Ye.StreamInvocation,e.headers||{},e.invocationId,e.target,e.arguments,e.streamIds]):this._encoder.encode([Ye.StreamInvocation,e.headers||{},e.invocationId,e.target,e.arguments]),cn.write(t.slice())}_writeStreamItem(e){const t=this._encoder.encode([Ye.StreamItem,e.headers||{},e.invocationId,e.item]);return cn.write(t.slice())}_writeCompletion(e){const t=e.error?this._errorResult:e.result?this._nonVoidResult:this._voidResult;let n;switch(t){case this._errorResult:n=this._encoder.encode([Ye.Completion,e.headers||{},e.invocationId,t,e.error]);break;case this._voidResult:n=this._encoder.encode([Ye.Completion,e.headers||{},e.invocationId,t]);break;case this._nonVoidResult:n=this._encoder.encode([Ye.Completion,e.headers||{},e.invocationId,t,e.result])}return cn.write(n.slice())}_writeCancelInvocation(e){const t=this._encoder.encode([Ye.CancelInvocation,e.headers||{},e.invocationId]);return cn.write(t.slice())}_readHeaders(e){const t=e[1];if("object"!=typeof t)throw new Error("Invalid headers.");return t}}let un=!1;function dn(){const e=document.querySelector("#blazor-error-ui");e&&(e.style.display="block"),un||(un=!0,document.querySelectorAll("#blazor-error-ui .reload").forEach((e=>{e.onclick=function(e){location.reload(),e.preventDefault()}})),document.querySelectorAll("#blazor-error-ui .dismiss").forEach((e=>{e.onclick=function(e){const t=document.querySelector("#blazor-error-ui");t&&(t.style.display="none"),e.preventDefault()}})))}const pn="function"==typeof TextDecoder?new TextDecoder("utf-8"):null,fn=pn?pn.decode.bind(pn):function(e){let t=0;const n=e.length,r=[],o=[];for(;t<n;){const n=e[t++];if(0===n)break;if(0==(128&n))r.push(n);else if(192==(224&n)){const o=63&e[t++];r.push((31&n)<<6|o)}else if(224==(240&n)){const o=63&e[t++],i=63&e[t++];r.push((31&n)<<12|o<<6|i)}else if(240==(248&n)){let o=(7&n)<<18|(63&e[t++])<<12|(63&e[t++])<<6|63&e[t++];o>65535&&(o-=65536,r.push(o>>>10&1023|55296),o=56320|1023&o),r.push(o)}r.length>1024&&(o.push(String.fromCharCode.apply(null,r)),r.length=0)}return o.push(String.fromCharCode.apply(null,r)),o.join("")},gn=Math.pow(2,32),mn=Math.pow(2,21)-1;function yn(e,t){return e[t]|e[t+1]<<8|e[t+2]<<16|e[t+3]<<24}function wn(e,t){return e[t]+(e[t+1]<<8)+(e[t+2]<<16)+(e[t+3]<<24>>>0)}function vn(e,t){const n=wn(e,t+4);if(n>mn)throw new Error(`Cannot read uint64 with high order part ${n}, because the result would exceed Number.MAX_SAFE_INTEGER.`);return n*gn+wn(e,t)}class bn{constructor(e){this.batchData=e;const t=new Cn(e);this.arrayRangeReader=new In(e),this.arrayBuilderSegmentReader=new kn(e),this.diffReader=new _n(e),this.editReader=new En(e,t),this.frameReader=new Sn(e,t)}updatedComponents(){return yn(this.batchData,this.batchData.length-20)}referenceFrames(){return yn(this.batchData,this.batchData.length-16)}disposedComponentIds(){return yn(this.batchData,this.batchData.length-12)}disposedEventHandlerIds(){return yn(this.batchData,this.batchData.length-8)}updatedComponentsEntry(e,t){const n=e+4*t;return yn(this.batchData,n)}referenceFramesEntry(e,t){return e+20*t}disposedComponentIdsEntry(e,t){const n=e+4*t;return yn(this.batchData,n)}disposedEventHandlerIdsEntry(e,t){const n=e+8*t;return vn(this.batchData,n)}}class _n{constructor(e){this.batchDataUint8=e}componentId(e){return yn(this.batchDataUint8,e)}edits(e){return e+4}editsEntry(e,t){return e+16*t}}class En{constructor(e,t){this.batchDataUint8=e,this.stringReader=t}editType(e){return yn(this.batchDataUint8,e)}siblingIndex(e){return yn(this.batchDataUint8,e+4)}newTreeIndex(e){return yn(this.batchDataUint8,e+8)}moveToSiblingIndex(e){return yn(this.batchDataUint8,e+8)}removedAttributeName(e){const t=yn(this.batchDataUint8,e+12);return this.stringReader.readString(t)}}class Sn{constructor(e,t){this.batchDataUint8=e,this.stringReader=t}frameType(e){return yn(this.batchDataUint8,e)}subtreeLength(e){return yn(this.batchDataUint8,e+4)}elementReferenceCaptureId(e){const t=yn(this.batchDataUint8,e+4);return this.stringReader.readString(t)}componentId(e){return yn(this.batchDataUint8,e+8)}elementName(e){const t=yn(this.batchDataUint8,e+8);return this.stringReader.readString(t)}textContent(e){const t=yn(this.batchDataUint8,e+4);return this.stringReader.readString(t)}markupContent(e){const t=yn(this.batchDataUint8,e+4);return this.stringReader.readString(t)}attributeName(e){const t=yn(this.batchDataUint8,e+4);return this.stringReader.readString(t)}attributeValue(e){const t=yn(this.batchDataUint8,e+8);return this.stringReader.readString(t)}attributeEventHandlerId(e){return vn(this.batchDataUint8,e+12)}}class Cn{constructor(e){this.batchDataUint8=e,this.stringTableStartIndex=yn(e,e.length-4)}readString(e){if(-1===e)return null;{const n=yn(this.batchDataUint8,this.stringTableStartIndex+4*e),r=function(e,t){let n=0,r=0;for(let o=0;o<4;o++){const i=e[t+o];if(n|=(127&i)<<r,i<128)break;r+=7}return n}(this.batchDataUint8,n),o=n+((t=r)<128?1:t<16384?2:t<2097152?3:4),i=new Uint8Array(this.batchDataUint8.buffer,this.batchDataUint8.byteOffset+o,r);return fn(i)}var t}}class In{constructor(e){this.batchDataUint8=e}count(e){return yn(this.batchDataUint8,e)}values(e){return e+4}}class kn{constructor(e){this.batchDataUint8=e}offset(e){return 0}count(e){return yn(this.batchDataUint8,e)}values(e){return e+4}}var Tn;!function(e){e[e.Trace=0]="Trace",e[e.Debug=1]="Debug",e[e.Information=2]="Information",e[e.Warning=3]="Warning",e[e.Error=4]="Error",e[e.Critical=5]="Critical",e[e.None=6]="None"}(Tn||(Tn={}));class xn{constructor(e,t){this.nextBatchId=2,this.browserRendererId=e,this.logger=t}static getOrCreate(e){return xn.instance||(xn.instance=new xn(0,e)),this.instance}async processBatch(e,t,n){if(e<this.nextBatchId)return await this.completeBatch(n,e),void this.logger.log(Tn.Debug,`Batch ${e} already processed. Waiting for batch ${this.nextBatchId}.`);if(e>this.nextBatchId)return this.fatalError?(this.logger.log(Tn.Debug,`Received a new batch ${e} but errored out on a previous batch ${this.nextBatchId-1}`),void await n.send("OnRenderCompleted",this.nextBatchId-1,this.fatalError.toString())):void this.logger.log(Tn.Debug,`Waiting for batch ${this.nextBatchId}. Batch ${e} not processed.`);try{this.nextBatchId++,this.logger.log(Tn.Debug,`Applying batch ${e}.`),function(e,t){const n=de[e];if(!n)throw new Error(`There is no browser renderer with ID ${e}.`);const r=t.arrayRangeReader,o=t.updatedComponents(),i=r.values(o),s=r.count(o),a=t.referenceFrames(),c=r.values(a),l=t.diffReader;for(let e=0;e<s;e++){const r=t.updatedComponentsEntry(i,e),o=l.componentId(r),s=l.edits(r);n.updateComponent(t,o,s,c)}const h=t.disposedComponentIds(),u=r.values(h),d=r.count(h);for(let e=0;e<d;e++){const r=t.disposedComponentIdsEntry(u,e);n.disposeComponent(r)}const p=t.disposedEventHandlerIds(),f=r.values(p),g=r.count(p);for(let e=0;e<g;e++){const r=t.disposedEventHandlerIdsEntry(f,e);n.disposeEventHandler(r)}pe&&(pe=!1,window.scrollTo&&window.scrollTo(0,0))}(this.browserRendererId,new bn(t)),await this.completeBatch(n,e)}catch(t){throw this.fatalError=t.toString(),this.logger.log(Tn.Error,`There was an error applying batch ${e}.`),n.send("OnRenderCompleted",e,t.toString()),t}}getLastBatchid(){return this.nextBatchId-1}async completeBatch(e,t){try{await e.send("OnRenderCompleted",t,null)}catch{this.logger.log(Tn.Warning,`Failed to deliver completion notification for render '${t}'.`)}}}class Dn{log(e,t){}}Dn.instance=new Dn;class Rn{constructor(e){this.minLevel=e}log(e,t){if(e>=this.minLevel){const n=`[${(new Date).toISOString()}] ${Tn[e]}: ${t}`;switch(e){case Tn.Critical:case Tn.Error:console.error(n);break;case Tn.Warning:console.warn(n);break;case Tn.Information:console.info(n);break;default:console.log(n)}}}}class Pn{constructor(e,t){this.circuitId=void 0,this.components=e,this.applicationState=t}reconnect(e){if(!this.circuitId)throw new Error("Circuit host not initialized.");return e.state!==Ge.Connected?Promise.resolve(!1):e.invoke("ConnectCircuit",this.circuitId)}initialize(e){if(this.circuitId)throw new Error(`Circuit host '${this.circuitId}' already initialized.`);this.circuitId=e}async startCircuit(e){if(e.state!==Ge.Connected)return!1;const t=await e.invoke("StartCircuit",ye.getBaseURI(),ye.getLocationHref(),JSON.stringify(this.components.map((e=>e.toRecord()))),this.applicationState||"");return!!t&&(this.initialize(t),!0)}resolveElement(e){const t=function(e){const t=f.get(e);if(t)return f.delete(e),t}(e);if(t)return M(t,!0);const n=Number.parseInt(e);if(!Number.isNaN(n))return function(e,t){if(!e.parentNode)throw new Error(`Comment not connected to the DOM ${e.textContent}`);const n=e.parentNode,r=M(n,!0),o=J(r);return Array.from(n.childNodes).forEach((e=>o.push(e))),e[B]=r,t&&(e[L]=t,M(t)),M(e)}(this.components[n].start,this.components[n].end);throw new Error(`Invalid sequence number or identifier '${e}'.`)}}const Un={configureSignalR:e=>{},logLevel:Tn.Warning,reconnectionOptions:{maxRetries:8,retryIntervalMilliseconds:2e4,dialogId:"components-reconnect-modal"}};class An{constructor(e,t,n,r){this.maxRetries=t,this.document=n,this.logger=r,this.addedToDom=!1,this.modal=this.document.createElement("div"),this.modal.id=e,this.maxRetries=t,this.modal.style.cssText=["position: fixed","top: 0","right: 0","bottom: 0","left: 0","z-index: 1050","display: none","overflow: hidden","background-color: #fff","opacity: 0.8","text-align: center","font-weight: bold","transition: visibility 0s linear 500ms"].join(";"),this.message=this.document.createElement("h5"),this.message.style.cssText="margin-top: 20px",this.button=this.document.createElement("button"),this.button.style.cssText="margin:5px auto 5px",this.button.textContent="Retry";const o=this.document.createElement("a");o.addEventListener("click",(()=>location.reload())),o.textContent="reload",this.reloadParagraph=this.document.createElement("p"),this.reloadParagraph.textContent="Alternatively, ",this.reloadParagraph.appendChild(o),this.modal.appendChild(this.message),this.modal.appendChild(this.button),this.modal.appendChild(this.reloadParagraph),this.loader=this.getLoader(),this.message.after(this.loader),this.button.addEventListener("click",(async()=>{this.show();try{await Ne.reconnect()||this.rejected()}catch(e){this.logger.log(Tn.Error,e),this.failed()}}))}show(){this.addedToDom||(this.addedToDom=!0,this.document.body.appendChild(this.modal)),this.modal.style.display="block",this.loader.style.display="inline-block",this.button.style.display="none",this.reloadParagraph.style.display="none",this.message.textContent="Attempting to reconnect to the server...",this.modal.style.visibility="hidden",setTimeout((()=>{this.modal.style.visibility="visible"}),0)}update(e){this.message.textContent=`Attempting to reconnect to the server: ${e} of ${this.maxRetries}`}hide(){this.modal.style.display="none"}failed(){this.button.style.display="block",this.reloadParagraph.style.display="none",this.loader.style.display="none";const e=this.document.createTextNode("Reconnection failed. Try "),t=this.document.createElement("a");t.textContent="reloading",t.setAttribute("href",""),t.addEventListener("click",(()=>location.reload()));const n=this.document.createTextNode(" the page if you're unable to reconnect.");this.message.replaceChildren(e,t,n)}rejected(){this.button.style.display="none",this.reloadParagraph.style.display="none",this.loader.style.display="none";const e=this.document.createTextNode("Could not reconnect to the server. "),t=this.document.createElement("a");t.textContent="Reload",t.setAttribute("href",""),t.addEventListener("click",(()=>location.reload()));const n=this.document.createTextNode(" the page to restore functionality.");this.message.replaceChildren(e,t,n)}getLoader(){const e=this.document.createElement("div");return e.style.cssText=["border: 0.3em solid #f3f3f3","border-top: 0.3em solid #3498db","border-radius: 50%","width: 2em","height: 2em","display: inline-block"].join(";"),e.animate([{transform:"rotate(0deg)"},{transform:"rotate(360deg)"}],{duration:2e3,iterations:1/0}),e}}class Nn{constructor(e,t,n){this.dialog=e,this.maxRetries=t,this.document=n,this.document=n;const r=this.document.getElementById(Nn.MaxRetriesId);r&&(r.innerText=this.maxRetries.toString())}show(){this.removeClasses(),this.dialog.classList.add(Nn.ShowClassName)}update(e){const t=this.document.getElementById(Nn.CurrentAttemptId);t&&(t.innerText=e.toString())}hide(){this.removeClasses(),this.dialog.classList.add(Nn.HideClassName)}failed(){this.removeClasses(),this.dialog.classList.add(Nn.FailedClassName)}rejected(){this.removeClasses(),this.dialog.classList.add(Nn.RejectedClassName)}removeClasses(){this.dialog.classList.remove(Nn.ShowClassName,Nn.HideClassName,Nn.FailedClassName,Nn.RejectedClassName)}}Nn.ShowClassName="components-reconnect-show",Nn.HideClassName="components-reconnect-hide",Nn.FailedClassName="components-reconnect-failed",Nn.RejectedClassName="components-reconnect-rejected",Nn.MaxRetriesId="components-reconnect-max-retries",Nn.CurrentAttemptId="components-reconnect-current-attempt";class $n{constructor(e,t,n){this._currentReconnectionProcess=null,this._logger=e,this._reconnectionDisplay=t,this._reconnectCallback=n||Ne.reconnect}onConnectionDown(e,t){if(!this._reconnectionDisplay){const t=document.getElementById(e.dialogId);this._reconnectionDisplay=t?new Nn(t,e.maxRetries,document):new An(e.dialogId,e.maxRetries,document,this._logger)}this._currentReconnectionProcess||(this._currentReconnectionProcess=new Bn(e,this._logger,this._reconnectCallback,this._reconnectionDisplay))}onConnectionUp(){this._currentReconnectionProcess&&(this._currentReconnectionProcess.dispose(),this._currentReconnectionProcess=null)}}class Bn{constructor(e,t,n,r){this.logger=t,this.reconnectCallback=n,this.isDisposed=!1,this.reconnectDisplay=r,this.reconnectDisplay.show(),this.attemptPeriodicReconnection(e)}dispose(){this.isDisposed=!0,this.reconnectDisplay.hide()}async attemptPeriodicReconnection(e){for(let t=0;t<e.maxRetries;t++){this.reconnectDisplay.update(t+1);const n=0===t&&e.retryIntervalMilliseconds>Bn.MaximumFirstRetryInterval?Bn.MaximumFirstRetryInterval:e.retryIntervalMilliseconds;if(await this.delay(n),this.isDisposed)break;try{return await this.reconnectCallback()?void 0:void this.reconnectDisplay.rejected()}catch(e){this.logger.log(Tn.Error,e)}}this.reconnectDisplay.failed()}delay(e){return new Promise((t=>setTimeout(t,e)))}}Bn.MaximumFirstRetryInterval=3e3;const Ln=/^\s*Blazor-Component-State:(?<state>[a-zA-Z0-9+/=]+)$/;function Mn(e){var t;if(e.nodeType===Node.COMMENT_NODE){const n=e.textContent||"",r=Ln.exec(n),o=r&&r.groups&&r.groups.state;return o&&(null===(t=e.parentNode)||void 0===t||t.removeChild(e)),o}if(!e.hasChildNodes())return;const n=e.childNodes;for(let e=0;e<n.length;e++){const t=Mn(n[e]);if(t)return t}}function On(e,t){if(!e.hasChildNodes())return[];const n=[],r=new zn(e.childNodes);for(;r.next()&&r.currentElement;){const e=jn(r,t);if(e)n.push(e);else{const e=On(r.currentElement,t);for(let t=0;t<e.length;t++){const r=e[t];n.push(r)}}}return n}const Fn=new RegExp(/^\s*Blazor:[^{]*(?<descriptor>.*)$/);function jn(e,t){const n=e.currentElement;if(n&&n.nodeType===Node.COMMENT_NODE&&n.textContent){const r=Fn.exec(n.textContent),o=r&&r.groups&&r.groups.descriptor;if(!o)return;try{const r=function(e){const t=JSON.parse(e),{type:n}=t;if("server"!==n&&"webassembly"!==n)throw new Error(`Invalid component type '${n}'.`);return t}(o);switch(t){case"webassembly":return function(e,t,n){const{type:r,assembly:o,typeName:i,parameterDefinitions:s,parameterValues:a,prerenderId:c}=e;if("webassembly"===r){if(!o)throw new Error("assembly must be defined when using a descriptor.");if(!i)throw new Error("typeName must be defined when using a descriptor.");if(c){const e=Hn(c,n);if(!e)throw new Error(`Could not find an end component comment for '${t}'`);return{type:r,assembly:o,typeName:i,parameterDefinitions:s&&atob(s),parameterValues:a&&atob(a),start:t,prerenderId:c,end:e}}return{type:r,assembly:o,typeName:i,parameterDefinitions:s&&atob(s),parameterValues:a&&atob(a),start:t}}}(r,n,e);case"server":return function(e,t,n){const{type:r,descriptor:o,sequence:i,prerenderId:s}=e;if("server"===r){if(!o)throw new Error("descriptor must be defined when using a descriptor.");if(void 0===i)throw new Error("sequence must be defined when using a descriptor.");if(!Number.isInteger(i))throw new Error(`Error parsing the sequence '${i}' for component '${JSON.stringify(e)}'`);if(s){const e=Hn(s,n);if(!e)throw new Error(`Could not find an end component comment for '${t}'`);return{type:r,sequence:i,descriptor:o,start:t,prerenderId:s,end:e}}return{type:r,sequence:i,descriptor:o,start:t}}}(r,n,e)}}catch(e){throw new Error(`Found malformed component comment at ${n.textContent}`)}}}function Hn(e,t){for(;t.next()&&t.currentElement;){const n=t.currentElement;if(n.nodeType!==Node.COMMENT_NODE)continue;if(!n.textContent)continue;const r=Fn.exec(n.textContent),o=r&&r[1];if(o)return Wn(o,e),n}}function Wn(e,t){const n=JSON.parse(e);if(1!==Object.keys(n).length)throw new Error(`Invalid end of component comment: '${e}'`);const r=n.prerenderId;if(!r)throw new Error(`End of component comment must have a value for the prerendered property: '${e}'`);if(r!==t)throw new Error(`End of component comment prerendered property must match the start comment prerender id: '${t}', '${r}'`)}class zn{constructor(e){this.childNodes=e,this.currentIndex=-1,this.length=e.length}next(){return this.currentIndex++,this.currentIndex<this.length?(this.currentElement=this.childNodes[this.currentIndex],!0):(this.currentElement=void 0,!1)}}class Jn{constructor(e,t,n,r,o){this.type=e,this.start=t,this.end=n,this.sequence=r,this.descriptor=o}toRecord(){return{type:this.type,sequence:this.sequence,descriptor:this.descriptor}}}class qn{constructor(e,t,n,r,o,i,s){this.id=qn.globalId++,this.type=e,this.assembly=r,this.typeName=o,this.parameterDefinitions=i,this.parameterValues=s,this.start=t,this.end=n}}qn.globalId=1;class Vn{constructor(){this.afterStartedCallbacks=[]}async importInitializersAsync(e,t){await Promise.all(e.map((e=>async function(e,n){const r=function(e){const t=document.baseURI;return t.endsWith("/")?`${t}${e}`:`${t}/${e}`}(n),o=await import(r);if(void 0===o)return;const{beforeStart:i,afterStarted:s}=o;return s&&e.afterStartedCallbacks.push(s),i?i(...t):void 0}(this,e))))}async invokeAfterStartedCallbacks(e){await C,await Promise.all(this.afterStartedCallbacks.map((t=>t(e))))}}let Kn,Xn=!1,Yn=!1;async function Gn(e){if(Yn)throw new Error("Blazor has already started.");Yn=!0;const t=function(e){const t={...Un,...e};return e&&e.reconnectionOptions&&(t.reconnectionOptions={...Un.reconnectionOptions,...e.reconnectionOptions}),t}(e),n=await async function(e){const t=await fetch("_blazor/initializers",{method:"GET",credentials:"include",cache:"no-cache"}),n=await t.json(),r=new Vn;return await r.importInitializersAsync(n,[e]),r}(t),r=new Rn(t.logLevel);Ne.reconnect=async e=>{if(Xn)return!1;const n=e||await Qn(t,r,s);return await s.reconnect(n)?(t.reconnectionHandler.onConnectionUp(),!0):(r.log(Tn.Information,"Reconnection attempt to the circuit was rejected by the server. This may indicate that the associated state is no longer available on the server."),!1)},Ne.defaultReconnectionHandler=new $n(r),t.reconnectionHandler=t.reconnectionHandler||Ne.defaultReconnectionHandler,r.log(Tn.Information,"Starting up Blazor server-side application.");const o=function(e,t){return function(e){const t=On(e,"server"),n=[];for(let e=0;e<t.length;e++){const r=t[e],o=new Jn(r.type,r.start,r.end,r.sequence,r.descriptor);n.push(o)}return n.sort(((e,t)=>e.sequence-t.sequence))}(e)}(document),i=Mn(document),s=new Pn(o,i||"");Ne._internal.navigationManager.listenForNavigationEvents(((e,t)=>Kn.send("OnLocationChanged",e,t))),Ne._internal.forceCloseConnection=()=>Kn.stop(),Ne._internal.sendJSDataStream=(e,t,n)=>function(e,t,n,r){setTimeout((async()=>{let o=5,i=(new Date).valueOf();try{const s=t instanceof Blob?t.size:t.byteLength;let a=0,c=0;for(;a<s;){const l=Math.min(r,s-a),h=await Ue(t,a,l);if(o--,o>1)await e.send("ReceiveJSDataChunk",n,c,h,null);else{if(!await e.invoke("ReceiveJSDataChunk",n,c,h,null))break;const t=(new Date).valueOf(),r=t-i;i=t,o=Math.max(1,Math.round(500/Math.max(1,r)))}a+=l,c++}}catch(t){await e.send("ReceiveJSDataChunk",n,-1,null,t.toString())}}),0)}(Kn,e,t,n);const a=await Qn(t,r,s);if(!await s.startCircuit(a))return void r.log(Tn.Error,"Failed to start the circuit.");let c=!1;const l=()=>{if(!c){const e=new FormData,t=s.circuitId;e.append("circuitId",t),c=navigator.sendBeacon("_blazor/disconnect",e)}};Ne.disconnect=l,window.addEventListener("unload",l,{capture:!1,once:!0}),r.log(Tn.Information,"Blazor server-side application started."),n.invokeAfterStartedCallbacks(Ne)}async function Qn(t,n,r){var o,i;const s=new hn;s.name="blazorpack";const a=(new Dt).withUrl("_blazor").withHubProtocol(s);t.configureSignalR(a);const c=a.build();c.on("JS.AttachComponent",((e,t)=>function(e,t,n,r){let o=de[0];o||(o=new se(0),de[0]=o),o.attachRootComponentToLogicalElement(n,t,!1)}(0,r.resolveElement(t),e))),c.on("JS.BeginInvokeJS",e.jsCallDispatcher.beginInvokeJSFromDotNet),c.on("JS.EndInvokeDotNet",e.jsCallDispatcher.endInvokeDotNetFromJS),c.on("JS.ReceiveByteArray",e.jsCallDispatcher.receiveByteArray),c.on("JS.BeginTransmitStream",(t=>{const n=new ReadableStream({start(e){c.stream("SendDotNetStreamToJS",t).subscribe({next:t=>e.enqueue(t),complete:()=>e.close(),error:t=>e.error(t)})}});e.jsCallDispatcher.supplyDotNetStream(t,n)}));const l=xn.getOrCreate(n);c.on("JS.RenderBatch",((e,t)=>{n.log(Tn.Debug,`Received render batch with id ${e} and ${t.byteLength} bytes.`),l.processBatch(e,t,c)})),c.onclose((e=>!Xn&&t.reconnectionHandler.onConnectionDown(t.reconnectionOptions,e))),c.on("JS.Error",(e=>{Xn=!0,Zn(c,e,n),dn()}));try{await c.start(),Kn=c}catch(e){if(Zn(c,e,n),"FailedToNegotiateWithServerError"===e.errorType)throw e;dn(),e.innerErrors&&(e.innerErrors.some((e=>"UnsupportedTransportError"===e.errorType&&e.transport===Ke.WebSockets))?n.log(Tn.Error,"Unable to connect, please ensure you are using an updated browser that supports WebSockets."):e.innerErrors.some((e=>"FailedToStartTransportError"===e.errorType&&e.transport===Ke.WebSockets))?n.log(Tn.Error,"Unable to connect, please ensure WebSockets are available. A VPN or proxy may be blocking the connection."):e.innerErrors.some((e=>"DisabledTransportError"===e.errorType&&e.transport===Ke.LongPolling))&&n.log(Tn.Error,"Unable to initiate a SignalR connection to the server. This might be because the server is not configured to support WebSockets. For additional details, visit https://aka.ms/blazor-server-websockets-error."))}return(null===(i=null===(o=c.connection)||void 0===o?void 0:o.features)||void 0===i?void 0:i.inherentKeepAlive)&&n.log(Tn.Warning,"Failed to connect via WebSockets, using the Long Polling fallback transport. This may be due to a VPN or proxy blocking the connection. To troubleshoot this, visit https://aka.ms/blazor-server-using-fallback-long-polling."),e.attachDispatcher({beginInvokeDotNetFromJS:(e,t,n,r,o)=>{c.send("BeginInvokeDotNetFromJS",e?e.toString():null,t,n,r||0,o)},endInvokeJSFromDotNet:(e,t,n)=>{c.send("EndInvokeJSFromDotNet",e,t,n)},sendByteArray:(e,t)=>{c.send("ReceiveByteArray",e,t)}}),c}function Zn(e,t,n){n.log(Tn.Error,t),e&&e.stop()}Ne.start=Gn,document&&document.currentScript&&"false"!==document.currentScript.getAttribute("autostart")&&Gn()})(); \ No newline at end of file
+(()=>{"use strict";var e,t,n,r={};r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),function(e){window.DotNet=e;const t=[],n=new Map,r=new Map,o="__jsObjectId",i="__byte[]";class s{constructor(e){this._jsObject=e,this._cachedFunctions=new Map}findFunction(e){const t=this._cachedFunctions.get(e);if(t)return t;let n,r=this._jsObject;if(e.split(".").forEach((t=>{if(!(t in r))throw new Error(`Could not find '${e}' ('${t}' was undefined).`);n=r,r=r[t]})),r instanceof Function)return r=r.bind(n),this._cachedFunctions.set(e,r),r;throw new Error(`The value '${e}' is not a function.`)}getWrappedObject(){return this._jsObject}}const a={},c={0:new s(window)};c[0]._cachedFunctions.set("import",(e=>("string"==typeof e&&e.startsWith("./")&&(e=document.baseURI+e.substr(2)),import(e))));let l,h=1,u=1,d=null;function p(e){t.push(e)}function f(e){if(e&&"object"==typeof e){c[u]=new s(e);const t={[o]:u};return u++,t}throw new Error(`Cannot create a JSObjectReference from the value '${e}'.`)}function g(e){let t=-1;if(e instanceof ArrayBuffer&&(e=new Uint8Array(e)),e instanceof Blob)t=e.size;else{if(!(e.buffer instanceof ArrayBuffer))throw new Error("Supplied value is not a typed array or blob.");if(void 0===e.byteLength)throw new Error(`Cannot create a JSStreamReference from the value '${e}' as it doesn't have a byteLength.`);t=e.byteLength}const n={__jsStreamReferenceLength:t};try{const t=f(e);n.__jsObjectId=t.__jsObjectId}catch(t){throw new Error(`Cannot create a JSStreamReference from the value '${e}'.`)}return n}function m(e){return e?JSON.parse(e,((e,n)=>t.reduce(((t,n)=>n(e,t)),n))):null}function y(e,t,n,r){const o=v();if(o.invokeDotNetFromJS){const i=D(r),s=o.invokeDotNetFromJS(e,t,n,i);return s?m(s):null}throw new Error("The current dispatcher does not support synchronous calls from JS to .NET. Use invokeMethodAsync instead.")}function w(e,t,n,r){if(e&&n)throw new Error(`For instance method calls, assemblyName should be null. Received '${e}'.`);const o=h++,i=new Promise(((e,t)=>{a[o]={resolve:e,reject:t}}));try{const i=D(r);v().beginInvokeDotNetFromJS(o,e,t,n,i)}catch(e){b(o,!1,e)}return i}function v(){if(null!==d)return d;throw new Error("No .NET call dispatcher has been set.")}function b(e,t,n){if(!a.hasOwnProperty(e))throw new Error(`There is no pending async call with ID ${e}.`);const r=a[e];delete a[e],t?r.resolve(n):r.reject(n)}function _(e){return e instanceof Error?`${e.message}\n${e.stack}`:e?e.toString():"null"}function E(e,t){const n=c[t];if(n)return n.findFunction(e);throw new Error(`JS object instance with ID ${t} does not exist (has it been disposed?).`)}function S(e){delete c[e]}e.attachDispatcher=function(e){d=e},e.attachReviver=p,e.invokeMethod=function(e,t,...n){return y(e,t,null,n)},e.invokeMethodAsync=function(e,t,...n){return w(e,t,null,n)},e.createJSObjectReference=f,e.createJSStreamReference=g,e.disposeJSObjectReference=function(e){const t=e&&e.__jsObjectId;"number"==typeof t&&S(t)},function(e){e[e.Default=0]="Default",e[e.JSObjectReference=1]="JSObjectReference",e[e.JSStreamReference=2]="JSStreamReference",e[e.JSVoidResult=3]="JSVoidResult"}(l=e.JSCallResultType||(e.JSCallResultType={})),e.jsCallDispatcher={findJSFunction:E,disposeJSObjectReferenceById:S,invokeJSFromDotNet:(e,t,n,r)=>{const o=T(E(e,r).apply(null,m(t)),n);return null==o?null:D(o)},beginInvokeJSFromDotNet:(e,t,n,r,o)=>{const i=new Promise((e=>{e(E(t,o).apply(null,m(n)))}));e&&i.then((t=>D([e,!0,T(t,r)]))).then((t=>v().endInvokeJSFromDotNet(e,!0,t)),(t=>v().endInvokeJSFromDotNet(e,!1,JSON.stringify([e,!1,_(t)]))))},endInvokeDotNetFromJS:(e,t,n)=>{const r=t?m(n):new Error(n);b(parseInt(e,10),t,r)},receiveByteArray:(e,t)=>{n.set(e,t)},supplyDotNetStream:(e,t)=>{if(r.has(e)){const n=r.get(e);r.delete(e),n.resolve(t)}else{const n=new k;n.resolve(t),r.set(e,n)}}};class C{constructor(e){this._id=e}invokeMethod(e,...t){return y(null,e,this._id,t)}invokeMethodAsync(e,...t){return w(null,e,this._id,t)}dispose(){w(null,"__Dispose",this._id,null).catch((e=>console.error(e)))}serializeAsArg(){return{__dotNetObject:this._id}}}e.DotNetObject=C,p((function(e,t){if(t&&"object"==typeof t){if(t.hasOwnProperty("__dotNetObject"))return new C(t.__dotNetObject);if(t.hasOwnProperty(o)){const e=t.__jsObjectId,n=c[e];if(n)return n.getWrappedObject();throw new Error(`JS object instance with Id '${e}' does not exist. It may have been disposed.`)}if(t.hasOwnProperty(i)){const e=t["__byte[]"],r=n.get(e);if(void 0===r)throw new Error(`Byte array index '${e}' does not exist.`);return n.delete(e),r}if(t.hasOwnProperty("__dotNetStream"))return new I(t.__dotNetStream)}return t}));class I{constructor(e){if(r.has(e))this._streamPromise=r.get(e).streamPromise,r.delete(e);else{const t=new k;r.set(e,t),this._streamPromise=t.streamPromise}}stream(){return this._streamPromise}async arrayBuffer(){return new Response(await this.stream()).arrayBuffer()}}class k{constructor(){this.streamPromise=new Promise(((e,t)=>{this.resolve=e,this.reject=t}))}}function T(e,t){switch(t){case l.Default:return e;case l.JSObjectReference:return f(e);case l.JSStreamReference:return g(e);case l.JSVoidResult:return null;default:throw new Error(`Invalid JS call result type '${t}'.`)}}let x=0;function D(e){return x=0,JSON.stringify(e,R)}function R(e,t){if(t instanceof C)return t.serializeAsArg();if(t instanceof Uint8Array){d.sendByteArray(x,t);const e={[i]:x};return x++,e}return t}}(e||(e={})),function(e){e[e.prependFrame=1]="prependFrame",e[e.removeFrame=2]="removeFrame",e[e.setAttribute=3]="setAttribute",e[e.removeAttribute=4]="removeAttribute",e[e.updateText=5]="updateText",e[e.stepIn=6]="stepIn",e[e.stepOut=7]="stepOut",e[e.updateMarkup=8]="updateMarkup",e[e.permutationListEntry=9]="permutationListEntry",e[e.permutationListEnd=10]="permutationListEnd"}(t||(t={})),function(e){e[e.element=1]="element",e[e.text=2]="text",e[e.attribute=3]="attribute",e[e.component=4]="component",e[e.region=5]="region",e[e.elementReferenceCapture=6]="elementReferenceCapture",e[e.markup=8]="markup"}(n||(n={}));class o{constructor(e,t){this.componentId=e,this.fieldValue=t}static fromEvent(e,t){const n=t.target;if(n instanceof Element){const t=function(e){return e instanceof HTMLInputElement?e.type&&"checkbox"===e.type.toLowerCase()?{value:e.checked}:{value:e.value}:e instanceof HTMLSelectElement||e instanceof HTMLTextAreaElement?{value:e.value}:null}(n);if(t)return new o(e,t.value)}return null}}const i=new Map,s=new Map,a=[];function c(e){return i.get(e)}function l(e){const t=i.get(e);return(null==t?void 0:t.browserEventName)||e}function h(e,t){e.forEach((e=>i.set(e,t)))}function u(e){const t=[];for(let n=0;n<e.length;n++){const r=e[n];t.push({identifier:r.identifier,clientX:r.clientX,clientY:r.clientY,screenX:r.screenX,screenY:r.screenY,pageX:r.pageX,pageY:r.pageY})}return t}function d(e){return{detail:e.detail,screenX:e.screenX,screenY:e.screenY,clientX:e.clientX,clientY:e.clientY,offsetX:e.offsetX,offsetY:e.offsetY,pageX:e.pageX,pageY:e.pageY,button:e.button,buttons:e.buttons,ctrlKey:e.ctrlKey,shiftKey:e.shiftKey,altKey:e.altKey,metaKey:e.metaKey,type:e.type}}h(["input","change"],{createEventArgs:function(e){const t=e.target;if(function(e){return-1!==p.indexOf(e.getAttribute("type"))}(t)){const e=function(e){const t=e.value,n=e.type;switch(n){case"date":case"month":case"week":return t;case"datetime-local":return 16===t.length?t+":00":t;case"time":return 5===t.length?t+":00":t}throw new Error(`Invalid element type '${n}'.`)}(t);return{value:e}}if(function(e){return e instanceof HTMLSelectElement&&"select-multiple"===e.type}(t)){const e=t;return{value:Array.from(e.options).filter((e=>e.selected)).map((e=>e.value))}}{const e=function(e){return!!e&&"INPUT"===e.tagName&&"checkbox"===e.getAttribute("type")}(t);return{value:e?!!t.checked:t.value}}}}),h(["copy","cut","paste"],{createEventArgs:e=>({type:e.type})}),h(["drag","dragend","dragenter","dragleave","dragover","dragstart","drop"],{createEventArgs:e=>{return{...d(t=e),dataTransfer:t.dataTransfer?{dropEffect:t.dataTransfer.dropEffect,effectAllowed:t.dataTransfer.effectAllowed,files:Array.from(t.dataTransfer.files).map((e=>e.name)),items:Array.from(t.dataTransfer.items).map((e=>({kind:e.kind,type:e.type}))),types:t.dataTransfer.types}:null};var t}}),h(["focus","blur","focusin","focusout"],{createEventArgs:e=>({type:e.type})}),h(["keydown","keyup","keypress"],{createEventArgs:e=>{return{key:(t=e).key,code:t.code,location:t.location,repeat:t.repeat,ctrlKey:t.ctrlKey,shiftKey:t.shiftKey,altKey:t.altKey,metaKey:t.metaKey,type:t.type};var t}}),h(["contextmenu","click","mouseover","mouseout","mousemove","mousedown","mouseup","mouseleave","mouseenter","dblclick"],{createEventArgs:e=>d(e)}),h(["error"],{createEventArgs:e=>{return{message:(t=e).message,filename:t.filename,lineno:t.lineno,colno:t.colno,type:t.type};var t}}),h(["loadstart","timeout","abort","load","loadend","progress"],{createEventArgs:e=>{return{lengthComputable:(t=e).lengthComputable,loaded:t.loaded,total:t.total,type:t.type};var t}}),h(["touchcancel","touchend","touchmove","touchenter","touchleave","touchstart"],{createEventArgs:e=>{return{detail:(t=e).detail,touches:u(t.touches),targetTouches:u(t.targetTouches),changedTouches:u(t.changedTouches),ctrlKey:t.ctrlKey,shiftKey:t.shiftKey,altKey:t.altKey,metaKey:t.metaKey,type:t.type};var t}}),h(["gotpointercapture","lostpointercapture","pointercancel","pointerdown","pointerenter","pointerleave","pointermove","pointerout","pointerover","pointerup"],{createEventArgs:e=>{return{...d(t=e),pointerId:t.pointerId,width:t.width,height:t.height,pressure:t.pressure,tiltX:t.tiltX,tiltY:t.tiltY,pointerType:t.pointerType,isPrimary:t.isPrimary};var t}}),h(["wheel","mousewheel"],{createEventArgs:e=>{return{...d(t=e),deltaX:t.deltaX,deltaY:t.deltaY,deltaZ:t.deltaZ,deltaMode:t.deltaMode};var t}}),h(["toggle"],{createEventArgs:()=>({})});const p=["date","datetime-local","month","time","week"],f=new Map;let g,m,y=0;const w={async add(e,t,n){if(!n)throw new Error("initialParameters must be an object, even if empty.");const r="__bl-dynamic-root:"+(++y).toString();f.set(r,e);const o=await _().invokeMethodAsync("AddRootComponent",t,r),i=new b(o,m[t]);return await i.setParameters(n),i}};class v{invoke(e){return this._callback(e)}setCallback(t){this._selfJSObjectReference||(this._selfJSObjectReference=e.createJSObjectReference(this)),this._callback=t}getJSObjectReference(){return this._selfJSObjectReference}dispose(){this._selfJSObjectReference&&e.disposeJSObjectReference(this._selfJSObjectReference)}}class b{constructor(e,t){this._jsEventCallbackWrappers=new Map,this._componentId=e;for(const e of t)"eventcallback"===e.type&&this._jsEventCallbackWrappers.set(e.name.toLowerCase(),new v)}setParameters(e){const t={},n=Object.entries(e||{}),r=n.length;for(const[e,r]of n){const n=this._jsEventCallbackWrappers.get(e.toLowerCase());n&&r?(n.setCallback(r),t[e]=n.getJSObjectReference()):t[e]=r}return _().invokeMethodAsync("SetRootComponentParameters",this._componentId,r,t)}async dispose(){if(null!==this._componentId){await _().invokeMethodAsync("RemoveRootComponent",this._componentId),this._componentId=null;for(const e of this._jsEventCallbackWrappers.values())e.dispose()}}}function _(){if(!g)throw new Error("Dynamic root components have not been enabled in this application.");return g}const E=new Map;let S;const C=new Promise((e=>{S=e}));function I(e,t,n){return T(e,t.eventHandlerId,(()=>k(e).invokeMethodAsync("DispatchEventAsync",t,n)))}function k(e){const t=E.get(e);if(!t)throw new Error(`No interop methods are registered for renderer ${e}`);return t}let T=(e,t,n)=>n();const x=N(["abort","blur","canplay","canplaythrough","change","cuechange","durationchange","emptied","ended","error","focus","load","loadeddata","loadedmetadata","loadend","loadstart","mouseenter","mouseleave","pause","play","playing","progress","ratechange","reset","scroll","seeked","seeking","stalled","submit","suspend","timeupdate","toggle","unload","volumechange","waiting","DOMNodeInsertedIntoDocument","DOMNodeRemovedFromDocument"]),D={submit:!0},R=N(["click","dblclick","mousedown","mousemove","mouseup"]);class P{constructor(e){this.browserRendererId=e,this.afterClickCallbacks=[];const t=++P.nextEventDelegatorId;this.eventsCollectionKey=`_blazorEvents_${t}`,this.eventInfoStore=new U(this.onGlobalEvent.bind(this))}setListener(e,t,n,r){const o=this.getEventHandlerInfosForElement(e,!0),i=o.getHandler(t);if(i)this.eventInfoStore.update(i.eventHandlerId,n);else{const i={element:e,eventName:t,eventHandlerId:n,renderingComponentId:r};this.eventInfoStore.add(i),o.setHandler(t,i)}}getHandler(e){return this.eventInfoStore.get(e)}removeListener(e){const t=this.eventInfoStore.remove(e);if(t){const e=t.element,n=this.getEventHandlerInfosForElement(e,!1);n&&n.removeHandler(t.eventName)}}notifyAfterClick(e){this.afterClickCallbacks.push(e),this.eventInfoStore.addGlobalListener("click")}setStopPropagation(e,t,n){this.getEventHandlerInfosForElement(e,!0).stopPropagation(t,n)}setPreventDefault(e,t,n){this.getEventHandlerInfosForElement(e,!0).preventDefault(t,n)}onGlobalEvent(e){if(!(e.target instanceof Element))return;this.dispatchGlobalEventToAllElements(e.type,e);const t=(n=e.type,s.get(n));var n;t&&t.forEach((t=>this.dispatchGlobalEventToAllElements(t,e))),"click"===e.type&&this.afterClickCallbacks.forEach((t=>t(e)))}dispatchGlobalEventToAllElements(e,t){const n=t.composedPath();let r=n.shift(),i=null,s=!1;const a=Object.prototype.hasOwnProperty.call(x,e);let l=!1;for(;r;){const d=r,p=this.getEventHandlerInfosForElement(d,!1);if(p){const n=p.getHandler(e);if(n&&(h=d,u=t.type,!((h instanceof HTMLButtonElement||h instanceof HTMLInputElement||h instanceof HTMLTextAreaElement||h instanceof HTMLSelectElement)&&Object.prototype.hasOwnProperty.call(R,u)&&h.disabled))){if(!s){const n=c(e);i=(null==n?void 0:n.createEventArgs)?n.createEventArgs(t):{},s=!0}Object.prototype.hasOwnProperty.call(D,t.type)&&t.preventDefault(),I(this.browserRendererId,{eventHandlerId:n.eventHandlerId,eventName:e,eventFieldInfo:o.fromEvent(n.renderingComponentId,t)},i)}p.stopPropagation(e)&&(l=!0),p.preventDefault(e)&&t.preventDefault()}r=a||l?void 0:n.shift()}var h,u}getEventHandlerInfosForElement(e,t){return Object.prototype.hasOwnProperty.call(e,this.eventsCollectionKey)?e[this.eventsCollectionKey]:t?e[this.eventsCollectionKey]=new A:null}}P.nextEventDelegatorId=0;class U{constructor(e){this.globalListener=e,this.infosByEventHandlerId={},this.countByEventName={},a.push(this.handleEventNameAliasAdded.bind(this))}add(e){if(this.infosByEventHandlerId[e.eventHandlerId])throw new Error(`Event ${e.eventHandlerId} is already tracked`);this.infosByEventHandlerId[e.eventHandlerId]=e,this.addGlobalListener(e.eventName)}get(e){return this.infosByEventHandlerId[e]}addGlobalListener(e){if(e=l(e),Object.prototype.hasOwnProperty.call(this.countByEventName,e))this.countByEventName[e]++;else{this.countByEventName[e]=1;const t=Object.prototype.hasOwnProperty.call(x,e);document.addEventListener(e,this.globalListener,t)}}update(e,t){if(Object.prototype.hasOwnProperty.call(this.infosByEventHandlerId,t))throw new Error(`Event ${t} is already tracked`);const n=this.infosByEventHandlerId[e];delete this.infosByEventHandlerId[e],n.eventHandlerId=t,this.infosByEventHandlerId[t]=n}remove(e){const t=this.infosByEventHandlerId[e];if(t){delete this.infosByEventHandlerId[e];const n=l(t.eventName);0==--this.countByEventName[n]&&(delete this.countByEventName[n],document.removeEventListener(n,this.globalListener))}return t}handleEventNameAliasAdded(e,t){if(Object.prototype.hasOwnProperty.call(this.countByEventName,e)){const n=this.countByEventName[e];delete this.countByEventName[e],document.removeEventListener(e,this.globalListener),this.addGlobalListener(t),this.countByEventName[t]+=n-1}}}class A{constructor(){this.handlers={},this.preventDefaultFlags=null,this.stopPropagationFlags=null}getHandler(e){return Object.prototype.hasOwnProperty.call(this.handlers,e)?this.handlers[e]:null}setHandler(e,t){this.handlers[e]=t}removeHandler(e){delete this.handlers[e]}preventDefault(e,t){return void 0!==t&&(this.preventDefaultFlags=this.preventDefaultFlags||{},this.preventDefaultFlags[e]=t),!!this.preventDefaultFlags&&this.preventDefaultFlags[e]}stopPropagation(e,t){return void 0!==t&&(this.stopPropagationFlags=this.stopPropagationFlags||{},this.stopPropagationFlags[e]=t),!!this.stopPropagationFlags&&this.stopPropagationFlags[e]}}function N(e){const t={};return e.forEach((e=>{t[e]=!0})),t}const $=G("_blazorLogicalChildren"),B=G("_blazorLogicalParent"),L=G("_blazorLogicalEnd");function M(e,t){if(e.childNodes.length>0&&!t)throw new Error("New logical elements must start empty, or allowExistingContents must be true");return $ in e||(e[$]=[]),e}function O(e,t){const n=document.createComment("!");return F(n,e,t),n}function F(e,t,n){const r=e;if(e instanceof Comment&&J(r)&&J(r).length>0)throw new Error("Not implemented: inserting non-empty logical container");if(H(r))throw new Error("Not implemented: moving existing logical children");const o=J(t);if(n<o.length){const t=o[n];t.parentNode.insertBefore(e,t),o.splice(n,0,r)}else X(e,t),o.push(r);r[B]=t,$ in r||(r[$]=[])}function j(e,t){const n=J(e).splice(t,1)[0];if(n instanceof Comment){const e=J(n);if(e)for(;e.length>0;)j(n,0)}const r=n;r.parentNode.removeChild(r)}function H(e){return e[B]||null}function W(e,t){return J(e)[t]}function z(e){const t=V(e);return"http://www.w3.org/2000/svg"===t.namespaceURI&&"foreignObject"!==t.tagName}function J(e){return e[$]}function q(e,t){const n=J(e);t.forEach((e=>{e.moveRangeStart=n[e.fromSiblingIndex],e.moveRangeEnd=Y(e.moveRangeStart)})),t.forEach((t=>{const r=document.createComment("marker");t.moveToBeforeMarker=r;const o=n[t.toSiblingIndex+1];o?o.parentNode.insertBefore(r,o):X(r,e)})),t.forEach((e=>{const t=e.moveToBeforeMarker,n=t.parentNode,r=e.moveRangeStart,o=e.moveRangeEnd;let i=r;for(;i;){const e=i.nextSibling;if(n.insertBefore(i,t),i===o)break;i=e}n.removeChild(t)})),t.forEach((e=>{n[e.toSiblingIndex]=e.moveRangeStart}))}function V(e){if(e instanceof Element||e instanceof DocumentFragment)return e;if(e instanceof Comment)return e.parentNode;throw new Error("Not a valid logical element")}function K(e){const t=J(H(e));return t[Array.prototype.indexOf.call(t,e)+1]||null}function X(e,t){if(t instanceof Element||t instanceof DocumentFragment)t.appendChild(e);else{if(!(t instanceof Comment))throw new Error(`Cannot append node because the parent is not a valid logical element. Parent: ${t}`);{const n=K(t);n?n.parentNode.insertBefore(e,n):X(e,H(t))}}}function Y(e){if(e instanceof Element||e instanceof DocumentFragment)return e;const t=K(e);if(t)return t.previousSibling;{const t=H(e);return t instanceof Element||t instanceof DocumentFragment?t.lastChild:Y(t)}}function G(e){return"function"==typeof Symbol?Symbol():e}function Q(e){return`_bl_${e}`}e.attachReviver(((e,t)=>t&&"object"==typeof t&&Object.prototype.hasOwnProperty.call(t,"__internalId")&&"string"==typeof t.__internalId?function(e){const t=`[${Q(e)}]`;return document.querySelector(t)}(t.__internalId):t));const Z="_blazorDeferredValue",ee=document.createElement("template"),te=document.createElementNS("http://www.w3.org/2000/svg","g"),ne={},re="__internal_",oe="preventDefault_",ie="stopPropagation_";class se{constructor(e){this.rootComponentIds=new Set,this.childComponentLocations={},this.eventDelegator=new P(e),this.eventDelegator.notifyAfterClick((e=>{if(!fe)return;if(0!==e.button||function(e){return e.ctrlKey||e.shiftKey||e.altKey||e.metaKey}(e))return;if(e.defaultPrevented)return;const t=function(e){const t=!window._blazorDisableComposedPath&&e.composedPath&&e.composedPath();if(t){for(let e=0;e<t.length;e++){const n=t[e];if(n instanceof Element&&"A"===n.tagName)return n}return null}return Se(e.target,"A")}(e);if(t&&function(e){const t=e.getAttribute("target");return(!t||"_self"===t)&&e.hasAttribute("href")&&!e.hasAttribute("download")}(t)){const n=Ee(t.getAttribute("href"));Ce(n)&&(e.preventDefault(),ve(n,!0,!1))}}))}attachRootComponentToLogicalElement(e,t,n){this.attachComponentToElement(e,t),this.rootComponentIds.add(e),n||(ne[e]=t)}updateComponent(e,t,n,r){var o;const i=this.childComponentLocations[t];if(!i)throw new Error(`No element is currently associated with component ${t}`);const s=ne[t];if(s){const e=function(e){return e[L]||null}(s);delete ne[t],e?function(e,t){const n=H(e);if(!n)throw new Error("Can't clear between nodes. The start node does not have a logical parent.");const r=J(n),o=r.indexOf(e)+1,i=r.indexOf(t);for(let e=o;e<=i;e++)j(n,o);e.textContent="!"}(s,e):function(e){let t;for(;t=e.firstChild;)e.removeChild(t)}(s)}const a=null===(o=V(i))||void 0===o?void 0:o.getRootNode(),c=a&&a.activeElement;this.applyEdits(e,t,i,0,n,r),c instanceof HTMLElement&&a&&a.activeElement!==c&&c.focus()}disposeComponent(e){this.rootComponentIds.delete(e)&&function(e){const t=J(e);for(;t.length;)j(e,0)}(this.childComponentLocations[e]),delete this.childComponentLocations[e]}disposeEventHandler(e){this.eventDelegator.removeListener(e)}attachComponentToElement(e,t){this.childComponentLocations[e]=t}applyEdits(e,n,r,o,i,s){let a,c=0,l=o;const h=e.arrayBuilderSegmentReader,u=e.editReader,d=e.frameReader,p=h.values(i),f=h.offset(i),g=f+h.count(i);for(let i=f;i<g;i++){const h=e.diffReader.editsEntry(p,i),f=u.editType(h);switch(f){case t.prependFrame:{const t=u.newTreeIndex(h),o=e.referenceFramesEntry(s,t),i=u.siblingIndex(h);this.insertFrame(e,n,r,l+i,s,o,t);break}case t.removeFrame:j(r,l+u.siblingIndex(h));break;case t.setAttribute:{const t=u.newTreeIndex(h),o=e.referenceFramesEntry(s,t),i=W(r,l+u.siblingIndex(h));if(!(i instanceof Element))throw new Error("Cannot set attribute on non-element child");this.applyAttribute(e,n,i,o);break}case t.removeAttribute:{const t=W(r,l+u.siblingIndex(h));if(!(t instanceof HTMLElement))throw new Error("Cannot remove attribute from non-element child");{const n=u.removedAttributeName(h);this.tryApplySpecialProperty(e,t,n,null)||t.removeAttribute(n)}break}case t.updateText:{const t=u.newTreeIndex(h),n=e.referenceFramesEntry(s,t),o=W(r,l+u.siblingIndex(h));if(!(o instanceof Text))throw new Error("Cannot set text content on non-text child");o.textContent=d.textContent(n);break}case t.updateMarkup:{const t=u.newTreeIndex(h),n=e.referenceFramesEntry(s,t),o=u.siblingIndex(h);j(r,l+o),this.insertMarkup(e,r,l+o,n);break}case t.stepIn:r=W(r,l+u.siblingIndex(h)),c++,l=0;break;case t.stepOut:r=H(r),c--,l=0===c?o:0;break;case t.permutationListEntry:a=a||[],a.push({fromSiblingIndex:l+u.siblingIndex(h),toSiblingIndex:l+u.moveToSiblingIndex(h)});break;case t.permutationListEnd:q(r,a),a=void 0;break;default:throw new Error(`Unknown edit type: ${f}`)}}}insertFrame(e,t,r,o,i,s,a){const c=e.frameReader,l=c.frameType(s);switch(l){case n.element:return this.insertElement(e,t,r,o,i,s,a),1;case n.text:return this.insertText(e,r,o,s),1;case n.attribute:throw new Error("Attribute frames should only be present as leading children of element frames.");case n.component:return this.insertComponent(e,r,o,s),1;case n.region:return this.insertFrameRange(e,t,r,o,i,a+1,a+c.subtreeLength(s));case n.elementReferenceCapture:if(r instanceof Element)return h=r,u=c.elementReferenceCaptureId(s),h.setAttribute(Q(u),""),0;throw new Error("Reference capture frames can only be children of element frames.");case n.markup:return this.insertMarkup(e,r,o,s),1;default:throw new Error(`Unknown frame type: ${l}`)}var h,u}insertElement(e,t,r,o,i,s,a){const c=e.frameReader,l=c.elementName(s),h="svg"===l||z(r)?document.createElementNS("http://www.w3.org/2000/svg",l):document.createElement(l),u=M(h);let d=!1;const p=a+c.subtreeLength(s);for(let s=a+1;s<p;s++){const a=e.referenceFramesEntry(i,s);if(c.frameType(a)!==n.attribute){F(h,r,o),d=!0,this.insertFrameRange(e,t,u,0,i,s,p);break}this.applyAttribute(e,t,h,a)}d||F(h,r,o),h instanceof HTMLOptionElement?this.trySetSelectValueFromOptionElement(h):Z in h&&ue(h,h._blazorDeferredValue)}trySetSelectValueFromOptionElement(e){const t=this.findClosestAncestorSelectElement(e);if(!function(e){return!!e&&Z in e}(t))return!1;if(le(t))e.selected=-1!==t._blazorDeferredValue.indexOf(e.value);else{if(t._blazorDeferredValue!==e.value)return!1;he(t,e.value),delete t._blazorDeferredValue}return!0}insertComponent(e,t,n,r){const o=O(t,n),i=e.frameReader.componentId(r);this.attachComponentToElement(i,o)}insertText(e,t,n,r){const o=e.frameReader.textContent(r);F(document.createTextNode(o),t,n)}insertMarkup(e,t,n,r){const o=O(t,n),i=(s=e.frameReader.markupContent(r),z(t)?(te.innerHTML=s||" ",te):(ee.innerHTML=s||" ",ee.content));var s;let a=0;for(;i.firstChild;)F(i.firstChild,o,a++)}applyAttribute(e,t,n,r){const o=e.frameReader,i=o.attributeName(r),s=o.attributeEventHandlerId(r);if(s){const e=ce(i);this.eventDelegator.setListener(n,e,s,t)}else this.tryApplySpecialProperty(e,n,i,r)||n.setAttribute(i,o.attributeValue(r))}tryApplySpecialProperty(e,t,n,r){switch(n){case"value":return this.tryApplyValueProperty(e,t,r);case"checked":return this.tryApplyCheckedProperty(e,t,r);default:return!!n.startsWith(re)&&(this.applyInternalAttribute(e,t,n.substring(re.length),r),!0)}}applyInternalAttribute(e,t,n,r){const o=r?e.frameReader.attributeValue(r):null;if(n.startsWith(ie)){const e=ce(n.substring(ie.length));this.eventDelegator.setStopPropagation(t,e,null!==o)}else{if(!n.startsWith(oe))throw new Error(`Unsupported internal attribute '${n}'`);{const e=ce(n.substring(oe.length));this.eventDelegator.setPreventDefault(t,e,null!==o)}}}tryApplyValueProperty(e,t,n){const r=e.frameReader;let o=n?r.attributeValue(n):null;switch(o&&"INPUT"===t.tagName&&(o=function(e,t){switch(t.getAttribute("type")){case"time":return 8!==e.length||!e.endsWith("00")&&t.hasAttribute("step")?e:e.substring(0,5);case"datetime-local":return 19!==e.length||!e.endsWith("00")&&t.hasAttribute("step")?e:e.substring(0,16);default:return e}}(o,t)),t.tagName){case"INPUT":case"SELECT":case"TEXTAREA":return o&&t instanceof HTMLSelectElement&&le(t)&&(o=JSON.parse(o)),ue(t,o),t._blazorDeferredValue=o,!0;case"OPTION":return o||""===o?t.setAttribute("value",o):t.removeAttribute("value"),this.trySetSelectValueFromOptionElement(t),!0;default:return!1}}tryApplyCheckedProperty(e,t,n){if("INPUT"===t.tagName){const r=n?e.frameReader.attributeValue(n):null;return t.checked=null!==r,!0}return!1}findClosestAncestorSelectElement(e){for(;e;){if(e instanceof HTMLSelectElement)return e;e=e.parentElement}return null}insertFrameRange(e,t,n,r,o,i,s){const a=r;for(let a=i;a<s;a++){const i=e.referenceFramesEntry(o,a);r+=this.insertFrame(e,t,n,r,o,i,a),a+=ae(e,i)}return r-a}}function ae(e,t){const r=e.frameReader;switch(r.frameType(t)){case n.component:case n.element:case n.region:return r.subtreeLength(t)-1;default:return 0}}function ce(e){if(e.startsWith("on"))return e.substring(2);throw new Error(`Attribute should be an event name, but doesn't start with 'on'. Value: '${e}'`)}function le(e){return"select-multiple"===e.type}function he(e,t){e.value=t||""}function ue(e,t){e instanceof HTMLSelectElement?le(e)?function(e,t){t||(t=[]);for(let n=0;n<e.options.length;n++)e.options[n].selected=-1!==t.indexOf(e.options[n].value)}(e,t):he(e,t):e.value=t}const de={};let pe=!1,fe=!1,ge=!1,me=null;const ye={listenForNavigationEvents:function(e){me=e,ge||(ge=!0,window.addEventListener("popstate",(()=>be(!1))))},enableNavigationInterception:function(){fe=!0},navigateTo:we,getBaseURI:()=>document.baseURI,getLocationHref:()=>location.href};function we(e,t,n=!1){const r=Ee(e),o=t instanceof Object?t:{forceLoad:t,replaceHistoryEntry:n};!o.forceLoad&&Ce(r)?ve(r,!1,o.replaceHistoryEntry):function(e,t){if(location.href===e){const t=e+"?";history.replaceState(null,"",t),location.replace(e)}else t?location.replace(e):location.href=e}(e,o.replaceHistoryEntry)}function ve(e,t,n){pe=!0,n?history.replaceState(null,"",e):history.pushState(null,"",e),be(t)}async function be(e){me&&await me(location.href,e)}let _e;function Ee(e){return _e=_e||document.createElement("a"),_e.href=e,_e.href}function Se(e,t){return e?e.tagName===t?e:Se(e.parentElement,t):null}function Ce(e){const t=(n=document.baseURI).substring(0,n.lastIndexOf("/"));var n;const r=e.charAt(t.length);return e.startsWith(t)&&(""===r||"/"===r||"?"===r||"#"===r)}const Ie={focus:function(e,t){if(e instanceof HTMLElement)e.focus({preventScroll:t});else{if(!(e instanceof SVGElement))throw new Error("Unable to focus an invalid element.");if(!e.hasAttribute("tabindex"))throw new Error("Unable to focus an SVG element that does not have a tabindex.");e.focus({preventScroll:t})}},focusBySelector:function(e){const t=document.querySelector(e);t&&(t.hasAttribute("tabindex")||(t.tabIndex=-1),t.focus())}},ke={init:function(e,t,n,r=50){const o=xe(t);(o||document.documentElement).style.overflowAnchor="none";const i=document.createRange();h(n.parentElement)&&(t.style.display="table-row",n.style.display="table-row");const s=new IntersectionObserver((function(r){r.forEach((r=>{var o;if(!r.isIntersecting)return;i.setStartAfter(t),i.setEndBefore(n);const s=i.getBoundingClientRect().height,a=null===(o=r.rootBounds)||void 0===o?void 0:o.height;r.target===t?e.invokeMethodAsync("OnSpacerBeforeVisible",r.intersectionRect.top-r.boundingClientRect.top,s,a):r.target===n&&n.offsetHeight>0&&e.invokeMethodAsync("OnSpacerAfterVisible",r.boundingClientRect.bottom-r.intersectionRect.bottom,s,a)}))}),{root:o,rootMargin:`${r}px`});s.observe(t),s.observe(n);const a=l(t),c=l(n);function l(e){const t={attributes:!0},n=new MutationObserver(((n,r)=>{h(e.parentElement)&&(r.disconnect(),e.style.display="table-row",r.observe(e,t)),s.unobserve(e),s.observe(e)}));return n.observe(e,t),n}function h(e){return null!==e&&(e instanceof HTMLTableElement&&""===e.style.display||"table"===e.style.display||e instanceof HTMLTableSectionElement&&""===e.style.display||"table-row-group"===e.style.display)}Te[e._id]={intersectionObserver:s,mutationObserverBefore:a,mutationObserverAfter:c}},dispose:function(e){const t=Te[e._id];t&&(t.intersectionObserver.disconnect(),t.mutationObserverBefore.disconnect(),t.mutationObserverAfter.disconnect(),e.dispose(),delete Te[e._id])}},Te={};function xe(e){return e&&e!==document.body&&e!==document.documentElement?"visible"!==getComputedStyle(e).overflowY?e:xe(e.parentElement):null}const De={getAndRemoveExistingTitle:function(){var e;const t=document.head?document.head.getElementsByTagName("title"):[];if(0===t.length)return null;let n=null;for(let r=t.length-1;r>=0;r--){const o=t[r],i=o.previousSibling;i instanceof Comment&&null!==H(i)||(null===n&&(n=o.textContent),null===(e=o.parentNode)||void 0===e||e.removeChild(o))}return n}},Re={init:function(e,t){t._blazorInputFileNextFileId=0,t.addEventListener("click",(function(){t.value=""})),t.addEventListener("change",(function(){t._blazorFilesById={};const n=Array.prototype.map.call(t.files,(function(e){const n={id:++t._blazorInputFileNextFileId,lastModified:new Date(e.lastModified).toISOString(),name:e.name,size:e.size,contentType:e.type,readPromise:void 0,arrayBuffer:void 0,blob:e};return t._blazorFilesById[n.id]=n,n}));e.invokeMethodAsync("NotifyChange",n)}))},toImageFile:async function(e,t,n,r,o){const i=Pe(e,t),s=await new Promise((function(e){const t=new Image;t.onload=function(){URL.revokeObjectURL(t.src),e(t)},t.onerror=function(){t.onerror=null,URL.revokeObjectURL(t.src)},t.src=URL.createObjectURL(i.blob)})),a=await new Promise((function(e){var t;const i=Math.min(1,r/s.width),a=Math.min(1,o/s.height),c=Math.min(i,a),l=document.createElement("canvas");l.width=Math.round(s.width*c),l.height=Math.round(s.height*c),null===(t=l.getContext("2d"))||void 0===t||t.drawImage(s,0,0,l.width,l.height),l.toBlob(e,n)})),c={id:++e._blazorInputFileNextFileId,lastModified:i.lastModified,name:i.name,size:(null==a?void 0:a.size)||0,contentType:n,blob:a||i.blob};return e._blazorFilesById[c.id]=c,c},readFileData:async function(e,t){return Pe(e,t).blob}};function Pe(e,t){const n=e._blazorFilesById[t];if(!n)throw new Error(`There is no file with ID ${t}. The file list may have changed. See https://aka.ms/aspnet/blazor-input-file-multiple-selections.`);return n}async function Ue(e,t,n){return e instanceof Blob?await async function(e,t,n){const r=e.slice(t,t+n),o=await r.arrayBuffer();return new Uint8Array(o)}(e,t,n):function(e,t,n){return new Uint8Array(e.buffer,e.byteOffset+t,n)}(e,t,n)}const Ae=new Map,Ne={navigateTo:we,registerCustomEventType:function(e,t){if(!t)throw new Error("The options parameter is required.");if(i.has(e))throw new Error(`The event '${e}' is already registered.`);if(t.browserEventName){const n=s.get(t.browserEventName);n?n.push(e):s.set(t.browserEventName,[e]),a.forEach((n=>n(e,t.browserEventName)))}i.set(e,t)},rootComponents:w,_internal:{navigationManager:ye,domWrapper:Ie,Virtualize:ke,PageTitle:De,InputFile:Re,getJSDataStreamChunk:Ue,receiveDotNetDataStream:function(t,n,r,o){let i=Ae.get(t);if(!i){const n=new ReadableStream({start(e){Ae.set(t,e),i=e}});e.jsCallDispatcher.supplyDotNetStream(t,n)}o?(i.error(o),Ae.delete(t)):0===r?(i.close(),Ae.delete(t)):i.enqueue(n.length===r?n:n.subarray(0,r))},attachWebRendererInterop:function(t,n,r,o){if(E.has(t))throw new Error(`Interop methods are already registered for renderer ${t}`);E.set(t,n),Object.keys(r).length>0&&function(t,n,r){if(g)throw new Error("Dynamic root components have already been enabled.");g=t,m=n;for(const[t,o]of Object.entries(r)){const r=e.jsCallDispatcher.findJSFunction(t,0);for(const e of o)r(e,n[e])}}(k(t),r,o),S()}}};window.Blazor=Ne;const $e=[0,2e3,1e4,3e4,null];class Be{constructor(e){this._retryDelays=void 0!==e?[...e,null]:$e}nextRetryDelayInMilliseconds(e){return this._retryDelays[e.previousRetryCount]}}class Le extends Error{constructor(e,t){const n=new.target.prototype;super(`${e}: Status code '${t}'`),this.statusCode=t,this.__proto__=n}}class Me extends Error{constructor(e="A timeout occurred."){const t=new.target.prototype;super(e),this.__proto__=t}}class Oe extends Error{constructor(e="An abort occurred."){const t=new.target.prototype;super(e),this.__proto__=t}}class Fe extends Error{constructor(e,t){const n=new.target.prototype;super(e),this.transport=t,this.errorType="UnsupportedTransportError",this.__proto__=n}}class je extends Error{constructor(e,t){const n=new.target.prototype;super(e),this.transport=t,this.errorType="DisabledTransportError",this.__proto__=n}}class He extends Error{constructor(e,t){const n=new.target.prototype;super(e),this.transport=t,this.errorType="FailedToStartTransportError",this.__proto__=n}}class We extends Error{constructor(e){const t=new.target.prototype;super(e),this.errorType="FailedToNegotiateWithServerError",this.__proto__=t}}class ze extends Error{constructor(e,t){const n=new.target.prototype;super(e),this.innerErrors=t,this.__proto__=n}}class Je{constructor(e,t,n){this.statusCode=e,this.statusText=t,this.content=n}}class qe{get(e,t){return this.send({...t,method:"GET",url:e})}post(e,t){return this.send({...t,method:"POST",url:e})}delete(e,t){return this.send({...t,method:"DELETE",url:e})}getCookieString(e){return""}}var Ve,Ke,Xe,Ye,Ge;!function(e){e[e.Trace=0]="Trace",e[e.Debug=1]="Debug",e[e.Information=2]="Information",e[e.Warning=3]="Warning",e[e.Error=4]="Error",e[e.Critical=5]="Critical",e[e.None=6]="None"}(Ve||(Ve={}));class Qe{constructor(){}log(e,t){}}Qe.instance=new Qe;class Ze{static isRequired(e,t){if(null==e)throw new Error(`The '${t}' argument is required.`)}static isNotEmpty(e,t){if(!e||e.match(/^\s*$/))throw new Error(`The '${t}' argument should not be empty.`)}static isIn(e,t,n){if(!(e in t))throw new Error(`Unknown ${n} value: ${e}.`)}}class et{static get isBrowser(){return"object"==typeof window&&"object"==typeof window.document}static get isWebWorker(){return"object"==typeof self&&"importScripts"in self}static get isReactNative(){return"object"==typeof window&&void 0===window.document}static get isNode(){return!this.isBrowser&&!this.isWebWorker&&!this.isReactNative}}function tt(e,t){let n="";return nt(e)?(n=`Binary data of length ${e.byteLength}`,t&&(n+=`. Content: '${function(e){const t=new Uint8Array(e);let n="";return t.forEach((e=>{n+=`0x${e<16?"0":""}${e.toString(16)} `})),n.substr(0,n.length-1)}(e)}'`)):"string"==typeof e&&(n=`String data of length ${e.length}`,t&&(n+=`. Content: '${e}'`)),n}function nt(e){return e&&"undefined"!=typeof ArrayBuffer&&(e instanceof ArrayBuffer||e.constructor&&"ArrayBuffer"===e.constructor.name)}async function rt(e,t,n,r,o,i,s){let a={};if(o){const e=await o();e&&(a={Authorization:`Bearer ${e}`})}const[c,l]=st();a[c]=l,e.log(Ve.Trace,`(${t} transport) sending data. ${tt(i,s.logMessageContent)}.`);const h=nt(i)?"arraybuffer":"text",u=await n.post(r,{content:i,headers:{...a,...s.headers},responseType:h,timeout:s.timeout,withCredentials:s.withCredentials});e.log(Ve.Trace,`(${t} transport) request complete. Response status: ${u.statusCode}.`)}class ot{constructor(e,t){this._subject=e,this._observer=t}dispose(){const e=this._subject.observers.indexOf(this._observer);e>-1&&this._subject.observers.splice(e,1),0===this._subject.observers.length&&this._subject.cancelCallback&&this._subject.cancelCallback().catch((e=>{}))}}class it{constructor(e){this._minLevel=e,this.out=console}log(e,t){if(e>=this._minLevel){const n=`[${(new Date).toISOString()}] ${Ve[e]}: ${t}`;switch(e){case Ve.Critical:case Ve.Error:this.out.error(n);break;case Ve.Warning:this.out.warn(n);break;case Ve.Information:this.out.info(n);break;default:this.out.log(n)}}}}function st(){let e="X-SignalR-User-Agent";return et.isNode&&(e="User-Agent"),[e,at("0.0.0-DEV_BUILD",ct(),et.isNode?"NodeJS":"Browser",lt())]}function at(e,t,n,r){let o="Microsoft SignalR/";const i=e.split(".");return o+=`${i[0]}.${i[1]}`,o+=` (${e}; `,o+=t&&""!==t?`${t}; `:"Unknown OS; ",o+=`${n}`,o+=r?`; ${r}`:"; Unknown Runtime Version",o+=")",o}function ct(){if(!et.isNode)return"";switch(process.platform){case"win32":return"Windows NT";case"darwin":return"macOS";case"linux":return"Linux";default:return process.platform}}function lt(){if(et.isNode)return process.versions.node}function ht(e){return e.stack?e.stack:e.message?e.message:`${e}`}class ut extends qe{constructor(e){if(super(),this._logger=e,"undefined"==typeof fetch){const e=require;this._jar=new(e("tough-cookie").CookieJar),this._fetchType=e("node-fetch"),this._fetchType=e("fetch-cookie")(this._fetchType,this._jar)}else this._fetchType=fetch.bind(function(){if("undefined"!=typeof globalThis)return globalThis;if("undefined"!=typeof self)return self;if("undefined"!=typeof window)return window;if(void 0!==r.g)return r.g;throw new Error("could not find global")}());if("undefined"==typeof AbortController){const e=require;this._abortControllerType=e("abort-controller")}else this._abortControllerType=AbortController}async send(e){if(e.abortSignal&&e.abortSignal.aborted)throw new Oe;if(!e.method)throw new Error("No method defined.");if(!e.url)throw new Error("No url defined.");const t=new this._abortControllerType;let n;e.abortSignal&&(e.abortSignal.onabort=()=>{t.abort(),n=new Oe});let r,o=null;if(e.timeout){const r=e.timeout;o=setTimeout((()=>{t.abort(),this._logger.log(Ve.Warning,"Timeout from HTTP request."),n=new Me}),r)}""===e.content&&(e.content=void 0),e.content&&(e.headers=e.headers||{},nt(e.content)?e.headers["Content-Type"]="application/octet-stream":e.headers["Content-Type"]="text/plain;charset=UTF-8");try{r=await this._fetchType(e.url,{body:e.content,cache:"no-cache",credentials:!0===e.withCredentials?"include":"same-origin",headers:{"X-Requested-With":"XMLHttpRequest",...e.headers},method:e.method,mode:"cors",redirect:"follow",signal:t.signal})}catch(e){if(n)throw n;throw this._logger.log(Ve.Warning,`Error from HTTP request. ${e}.`),e}finally{o&&clearTimeout(o),e.abortSignal&&(e.abortSignal.onabort=null)}if(!r.ok){const e=await dt(r,"text");throw new Le(e||r.statusText,r.status)}const i=dt(r,e.responseType),s=await i;return new Je(r.status,r.statusText,s)}getCookieString(e){return""}}function dt(e,t){let n;switch(t){case"arraybuffer":n=e.arrayBuffer();break;case"text":default:n=e.text();break;case"blob":case"document":case"json":throw new Error(`${t} is not supported.`)}return n}class pt extends qe{constructor(e){super(),this._logger=e}send(e){return e.abortSignal&&e.abortSignal.aborted?Promise.reject(new Oe):e.method?e.url?new Promise(((t,n)=>{const r=new XMLHttpRequest;r.open(e.method,e.url,!0),r.withCredentials=void 0===e.withCredentials||e.withCredentials,r.setRequestHeader("X-Requested-With","XMLHttpRequest"),""===e.content&&(e.content=void 0),e.content&&(nt(e.content)?r.setRequestHeader("Content-Type","application/octet-stream"):r.setRequestHeader("Content-Type","text/plain;charset=UTF-8"));const o=e.headers;o&&Object.keys(o).forEach((e=>{r.setRequestHeader(e,o[e])})),e.responseType&&(r.responseType=e.responseType),e.abortSignal&&(e.abortSignal.onabort=()=>{r.abort(),n(new Oe)}),e.timeout&&(r.timeout=e.timeout),r.onload=()=>{e.abortSignal&&(e.abortSignal.onabort=null),r.status>=200&&r.status<300?t(new Je(r.status,r.statusText,r.response||r.responseText)):n(new Le(r.response||r.responseText||r.statusText,r.status))},r.onerror=()=>{this._logger.log(Ve.Warning,`Error from HTTP request. ${r.status}: ${r.statusText}.`),n(new Le(r.statusText,r.status))},r.ontimeout=()=>{this._logger.log(Ve.Warning,"Timeout from HTTP request."),n(new Me)},r.send(e.content)})):Promise.reject(new Error("No url defined.")):Promise.reject(new Error("No method defined."))}}class ft extends qe{constructor(e){if(super(),"undefined"!=typeof fetch)this._httpClient=new ut(e);else{if("undefined"==typeof XMLHttpRequest)throw new Error("No usable HttpClient found.");this._httpClient=new pt(e)}}send(e){return e.abortSignal&&e.abortSignal.aborted?Promise.reject(new Oe):e.method?e.url?this._httpClient.send(e):Promise.reject(new Error("No url defined.")):Promise.reject(new Error("No method defined."))}getCookieString(e){return this._httpClient.getCookieString(e)}}class gt{}gt.Authorization="Authorization",gt.Cookie="Cookie",function(e){e[e.None=0]="None",e[e.WebSockets=1]="WebSockets",e[e.ServerSentEvents=2]="ServerSentEvents",e[e.LongPolling=4]="LongPolling"}(Ke||(Ke={})),function(e){e[e.Text=1]="Text",e[e.Binary=2]="Binary"}(Xe||(Xe={}));class mt{constructor(){this._isAborted=!1,this.onabort=null}abort(){this._isAborted||(this._isAborted=!0,this.onabort&&this.onabort())}get signal(){return this}get aborted(){return this._isAborted}}class yt{constructor(e,t,n,r){this._httpClient=e,this._accessTokenFactory=t,this._logger=n,this._pollAbort=new mt,this._options=r,this._running=!1,this.onreceive=null,this.onclose=null}get pollAborted(){return this._pollAbort.aborted}async connect(e,t){if(Ze.isRequired(e,"url"),Ze.isRequired(t,"transferFormat"),Ze.isIn(t,Xe,"transferFormat"),this._url=e,this._logger.log(Ve.Trace,"(LongPolling transport) Connecting."),t===Xe.Binary&&"undefined"!=typeof XMLHttpRequest&&"string"!=typeof(new XMLHttpRequest).responseType)throw new Error("Binary protocols over XmlHttpRequest not implementing advanced features are not supported.");const[n,r]=st(),o={[n]:r,...this._options.headers},i={abortSignal:this._pollAbort.signal,headers:o,timeout:1e5,withCredentials:this._options.withCredentials};t===Xe.Binary&&(i.responseType="arraybuffer"),await this._updateHeaderToken(i);const s=`${e}&_=${Date.now()}`;this._logger.log(Ve.Trace,`(LongPolling transport) polling: ${s}.`);const a=await this._httpClient.get(s,i);200!==a.statusCode?(this._logger.log(Ve.Error,`(LongPolling transport) Unexpected response code: ${a.statusCode}.`),this._closeError=new Le(a.statusText||"",a.statusCode),this._running=!1):this._running=!0,this._receiving=this._poll(this._url,i)}async _updateHeaderToken(e){if(e.headers||(e.headers={}),this._accessTokenFactory){const t=await this._accessTokenFactory();t?e.headers[gt.Authorization]=`Bearer ${t}`:e.headers[gt.Authorization]&&delete e.headers[gt.Authorization]}}async _poll(e,t){try{for(;this._running;){await this._updateHeaderToken(t);try{const n=`${e}&_=${Date.now()}`;this._logger.log(Ve.Trace,`(LongPolling transport) polling: ${n}.`);const r=await this._httpClient.get(n,t);204===r.statusCode?(this._logger.log(Ve.Information,"(LongPolling transport) Poll terminated by server."),this._running=!1):200!==r.statusCode?(this._logger.log(Ve.Error,`(LongPolling transport) Unexpected response code: ${r.statusCode}.`),this._closeError=new Le(r.statusText||"",r.statusCode),this._running=!1):r.content?(this._logger.log(Ve.Trace,`(LongPolling transport) data received. ${tt(r.content,this._options.logMessageContent)}.`),this.onreceive&&this.onreceive(r.content)):this._logger.log(Ve.Trace,"(LongPolling transport) Poll timed out, reissuing.")}catch(e){this._running?e instanceof Me?this._logger.log(Ve.Trace,"(LongPolling transport) Poll timed out, reissuing."):(this._closeError=e,this._running=!1):this._logger.log(Ve.Trace,`(LongPolling transport) Poll errored after shutdown: ${e.message}`)}}}finally{this._logger.log(Ve.Trace,"(LongPolling transport) Polling complete."),this.pollAborted||this._raiseOnClose()}}async send(e){return this._running?rt(this._logger,"LongPolling",this._httpClient,this._url,this._accessTokenFactory,e,this._options):Promise.reject(new Error("Cannot send until the transport is connected"))}async stop(){this._logger.log(Ve.Trace,"(LongPolling transport) Stopping polling."),this._running=!1,this._pollAbort.abort();try{await this._receiving,this._logger.log(Ve.Trace,`(LongPolling transport) sending DELETE request to ${this._url}.`);const e={},[t,n]=st();e[t]=n;const r={headers:{...e,...this._options.headers},timeout:this._options.timeout,withCredentials:this._options.withCredentials};await this._updateHeaderToken(r),await this._httpClient.delete(this._url,r),this._logger.log(Ve.Trace,"(LongPolling transport) DELETE request sent.")}finally{this._logger.log(Ve.Trace,"(LongPolling transport) Stop finished."),this._raiseOnClose()}}_raiseOnClose(){if(this.onclose){let e="(LongPolling transport) Firing onclose event.";this._closeError&&(e+=" Error: "+this._closeError),this._logger.log(Ve.Trace,e),this.onclose(this._closeError)}}}class wt{constructor(e,t,n,r){this._httpClient=e,this._accessTokenFactory=t,this._logger=n,this._options=r,this.onreceive=null,this.onclose=null}async connect(e,t){if(Ze.isRequired(e,"url"),Ze.isRequired(t,"transferFormat"),Ze.isIn(t,Xe,"transferFormat"),this._logger.log(Ve.Trace,"(SSE transport) Connecting."),this._url=e,this._accessTokenFactory){const t=await this._accessTokenFactory();t&&(e+=(e.indexOf("?")<0?"?":"&")+`access_token=${encodeURIComponent(t)}`)}return new Promise(((n,r)=>{let o,i=!1;if(t===Xe.Text){if(et.isBrowser||et.isWebWorker)o=new this._options.EventSource(e,{withCredentials:this._options.withCredentials});else{const t=this._httpClient.getCookieString(e),n={};n.Cookie=t;const[r,i]=st();n[r]=i,o=new this._options.EventSource(e,{withCredentials:this._options.withCredentials,headers:{...n,...this._options.headers}})}try{o.onmessage=e=>{if(this.onreceive)try{this._logger.log(Ve.Trace,`(SSE transport) data received. ${tt(e.data,this._options.logMessageContent)}.`),this.onreceive(e.data)}catch(e){return void this._close(e)}},o.onerror=e=>{i?this._close():r(new Error("EventSource failed to connect. The connection could not be found on the server, either the connection ID is not present on the server, or a proxy is refusing/buffering the connection. If you have multiple servers check that sticky sessions are enabled."))},o.onopen=()=>{this._logger.log(Ve.Information,`SSE connected to ${this._url}`),this._eventSource=o,i=!0,n()}}catch(e){return void r(e)}}else r(new Error("The Server-Sent Events transport only supports the 'Text' transfer format"))}))}async send(e){return this._eventSource?rt(this._logger,"SSE",this._httpClient,this._url,this._accessTokenFactory,e,this._options):Promise.reject(new Error("Cannot send until the transport is connected"))}stop(){return this._close(),Promise.resolve()}_close(e){this._eventSource&&(this._eventSource.close(),this._eventSource=void 0,this.onclose&&this.onclose(e))}}class vt{constructor(e,t,n,r,o,i){this._logger=n,this._accessTokenFactory=t,this._logMessageContent=r,this._webSocketConstructor=o,this._httpClient=e,this.onreceive=null,this.onclose=null,this._headers=i}async connect(e,t){let n;return Ze.isRequired(e,"url"),Ze.isRequired(t,"transferFormat"),Ze.isIn(t,Xe,"transferFormat"),this._logger.log(Ve.Trace,"(WebSockets transport) Connecting."),this._accessTokenFactory&&(n=await this._accessTokenFactory()),new Promise(((r,o)=>{let i;e=e.replace(/^http/,"ws");const s=this._httpClient.getCookieString(e);let a=!1;if(et.isReactNative){const t={},[r,o]=st();t[r]=o,n&&(t[gt.Authorization]=`Bearer ${n}`),s&&(t[gt.Cookie]=s),i=new this._webSocketConstructor(e,void 0,{headers:{...t,...this._headers}})}else n&&(e+=(e.indexOf("?")<0?"?":"&")+`access_token=${encodeURIComponent(n)}`);i||(i=new this._webSocketConstructor(e)),t===Xe.Binary&&(i.binaryType="arraybuffer"),i.onopen=t=>{this._logger.log(Ve.Information,`WebSocket connected to ${e}.`),this._webSocket=i,a=!0,r()},i.onerror=e=>{let t=null;t="undefined"!=typeof ErrorEvent&&e instanceof ErrorEvent?e.error:"There was an error with the transport",this._logger.log(Ve.Information,`(WebSockets transport) ${t}.`)},i.onmessage=e=>{if(this._logger.log(Ve.Trace,`(WebSockets transport) data received. ${tt(e.data,this._logMessageContent)}.`),this.onreceive)try{this.onreceive(e.data)}catch(e){return void this._close(e)}},i.onclose=e=>{if(a)this._close(e);else{let t=null;t="undefined"!=typeof ErrorEvent&&e instanceof ErrorEvent?e.error:"WebSocket failed to connect. The connection could not be found on the server, either the endpoint may not be a SignalR endpoint, the connection ID is not present on the server, or there is a proxy blocking WebSockets. If you have multiple servers check that sticky sessions are enabled.",o(new Error(t))}}}))}send(e){return this._webSocket&&this._webSocket.readyState===this._webSocketConstructor.OPEN?(this._logger.log(Ve.Trace,`(WebSockets transport) sending data. ${tt(e,this._logMessageContent)}.`),this._webSocket.send(e),Promise.resolve()):Promise.reject("WebSocket is not in the OPEN state")}stop(){return this._webSocket&&this._close(void 0),Promise.resolve()}_close(e){this._webSocket&&(this._webSocket.onclose=()=>{},this._webSocket.onmessage=()=>{},this._webSocket.onerror=()=>{},this._webSocket.close(),this._webSocket=void 0),this._logger.log(Ve.Trace,"(WebSockets transport) socket closed."),this.onclose&&(!this._isCloseEvent(e)||!1!==e.wasClean&&1e3===e.code?e instanceof Error?this.onclose(e):this.onclose():this.onclose(new Error(`WebSocket closed with status code: ${e.code} (${e.reason||"no reason given"}).`)))}_isCloseEvent(e){return e&&"boolean"==typeof e.wasClean&&"number"==typeof e.code}}class bt{constructor(e,t={}){var n;if(this._stopPromiseResolver=()=>{},this.features={},this._negotiateVersion=1,Ze.isRequired(e,"url"),this._logger=void 0===(n=t.logger)?new it(Ve.Information):null===n?Qe.instance:void 0!==n.log?n:new it(n),this.baseUrl=this._resolveUrl(e),(t=t||{}).logMessageContent=void 0!==t.logMessageContent&&t.logMessageContent,"boolean"!=typeof t.withCredentials&&void 0!==t.withCredentials)throw new Error("withCredentials option was not a 'boolean' or 'undefined' value");t.withCredentials=void 0===t.withCredentials||t.withCredentials,t.timeout=void 0===t.timeout?1e5:t.timeout,"undefined"==typeof WebSocket||t.WebSocket||(t.WebSocket=WebSocket),"undefined"==typeof EventSource||t.EventSource||(t.EventSource=EventSource),this._httpClient=t.httpClient||new ft(this._logger),this._connectionState="Disconnected",this._connectionStarted=!1,this._options=t,this.onreceive=null,this.onclose=null}async start(e){if(e=e||Xe.Binary,Ze.isIn(e,Xe,"transferFormat"),this._logger.log(Ve.Debug,`Starting connection with transfer format '${Xe[e]}'.`),"Disconnected"!==this._connectionState)return Promise.reject(new Error("Cannot start an HttpConnection that is not in the 'Disconnected' state."));if(this._connectionState="Connecting",this._startInternalPromise=this._startInternal(e),await this._startInternalPromise,"Disconnecting"===this._connectionState){const e="Failed to start the HttpConnection before stop() was called.";return this._logger.log(Ve.Error,e),await this._stopPromise,Promise.reject(new Oe(e))}if("Connected"!==this._connectionState){const e="HttpConnection.startInternal completed gracefully but didn't enter the connection into the connected state!";return this._logger.log(Ve.Error,e),Promise.reject(new Oe(e))}this._connectionStarted=!0}send(e){return"Connected"!==this._connectionState?Promise.reject(new Error("Cannot send data if the connection is not in the 'Connected' State.")):(this._sendQueue||(this._sendQueue=new _t(this.transport)),this._sendQueue.send(e))}async stop(e){return"Disconnected"===this._connectionState?(this._logger.log(Ve.Debug,`Call to HttpConnection.stop(${e}) ignored because the connection is already in the disconnected state.`),Promise.resolve()):"Disconnecting"===this._connectionState?(this._logger.log(Ve.Debug,`Call to HttpConnection.stop(${e}) ignored because the connection is already in the disconnecting state.`),this._stopPromise):(this._connectionState="Disconnecting",this._stopPromise=new Promise((e=>{this._stopPromiseResolver=e})),await this._stopInternal(e),void await this._stopPromise)}async _stopInternal(e){this._stopError=e;try{await this._startInternalPromise}catch(e){}if(this.transport){try{await this.transport.stop()}catch(e){this._logger.log(Ve.Error,`HttpConnection.transport.stop() threw error '${e}'.`),this._stopConnection()}this.transport=void 0}else this._logger.log(Ve.Debug,"HttpConnection.transport is undefined in HttpConnection.stop() because start() failed.")}async _startInternal(e){let t=this.baseUrl;this._accessTokenFactory=this._options.accessTokenFactory;try{if(this._options.skipNegotiation){if(this._options.transport!==Ke.WebSockets)throw new Error("Negotiation can only be skipped when using the WebSocket transport directly.");this.transport=this._constructTransport(Ke.WebSockets),await this._startTransport(t,e)}else{let n=null,r=0;do{if(n=await this._getNegotiationResponse(t),"Disconnecting"===this._connectionState||"Disconnected"===this._connectionState)throw new Oe("The connection was stopped during negotiation.");if(n.error)throw new Error(n.error);if(n.ProtocolVersion)throw new Error("Detected a connection attempt to an ASP.NET SignalR Server. This client only supports connecting to an ASP.NET Core SignalR Server. See https://aka.ms/signalr-core-differences for details.");if(n.url&&(t=n.url),n.accessToken){const e=n.accessToken;this._accessTokenFactory=()=>e}r++}while(n.url&&r<100);if(100===r&&n.url)throw new Error("Negotiate redirection limit exceeded.");await this._createTransport(t,this._options.transport,n,e)}this.transport instanceof yt&&(this.features.inherentKeepAlive=!0),"Connecting"===this._connectionState&&(this._logger.log(Ve.Debug,"The HttpConnection connected successfully."),this._connectionState="Connected")}catch(e){return this._logger.log(Ve.Error,"Failed to start the connection: "+e),this._connectionState="Disconnected",this.transport=void 0,this._stopPromiseResolver(),Promise.reject(e)}}async _getNegotiationResponse(e){const t={};if(this._accessTokenFactory){const e=await this._accessTokenFactory();e&&(t[gt.Authorization]=`Bearer ${e}`)}const[n,r]=st();t[n]=r;const o=this._resolveNegotiateUrl(e);this._logger.log(Ve.Debug,`Sending negotiation request: ${o}.`);try{const e=await this._httpClient.post(o,{content:"",headers:{...t,...this._options.headers},timeout:this._options.timeout,withCredentials:this._options.withCredentials});if(200!==e.statusCode)return Promise.reject(new Error(`Unexpected status code returned from negotiate '${e.statusCode}'`));const n=JSON.parse(e.content);return(!n.negotiateVersion||n.negotiateVersion<1)&&(n.connectionToken=n.connectionId),n}catch(e){let t="Failed to complete negotiation with the server: "+e;return e instanceof Le&&404===e.statusCode&&(t+=" Either this is not a SignalR endpoint or there is a proxy blocking the connection."),this._logger.log(Ve.Error,t),Promise.reject(new We(t))}}_createConnectUrl(e,t){return t?e+(-1===e.indexOf("?")?"?":"&")+`id=${t}`:e}async _createTransport(e,t,n,r){let o=this._createConnectUrl(e,n.connectionToken);if(this._isITransport(t))return this._logger.log(Ve.Debug,"Connection was provided an instance of ITransport, using that directly."),this.transport=t,await this._startTransport(o,r),void(this.connectionId=n.connectionId);const i=[],s=n.availableTransports||[];let a=n;for(const n of s){const s=this._resolveTransportOrError(n,t,r);if(s instanceof Error)i.push(`${n.transport} failed:`),i.push(s);else if(this._isITransport(s)){if(this.transport=s,!a){try{a=await this._getNegotiationResponse(e)}catch(e){return Promise.reject(e)}o=this._createConnectUrl(e,a.connectionToken)}try{return await this._startTransport(o,r),void(this.connectionId=a.connectionId)}catch(e){if(this._logger.log(Ve.Error,`Failed to start the transport '${n.transport}': ${e}`),a=void 0,i.push(new He(`${n.transport} failed: ${e}`,Ke[n.transport])),"Connecting"!==this._connectionState){const e="Failed to select transport before stop() was called.";return this._logger.log(Ve.Debug,e),Promise.reject(new Oe(e))}}}}return i.length>0?Promise.reject(new ze(`Unable to connect to the server with any of the available transports. ${i.join(" ")}`,i)):Promise.reject(new Error("None of the transports supported by the client are supported by the server."))}_constructTransport(e){switch(e){case Ke.WebSockets:if(!this._options.WebSocket)throw new Error("'WebSocket' is not supported in your environment.");return new vt(this._httpClient,this._accessTokenFactory,this._logger,this._options.logMessageContent,this._options.WebSocket,this._options.headers||{});case Ke.ServerSentEvents:if(!this._options.EventSource)throw new Error("'EventSource' is not supported in your environment.");return new wt(this._httpClient,this._accessTokenFactory,this._logger,this._options);case Ke.LongPolling:return new yt(this._httpClient,this._accessTokenFactory,this._logger,this._options);default:throw new Error(`Unknown transport: ${e}.`)}}_startTransport(e,t){return this.transport.onreceive=this.onreceive,this.transport.onclose=e=>this._stopConnection(e),this.transport.connect(e,t)}_resolveTransportOrError(e,t,n){const r=Ke[e.transport];if(null==r)return this._logger.log(Ve.Debug,`Skipping transport '${e.transport}' because it is not supported by this client.`),new Error(`Skipping transport '${e.transport}' because it is not supported by this client.`);if(!function(e,t){return!e||0!=(t&e)}(t,r))return this._logger.log(Ve.Debug,`Skipping transport '${Ke[r]}' because it was disabled by the client.`),new je(`'${Ke[r]}' is disabled by the client.`,r);if(!(e.transferFormats.map((e=>Xe[e])).indexOf(n)>=0))return this._logger.log(Ve.Debug,`Skipping transport '${Ke[r]}' because it does not support the requested transfer format '${Xe[n]}'.`),new Error(`'${Ke[r]}' does not support ${Xe[n]}.`);if(r===Ke.WebSockets&&!this._options.WebSocket||r===Ke.ServerSentEvents&&!this._options.EventSource)return this._logger.log(Ve.Debug,`Skipping transport '${Ke[r]}' because it is not supported in your environment.'`),new Fe(`'${Ke[r]}' is not supported in your environment.`,r);this._logger.log(Ve.Debug,`Selecting transport '${Ke[r]}'.`);try{return this._constructTransport(r)}catch(e){return e}}_isITransport(e){return e&&"object"==typeof e&&"connect"in e}_stopConnection(e){if(this._logger.log(Ve.Debug,`HttpConnection.stopConnection(${e}) called while in state ${this._connectionState}.`),this.transport=void 0,e=this._stopError||e,this._stopError=void 0,"Disconnected"!==this._connectionState){if("Connecting"===this._connectionState)throw this._logger.log(Ve.Warning,`Call to HttpConnection.stopConnection(${e}) was ignored because the connection is still in the connecting state.`),new Error(`HttpConnection.stopConnection(${e}) was called while the connection is still in the connecting state.`);if("Disconnecting"===this._connectionState&&this._stopPromiseResolver(),e?this._logger.log(Ve.Error,`Connection disconnected with error '${e}'.`):this._logger.log(Ve.Information,"Connection disconnected."),this._sendQueue&&(this._sendQueue.stop().catch((e=>{this._logger.log(Ve.Error,`TransportSendQueue.stop() threw error '${e}'.`)})),this._sendQueue=void 0),this.connectionId=void 0,this._connectionState="Disconnected",this._connectionStarted){this._connectionStarted=!1;try{this.onclose&&this.onclose(e)}catch(t){this._logger.log(Ve.Error,`HttpConnection.onclose(${e}) threw error '${t}'.`)}}}else this._logger.log(Ve.Debug,`Call to HttpConnection.stopConnection(${e}) was ignored because the connection is already in the disconnected state.`)}_resolveUrl(e){if(0===e.lastIndexOf("https://",0)||0===e.lastIndexOf("http://",0))return e;if(!et.isBrowser)throw new Error(`Cannot resolve '${e}'.`);const t=window.document.createElement("a");return t.href=e,this._logger.log(Ve.Information,`Normalizing '${e}' to '${t.href}'.`),t.href}_resolveNegotiateUrl(e){const t=e.indexOf("?");let n=e.substring(0,-1===t?e.length:t);return"/"!==n[n.length-1]&&(n+="/"),n+="negotiate",n+=-1===t?"":e.substring(t),-1===n.indexOf("negotiateVersion")&&(n+=-1===t?"?":"&",n+="negotiateVersion="+this._negotiateVersion),n}}class _t{constructor(e){this._transport=e,this._buffer=[],this._executing=!0,this._sendBufferedData=new Et,this._transportResult=new Et,this._sendLoopPromise=this._sendLoop()}send(e){return this._bufferData(e),this._transportResult||(this._transportResult=new Et),this._transportResult.promise}stop(){return this._executing=!1,this._sendBufferedData.resolve(),this._sendLoopPromise}_bufferData(e){if(this._buffer.length&&typeof this._buffer[0]!=typeof e)throw new Error(`Expected data to be of type ${typeof this._buffer} but was of type ${typeof e}`);this._buffer.push(e),this._sendBufferedData.resolve()}async _sendLoop(){for(;;){if(await this._sendBufferedData.promise,!this._executing){this._transportResult&&this._transportResult.reject("Connection stopped.");break}this._sendBufferedData=new Et;const e=this._transportResult;this._transportResult=void 0;const t="string"==typeof this._buffer[0]?this._buffer.join(""):_t._concatBuffers(this._buffer);this._buffer.length=0;try{await this._transport.send(t),e.resolve()}catch(t){e.reject(t)}}}static _concatBuffers(e){const t=e.map((e=>e.byteLength)).reduce(((e,t)=>e+t)),n=new Uint8Array(t);let r=0;for(const t of e)n.set(new Uint8Array(t),r),r+=t.byteLength;return n.buffer}}class Et{constructor(){this.promise=new Promise(((e,t)=>[this._resolver,this._rejecter]=[e,t]))}resolve(){this._resolver()}reject(e){this._rejecter(e)}}class St{static write(e){return`${e}${St.RecordSeparator}`}static parse(e){if(e[e.length-1]!==St.RecordSeparator)throw new Error("Message is incomplete.");const t=e.split(St.RecordSeparator);return t.pop(),t}}St.RecordSeparatorCode=30,St.RecordSeparator=String.fromCharCode(St.RecordSeparatorCode);class Ct{writeHandshakeRequest(e){return St.write(JSON.stringify(e))}parseHandshakeResponse(e){let t,n;if(nt(e)){const r=new Uint8Array(e),o=r.indexOf(St.RecordSeparatorCode);if(-1===o)throw new Error("Message is incomplete.");const i=o+1;t=String.fromCharCode.apply(null,Array.prototype.slice.call(r.slice(0,i))),n=r.byteLength>i?r.slice(i).buffer:null}else{const r=e,o=r.indexOf(St.RecordSeparator);if(-1===o)throw new Error("Message is incomplete.");const i=o+1;t=r.substring(0,i),n=r.length>i?r.substring(i):null}const r=St.parse(t),o=JSON.parse(r[0]);if(o.type)throw new Error("Expected a handshake response from the server.");return[n,o]}}!function(e){e[e.Invocation=1]="Invocation",e[e.StreamItem=2]="StreamItem",e[e.Completion=3]="Completion",e[e.StreamInvocation=4]="StreamInvocation",e[e.CancelInvocation=5]="CancelInvocation",e[e.Ping=6]="Ping",e[e.Close=7]="Close"}(Ye||(Ye={}));class It{constructor(){this.observers=[]}next(e){for(const t of this.observers)t.next(e)}error(e){for(const t of this.observers)t.error&&t.error(e)}complete(){for(const e of this.observers)e.complete&&e.complete()}subscribe(e){return this.observers.push(e),new ot(this,e)}}!function(e){e.Disconnected="Disconnected",e.Connecting="Connecting",e.Connected="Connected",e.Disconnecting="Disconnecting",e.Reconnecting="Reconnecting"}(Ge||(Ge={}));class kt{constructor(e,t,n,r){this._nextKeepAlive=0,this._freezeEventListener=()=>{this._logger.log(Ve.Warning,"The page is being frozen, this will likely lead to the connection being closed and messages being lost. For more information see the docs at https://docs.microsoft.com/aspnet/core/signalr/javascript-client#bsleep")},Ze.isRequired(e,"connection"),Ze.isRequired(t,"logger"),Ze.isRequired(n,"protocol"),this.serverTimeoutInMilliseconds=3e4,this.keepAliveIntervalInMilliseconds=15e3,this._logger=t,this._protocol=n,this.connection=e,this._reconnectPolicy=r,this._handshakeProtocol=new Ct,this.connection.onreceive=e=>this._processIncomingData(e),this.connection.onclose=e=>this._connectionClosed(e),this._callbacks={},this._methods={},this._closedCallbacks=[],this._reconnectingCallbacks=[],this._reconnectedCallbacks=[],this._invocationId=0,this._receivedHandshakeResponse=!1,this._connectionState=Ge.Disconnected,this._connectionStarted=!1,this._cachedPingMessage=this._protocol.writeMessage({type:Ye.Ping})}static create(e,t,n,r){return new kt(e,t,n,r)}get state(){return this._connectionState}get connectionId(){return this.connection&&this.connection.connectionId||null}get baseUrl(){return this.connection.baseUrl||""}set baseUrl(e){if(this._connectionState!==Ge.Disconnected&&this._connectionState!==Ge.Reconnecting)throw new Error("The HubConnection must be in the Disconnected or Reconnecting state to change the url.");if(!e)throw new Error("The HubConnection url must be a valid url.");this.connection.baseUrl=e}start(){return this._startPromise=this._startWithStateTransitions(),this._startPromise}async _startWithStateTransitions(){if(this._connectionState!==Ge.Disconnected)return Promise.reject(new Error("Cannot start a HubConnection that is not in the 'Disconnected' state."));this._connectionState=Ge.Connecting,this._logger.log(Ve.Debug,"Starting HubConnection.");try{await this._startInternal(),et.isBrowser&&window.document.addEventListener("freeze",this._freezeEventListener),this._connectionState=Ge.Connected,this._connectionStarted=!0,this._logger.log(Ve.Debug,"HubConnection connected successfully.")}catch(e){return this._connectionState=Ge.Disconnected,this._logger.log(Ve.Debug,`HubConnection failed to start successfully because of error '${e}'.`),Promise.reject(e)}}async _startInternal(){this._stopDuringStartError=void 0,this._receivedHandshakeResponse=!1;const e=new Promise(((e,t)=>{this._handshakeResolver=e,this._handshakeRejecter=t}));await this.connection.start(this._protocol.transferFormat);try{const t={protocol:this._protocol.name,version:this._protocol.version};if(this._logger.log(Ve.Debug,"Sending handshake request."),await this._sendMessage(this._handshakeProtocol.writeHandshakeRequest(t)),this._logger.log(Ve.Information,`Using HubProtocol '${this._protocol.name}'.`),this._cleanupTimeout(),this._resetTimeoutPeriod(),this._resetKeepAliveInterval(),await e,this._stopDuringStartError)throw this._stopDuringStartError}catch(e){throw this._logger.log(Ve.Debug,`Hub handshake failed with error '${e}' during start(). Stopping HubConnection.`),this._cleanupTimeout(),this._cleanupPingTimer(),await this.connection.stop(e),e}}async stop(){const e=this._startPromise;this._stopPromise=this._stopInternal(),await this._stopPromise;try{await e}catch(e){}}_stopInternal(e){return this._connectionState===Ge.Disconnected?(this._logger.log(Ve.Debug,`Call to HubConnection.stop(${e}) ignored because it is already in the disconnected state.`),Promise.resolve()):this._connectionState===Ge.Disconnecting?(this._logger.log(Ve.Debug,`Call to HttpConnection.stop(${e}) ignored because the connection is already in the disconnecting state.`),this._stopPromise):(this._connectionState=Ge.Disconnecting,this._logger.log(Ve.Debug,"Stopping HubConnection."),this._reconnectDelayHandle?(this._logger.log(Ve.Debug,"Connection stopped during reconnect delay. Done reconnecting."),clearTimeout(this._reconnectDelayHandle),this._reconnectDelayHandle=void 0,this._completeClose(),Promise.resolve()):(this._cleanupTimeout(),this._cleanupPingTimer(),this._stopDuringStartError=e||new Oe("The connection was stopped before the hub handshake could complete."),this.connection.stop(e)))}stream(e,...t){const[n,r]=this._replaceStreamingParams(t),o=this._createStreamInvocation(e,t,r);let i;const s=new It;return s.cancelCallback=()=>{const e=this._createCancelInvocation(o.invocationId);return delete this._callbacks[o.invocationId],i.then((()=>this._sendWithProtocol(e)))},this._callbacks[o.invocationId]=(e,t)=>{t?s.error(t):e&&(e.type===Ye.Completion?e.error?s.error(new Error(e.error)):s.complete():s.next(e.item))},i=this._sendWithProtocol(o).catch((e=>{s.error(e),delete this._callbacks[o.invocationId]})),this._launchStreams(n,i),s}_sendMessage(e){return this._resetKeepAliveInterval(),this.connection.send(e)}_sendWithProtocol(e){return this._sendMessage(this._protocol.writeMessage(e))}send(e,...t){const[n,r]=this._replaceStreamingParams(t),o=this._sendWithProtocol(this._createInvocation(e,t,!0,r));return this._launchStreams(n,o),o}invoke(e,...t){const[n,r]=this._replaceStreamingParams(t),o=this._createInvocation(e,t,!1,r);return new Promise(((e,t)=>{this._callbacks[o.invocationId]=(n,r)=>{r?t(r):n&&(n.type===Ye.Completion?n.error?t(new Error(n.error)):e(n.result):t(new Error(`Unexpected message type: ${n.type}`)))};const r=this._sendWithProtocol(o).catch((e=>{t(e),delete this._callbacks[o.invocationId]}));this._launchStreams(n,r)}))}on(e,t){e&&t&&(e=e.toLowerCase(),this._methods[e]||(this._methods[e]=[]),-1===this._methods[e].indexOf(t)&&this._methods[e].push(t))}off(e,t){if(!e)return;e=e.toLowerCase();const n=this._methods[e];if(n)if(t){const r=n.indexOf(t);-1!==r&&(n.splice(r,1),0===n.length&&delete this._methods[e])}else delete this._methods[e]}onclose(e){e&&this._closedCallbacks.push(e)}onreconnecting(e){e&&this._reconnectingCallbacks.push(e)}onreconnected(e){e&&this._reconnectedCallbacks.push(e)}_processIncomingData(e){if(this._cleanupTimeout(),this._receivedHandshakeResponse||(e=this._processHandshakeResponse(e),this._receivedHandshakeResponse=!0),e){const t=this._protocol.parseMessages(e,this._logger);for(const e of t)switch(e.type){case Ye.Invocation:this._invokeClientMethod(e);break;case Ye.StreamItem:case Ye.Completion:{const t=this._callbacks[e.invocationId];if(t){e.type===Ye.Completion&&delete this._callbacks[e.invocationId];try{t(e)}catch(e){this._logger.log(Ve.Error,`Stream callback threw error: ${ht(e)}`)}}break}case Ye.Ping:break;case Ye.Close:{this._logger.log(Ve.Information,"Close message received from server.");const t=e.error?new Error("Server returned an error on close: "+e.error):void 0;!0===e.allowReconnect?this.connection.stop(t):this._stopPromise=this._stopInternal(t);break}default:this._logger.log(Ve.Warning,`Invalid message type: ${e.type}.`)}}this._resetTimeoutPeriod()}_processHandshakeResponse(e){let t,n;try{[n,t]=this._handshakeProtocol.parseHandshakeResponse(e)}catch(e){const t="Error parsing handshake response: "+e;this._logger.log(Ve.Error,t);const n=new Error(t);throw this._handshakeRejecter(n),n}if(t.error){const e="Server returned handshake error: "+t.error;this._logger.log(Ve.Error,e);const n=new Error(e);throw this._handshakeRejecter(n),n}return this._logger.log(Ve.Debug,"Server handshake complete."),this._handshakeResolver(),n}_resetKeepAliveInterval(){this.connection.features.inherentKeepAlive||(this._nextKeepAlive=(new Date).getTime()+this.keepAliveIntervalInMilliseconds,this._cleanupPingTimer())}_resetTimeoutPeriod(){if(!(this.connection.features&&this.connection.features.inherentKeepAlive||(this._timeoutHandle=setTimeout((()=>this.serverTimeout()),this.serverTimeoutInMilliseconds),void 0!==this._pingServerHandle))){let e=this._nextKeepAlive-(new Date).getTime();e<0&&(e=0),this._pingServerHandle=setTimeout((async()=>{if(this._connectionState===Ge.Connected)try{await this._sendMessage(this._cachedPingMessage)}catch{this._cleanupPingTimer()}}),e)}}serverTimeout(){this.connection.stop(new Error("Server timeout elapsed without receiving a message from the server."))}async _invokeClientMethod(e){const t=e.target.toLowerCase(),n=this._methods[t];if(!n)return this._logger.log(Ve.Warning,`No client method with the name '${t}' found.`),void(e.invocationId&&(this._logger.log(Ve.Warning,`No result given for '${t}' method and invocation ID '${e.invocationId}'.`),await this._sendWithProtocol(this._createCompletionMessage(e.invocationId,"Client didn't provide a result.",null))));const r=n.slice(),o=!!e.invocationId;let i,s,a;for(const n of r)try{const r=i;i=await n.apply(this,e.arguments),o&&i&&r&&(this._logger.log(Ve.Error,`Multiple results provided for '${t}'. Sending error to server.`),a=this._createCompletionMessage(e.invocationId,"Client provided multiple results.",null)),s=void 0}catch(e){s=e,this._logger.log(Ve.Error,`A callback for the method '${t}' threw error '${e}'.`)}a?await this._sendWithProtocol(a):o?(s?a=this._createCompletionMessage(e.invocationId,`${s}`,null):void 0!==i?a=this._createCompletionMessage(e.invocationId,null,i):(this._logger.log(Ve.Warning,`No result given for '${t}' method and invocation ID '${e.invocationId}'.`),a=this._createCompletionMessage(e.invocationId,"Client didn't provide a result.",null)),await this._sendWithProtocol(a)):i&&this._logger.log(Ve.Error,`Result given for '${t}' method but server is not expecting a result.`)}_connectionClosed(e){this._logger.log(Ve.Debug,`HubConnection.connectionClosed(${e}) called while in state ${this._connectionState}.`),this._stopDuringStartError=this._stopDuringStartError||e||new Oe("The underlying connection was closed before the hub handshake could complete."),this._handshakeResolver&&this._handshakeResolver(),this._cancelCallbacksWithError(e||new Error("Invocation canceled due to the underlying connection being closed.")),this._cleanupTimeout(),this._cleanupPingTimer(),this._connectionState===Ge.Disconnecting?this._completeClose(e):this._connectionState===Ge.Connected&&this._reconnectPolicy?this._reconnect(e):this._connectionState===Ge.Connected&&this._completeClose(e)}_completeClose(e){if(this._connectionStarted){this._connectionState=Ge.Disconnected,this._connectionStarted=!1,et.isBrowser&&window.document.removeEventListener("freeze",this._freezeEventListener);try{this._closedCallbacks.forEach((t=>t.apply(this,[e])))}catch(t){this._logger.log(Ve.Error,`An onclose callback called with error '${e}' threw error '${t}'.`)}}}async _reconnect(e){const t=Date.now();let n=0,r=void 0!==e?e:new Error("Attempting to reconnect due to a unknown error."),o=this._getNextRetryDelay(n++,0,r);if(null===o)return this._logger.log(Ve.Debug,"Connection not reconnecting because the IRetryPolicy returned null on the first reconnect attempt."),void this._completeClose(e);if(this._connectionState=Ge.Reconnecting,e?this._logger.log(Ve.Information,`Connection reconnecting because of error '${e}'.`):this._logger.log(Ve.Information,"Connection reconnecting."),0!==this._reconnectingCallbacks.length){try{this._reconnectingCallbacks.forEach((t=>t.apply(this,[e])))}catch(t){this._logger.log(Ve.Error,`An onreconnecting callback called with error '${e}' threw error '${t}'.`)}if(this._connectionState!==Ge.Reconnecting)return void this._logger.log(Ve.Debug,"Connection left the reconnecting state in onreconnecting callback. Done reconnecting.")}for(;null!==o;){if(this._logger.log(Ve.Information,`Reconnect attempt number ${n} will start in ${o} ms.`),await new Promise((e=>{this._reconnectDelayHandle=setTimeout(e,o)})),this._reconnectDelayHandle=void 0,this._connectionState!==Ge.Reconnecting)return void this._logger.log(Ve.Debug,"Connection left the reconnecting state during reconnect delay. Done reconnecting.");try{if(await this._startInternal(),this._connectionState=Ge.Connected,this._logger.log(Ve.Information,"HubConnection reconnected successfully."),0!==this._reconnectedCallbacks.length)try{this._reconnectedCallbacks.forEach((e=>e.apply(this,[this.connection.connectionId])))}catch(e){this._logger.log(Ve.Error,`An onreconnected callback called with connectionId '${this.connection.connectionId}; threw error '${e}'.`)}return}catch(e){if(this._logger.log(Ve.Information,`Reconnect attempt failed because of error '${e}'.`),this._connectionState!==Ge.Reconnecting)return this._logger.log(Ve.Debug,`Connection moved to the '${this._connectionState}' from the reconnecting state during reconnect attempt. Done reconnecting.`),void(this._connectionState===Ge.Disconnecting&&this._completeClose());r=e instanceof Error?e:new Error(e.toString()),o=this._getNextRetryDelay(n++,Date.now()-t,r)}}this._logger.log(Ve.Information,`Reconnect retries have been exhausted after ${Date.now()-t} ms and ${n} failed attempts. Connection disconnecting.`),this._completeClose()}_getNextRetryDelay(e,t,n){try{return this._reconnectPolicy.nextRetryDelayInMilliseconds({elapsedMilliseconds:t,previousRetryCount:e,retryReason:n})}catch(n){return this._logger.log(Ve.Error,`IRetryPolicy.nextRetryDelayInMilliseconds(${e}, ${t}) threw error '${n}'.`),null}}_cancelCallbacksWithError(e){const t=this._callbacks;this._callbacks={},Object.keys(t).forEach((n=>{const r=t[n];try{r(null,e)}catch(t){this._logger.log(Ve.Error,`Stream 'error' callback called with '${e}' threw error: ${ht(t)}`)}}))}_cleanupPingTimer(){this._pingServerHandle&&(clearTimeout(this._pingServerHandle),this._pingServerHandle=void 0)}_cleanupTimeout(){this._timeoutHandle&&clearTimeout(this._timeoutHandle)}_createInvocation(e,t,n,r){if(n)return 0!==r.length?{arguments:t,streamIds:r,target:e,type:Ye.Invocation}:{arguments:t,target:e,type:Ye.Invocation};{const n=this._invocationId;return this._invocationId++,0!==r.length?{arguments:t,invocationId:n.toString(),streamIds:r,target:e,type:Ye.Invocation}:{arguments:t,invocationId:n.toString(),target:e,type:Ye.Invocation}}}_launchStreams(e,t){if(0!==e.length){t||(t=Promise.resolve());for(const n in e)e[n].subscribe({complete:()=>{t=t.then((()=>this._sendWithProtocol(this._createCompletionMessage(n))))},error:e=>{let r;r=e instanceof Error?e.message:e&&e.toString?e.toString():"Unknown error",t=t.then((()=>this._sendWithProtocol(this._createCompletionMessage(n,r))))},next:e=>{t=t.then((()=>this._sendWithProtocol(this._createStreamItemMessage(n,e))))}})}}_replaceStreamingParams(e){const t=[],n=[];for(let r=0;r<e.length;r++){const o=e[r];if(this._isObservable(o)){const i=this._invocationId;this._invocationId++,t[i]=o,n.push(i.toString()),e.splice(r,1)}}return[t,n]}_isObservable(e){return e&&e.subscribe&&"function"==typeof e.subscribe}_createStreamInvocation(e,t,n){const r=this._invocationId;return this._invocationId++,0!==n.length?{arguments:t,invocationId:r.toString(),streamIds:n,target:e,type:Ye.StreamInvocation}:{arguments:t,invocationId:r.toString(),target:e,type:Ye.StreamInvocation}}_createCancelInvocation(e){return{invocationId:e,type:Ye.CancelInvocation}}_createStreamItemMessage(e,t){return{invocationId:e,item:t,type:Ye.StreamItem}}_createCompletionMessage(e,t,n){return t?{error:t,invocationId:e,type:Ye.Completion}:{invocationId:e,result:n,type:Ye.Completion}}}class Tt{constructor(){this.name="json",this.version=1,this.transferFormat=Xe.Text}parseMessages(e,t){if("string"!=typeof e)throw new Error("Invalid input for JSON hub protocol. Expected a string.");if(!e)return[];null===t&&(t=Qe.instance);const n=St.parse(e),r=[];for(const e of n){const n=JSON.parse(e);if("number"!=typeof n.type)throw new Error("Invalid payload.");switch(n.type){case Ye.Invocation:this._isInvocationMessage(n);break;case Ye.StreamItem:this._isStreamItemMessage(n);break;case Ye.Completion:this._isCompletionMessage(n);break;case Ye.Ping:case Ye.Close:break;default:t.log(Ve.Information,"Unknown message type '"+n.type+"' ignored.");continue}r.push(n)}return r}writeMessage(e){return St.write(JSON.stringify(e))}_isInvocationMessage(e){this._assertNotEmptyString(e.target,"Invalid payload for Invocation message."),void 0!==e.invocationId&&this._assertNotEmptyString(e.invocationId,"Invalid payload for Invocation message.")}_isStreamItemMessage(e){if(this._assertNotEmptyString(e.invocationId,"Invalid payload for StreamItem message."),void 0===e.item)throw new Error("Invalid payload for StreamItem message.")}_isCompletionMessage(e){if(e.result&&e.error)throw new Error("Invalid payload for Completion message.");!e.result&&e.error&&this._assertNotEmptyString(e.error,"Invalid payload for Completion message."),this._assertNotEmptyString(e.invocationId,"Invalid payload for Completion message.")}_assertNotEmptyString(e,t){if("string"!=typeof e||""===e)throw new Error(t)}}const xt={trace:Ve.Trace,debug:Ve.Debug,info:Ve.Information,information:Ve.Information,warn:Ve.Warning,warning:Ve.Warning,error:Ve.Error,critical:Ve.Critical,none:Ve.None};class Dt{configureLogging(e){if(Ze.isRequired(e,"logging"),void 0!==e.log)this.logger=e;else if("string"==typeof e){const t=function(e){const t=xt[e.toLowerCase()];if(void 0!==t)return t;throw new Error(`Unknown log level: ${e}`)}(e);this.logger=new it(t)}else this.logger=new it(e);return this}withUrl(e,t){return Ze.isRequired(e,"url"),Ze.isNotEmpty(e,"url"),this.url=e,this.httpConnectionOptions="object"==typeof t?{...this.httpConnectionOptions,...t}:{...this.httpConnectionOptions,transport:t},this}withHubProtocol(e){return Ze.isRequired(e,"protocol"),this.protocol=e,this}withAutomaticReconnect(e){if(this.reconnectPolicy)throw new Error("A reconnectPolicy has already been set.");return e?Array.isArray(e)?this.reconnectPolicy=new Be(e):this.reconnectPolicy=e:this.reconnectPolicy=new Be,this}build(){const e=this.httpConnectionOptions||{};if(void 0===e.logger&&(e.logger=this.logger),!this.url)throw new Error("The 'HubConnectionBuilder.withUrl' method must be called before building the connection.");const t=new bt(this.url,e);return kt.create(t,this.logger||Qe.instance,this.protocol||new Tt,this.reconnectPolicy)}}var Rt=4294967295;function Pt(e,t,n){var r=Math.floor(n/4294967296),o=n;e.setUint32(t,r),e.setUint32(t+4,o)}function Ut(e,t){return 4294967296*e.getInt32(t)+e.getUint32(t+4)}var At=("undefined"==typeof process||"never"!==process.env.TEXT_ENCODING)&&"undefined"!=typeof TextEncoder&&"undefined"!=typeof TextDecoder;function Nt(e){for(var t=e.length,n=0,r=0;r<t;){var o=e.charCodeAt(r++);if(0!=(4294967168&o))if(0==(4294965248&o))n+=2;else{if(o>=55296&&o<=56319&&r<t){var i=e.charCodeAt(r);56320==(64512&i)&&(++r,o=((1023&o)<<10)+(1023&i)+65536)}n+=0==(4294901760&o)?3:4}else n++}return n}var $t=At?new TextEncoder:void 0,Bt=At?"undefined"!=typeof process&&"force"!==process.env.TEXT_ENCODING?200:0:Rt,Lt=(null==$t?void 0:$t.encodeInto)?function(e,t,n){$t.encodeInto(e,t.subarray(n))}:function(e,t,n){t.set($t.encode(e),n)};function Mt(e,t,n){for(var r=t,o=r+n,i=[],s="";r<o;){var a=e[r++];if(0==(128&a))i.push(a);else if(192==(224&a)){var c=63&e[r++];i.push((31&a)<<6|c)}else if(224==(240&a)){c=63&e[r++];var l=63&e[r++];i.push((31&a)<<12|c<<6|l)}else if(240==(248&a)){var h=(7&a)<<18|(c=63&e[r++])<<12|(l=63&e[r++])<<6|63&e[r++];h>65535&&(h-=65536,i.push(h>>>10&1023|55296),h=56320|1023&h),i.push(h)}else i.push(a);i.length>=4096&&(s+=String.fromCharCode.apply(String,i),i.length=0)}return i.length>0&&(s+=String.fromCharCode.apply(String,i)),s}var Ot,Ft=At?new TextDecoder:null,jt=At?"undefined"!=typeof process&&"force"!==process.env.TEXT_DECODER?200:0:Rt,Ht=function(e,t){this.type=e,this.data=t},Wt=(Ot=function(e,t){return Ot=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},Ot(e,t)},function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function n(){this.constructor=e}Ot(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),zt=function(e){function t(n){var r=e.call(this,n)||this,o=Object.create(t.prototype);return Object.setPrototypeOf(r,o),Object.defineProperty(r,"name",{configurable:!0,enumerable:!1,value:t.name}),r}return Wt(t,e),t}(Error),Jt={type:-1,encode:function(e){var t,n,r,o;return e instanceof Date?function(e){var t,n=e.sec,r=e.nsec;if(n>=0&&r>=0&&n<=17179869183){if(0===r&&n<=4294967295){var o=new Uint8Array(4);return(t=new DataView(o.buffer)).setUint32(0,n),o}var i=n/4294967296,s=4294967295&n;return o=new Uint8Array(8),(t=new DataView(o.buffer)).setUint32(0,r<<2|3&i),t.setUint32(4,s),o}return o=new Uint8Array(12),(t=new DataView(o.buffer)).setUint32(0,r),Pt(t,4,n),o}((r=1e6*((t=e.getTime())-1e3*(n=Math.floor(t/1e3))),{sec:n+(o=Math.floor(r/1e9)),nsec:r-1e9*o})):null},decode:function(e){var t=function(e){var t=new DataView(e.buffer,e.byteOffset,e.byteLength);switch(e.byteLength){case 4:return{sec:t.getUint32(0),nsec:0};case 8:var n=t.getUint32(0);return{sec:4294967296*(3&n)+t.getUint32(4),nsec:n>>>2};case 12:return{sec:Ut(t,4),nsec:t.getUint32(0)};default:throw new zt("Unrecognized data size for timestamp (expected 4, 8, or 12): "+e.length)}}(e);return new Date(1e3*t.sec+t.nsec/1e6)}},qt=function(){function e(){this.builtInEncoders=[],this.builtInDecoders=[],this.encoders=[],this.decoders=[],this.register(Jt)}return e.prototype.register=function(e){var t=e.type,n=e.encode,r=e.decode;if(t>=0)this.encoders[t]=n,this.decoders[t]=r;else{var o=1+t;this.builtInEncoders[o]=n,this.builtInDecoders[o]=r}},e.prototype.tryToEncode=function(e,t){for(var n=0;n<this.builtInEncoders.length;n++)if(null!=(r=this.builtInEncoders[n])&&null!=(o=r(e,t)))return new Ht(-1-n,o);for(n=0;n<this.encoders.length;n++){var r,o;if(null!=(r=this.encoders[n])&&null!=(o=r(e,t)))return new Ht(n,o)}return e instanceof Ht?e:null},e.prototype.decode=function(e,t,n){var r=t<0?this.builtInDecoders[-1-t]:this.decoders[t];return r?r(e,t,n):new Ht(t,e)},e.defaultCodec=new e,e}();function Vt(e){return e instanceof Uint8Array?e:ArrayBuffer.isView(e)?new Uint8Array(e.buffer,e.byteOffset,e.byteLength):e instanceof ArrayBuffer?new Uint8Array(e):Uint8Array.from(e)}var Kt=function(){function e(e,t,n,r,o,i,s,a){void 0===e&&(e=qt.defaultCodec),void 0===t&&(t=void 0),void 0===n&&(n=100),void 0===r&&(r=2048),void 0===o&&(o=!1),void 0===i&&(i=!1),void 0===s&&(s=!1),void 0===a&&(a=!1),this.extensionCodec=e,this.context=t,this.maxDepth=n,this.initialBufferSize=r,this.sortKeys=o,this.forceFloat32=i,this.ignoreUndefined=s,this.forceIntegerToFloat=a,this.pos=0,this.view=new DataView(new ArrayBuffer(this.initialBufferSize)),this.bytes=new Uint8Array(this.view.buffer)}return e.prototype.getUint8Array=function(){return this.bytes.subarray(0,this.pos)},e.prototype.reinitializeState=function(){this.pos=0},e.prototype.encode=function(e){return this.reinitializeState(),this.doEncode(e,1),this.getUint8Array()},e.prototype.doEncode=function(e,t){if(t>this.maxDepth)throw new Error("Too deep objects in depth "+t);null==e?this.encodeNil():"boolean"==typeof e?this.encodeBoolean(e):"number"==typeof e?this.encodeNumber(e):"string"==typeof e?this.encodeString(e):this.encodeObject(e,t)},e.prototype.ensureBufferSizeToWrite=function(e){var t=this.pos+e;this.view.byteLength<t&&this.resizeBuffer(2*t)},e.prototype.resizeBuffer=function(e){var t=new ArrayBuffer(e),n=new Uint8Array(t),r=new DataView(t);n.set(this.bytes),this.view=r,this.bytes=n},e.prototype.encodeNil=function(){this.writeU8(192)},e.prototype.encodeBoolean=function(e){!1===e?this.writeU8(194):this.writeU8(195)},e.prototype.encodeNumber=function(e){Number.isSafeInteger(e)&&!this.forceIntegerToFloat?e>=0?e<128?this.writeU8(e):e<256?(this.writeU8(204),this.writeU8(e)):e<65536?(this.writeU8(205),this.writeU16(e)):e<4294967296?(this.writeU8(206),this.writeU32(e)):(this.writeU8(207),this.writeU64(e)):e>=-32?this.writeU8(224|e+32):e>=-128?(this.writeU8(208),this.writeI8(e)):e>=-32768?(this.writeU8(209),this.writeI16(e)):e>=-2147483648?(this.writeU8(210),this.writeI32(e)):(this.writeU8(211),this.writeI64(e)):this.forceFloat32?(this.writeU8(202),this.writeF32(e)):(this.writeU8(203),this.writeF64(e))},e.prototype.writeStringHeader=function(e){if(e<32)this.writeU8(160+e);else if(e<256)this.writeU8(217),this.writeU8(e);else if(e<65536)this.writeU8(218),this.writeU16(e);else{if(!(e<4294967296))throw new Error("Too long string: "+e+" bytes in UTF-8");this.writeU8(219),this.writeU32(e)}},e.prototype.encodeString=function(e){if(e.length>Bt){var t=Nt(e);this.ensureBufferSizeToWrite(5+t),this.writeStringHeader(t),Lt(e,this.bytes,this.pos),this.pos+=t}else t=Nt(e),this.ensureBufferSizeToWrite(5+t),this.writeStringHeader(t),function(e,t,n){for(var r=e.length,o=n,i=0;i<r;){var s=e.charCodeAt(i++);if(0!=(4294967168&s)){if(0==(4294965248&s))t[o++]=s>>6&31|192;else{if(s>=55296&&s<=56319&&i<r){var a=e.charCodeAt(i);56320==(64512&a)&&(++i,s=((1023&s)<<10)+(1023&a)+65536)}0==(4294901760&s)?(t[o++]=s>>12&15|224,t[o++]=s>>6&63|128):(t[o++]=s>>18&7|240,t[o++]=s>>12&63|128,t[o++]=s>>6&63|128)}t[o++]=63&s|128}else t[o++]=s}}(e,this.bytes,this.pos),this.pos+=t},e.prototype.encodeObject=function(e,t){var n=this.extensionCodec.tryToEncode(e,this.context);if(null!=n)this.encodeExtension(n);else if(Array.isArray(e))this.encodeArray(e,t);else if(ArrayBuffer.isView(e))this.encodeBinary(e);else{if("object"!=typeof e)throw new Error("Unrecognized object: "+Object.prototype.toString.apply(e));this.encodeMap(e,t)}},e.prototype.encodeBinary=function(e){var t=e.byteLength;if(t<256)this.writeU8(196),this.writeU8(t);else if(t<65536)this.writeU8(197),this.writeU16(t);else{if(!(t<4294967296))throw new Error("Too large binary: "+t);this.writeU8(198),this.writeU32(t)}var n=Vt(e);this.writeU8a(n)},e.prototype.encodeArray=function(e,t){var n=e.length;if(n<16)this.writeU8(144+n);else if(n<65536)this.writeU8(220),this.writeU16(n);else{if(!(n<4294967296))throw new Error("Too large array: "+n);this.writeU8(221),this.writeU32(n)}for(var r=0,o=e;r<o.length;r++){var i=o[r];this.doEncode(i,t+1)}},e.prototype.countWithoutUndefined=function(e,t){for(var n=0,r=0,o=t;r<o.length;r++)void 0!==e[o[r]]&&n++;return n},e.prototype.encodeMap=function(e,t){var n=Object.keys(e);this.sortKeys&&n.sort();var r=this.ignoreUndefined?this.countWithoutUndefined(e,n):n.length;if(r<16)this.writeU8(128+r);else if(r<65536)this.writeU8(222),this.writeU16(r);else{if(!(r<4294967296))throw new Error("Too large map object: "+r);this.writeU8(223),this.writeU32(r)}for(var o=0,i=n;o<i.length;o++){var s=i[o],a=e[s];this.ignoreUndefined&&void 0===a||(this.encodeString(s),this.doEncode(a,t+1))}},e.prototype.encodeExtension=function(e){var t=e.data.length;if(1===t)this.writeU8(212);else if(2===t)this.writeU8(213);else if(4===t)this.writeU8(214);else if(8===t)this.writeU8(215);else if(16===t)this.writeU8(216);else if(t<256)this.writeU8(199),this.writeU8(t);else if(t<65536)this.writeU8(200),this.writeU16(t);else{if(!(t<4294967296))throw new Error("Too large extension object: "+t);this.writeU8(201),this.writeU32(t)}this.writeI8(e.type),this.writeU8a(e.data)},e.prototype.writeU8=function(e){this.ensureBufferSizeToWrite(1),this.view.setUint8(this.pos,e),this.pos++},e.prototype.writeU8a=function(e){var t=e.length;this.ensureBufferSizeToWrite(t),this.bytes.set(e,this.pos),this.pos+=t},e.prototype.writeI8=function(e){this.ensureBufferSizeToWrite(1),this.view.setInt8(this.pos,e),this.pos++},e.prototype.writeU16=function(e){this.ensureBufferSizeToWrite(2),this.view.setUint16(this.pos,e),this.pos+=2},e.prototype.writeI16=function(e){this.ensureBufferSizeToWrite(2),this.view.setInt16(this.pos,e),this.pos+=2},e.prototype.writeU32=function(e){this.ensureBufferSizeToWrite(4),this.view.setUint32(this.pos,e),this.pos+=4},e.prototype.writeI32=function(e){this.ensureBufferSizeToWrite(4),this.view.setInt32(this.pos,e),this.pos+=4},e.prototype.writeF32=function(e){this.ensureBufferSizeToWrite(4),this.view.setFloat32(this.pos,e),this.pos+=4},e.prototype.writeF64=function(e){this.ensureBufferSizeToWrite(8),this.view.setFloat64(this.pos,e),this.pos+=8},e.prototype.writeU64=function(e){this.ensureBufferSizeToWrite(8),function(e,t,n){var r=n/4294967296,o=n;e.setUint32(t,r),e.setUint32(t+4,o)}(this.view,this.pos,e),this.pos+=8},e.prototype.writeI64=function(e){this.ensureBufferSizeToWrite(8),Pt(this.view,this.pos,e),this.pos+=8},e}();function Xt(e){return(e<0?"-":"")+"0x"+Math.abs(e).toString(16).padStart(2,"0")}var Yt,Gt,Qt,Zt,en=function(){function e(e,t){void 0===e&&(e=16),void 0===t&&(t=16),this.maxKeyLength=e,this.maxLengthPerKey=t,this.hit=0,this.miss=0,this.caches=[];for(var n=0;n<this.maxKeyLength;n++)this.caches.push([])}return e.prototype.canBeCached=function(e){return e>0&&e<=this.maxKeyLength},e.prototype.find=function(e,t,n){e:for(var r=0,o=this.caches[n-1];r<o.length;r++){for(var i=o[r],s=i.bytes,a=0;a<n;a++)if(s[a]!==e[t+a])continue e;return i.str}return null},e.prototype.store=function(e,t){var n=this.caches[e.length-1],r={bytes:e,str:t};n.length>=this.maxLengthPerKey?n[Math.random()*n.length|0]=r:n.push(r)},e.prototype.decode=function(e,t,n){var r=this.find(e,t,n);if(null!=r)return this.hit++,r;this.miss++;var o=Mt(e,t,n),i=Uint8Array.prototype.slice.call(e,t,t+n);return this.store(i,o),o},e}(),tn=function(e,t){var n,r,o,i,s={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function a(i){return function(a){return function(i){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(o=2&i[0]?r.return:i[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,i[1])).done)return o;switch(r=0,o&&(i=[2&i[0],o.value]),i[0]){case 0:case 1:o=i;break;case 4:return s.label++,{value:i[1],done:!1};case 5:s.label++,r=i[1],i=[0];continue;case 7:i=s.ops.pop(),s.trys.pop();continue;default:if(!((o=(o=s.trys).length>0&&o[o.length-1])||6!==i[0]&&2!==i[0])){s=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]<o[3])){s.label=i[1];break}if(6===i[0]&&s.label<o[1]){s.label=o[1],o=i;break}if(o&&s.label<o[2]){s.label=o[2],s.ops.push(i);break}o[2]&&s.ops.pop(),s.trys.pop();continue}i=t.call(e,s)}catch(e){i=[6,e],r=0}finally{n=o=0}if(5&i[0])throw i[1];return{value:i[0]?i[1]:void 0,done:!0}}([i,a])}}},nn=function(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t,n=e[Symbol.asyncIterator];return n?n.call(e):(e="function"==typeof __values?__values(e):e[Symbol.iterator](),t={},r("next"),r("throw"),r("return"),t[Symbol.asyncIterator]=function(){return this},t);function r(n){t[n]=e[n]&&function(t){return new Promise((function(r,o){!function(e,t,n,r){Promise.resolve(r).then((function(t){e({value:t,done:n})}),t)}(r,o,(t=e[n](t)).done,t.value)}))}}},rn=function(e){return this instanceof rn?(this.v=e,this):new rn(e)},on=function(e,t,n){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var r,o=n.apply(e,t||[]),i=[];return r={},s("next"),s("throw"),s("return"),r[Symbol.asyncIterator]=function(){return this},r;function s(e){o[e]&&(r[e]=function(t){return new Promise((function(n,r){i.push([e,t,n,r])>1||a(e,t)}))})}function a(e,t){try{(n=o[e](t)).value instanceof rn?Promise.resolve(n.value.v).then(c,l):h(i[0][2],n)}catch(e){h(i[0][3],e)}var n}function c(e){a("next",e)}function l(e){a("throw",e)}function h(e,t){e(t),i.shift(),i.length&&a(i[0][0],i[0][1])}},sn=new DataView(new ArrayBuffer(0)),an=new Uint8Array(sn.buffer),cn=function(){try{sn.getInt8(0)}catch(e){return e.constructor}throw new Error("never reached")}(),ln=new cn("Insufficient data"),hn=new en,un=function(){function e(e,t,n,r,o,i,s,a){void 0===e&&(e=qt.defaultCodec),void 0===t&&(t=void 0),void 0===n&&(n=Rt),void 0===r&&(r=Rt),void 0===o&&(o=Rt),void 0===i&&(i=Rt),void 0===s&&(s=Rt),void 0===a&&(a=hn),this.extensionCodec=e,this.context=t,this.maxStrLength=n,this.maxBinLength=r,this.maxArrayLength=o,this.maxMapLength=i,this.maxExtLength=s,this.keyDecoder=a,this.totalPos=0,this.pos=0,this.view=sn,this.bytes=an,this.headByte=-1,this.stack=[]}return e.prototype.reinitializeState=function(){this.totalPos=0,this.headByte=-1,this.stack.length=0},e.prototype.setBuffer=function(e){this.bytes=Vt(e),this.view=function(e){if(e instanceof ArrayBuffer)return new DataView(e);var t=Vt(e);return new DataView(t.buffer,t.byteOffset,t.byteLength)}(this.bytes),this.pos=0},e.prototype.appendBuffer=function(e){if(-1!==this.headByte||this.hasRemaining(1)){var t=this.bytes.subarray(this.pos),n=Vt(e),r=new Uint8Array(t.length+n.length);r.set(t),r.set(n,t.length),this.setBuffer(r)}else this.setBuffer(e)},e.prototype.hasRemaining=function(e){return this.view.byteLength-this.pos>=e},e.prototype.createExtraByteError=function(e){var t=this.view,n=this.pos;return new RangeError("Extra "+(t.byteLength-n)+" of "+t.byteLength+" byte(s) found at buffer["+e+"]")},e.prototype.decode=function(e){this.reinitializeState(),this.setBuffer(e);var t=this.doDecodeSync();if(this.hasRemaining(1))throw this.createExtraByteError(this.pos);return t},e.prototype.decodeMulti=function(e){return tn(this,(function(t){switch(t.label){case 0:this.reinitializeState(),this.setBuffer(e),t.label=1;case 1:return this.hasRemaining(1)?[4,this.doDecodeSync()]:[3,3];case 2:return t.sent(),[3,1];case 3:return[2]}}))},e.prototype.decodeAsync=function(e){var t,n,r,o,i,s,a;return i=this,void 0,a=function(){var i,s,a,c,l,h,u,d;return tn(this,(function(p){switch(p.label){case 0:i=!1,p.label=1;case 1:p.trys.push([1,6,7,12]),t=nn(e),p.label=2;case 2:return[4,t.next()];case 3:if((n=p.sent()).done)return[3,5];if(a=n.value,i)throw this.createExtraByteError(this.totalPos);this.appendBuffer(a);try{s=this.doDecodeSync(),i=!0}catch(e){if(!(e instanceof cn))throw e}this.totalPos+=this.pos,p.label=4;case 4:return[3,2];case 5:return[3,12];case 6:return c=p.sent(),r={error:c},[3,12];case 7:return p.trys.push([7,,10,11]),n&&!n.done&&(o=t.return)?[4,o.call(t)]:[3,9];case 8:p.sent(),p.label=9;case 9:return[3,11];case 10:if(r)throw r.error;return[7];case 11:return[7];case 12:if(i){if(this.hasRemaining(1))throw this.createExtraByteError(this.totalPos);return[2,s]}throw h=(l=this).headByte,u=l.pos,d=l.totalPos,new RangeError("Insufficient data in parsing "+Xt(h)+" at "+d+" ("+u+" in the current buffer)")}}))},new((s=void 0)||(s=Promise))((function(e,t){function n(e){try{o(a.next(e))}catch(e){t(e)}}function r(e){try{o(a.throw(e))}catch(e){t(e)}}function o(t){var o;t.done?e(t.value):(o=t.value,o instanceof s?o:new s((function(e){e(o)}))).then(n,r)}o((a=a.apply(i,[])).next())}))},e.prototype.decodeArrayStream=function(e){return this.decodeMultiAsync(e,!0)},e.prototype.decodeStream=function(e){return this.decodeMultiAsync(e,!1)},e.prototype.decodeMultiAsync=function(e,t){return on(this,arguments,(function(){var n,r,o,i,s,a,c,l,h;return tn(this,(function(u){switch(u.label){case 0:n=t,r=-1,u.label=1;case 1:u.trys.push([1,13,14,19]),o=nn(e),u.label=2;case 2:return[4,rn(o.next())];case 3:if((i=u.sent()).done)return[3,12];if(s=i.value,t&&0===r)throw this.createExtraByteError(this.totalPos);this.appendBuffer(s),n&&(r=this.readArraySize(),n=!1,this.complete()),u.label=4;case 4:u.trys.push([4,9,,10]),u.label=5;case 5:return[4,rn(this.doDecodeSync())];case 6:return[4,u.sent()];case 7:return u.sent(),0==--r?[3,8]:[3,5];case 8:return[3,10];case 9:if(!((a=u.sent())instanceof cn))throw a;return[3,10];case 10:this.totalPos+=this.pos,u.label=11;case 11:return[3,2];case 12:return[3,19];case 13:return c=u.sent(),l={error:c},[3,19];case 14:return u.trys.push([14,,17,18]),i&&!i.done&&(h=o.return)?[4,rn(h.call(o))]:[3,16];case 15:u.sent(),u.label=16;case 16:return[3,18];case 17:if(l)throw l.error;return[7];case 18:return[7];case 19:return[2]}}))}))},e.prototype.doDecodeSync=function(){e:for(;;){var e=this.readHeadByte(),t=void 0;if(e>=224)t=e-256;else if(e<192)if(e<128)t=e;else if(e<144){if(0!=(r=e-128)){this.pushMapState(r),this.complete();continue e}t={}}else if(e<160){if(0!=(r=e-144)){this.pushArrayState(r),this.complete();continue e}t=[]}else{var n=e-160;t=this.decodeUtf8String(n,0)}else if(192===e)t=null;else if(194===e)t=!1;else if(195===e)t=!0;else if(202===e)t=this.readF32();else if(203===e)t=this.readF64();else if(204===e)t=this.readU8();else if(205===e)t=this.readU16();else if(206===e)t=this.readU32();else if(207===e)t=this.readU64();else if(208===e)t=this.readI8();else if(209===e)t=this.readI16();else if(210===e)t=this.readI32();else if(211===e)t=this.readI64();else if(217===e)n=this.lookU8(),t=this.decodeUtf8String(n,1);else if(218===e)n=this.lookU16(),t=this.decodeUtf8String(n,2);else if(219===e)n=this.lookU32(),t=this.decodeUtf8String(n,4);else if(220===e){if(0!==(r=this.readU16())){this.pushArrayState(r),this.complete();continue e}t=[]}else if(221===e){if(0!==(r=this.readU32())){this.pushArrayState(r),this.complete();continue e}t=[]}else if(222===e){if(0!==(r=this.readU16())){this.pushMapState(r),this.complete();continue e}t={}}else if(223===e){if(0!==(r=this.readU32())){this.pushMapState(r),this.complete();continue e}t={}}else if(196===e){var r=this.lookU8();t=this.decodeBinary(r,1)}else if(197===e)r=this.lookU16(),t=this.decodeBinary(r,2);else if(198===e)r=this.lookU32(),t=this.decodeBinary(r,4);else if(212===e)t=this.decodeExtension(1,0);else if(213===e)t=this.decodeExtension(2,0);else if(214===e)t=this.decodeExtension(4,0);else if(215===e)t=this.decodeExtension(8,0);else if(216===e)t=this.decodeExtension(16,0);else if(199===e)r=this.lookU8(),t=this.decodeExtension(r,1);else if(200===e)r=this.lookU16(),t=this.decodeExtension(r,2);else{if(201!==e)throw new zt("Unrecognized type byte: "+Xt(e));r=this.lookU32(),t=this.decodeExtension(r,4)}this.complete();for(var o=this.stack;o.length>0;){var i=o[o.length-1];if(0===i.type){if(i.array[i.position]=t,i.position++,i.position!==i.size)continue e;o.pop(),t=i.array}else{if(1===i.type){if("string"!=(s=typeof t)&&"number"!==s)throw new zt("The type of key must be string or number but "+typeof t);if("__proto__"===t)throw new zt("The key __proto__ is not allowed");i.key=t,i.type=2;continue e}if(i.map[i.key]=t,i.readCount++,i.readCount!==i.size){i.key=null,i.type=1;continue e}o.pop(),t=i.map}}return t}var s},e.prototype.readHeadByte=function(){return-1===this.headByte&&(this.headByte=this.readU8()),this.headByte},e.prototype.complete=function(){this.headByte=-1},e.prototype.readArraySize=function(){var e=this.readHeadByte();switch(e){case 220:return this.readU16();case 221:return this.readU32();default:if(e<160)return e-144;throw new zt("Unrecognized array type byte: "+Xt(e))}},e.prototype.pushMapState=function(e){if(e>this.maxMapLength)throw new zt("Max length exceeded: map length ("+e+") > maxMapLengthLength ("+this.maxMapLength+")");this.stack.push({type:1,size:e,key:null,readCount:0,map:{}})},e.prototype.pushArrayState=function(e){if(e>this.maxArrayLength)throw new zt("Max length exceeded: array length ("+e+") > maxArrayLength ("+this.maxArrayLength+")");this.stack.push({type:0,size:e,array:new Array(e),position:0})},e.prototype.decodeUtf8String=function(e,t){var n;if(e>this.maxStrLength)throw new zt("Max length exceeded: UTF-8 byte length ("+e+") > maxStrLength ("+this.maxStrLength+")");if(this.bytes.byteLength<this.pos+t+e)throw ln;var r,o=this.pos+t;return r=this.stateIsMapKey()&&(null===(n=this.keyDecoder)||void 0===n?void 0:n.canBeCached(e))?this.keyDecoder.decode(this.bytes,o,e):e>jt?function(e,t,n){var r=e.subarray(t,t+n);return Ft.decode(r)}(this.bytes,o,e):Mt(this.bytes,o,e),this.pos+=t+e,r},e.prototype.stateIsMapKey=function(){return this.stack.length>0&&1===this.stack[this.stack.length-1].type},e.prototype.decodeBinary=function(e,t){if(e>this.maxBinLength)throw new zt("Max length exceeded: bin length ("+e+") > maxBinLength ("+this.maxBinLength+")");if(!this.hasRemaining(e+t))throw ln;var n=this.pos+t,r=this.bytes.subarray(n,n+e);return this.pos+=t+e,r},e.prototype.decodeExtension=function(e,t){if(e>this.maxExtLength)throw new zt("Max length exceeded: ext length ("+e+") > maxExtLength ("+this.maxExtLength+")");var n=this.view.getInt8(this.pos+t),r=this.decodeBinary(e,t+1);return this.extensionCodec.decode(r,n,this.context)},e.prototype.lookU8=function(){return this.view.getUint8(this.pos)},e.prototype.lookU16=function(){return this.view.getUint16(this.pos)},e.prototype.lookU32=function(){return this.view.getUint32(this.pos)},e.prototype.readU8=function(){var e=this.view.getUint8(this.pos);return this.pos++,e},e.prototype.readI8=function(){var e=this.view.getInt8(this.pos);return this.pos++,e},e.prototype.readU16=function(){var e=this.view.getUint16(this.pos);return this.pos+=2,e},e.prototype.readI16=function(){var e=this.view.getInt16(this.pos);return this.pos+=2,e},e.prototype.readU32=function(){var e=this.view.getUint32(this.pos);return this.pos+=4,e},e.prototype.readI32=function(){var e=this.view.getInt32(this.pos);return this.pos+=4,e},e.prototype.readU64=function(){var e,t,n=(e=this.view,t=this.pos,4294967296*e.getUint32(t)+e.getUint32(t+4));return this.pos+=8,n},e.prototype.readI64=function(){var e=Ut(this.view,this.pos);return this.pos+=8,e},e.prototype.readF32=function(){var e=this.view.getFloat32(this.pos);return this.pos+=4,e},e.prototype.readF64=function(){var e=this.view.getFloat64(this.pos);return this.pos+=8,e},e}();!function(e){e[e.Invocation=1]="Invocation",e[e.StreamItem=2]="StreamItem",e[e.Completion=3]="Completion",e[e.StreamInvocation=4]="StreamInvocation",e[e.CancelInvocation=5]="CancelInvocation",e[e.Ping=6]="Ping",e[e.Close=7]="Close"}(Yt||(Yt={})),function(e){e[e.None=0]="None",e[e.WebSockets=1]="WebSockets",e[e.ServerSentEvents=2]="ServerSentEvents",e[e.LongPolling=4]="LongPolling"}(Gt||(Gt={})),function(e){e[e.Text=1]="Text",e[e.Binary=2]="Binary"}(Qt||(Qt={}));class dn{constructor(){}log(e,t){}}dn.instance=new dn,function(e){e[e.Trace=0]="Trace",e[e.Debug=1]="Debug",e[e.Information=2]="Information",e[e.Warning=3]="Warning",e[e.Error=4]="Error",e[e.Critical=5]="Critical",e[e.None=6]="None"}(Zt||(Zt={}));class pn{static write(e){let t=e.byteLength||e.length;const n=[];do{let e=127&t;t>>=7,t>0&&(e|=128),n.push(e)}while(t>0);t=e.byteLength||e.length;const r=new Uint8Array(n.length+t);return r.set(n,0),r.set(e,n.length),r.buffer}static parse(e){const t=[],n=new Uint8Array(e),r=[0,7,14,21,28];for(let o=0;o<e.byteLength;){let i,s=0,a=0;do{i=n[o+s],a|=(127&i)<<r[s],s++}while(s<Math.min(5,e.byteLength-o)&&0!=(128&i));if(0!=(128&i)&&s<5)throw new Error("Cannot read message size.");if(5===s&&i>7)throw new Error("Messages bigger than 2GB are not supported.");if(!(n.byteLength>=o+s+a))throw new Error("Incomplete message.");t.push(n.slice?n.slice(o+s,o+s+a):n.subarray(o+s,o+s+a)),o=o+s+a}return t}}const fn=new Uint8Array([145,Yt.Ping]);class gn{constructor(e){this.name="messagepack",this.version=1,this.transferFormat=Qt.Binary,this._errorResult=1,this._voidResult=2,this._nonVoidResult=3,e=e||{},this._encoder=new Kt(e.extensionCodec,e.context,e.maxDepth,e.initialBufferSize,e.sortKeys,e.forceFloat32,e.ignoreUndefined,e.forceIntegerToFloat),this._decoder=new un(e.extensionCodec,e.context,e.maxStrLength,e.maxBinLength,e.maxArrayLength,e.maxMapLength,e.maxExtLength)}parseMessages(e,t){if(!(n=e)||"undefined"==typeof ArrayBuffer||!(n instanceof ArrayBuffer||n.constructor&&"ArrayBuffer"===n.constructor.name))throw new Error("Invalid input for MessagePack hub protocol. Expected an ArrayBuffer.");var n;null===t&&(t=dn.instance);const r=pn.parse(e),o=[];for(const e of r){const n=this._parseMessage(e,t);n&&o.push(n)}return o}writeMessage(e){switch(e.type){case Yt.Invocation:return this._writeInvocation(e);case Yt.StreamInvocation:return this._writeStreamInvocation(e);case Yt.StreamItem:return this._writeStreamItem(e);case Yt.Completion:return this._writeCompletion(e);case Yt.Ping:return pn.write(fn);case Yt.CancelInvocation:return this._writeCancelInvocation(e);default:throw new Error("Invalid message type.")}}_parseMessage(e,t){if(0===e.length)throw new Error("Invalid payload.");const n=this._decoder.decode(e);if(0===n.length||!(n instanceof Array))throw new Error("Invalid payload.");const r=n[0];switch(r){case Yt.Invocation:return this._createInvocationMessage(this._readHeaders(n),n);case Yt.StreamItem:return this._createStreamItemMessage(this._readHeaders(n),n);case Yt.Completion:return this._createCompletionMessage(this._readHeaders(n),n);case Yt.Ping:return this._createPingMessage(n);case Yt.Close:return this._createCloseMessage(n);default:return t.log(Zt.Information,"Unknown message type '"+r+"' ignored."),null}}_createCloseMessage(e){if(e.length<2)throw new Error("Invalid payload for Close message.");return{allowReconnect:e.length>=3?e[2]:void 0,error:e[1],type:Yt.Close}}_createPingMessage(e){if(e.length<1)throw new Error("Invalid payload for Ping message.");return{type:Yt.Ping}}_createInvocationMessage(e,t){if(t.length<5)throw new Error("Invalid payload for Invocation message.");const n=t[2];return n?{arguments:t[4],headers:e,invocationId:n,streamIds:[],target:t[3],type:Yt.Invocation}:{arguments:t[4],headers:e,streamIds:[],target:t[3],type:Yt.Invocation}}_createStreamItemMessage(e,t){if(t.length<4)throw new Error("Invalid payload for StreamItem message.");return{headers:e,invocationId:t[2],item:t[3],type:Yt.StreamItem}}_createCompletionMessage(e,t){if(t.length<4)throw new Error("Invalid payload for Completion message.");const n=t[3];if(n!==this._voidResult&&t.length<5)throw new Error("Invalid payload for Completion message.");let r,o;switch(n){case this._errorResult:r=t[4];break;case this._nonVoidResult:o=t[4]}return{error:r,headers:e,invocationId:t[2],result:o,type:Yt.Completion}}_writeInvocation(e){let t;return t=e.streamIds?this._encoder.encode([Yt.Invocation,e.headers||{},e.invocationId||null,e.target,e.arguments,e.streamIds]):this._encoder.encode([Yt.Invocation,e.headers||{},e.invocationId||null,e.target,e.arguments]),pn.write(t.slice())}_writeStreamInvocation(e){let t;return t=e.streamIds?this._encoder.encode([Yt.StreamInvocation,e.headers||{},e.invocationId,e.target,e.arguments,e.streamIds]):this._encoder.encode([Yt.StreamInvocation,e.headers||{},e.invocationId,e.target,e.arguments]),pn.write(t.slice())}_writeStreamItem(e){const t=this._encoder.encode([Yt.StreamItem,e.headers||{},e.invocationId,e.item]);return pn.write(t.slice())}_writeCompletion(e){const t=e.error?this._errorResult:e.result?this._nonVoidResult:this._voidResult;let n;switch(t){case this._errorResult:n=this._encoder.encode([Yt.Completion,e.headers||{},e.invocationId,t,e.error]);break;case this._voidResult:n=this._encoder.encode([Yt.Completion,e.headers||{},e.invocationId,t]);break;case this._nonVoidResult:n=this._encoder.encode([Yt.Completion,e.headers||{},e.invocationId,t,e.result])}return pn.write(n.slice())}_writeCancelInvocation(e){const t=this._encoder.encode([Yt.CancelInvocation,e.headers||{},e.invocationId]);return pn.write(t.slice())}_readHeaders(e){const t=e[1];if("object"!=typeof t)throw new Error("Invalid headers.");return t}}let mn=!1;function yn(){const e=document.querySelector("#blazor-error-ui");e&&(e.style.display="block"),mn||(mn=!0,document.querySelectorAll("#blazor-error-ui .reload").forEach((e=>{e.onclick=function(e){location.reload(),e.preventDefault()}})),document.querySelectorAll("#blazor-error-ui .dismiss").forEach((e=>{e.onclick=function(e){const t=document.querySelector("#blazor-error-ui");t&&(t.style.display="none"),e.preventDefault()}})))}const wn="function"==typeof TextDecoder?new TextDecoder("utf-8"):null,vn=wn?wn.decode.bind(wn):function(e){let t=0;const n=e.length,r=[],o=[];for(;t<n;){const n=e[t++];if(0===n)break;if(0==(128&n))r.push(n);else if(192==(224&n)){const o=63&e[t++];r.push((31&n)<<6|o)}else if(224==(240&n)){const o=63&e[t++],i=63&e[t++];r.push((31&n)<<12|o<<6|i)}else if(240==(248&n)){let o=(7&n)<<18|(63&e[t++])<<12|(63&e[t++])<<6|63&e[t++];o>65535&&(o-=65536,r.push(o>>>10&1023|55296),o=56320|1023&o),r.push(o)}r.length>1024&&(o.push(String.fromCharCode.apply(null,r)),r.length=0)}return o.push(String.fromCharCode.apply(null,r)),o.join("")},bn=Math.pow(2,32),_n=Math.pow(2,21)-1;function En(e,t){return e[t]|e[t+1]<<8|e[t+2]<<16|e[t+3]<<24}function Sn(e,t){return e[t]+(e[t+1]<<8)+(e[t+2]<<16)+(e[t+3]<<24>>>0)}function Cn(e,t){const n=Sn(e,t+4);if(n>_n)throw new Error(`Cannot read uint64 with high order part ${n}, because the result would exceed Number.MAX_SAFE_INTEGER.`);return n*bn+Sn(e,t)}class In{constructor(e){this.batchData=e;const t=new Dn(e);this.arrayRangeReader=new Rn(e),this.arrayBuilderSegmentReader=new Pn(e),this.diffReader=new kn(e),this.editReader=new Tn(e,t),this.frameReader=new xn(e,t)}updatedComponents(){return En(this.batchData,this.batchData.length-20)}referenceFrames(){return En(this.batchData,this.batchData.length-16)}disposedComponentIds(){return En(this.batchData,this.batchData.length-12)}disposedEventHandlerIds(){return En(this.batchData,this.batchData.length-8)}updatedComponentsEntry(e,t){const n=e+4*t;return En(this.batchData,n)}referenceFramesEntry(e,t){return e+20*t}disposedComponentIdsEntry(e,t){const n=e+4*t;return En(this.batchData,n)}disposedEventHandlerIdsEntry(e,t){const n=e+8*t;return Cn(this.batchData,n)}}class kn{constructor(e){this.batchDataUint8=e}componentId(e){return En(this.batchDataUint8,e)}edits(e){return e+4}editsEntry(e,t){return e+16*t}}class Tn{constructor(e,t){this.batchDataUint8=e,this.stringReader=t}editType(e){return En(this.batchDataUint8,e)}siblingIndex(e){return En(this.batchDataUint8,e+4)}newTreeIndex(e){return En(this.batchDataUint8,e+8)}moveToSiblingIndex(e){return En(this.batchDataUint8,e+8)}removedAttributeName(e){const t=En(this.batchDataUint8,e+12);return this.stringReader.readString(t)}}class xn{constructor(e,t){this.batchDataUint8=e,this.stringReader=t}frameType(e){return En(this.batchDataUint8,e)}subtreeLength(e){return En(this.batchDataUint8,e+4)}elementReferenceCaptureId(e){const t=En(this.batchDataUint8,e+4);return this.stringReader.readString(t)}componentId(e){return En(this.batchDataUint8,e+8)}elementName(e){const t=En(this.batchDataUint8,e+8);return this.stringReader.readString(t)}textContent(e){const t=En(this.batchDataUint8,e+4);return this.stringReader.readString(t)}markupContent(e){const t=En(this.batchDataUint8,e+4);return this.stringReader.readString(t)}attributeName(e){const t=En(this.batchDataUint8,e+4);return this.stringReader.readString(t)}attributeValue(e){const t=En(this.batchDataUint8,e+8);return this.stringReader.readString(t)}attributeEventHandlerId(e){return Cn(this.batchDataUint8,e+12)}}class Dn{constructor(e){this.batchDataUint8=e,this.stringTableStartIndex=En(e,e.length-4)}readString(e){if(-1===e)return null;{const n=En(this.batchDataUint8,this.stringTableStartIndex+4*e),r=function(e,t){let n=0,r=0;for(let o=0;o<4;o++){const i=e[t+o];if(n|=(127&i)<<r,i<128)break;r+=7}return n}(this.batchDataUint8,n),o=n+((t=r)<128?1:t<16384?2:t<2097152?3:4),i=new Uint8Array(this.batchDataUint8.buffer,this.batchDataUint8.byteOffset+o,r);return vn(i)}var t}}class Rn{constructor(e){this.batchDataUint8=e}count(e){return En(this.batchDataUint8,e)}values(e){return e+4}}class Pn{constructor(e){this.batchDataUint8=e}offset(e){return 0}count(e){return En(this.batchDataUint8,e)}values(e){return e+4}}var Un;!function(e){e[e.Trace=0]="Trace",e[e.Debug=1]="Debug",e[e.Information=2]="Information",e[e.Warning=3]="Warning",e[e.Error=4]="Error",e[e.Critical=5]="Critical",e[e.None=6]="None"}(Un||(Un={}));class An{constructor(e,t){this.nextBatchId=2,this.browserRendererId=e,this.logger=t}static getOrCreate(e){return An.instance||(An.instance=new An(0,e)),this.instance}async processBatch(e,t,n){if(e<this.nextBatchId)return await this.completeBatch(n,e),void this.logger.log(Un.Debug,`Batch ${e} already processed. Waiting for batch ${this.nextBatchId}.`);if(e>this.nextBatchId)return this.fatalError?(this.logger.log(Un.Debug,`Received a new batch ${e} but errored out on a previous batch ${this.nextBatchId-1}`),void await n.send("OnRenderCompleted",this.nextBatchId-1,this.fatalError.toString())):void this.logger.log(Un.Debug,`Waiting for batch ${this.nextBatchId}. Batch ${e} not processed.`);try{this.nextBatchId++,this.logger.log(Un.Debug,`Applying batch ${e}.`),function(e,t){const n=de[e];if(!n)throw new Error(`There is no browser renderer with ID ${e}.`);const r=t.arrayRangeReader,o=t.updatedComponents(),i=r.values(o),s=r.count(o),a=t.referenceFrames(),c=r.values(a),l=t.diffReader;for(let e=0;e<s;e++){const r=t.updatedComponentsEntry(i,e),o=l.componentId(r),s=l.edits(r);n.updateComponent(t,o,s,c)}const h=t.disposedComponentIds(),u=r.values(h),d=r.count(h);for(let e=0;e<d;e++){const r=t.disposedComponentIdsEntry(u,e);n.disposeComponent(r)}const p=t.disposedEventHandlerIds(),f=r.values(p),g=r.count(p);for(let e=0;e<g;e++){const r=t.disposedEventHandlerIdsEntry(f,e);n.disposeEventHandler(r)}pe&&(pe=!1,window.scrollTo&&window.scrollTo(0,0))}(this.browserRendererId,new In(t)),await this.completeBatch(n,e)}catch(t){throw this.fatalError=t.toString(),this.logger.log(Un.Error,`There was an error applying batch ${e}.`),n.send("OnRenderCompleted",e,t.toString()),t}}getLastBatchid(){return this.nextBatchId-1}async completeBatch(e,t){try{await e.send("OnRenderCompleted",t,null)}catch{this.logger.log(Un.Warning,`Failed to deliver completion notification for render '${t}'.`)}}}class Nn{log(e,t){}}Nn.instance=new Nn;class $n{constructor(e){this.minLevel=e}log(e,t){if(e>=this.minLevel){const n=`[${(new Date).toISOString()}] ${Un[e]}: ${t}`;switch(e){case Un.Critical:case Un.Error:console.error(n);break;case Un.Warning:console.warn(n);break;case Un.Information:console.info(n);break;default:console.log(n)}}}}class Bn{constructor(e,t){this.circuitId=void 0,this.components=e,this.applicationState=t}reconnect(e){if(!this.circuitId)throw new Error("Circuit host not initialized.");return e.state!==Ge.Connected?Promise.resolve(!1):e.invoke("ConnectCircuit",this.circuitId)}initialize(e){if(this.circuitId)throw new Error(`Circuit host '${this.circuitId}' already initialized.`);this.circuitId=e}async startCircuit(e){if(e.state!==Ge.Connected)return!1;const t=await e.invoke("StartCircuit",ye.getBaseURI(),ye.getLocationHref(),JSON.stringify(this.components.map((e=>e.toRecord()))),this.applicationState||"");return!!t&&(this.initialize(t),!0)}resolveElement(e){const t=function(e){const t=f.get(e);if(t)return f.delete(e),t}(e);if(t)return M(t,!0);const n=Number.parseInt(e);if(!Number.isNaN(n))return function(e,t){if(!e.parentNode)throw new Error(`Comment not connected to the DOM ${e.textContent}`);const n=e.parentNode,r=M(n,!0),o=J(r);return Array.from(n.childNodes).forEach((e=>o.push(e))),e[B]=r,t&&(e[L]=t,M(t)),M(e)}(this.components[n].start,this.components[n].end);throw new Error(`Invalid sequence number or identifier '${e}'.`)}}const Ln={configureSignalR:e=>{},logLevel:Un.Warning,reconnectionOptions:{maxRetries:8,retryIntervalMilliseconds:2e4,dialogId:"components-reconnect-modal"}};class Mn{constructor(e,t,n,r){this.maxRetries=t,this.document=n,this.logger=r,this.addedToDom=!1,this.modal=this.document.createElement("div"),this.modal.id=e,this.maxRetries=t,this.modal.style.cssText=["position: fixed","top: 0","right: 0","bottom: 0","left: 0","z-index: 1050","display: none","overflow: hidden","background-color: #fff","opacity: 0.8","text-align: center","font-weight: bold","transition: visibility 0s linear 500ms"].join(";"),this.message=this.document.createElement("h5"),this.message.style.cssText="margin-top: 20px",this.button=this.document.createElement("button"),this.button.style.cssText="margin:5px auto 5px",this.button.textContent="Retry";const o=this.document.createElement("a");o.addEventListener("click",(()=>location.reload())),o.textContent="reload",this.reloadParagraph=this.document.createElement("p"),this.reloadParagraph.textContent="Alternatively, ",this.reloadParagraph.appendChild(o),this.modal.appendChild(this.message),this.modal.appendChild(this.button),this.modal.appendChild(this.reloadParagraph),this.loader=this.getLoader(),this.message.after(this.loader),this.button.addEventListener("click",(async()=>{this.show();try{await Ne.reconnect()||this.rejected()}catch(e){this.logger.log(Un.Error,e),this.failed()}}))}show(){this.addedToDom||(this.addedToDom=!0,this.document.body.appendChild(this.modal)),this.modal.style.display="block",this.loader.style.display="inline-block",this.button.style.display="none",this.reloadParagraph.style.display="none",this.message.textContent="Attempting to reconnect to the server...",this.modal.style.visibility="hidden",setTimeout((()=>{this.modal.style.visibility="visible"}),0)}update(e){this.message.textContent=`Attempting to reconnect to the server: ${e} of ${this.maxRetries}`}hide(){this.modal.style.display="none"}failed(){this.button.style.display="block",this.reloadParagraph.style.display="none",this.loader.style.display="none";const e=this.document.createTextNode("Reconnection failed. Try "),t=this.document.createElement("a");t.textContent="reloading",t.setAttribute("href",""),t.addEventListener("click",(()=>location.reload()));const n=this.document.createTextNode(" the page if you're unable to reconnect.");this.message.replaceChildren(e,t,n)}rejected(){this.button.style.display="none",this.reloadParagraph.style.display="none",this.loader.style.display="none";const e=this.document.createTextNode("Could not reconnect to the server. "),t=this.document.createElement("a");t.textContent="Reload",t.setAttribute("href",""),t.addEventListener("click",(()=>location.reload()));const n=this.document.createTextNode(" the page to restore functionality.");this.message.replaceChildren(e,t,n)}getLoader(){const e=this.document.createElement("div");return e.style.cssText=["border: 0.3em solid #f3f3f3","border-top: 0.3em solid #3498db","border-radius: 50%","width: 2em","height: 2em","display: inline-block"].join(";"),e.animate([{transform:"rotate(0deg)"},{transform:"rotate(360deg)"}],{duration:2e3,iterations:1/0}),e}}class On{constructor(e,t,n){this.dialog=e,this.maxRetries=t,this.document=n,this.document=n;const r=this.document.getElementById(On.MaxRetriesId);r&&(r.innerText=this.maxRetries.toString())}show(){this.removeClasses(),this.dialog.classList.add(On.ShowClassName)}update(e){const t=this.document.getElementById(On.CurrentAttemptId);t&&(t.innerText=e.toString())}hide(){this.removeClasses(),this.dialog.classList.add(On.HideClassName)}failed(){this.removeClasses(),this.dialog.classList.add(On.FailedClassName)}rejected(){this.removeClasses(),this.dialog.classList.add(On.RejectedClassName)}removeClasses(){this.dialog.classList.remove(On.ShowClassName,On.HideClassName,On.FailedClassName,On.RejectedClassName)}}On.ShowClassName="components-reconnect-show",On.HideClassName="components-reconnect-hide",On.FailedClassName="components-reconnect-failed",On.RejectedClassName="components-reconnect-rejected",On.MaxRetriesId="components-reconnect-max-retries",On.CurrentAttemptId="components-reconnect-current-attempt";class Fn{constructor(e,t,n){this._currentReconnectionProcess=null,this._logger=e,this._reconnectionDisplay=t,this._reconnectCallback=n||Ne.reconnect}onConnectionDown(e,t){if(!this._reconnectionDisplay){const t=document.getElementById(e.dialogId);this._reconnectionDisplay=t?new On(t,e.maxRetries,document):new Mn(e.dialogId,e.maxRetries,document,this._logger)}this._currentReconnectionProcess||(this._currentReconnectionProcess=new jn(e,this._logger,this._reconnectCallback,this._reconnectionDisplay))}onConnectionUp(){this._currentReconnectionProcess&&(this._currentReconnectionProcess.dispose(),this._currentReconnectionProcess=null)}}class jn{constructor(e,t,n,r){this.logger=t,this.reconnectCallback=n,this.isDisposed=!1,this.reconnectDisplay=r,this.reconnectDisplay.show(),this.attemptPeriodicReconnection(e)}dispose(){this.isDisposed=!0,this.reconnectDisplay.hide()}async attemptPeriodicReconnection(e){for(let t=0;t<e.maxRetries;t++){this.reconnectDisplay.update(t+1);const n=0===t&&e.retryIntervalMilliseconds>jn.MaximumFirstRetryInterval?jn.MaximumFirstRetryInterval:e.retryIntervalMilliseconds;if(await this.delay(n),this.isDisposed)break;try{return await this.reconnectCallback()?void 0:void this.reconnectDisplay.rejected()}catch(e){this.logger.log(Un.Error,e)}}this.reconnectDisplay.failed()}delay(e){return new Promise((t=>setTimeout(t,e)))}}jn.MaximumFirstRetryInterval=3e3;const Hn=/^\s*Blazor-Component-State:(?<state>[a-zA-Z0-9+/=]+)$/;function Wn(e){var t;if(e.nodeType===Node.COMMENT_NODE){const n=e.textContent||"",r=Hn.exec(n),o=r&&r.groups&&r.groups.state;return o&&(null===(t=e.parentNode)||void 0===t||t.removeChild(e)),o}if(!e.hasChildNodes())return;const n=e.childNodes;for(let e=0;e<n.length;e++){const t=Wn(n[e]);if(t)return t}}function zn(e,t){if(!e.hasChildNodes())return[];const n=[],r=new Xn(e.childNodes);for(;r.next()&&r.currentElement;){const e=qn(r,t);if(e)n.push(e);else{const e=zn(r.currentElement,t);for(let t=0;t<e.length;t++){const r=e[t];n.push(r)}}}return n}const Jn=new RegExp(/^\s*Blazor:[^{]*(?<descriptor>.*)$/);function qn(e,t){const n=e.currentElement;if(n&&n.nodeType===Node.COMMENT_NODE&&n.textContent){const r=Jn.exec(n.textContent),o=r&&r.groups&&r.groups.descriptor;if(!o)return;try{const r=function(e){const t=JSON.parse(e),{type:n}=t;if("server"!==n&&"webassembly"!==n)throw new Error(`Invalid component type '${n}'.`);return t}(o);switch(t){case"webassembly":return function(e,t,n){const{type:r,assembly:o,typeName:i,parameterDefinitions:s,parameterValues:a,prerenderId:c}=e;if("webassembly"===r){if(!o)throw new Error("assembly must be defined when using a descriptor.");if(!i)throw new Error("typeName must be defined when using a descriptor.");if(c){const e=Vn(c,n);if(!e)throw new Error(`Could not find an end component comment for '${t}'`);return{type:r,assembly:o,typeName:i,parameterDefinitions:s&&atob(s),parameterValues:a&&atob(a),start:t,prerenderId:c,end:e}}return{type:r,assembly:o,typeName:i,parameterDefinitions:s&&atob(s),parameterValues:a&&atob(a),start:t}}}(r,n,e);case"server":return function(e,t,n){const{type:r,descriptor:o,sequence:i,prerenderId:s}=e;if("server"===r){if(!o)throw new Error("descriptor must be defined when using a descriptor.");if(void 0===i)throw new Error("sequence must be defined when using a descriptor.");if(!Number.isInteger(i))throw new Error(`Error parsing the sequence '${i}' for component '${JSON.stringify(e)}'`);if(s){const e=Vn(s,n);if(!e)throw new Error(`Could not find an end component comment for '${t}'`);return{type:r,sequence:i,descriptor:o,start:t,prerenderId:s,end:e}}return{type:r,sequence:i,descriptor:o,start:t}}}(r,n,e)}}catch(e){throw new Error(`Found malformed component comment at ${n.textContent}`)}}}function Vn(e,t){for(;t.next()&&t.currentElement;){const n=t.currentElement;if(n.nodeType!==Node.COMMENT_NODE)continue;if(!n.textContent)continue;const r=Jn.exec(n.textContent),o=r&&r[1];if(o)return Kn(o,e),n}}function Kn(e,t){const n=JSON.parse(e);if(1!==Object.keys(n).length)throw new Error(`Invalid end of component comment: '${e}'`);const r=n.prerenderId;if(!r)throw new Error(`End of component comment must have a value for the prerendered property: '${e}'`);if(r!==t)throw new Error(`End of component comment prerendered property must match the start comment prerender id: '${t}', '${r}'`)}class Xn{constructor(e){this.childNodes=e,this.currentIndex=-1,this.length=e.length}next(){return this.currentIndex++,this.currentIndex<this.length?(this.currentElement=this.childNodes[this.currentIndex],!0):(this.currentElement=void 0,!1)}}class Yn{constructor(e,t,n,r,o){this.type=e,this.start=t,this.end=n,this.sequence=r,this.descriptor=o}toRecord(){return{type:this.type,sequence:this.sequence,descriptor:this.descriptor}}}class Gn{constructor(e,t,n,r,o,i,s){this.id=Gn.globalId++,this.type=e,this.assembly=r,this.typeName=o,this.parameterDefinitions=i,this.parameterValues=s,this.start=t,this.end=n}}Gn.globalId=1;class Qn{constructor(){this.afterStartedCallbacks=[]}async importInitializersAsync(e,t){await Promise.all(e.map((e=>async function(e,n){const r=function(e){const t=document.baseURI;return t.endsWith("/")?`${t}${e}`:`${t}/${e}`}(n),o=await import(r);if(void 0===o)return;const{beforeStart:i,afterStarted:s}=o;return s&&e.afterStartedCallbacks.push(s),i?i(...t):void 0}(this,e))))}async invokeAfterStartedCallbacks(e){await C,await Promise.all(this.afterStartedCallbacks.map((t=>t(e))))}}let Zn,er=!1,tr=!1;async function nr(e){if(tr)throw new Error("Blazor has already started.");tr=!0;const t=function(e){const t={...Ln,...e};return e&&e.reconnectionOptions&&(t.reconnectionOptions={...Ln.reconnectionOptions,...e.reconnectionOptions}),t}(e),n=await async function(e){const t=await fetch("_blazor/initializers",{method:"GET",credentials:"include",cache:"no-cache"}),n=await t.json(),r=new Qn;return await r.importInitializersAsync(n,[e]),r}(t),r=new $n(t.logLevel);Ne.reconnect=async e=>{if(er)return!1;const n=e||await rr(t,r,s);return await s.reconnect(n)?(t.reconnectionHandler.onConnectionUp(),!0):(r.log(Un.Information,"Reconnection attempt to the circuit was rejected by the server. This may indicate that the associated state is no longer available on the server."),!1)},Ne.defaultReconnectionHandler=new Fn(r),t.reconnectionHandler=t.reconnectionHandler||Ne.defaultReconnectionHandler,r.log(Un.Information,"Starting up Blazor server-side application.");const o=function(e,t){return function(e){const t=zn(e,"server"),n=[];for(let e=0;e<t.length;e++){const r=t[e],o=new Yn(r.type,r.start,r.end,r.sequence,r.descriptor);n.push(o)}return n.sort(((e,t)=>e.sequence-t.sequence))}(e)}(document),i=Wn(document),s=new Bn(o,i||"");Ne._internal.navigationManager.listenForNavigationEvents(((e,t)=>Zn.send("OnLocationChanged",e,t))),Ne._internal.forceCloseConnection=()=>Zn.stop(),Ne._internal.sendJSDataStream=(e,t,n)=>function(e,t,n,r){setTimeout((async()=>{let o=5,i=(new Date).valueOf();try{const s=t instanceof Blob?t.size:t.byteLength;let a=0,c=0;for(;a<s;){const l=Math.min(r,s-a),h=await Ue(t,a,l);if(o--,o>1)await e.send("ReceiveJSDataChunk",n,c,h,null);else{if(!await e.invoke("ReceiveJSDataChunk",n,c,h,null))break;const t=(new Date).valueOf(),r=t-i;i=t,o=Math.max(1,Math.round(500/Math.max(1,r)))}a+=l,c++}}catch(t){await e.send("ReceiveJSDataChunk",n,-1,null,t.toString())}}),0)}(Zn,e,t,n);const a=await rr(t,r,s);if(!await s.startCircuit(a))return void r.log(Un.Error,"Failed to start the circuit.");let c=!1;const l=()=>{if(!c){const e=new FormData,t=s.circuitId;e.append("circuitId",t),c=navigator.sendBeacon("_blazor/disconnect",e)}};Ne.disconnect=l,window.addEventListener("unload",l,{capture:!1,once:!0}),r.log(Un.Information,"Blazor server-side application started."),n.invokeAfterStartedCallbacks(Ne)}async function rr(t,n,r){var o,i;const s=new gn;s.name="blazorpack";const a=(new Dt).withUrl("_blazor").withHubProtocol(s);t.configureSignalR(a);const c=a.build();c.on("JS.AttachComponent",((e,t)=>function(e,t,n,r){let o=de[0];o||(o=new se(0),de[0]=o),o.attachRootComponentToLogicalElement(n,t,!1)}(0,r.resolveElement(t),e))),c.on("JS.BeginInvokeJS",e.jsCallDispatcher.beginInvokeJSFromDotNet),c.on("JS.EndInvokeDotNet",e.jsCallDispatcher.endInvokeDotNetFromJS),c.on("JS.ReceiveByteArray",e.jsCallDispatcher.receiveByteArray),c.on("JS.BeginTransmitStream",(t=>{const n=new ReadableStream({start(e){c.stream("SendDotNetStreamToJS",t).subscribe({next:t=>e.enqueue(t),complete:()=>e.close(),error:t=>e.error(t)})}});e.jsCallDispatcher.supplyDotNetStream(t,n)}));const l=An.getOrCreate(n);c.on("JS.RenderBatch",((e,t)=>{n.log(Un.Debug,`Received render batch with id ${e} and ${t.byteLength} bytes.`),l.processBatch(e,t,c)})),c.onclose((e=>!er&&t.reconnectionHandler.onConnectionDown(t.reconnectionOptions,e))),c.on("JS.Error",(e=>{er=!0,or(c,e,n),yn()}));try{await c.start(),Zn=c}catch(e){if(or(c,e,n),"FailedToNegotiateWithServerError"===e.errorType)throw e;yn(),e.innerErrors&&(e.innerErrors.some((e=>"UnsupportedTransportError"===e.errorType&&e.transport===Ke.WebSockets))?n.log(Un.Error,"Unable to connect, please ensure you are using an updated browser that supports WebSockets."):e.innerErrors.some((e=>"FailedToStartTransportError"===e.errorType&&e.transport===Ke.WebSockets))?n.log(Un.Error,"Unable to connect, please ensure WebSockets are available. A VPN or proxy may be blocking the connection."):e.innerErrors.some((e=>"DisabledTransportError"===e.errorType&&e.transport===Ke.LongPolling))&&n.log(Un.Error,"Unable to initiate a SignalR connection to the server. This might be because the server is not configured to support WebSockets. For additional details, visit https://aka.ms/blazor-server-websockets-error."))}return(null===(i=null===(o=c.connection)||void 0===o?void 0:o.features)||void 0===i?void 0:i.inherentKeepAlive)&&n.log(Un.Warning,"Failed to connect via WebSockets, using the Long Polling fallback transport. This may be due to a VPN or proxy blocking the connection. To troubleshoot this, visit https://aka.ms/blazor-server-using-fallback-long-polling."),e.attachDispatcher({beginInvokeDotNetFromJS:(e,t,n,r,o)=>{c.send("BeginInvokeDotNetFromJS",e?e.toString():null,t,n,r||0,o)},endInvokeJSFromDotNet:(e,t,n)=>{c.send("EndInvokeJSFromDotNet",e,t,n)},sendByteArray:(e,t)=>{c.send("ReceiveByteArray",e,t)}}),c}function or(e,t,n){n.log(Un.Error,t),e&&e.stop()}Ne.start=nr,document&&document.currentScript&&"false"!==document.currentScript.getAttribute("autostart")&&nr()})(); \ No newline at end of file
diff --git a/src/Components/Web.JS/dist/Release/blazor.webview.js b/src/Components/Web.JS/dist/Release/blazor.webview.js
index 860bcf1e39..98fe283cd3 100644
--- a/src/Components/Web.JS/dist/Release/blazor.webview.js
+++ b/src/Components/Web.JS/dist/Release/blazor.webview.js
@@ -1 +1 @@
-(()=>{"use strict";var e,t,n;!function(e){window.DotNet=e;const t=[],n=new Map,r=new Map,o="__jsObjectId",a="__byte[]";class s{constructor(e){this._jsObject=e,this._cachedFunctions=new Map}findFunction(e){const t=this._cachedFunctions.get(e);if(t)return t;let n,r=this._jsObject;if(e.split(".").forEach((t=>{if(!(t in r))throw new Error(`Could not find '${e}' ('${t}' was undefined).`);n=r,r=r[t]})),r instanceof Function)return r=r.bind(n),this._cachedFunctions.set(e,r),r;throw new Error(`The value '${e}' is not a function.`)}getWrappedObject(){return this._jsObject}}const i={},c={0:new s(window)};c[0]._cachedFunctions.set("import",(e=>("string"==typeof e&&e.startsWith("./")&&(e=document.baseURI+e.substr(2)),import(e))));let l,u=1,d=1,f=null;function h(e){t.push(e)}function p(e){if(e&&"object"==typeof e){c[d]=new s(e);const t={[o]:d};return d++,t}throw new Error(`Cannot create a JSObjectReference from the value '${e}'.`)}function m(e){let t=-1;if(e instanceof ArrayBuffer&&(e=new Uint8Array(e)),e instanceof Blob)t=e.size;else{if(!(e.buffer instanceof ArrayBuffer))throw new Error("Supplied value is not a typed array or blob.");if(void 0===e.byteLength)throw new Error(`Cannot create a JSStreamReference from the value '${e}' as it doesn't have a byteLength.`);t=e.byteLength}const n={__jsStreamReferenceLength:t};try{const t=p(e);n.__jsObjectId=t.__jsObjectId}catch(t){throw new Error(`Cannot create a JSStreamReference from the value '${e}'.`)}return n}function b(e){return e?JSON.parse(e,((e,n)=>t.reduce(((t,n)=>n(e,t)),n))):null}function y(e,t,n,r){const o=v();if(o.invokeDotNetFromJS){const a=T(r),s=o.invokeDotNetFromJS(e,t,n,a);return s?b(s):null}throw new Error("The current dispatcher does not support synchronous calls from JS to .NET. Use invokeMethodAsync instead.")}function g(e,t,n,r){if(e&&n)throw new Error(`For instance method calls, assemblyName should be null. Received '${e}'.`);const o=u++,a=new Promise(((e,t)=>{i[o]={resolve:e,reject:t}}));try{const a=T(r);v().beginInvokeDotNetFromJS(o,e,t,n,a)}catch(e){w(o,!1,e)}return a}function v(){if(null!==f)return f;throw new Error("No .NET call dispatcher has been set.")}function w(e,t,n){if(!i.hasOwnProperty(e))throw new Error(`There is no pending async call with ID ${e}.`);const r=i[e];delete i[e],t?r.resolve(n):r.reject(n)}function E(e){return e instanceof Error?`${e.message}\n${e.stack}`:e?e.toString():"null"}function S(e,t){const n=c[t];if(n)return n.findFunction(e);throw new Error(`JS object instance with ID ${t} does not exist (has it been disposed?).`)}function I(e){delete c[e]}e.attachDispatcher=function(e){f=e},e.attachReviver=h,e.invokeMethod=function(e,t,...n){return y(e,t,null,n)},e.invokeMethodAsync=function(e,t,...n){return g(e,t,null,n)},e.createJSObjectReference=p,e.createJSStreamReference=m,e.disposeJSObjectReference=function(e){const t=e&&e.__jsObjectId;"number"==typeof t&&I(t)},function(e){e[e.Default=0]="Default",e[e.JSObjectReference=1]="JSObjectReference",e[e.JSStreamReference=2]="JSStreamReference",e[e.JSVoidResult=3]="JSVoidResult"}(l=e.JSCallResultType||(e.JSCallResultType={})),e.jsCallDispatcher={findJSFunction:S,disposeJSObjectReferenceById:I,invokeJSFromDotNet:(e,t,n,r)=>{const o=k(S(e,r).apply(null,b(t)),n);return null==o?null:T(o)},beginInvokeJSFromDotNet:(e,t,n,r,o)=>{const a=new Promise((e=>{e(S(t,o).apply(null,b(n)))}));e&&a.then((t=>T([e,!0,k(t,r)]))).then((t=>v().endInvokeJSFromDotNet(e,!0,t)),(t=>v().endInvokeJSFromDotNet(e,!1,JSON.stringify([e,!1,E(t)]))))},endInvokeDotNetFromJS:(e,t,n)=>{const r=t?b(n):new Error(n);w(parseInt(e,10),t,r)},receiveByteArray:(e,t)=>{n.set(e,t)},supplyDotNetStream:(e,t)=>{if(r.has(e)){const n=r.get(e);r.delete(e),n.resolve(t)}else{const n=new D;n.resolve(t),r.set(e,n)}}};class C{constructor(e){this._id=e}invokeMethod(e,...t){return y(null,e,this._id,t)}invokeMethodAsync(e,...t){return g(null,e,this._id,t)}dispose(){g(null,"__Dispose",this._id,null).catch((e=>console.error(e)))}serializeAsArg(){return{__dotNetObject:this._id}}}e.DotNetObject=C,h((function(e,t){if(t&&"object"==typeof t){if(t.hasOwnProperty("__dotNetObject"))return new C(t.__dotNetObject);if(t.hasOwnProperty(o)){const e=t.__jsObjectId,n=c[e];if(n)return n.getWrappedObject();throw new Error(`JS object instance with Id '${e}' does not exist. It may have been disposed.`)}if(t.hasOwnProperty(a)){const e=t["__byte[]"],r=n.get(e);if(void 0===r)throw new Error(`Byte array index '${e}' does not exist.`);return n.delete(e),r}if(t.hasOwnProperty("__dotNetStream"))return new A(t.__dotNetStream)}return t}));class A{constructor(e){if(r.has(e))this._streamPromise=r.get(e).streamPromise,r.delete(e);else{const t=new D;r.set(e,t),this._streamPromise=t.streamPromise}}stream(){return this._streamPromise}async arrayBuffer(){return new Response(await this.stream()).arrayBuffer()}}class D{constructor(){this.streamPromise=new Promise(((e,t)=>{this.resolve=e,this.reject=t}))}}function k(e,t){switch(t){case l.Default:return e;case l.JSObjectReference:return p(e);case l.JSStreamReference:return m(e);case l.JSVoidResult:return null;default:throw new Error(`Invalid JS call result type '${t}'.`)}}let R=0;function T(e){return R=0,JSON.stringify(e,N)}function N(e,t){if(t instanceof C)return t.serializeAsArg();if(t instanceof Uint8Array){f.sendByteArray(R,t);const e={[a]:R};return R++,e}return t}}(e||(e={})),function(e){e[e.prependFrame=1]="prependFrame",e[e.removeFrame=2]="removeFrame",e[e.setAttribute=3]="setAttribute",e[e.removeAttribute=4]="removeAttribute",e[e.updateText=5]="updateText",e[e.stepIn=6]="stepIn",e[e.stepOut=7]="stepOut",e[e.updateMarkup=8]="updateMarkup",e[e.permutationListEntry=9]="permutationListEntry",e[e.permutationListEnd=10]="permutationListEnd"}(t||(t={})),function(e){e[e.element=1]="element",e[e.text=2]="text",e[e.attribute=3]="attribute",e[e.component=4]="component",e[e.region=5]="region",e[e.elementReferenceCapture=6]="elementReferenceCapture",e[e.markup=8]="markup"}(n||(n={}));class r{constructor(e,t){this.componentId=e,this.fieldValue=t}static fromEvent(e,t){const n=t.target;if(n instanceof Element){const t=function(e){return e instanceof HTMLInputElement?e.type&&"checkbox"===e.type.toLowerCase()?{value:e.checked}:{value:e.value}:e instanceof HTMLSelectElement||e instanceof HTMLTextAreaElement?{value:e.value}:null}(n);if(t)return new r(e,t.value)}return null}}const o=new Map,a=new Map,s=[];function i(e){return o.get(e)}function c(e){const t=o.get(e);return(null==t?void 0:t.browserEventName)||e}function l(e,t){e.forEach((e=>o.set(e,t)))}function u(e){const t=[];for(let n=0;n<e.length;n++){const r=e[n];t.push({identifier:r.identifier,clientX:r.clientX,clientY:r.clientY,screenX:r.screenX,screenY:r.screenY,pageX:r.pageX,pageY:r.pageY})}return t}function d(e){return{detail:e.detail,screenX:e.screenX,screenY:e.screenY,clientX:e.clientX,clientY:e.clientY,offsetX:e.offsetX,offsetY:e.offsetY,pageX:e.pageX,pageY:e.pageY,button:e.button,buttons:e.buttons,ctrlKey:e.ctrlKey,shiftKey:e.shiftKey,altKey:e.altKey,metaKey:e.metaKey,type:e.type}}l(["input","change"],{createEventArgs:function(e){const t=e.target;if(function(e){return-1!==f.indexOf(e.getAttribute("type"))}(t)){const e=function(e){const t=e.value,n=e.type;switch(n){case"date":case"month":case"week":return t;case"datetime-local":return 16===t.length?t+":00":t;case"time":return 5===t.length?t+":00":t}throw new Error(`Invalid element type '${n}'.`)}(t);return{value:e}}if(function(e){return e instanceof HTMLSelectElement&&"select-multiple"===e.type}(t)){const e=t;return{value:Array.from(e.options).filter((e=>e.selected)).map((e=>e.value))}}{const e=function(e){return!!e&&"INPUT"===e.tagName&&"checkbox"===e.getAttribute("type")}(t);return{value:e?!!t.checked:t.value}}}}),l(["copy","cut","paste"],{createEventArgs:e=>({type:e.type})}),l(["drag","dragend","dragenter","dragleave","dragover","dragstart","drop"],{createEventArgs:e=>{return{...d(t=e),dataTransfer:t.dataTransfer?{dropEffect:t.dataTransfer.dropEffect,effectAllowed:t.dataTransfer.effectAllowed,files:Array.from(t.dataTransfer.files).map((e=>e.name)),items:Array.from(t.dataTransfer.items).map((e=>({kind:e.kind,type:e.type}))),types:t.dataTransfer.types}:null};var t}}),l(["focus","blur","focusin","focusout"],{createEventArgs:e=>({type:e.type})}),l(["keydown","keyup","keypress"],{createEventArgs:e=>{return{key:(t=e).key,code:t.code,location:t.location,repeat:t.repeat,ctrlKey:t.ctrlKey,shiftKey:t.shiftKey,altKey:t.altKey,metaKey:t.metaKey,type:t.type};var t}}),l(["contextmenu","click","mouseover","mouseout","mousemove","mousedown","mouseup","mouseleave","mouseenter","dblclick"],{createEventArgs:e=>d(e)}),l(["error"],{createEventArgs:e=>{return{message:(t=e).message,filename:t.filename,lineno:t.lineno,colno:t.colno,type:t.type};var t}}),l(["loadstart","timeout","abort","load","loadend","progress"],{createEventArgs:e=>{return{lengthComputable:(t=e).lengthComputable,loaded:t.loaded,total:t.total,type:t.type};var t}}),l(["touchcancel","touchend","touchmove","touchenter","touchleave","touchstart"],{createEventArgs:e=>{return{detail:(t=e).detail,touches:u(t.touches),targetTouches:u(t.targetTouches),changedTouches:u(t.changedTouches),ctrlKey:t.ctrlKey,shiftKey:t.shiftKey,altKey:t.altKey,metaKey:t.metaKey,type:t.type};var t}}),l(["gotpointercapture","lostpointercapture","pointercancel","pointerdown","pointerenter","pointerleave","pointermove","pointerout","pointerover","pointerup"],{createEventArgs:e=>{return{...d(t=e),pointerId:t.pointerId,width:t.width,height:t.height,pressure:t.pressure,tiltX:t.tiltX,tiltY:t.tiltY,pointerType:t.pointerType,isPrimary:t.isPrimary};var t}}),l(["wheel","mousewheel"],{createEventArgs:e=>{return{...d(t=e),deltaX:t.deltaX,deltaY:t.deltaY,deltaZ:t.deltaZ,deltaMode:t.deltaMode};var t}}),l(["toggle"],{createEventArgs:()=>({})});const f=["date","datetime-local","month","time","week"],h=new Map;let p,m,b=0;const y={async add(e,t,n){if(!n)throw new Error("initialParameters must be an object, even if empty.");const r="__bl-dynamic-root:"+(++b).toString();h.set(r,e);const o=await w().invokeMethodAsync("AddRootComponent",t,r),a=new v(o,m[t]);return await a.setParameters(n),a}};class g{invoke(e){return this._callback(e)}setCallback(t){this._selfJSObjectReference||(this._selfJSObjectReference=e.createJSObjectReference(this)),this._callback=t}getJSObjectReference(){return this._selfJSObjectReference}dispose(){this._selfJSObjectReference&&e.disposeJSObjectReference(this._selfJSObjectReference)}}class v{constructor(e,t){this._jsEventCallbackWrappers=new Map,this._componentId=e;for(const e of t)"eventcallback"===e.type&&this._jsEventCallbackWrappers.set(e.name.toLowerCase(),new g)}setParameters(e){const t={},n=Object.entries(e||{}),r=n.length;for(const[e,r]of n){const n=this._jsEventCallbackWrappers.get(e.toLowerCase());n&&r?(n.setCallback(r),t[e]=n.getJSObjectReference()):t[e]=r}return w().invokeMethodAsync("SetRootComponentParameters",this._componentId,r,t)}async dispose(){if(null!==this._componentId){await w().invokeMethodAsync("RemoveRootComponent",this._componentId),this._componentId=null;for(const e of this._jsEventCallbackWrappers.values())e.dispose()}}}function w(){if(!p)throw new Error("Dynamic root components have not been enabled in this application.");return p}const E=new Map;let S;const I=new Promise((e=>{S=e}));function C(e,t,n){return D(e,t.eventHandlerId,(()=>A(e).invokeMethodAsync("DispatchEventAsync",t,n)))}function A(e){const t=E.get(e);if(!t)throw new Error(`No interop methods are registered for renderer ${e}`);return t}let D=(e,t,n)=>n();const k=F(["abort","blur","canplay","canplaythrough","change","cuechange","durationchange","emptied","ended","error","focus","load","loadeddata","loadedmetadata","loadend","loadstart","mouseenter","mouseleave","pause","play","playing","progress","ratechange","reset","scroll","seeked","seeking","stalled","submit","suspend","timeupdate","toggle","unload","volumechange","waiting","DOMNodeInsertedIntoDocument","DOMNodeRemovedFromDocument"]),R={submit:!0},T=F(["click","dblclick","mousedown","mousemove","mouseup"]);class N{constructor(e){this.browserRendererId=e,this.afterClickCallbacks=[];const t=++N.nextEventDelegatorId;this.eventsCollectionKey=`_blazorEvents_${t}`,this.eventInfoStore=new _(this.onGlobalEvent.bind(this))}setListener(e,t,n,r){const o=this.getEventHandlerInfosForElement(e,!0),a=o.getHandler(t);if(a)this.eventInfoStore.update(a.eventHandlerId,n);else{const a={element:e,eventName:t,eventHandlerId:n,renderingComponentId:r};this.eventInfoStore.add(a),o.setHandler(t,a)}}getHandler(e){return this.eventInfoStore.get(e)}removeListener(e){const t=this.eventInfoStore.remove(e);if(t){const e=t.element,n=this.getEventHandlerInfosForElement(e,!1);n&&n.removeHandler(t.eventName)}}notifyAfterClick(e){this.afterClickCallbacks.push(e),this.eventInfoStore.addGlobalListener("click")}setStopPropagation(e,t,n){this.getEventHandlerInfosForElement(e,!0).stopPropagation(t,n)}setPreventDefault(e,t,n){this.getEventHandlerInfosForElement(e,!0).preventDefault(t,n)}onGlobalEvent(e){if(!(e.target instanceof Element))return;this.dispatchGlobalEventToAllElements(e.type,e);const t=(n=e.type,a.get(n));var n;t&&t.forEach((t=>this.dispatchGlobalEventToAllElements(t,e))),"click"===e.type&&this.afterClickCallbacks.forEach((t=>t(e)))}dispatchGlobalEventToAllElements(e,t){const n=t.composedPath();let o=n.shift(),a=null,s=!1;const c=Object.prototype.hasOwnProperty.call(k,e);let l=!1;for(;o;){const f=o,h=this.getEventHandlerInfosForElement(f,!1);if(h){const n=h.getHandler(e);if(n&&(u=f,d=t.type,!((u instanceof HTMLButtonElement||u instanceof HTMLInputElement||u instanceof HTMLTextAreaElement||u instanceof HTMLSelectElement)&&Object.prototype.hasOwnProperty.call(T,d)&&u.disabled))){if(!s){const n=i(e);a=(null==n?void 0:n.createEventArgs)?n.createEventArgs(t):{},s=!0}Object.prototype.hasOwnProperty.call(R,t.type)&&t.preventDefault(),C(this.browserRendererId,{eventHandlerId:n.eventHandlerId,eventName:e,eventFieldInfo:r.fromEvent(n.renderingComponentId,t)},a)}h.stopPropagation(e)&&(l=!0),h.preventDefault(e)&&t.preventDefault()}o=c||l?void 0:n.shift()}var u,d}getEventHandlerInfosForElement(e,t){return Object.prototype.hasOwnProperty.call(e,this.eventsCollectionKey)?e[this.eventsCollectionKey]:t?e[this.eventsCollectionKey]=new O:null}}N.nextEventDelegatorId=0;class _{constructor(e){this.globalListener=e,this.infosByEventHandlerId={},this.countByEventName={},s.push(this.handleEventNameAliasAdded.bind(this))}add(e){if(this.infosByEventHandlerId[e.eventHandlerId])throw new Error(`Event ${e.eventHandlerId} is already tracked`);this.infosByEventHandlerId[e.eventHandlerId]=e,this.addGlobalListener(e.eventName)}get(e){return this.infosByEventHandlerId[e]}addGlobalListener(e){if(e=c(e),Object.prototype.hasOwnProperty.call(this.countByEventName,e))this.countByEventName[e]++;else{this.countByEventName[e]=1;const t=Object.prototype.hasOwnProperty.call(k,e);document.addEventListener(e,this.globalListener,t)}}update(e,t){if(Object.prototype.hasOwnProperty.call(this.infosByEventHandlerId,t))throw new Error(`Event ${t} is already tracked`);const n=this.infosByEventHandlerId[e];delete this.infosByEventHandlerId[e],n.eventHandlerId=t,this.infosByEventHandlerId[t]=n}remove(e){const t=this.infosByEventHandlerId[e];if(t){delete this.infosByEventHandlerId[e];const n=c(t.eventName);0==--this.countByEventName[n]&&(delete this.countByEventName[n],document.removeEventListener(n,this.globalListener))}return t}handleEventNameAliasAdded(e,t){if(Object.prototype.hasOwnProperty.call(this.countByEventName,e)){const n=this.countByEventName[e];delete this.countByEventName[e],document.removeEventListener(e,this.globalListener),this.addGlobalListener(t),this.countByEventName[t]+=n-1}}}class O{constructor(){this.handlers={},this.preventDefaultFlags=null,this.stopPropagationFlags=null}getHandler(e){return Object.prototype.hasOwnProperty.call(this.handlers,e)?this.handlers[e]:null}setHandler(e,t){this.handlers[e]=t}removeHandler(e){delete this.handlers[e]}preventDefault(e,t){return void 0!==t&&(this.preventDefaultFlags=this.preventDefaultFlags||{},this.preventDefaultFlags[e]=t),!!this.preventDefaultFlags&&this.preventDefaultFlags[e]}stopPropagation(e,t){return void 0!==t&&(this.stopPropagationFlags=this.stopPropagationFlags||{},this.stopPropagationFlags[e]=t),!!this.stopPropagationFlags&&this.stopPropagationFlags[e]}}function F(e){const t={};return e.forEach((e=>{t[e]=!0})),t}const j=G("_blazorLogicalChildren"),x=G("_blazorLogicalParent"),L=G("_blazorLogicalEnd");function P(e,t){if(e.childNodes.length>0&&!t)throw new Error("New logical elements must start empty, or allowExistingContents must be true");return j in e||(e[j]=[]),e}function M(e,t){const n=document.createComment("!");return H(n,e,t),n}function H(e,t,n){const r=e;if(e instanceof Comment&&z(r)&&z(r).length>0)throw new Error("Not implemented: inserting non-empty logical container");if(U(r))throw new Error("Not implemented: moving existing logical children");const o=z(t);if(n<o.length){const t=o[n];t.parentNode.insertBefore(e,t),o.splice(n,0,r)}else X(e,t),o.push(r);r[x]=t,j in r||(r[j]=[])}function B(e,t){const n=z(e).splice(t,1)[0];if(n instanceof Comment){const e=z(n);if(e)for(;e.length>0;)B(n,0)}const r=n;r.parentNode.removeChild(r)}function U(e){return e[x]||null}function J(e,t){return z(e)[t]}function $(e){const t=V(e);return"http://www.w3.org/2000/svg"===t.namespaceURI&&"foreignObject"!==t.tagName}function z(e){return e[j]}function K(e,t){const n=z(e);t.forEach((e=>{e.moveRangeStart=n[e.fromSiblingIndex],e.moveRangeEnd=Y(e.moveRangeStart)})),t.forEach((t=>{const r=document.createComment("marker");t.moveToBeforeMarker=r;const o=n[t.toSiblingIndex+1];o?o.parentNode.insertBefore(r,o):X(r,e)})),t.forEach((e=>{const t=e.moveToBeforeMarker,n=t.parentNode,r=e.moveRangeStart,o=e.moveRangeEnd;let a=r;for(;a;){const e=a.nextSibling;if(n.insertBefore(a,t),a===o)break;a=e}n.removeChild(t)})),t.forEach((e=>{n[e.toSiblingIndex]=e.moveRangeStart}))}function V(e){if(e instanceof Element||e instanceof DocumentFragment)return e;if(e instanceof Comment)return e.parentNode;throw new Error("Not a valid logical element")}function W(e){const t=z(U(e));return t[Array.prototype.indexOf.call(t,e)+1]||null}function X(e,t){if(t instanceof Element||t instanceof DocumentFragment)t.appendChild(e);else{if(!(t instanceof Comment))throw new Error(`Cannot append node because the parent is not a valid logical element. Parent: ${t}`);{const n=W(t);n?n.parentNode.insertBefore(e,n):X(e,U(t))}}}function Y(e){if(e instanceof Element||e instanceof DocumentFragment)return e;const t=W(e);if(t)return t.previousSibling;{const t=U(e);return t instanceof Element||t instanceof DocumentFragment?t.lastChild:Y(t)}}function G(e){return"function"==typeof Symbol?Symbol():e}function q(e){return`_bl_${e}`}e.attachReviver(((e,t)=>t&&"object"==typeof t&&Object.prototype.hasOwnProperty.call(t,"__internalId")&&"string"==typeof t.__internalId?function(e){const t=`[${q(e)}]`;return document.querySelector(t)}(t.__internalId):t));const Z="_blazorDeferredValue",Q=document.createElement("template"),ee=document.createElementNS("http://www.w3.org/2000/svg","g"),te={},ne="__internal_",re="preventDefault_",oe="stopPropagation_";class ae{constructor(e){this.rootComponentIds=new Set,this.childComponentLocations={},this.eventDelegator=new N(e),this.eventDelegator.notifyAfterClick((e=>{if(!he)return;if(0!==e.button||function(e){return e.ctrlKey||e.shiftKey||e.altKey||e.metaKey}(e))return;if(e.defaultPrevented)return;const t=function(e){const t=!window._blazorDisableComposedPath&&e.composedPath&&e.composedPath();if(t){for(let e=0;e<t.length;e++){const n=t[e];if(n instanceof Element&&"A"===n.tagName)return n}return null}return Se(e.target,"A")}(e);if(t&&function(e){const t=e.getAttribute("target");return(!t||"_self"===t)&&e.hasAttribute("href")&&!e.hasAttribute("download")}(t)){const n=Ee(t.getAttribute("href"));Ie(n)&&(e.preventDefault(),ge(n,!0,!1))}}))}attachRootComponentToLogicalElement(e,t,n){this.attachComponentToElement(e,t),this.rootComponentIds.add(e),n||(te[e]=t)}updateComponent(e,t,n,r){var o;const a=this.childComponentLocations[t];if(!a)throw new Error(`No element is currently associated with component ${t}`);const s=te[t];if(s){const e=function(e){return e[L]||null}(s);delete te[t],e?function(e,t){const n=U(e);if(!n)throw new Error("Can't clear between nodes. The start node does not have a logical parent.");const r=z(n),o=r.indexOf(e)+1,a=r.indexOf(t);for(let e=o;e<=a;e++)B(n,o);e.textContent="!"}(s,e):function(e){let t;for(;t=e.firstChild;)e.removeChild(t)}(s)}const i=null===(o=V(a))||void 0===o?void 0:o.getRootNode(),c=i&&i.activeElement;this.applyEdits(e,t,a,0,n,r),c instanceof HTMLElement&&i&&i.activeElement!==c&&c.focus()}disposeComponent(e){this.rootComponentIds.delete(e)&&function(e){const t=z(e);for(;t.length;)B(e,0)}(this.childComponentLocations[e]),delete this.childComponentLocations[e]}disposeEventHandler(e){this.eventDelegator.removeListener(e)}attachComponentToElement(e,t){this.childComponentLocations[e]=t}applyEdits(e,n,r,o,a,s){let i,c=0,l=o;const u=e.arrayBuilderSegmentReader,d=e.editReader,f=e.frameReader,h=u.values(a),p=u.offset(a),m=p+u.count(a);for(let a=p;a<m;a++){const u=e.diffReader.editsEntry(h,a),p=d.editType(u);switch(p){case t.prependFrame:{const t=d.newTreeIndex(u),o=e.referenceFramesEntry(s,t),a=d.siblingIndex(u);this.insertFrame(e,n,r,l+a,s,o,t);break}case t.removeFrame:B(r,l+d.siblingIndex(u));break;case t.setAttribute:{const t=d.newTreeIndex(u),o=e.referenceFramesEntry(s,t),a=J(r,l+d.siblingIndex(u));if(!(a instanceof Element))throw new Error("Cannot set attribute on non-element child");this.applyAttribute(e,n,a,o);break}case t.removeAttribute:{const t=J(r,l+d.siblingIndex(u));if(!(t instanceof HTMLElement))throw new Error("Cannot remove attribute from non-element child");{const n=d.removedAttributeName(u);this.tryApplySpecialProperty(e,t,n,null)||t.removeAttribute(n)}break}case t.updateText:{const t=d.newTreeIndex(u),n=e.referenceFramesEntry(s,t),o=J(r,l+d.siblingIndex(u));if(!(o instanceof Text))throw new Error("Cannot set text content on non-text child");o.textContent=f.textContent(n);break}case t.updateMarkup:{const t=d.newTreeIndex(u),n=e.referenceFramesEntry(s,t),o=d.siblingIndex(u);B(r,l+o),this.insertMarkup(e,r,l+o,n);break}case t.stepIn:r=J(r,l+d.siblingIndex(u)),c++,l=0;break;case t.stepOut:r=U(r),c--,l=0===c?o:0;break;case t.permutationListEntry:i=i||[],i.push({fromSiblingIndex:l+d.siblingIndex(u),toSiblingIndex:l+d.moveToSiblingIndex(u)});break;case t.permutationListEnd:K(r,i),i=void 0;break;default:throw new Error(`Unknown edit type: ${p}`)}}}insertFrame(e,t,r,o,a,s,i){const c=e.frameReader,l=c.frameType(s);switch(l){case n.element:return this.insertElement(e,t,r,o,a,s,i),1;case n.text:return this.insertText(e,r,o,s),1;case n.attribute:throw new Error("Attribute frames should only be present as leading children of element frames.");case n.component:return this.insertComponent(e,r,o,s),1;case n.region:return this.insertFrameRange(e,t,r,o,a,i+1,i+c.subtreeLength(s));case n.elementReferenceCapture:if(r instanceof Element)return u=r,d=c.elementReferenceCaptureId(s),u.setAttribute(q(d),""),0;throw new Error("Reference capture frames can only be children of element frames.");case n.markup:return this.insertMarkup(e,r,o,s),1;default:throw new Error(`Unknown frame type: ${l}`)}var u,d}insertElement(e,t,r,o,a,s,i){const c=e.frameReader,l=c.elementName(s),u="svg"===l||$(r)?document.createElementNS("http://www.w3.org/2000/svg",l):document.createElement(l),d=P(u);let f=!1;const h=i+c.subtreeLength(s);for(let s=i+1;s<h;s++){const i=e.referenceFramesEntry(a,s);if(c.frameType(i)!==n.attribute){H(u,r,o),f=!0,this.insertFrameRange(e,t,d,0,a,s,h);break}this.applyAttribute(e,t,u,i)}f||H(u,r,o),u instanceof HTMLOptionElement?this.trySetSelectValueFromOptionElement(u):Z in u&&ue(u,u._blazorDeferredValue)}trySetSelectValueFromOptionElement(e){const t=this.findClosestAncestorSelectElement(e);if(!function(e){return!!e&&Z in e}(t))return!1;if(ce(t))e.selected=-1!==t._blazorDeferredValue.indexOf(e.value);else{if(t._blazorDeferredValue!==e.value)return!1;le(t,e.value),delete t._blazorDeferredValue}return!0}insertComponent(e,t,n,r){const o=M(t,n),a=e.frameReader.componentId(r);this.attachComponentToElement(a,o)}insertText(e,t,n,r){const o=e.frameReader.textContent(r);H(document.createTextNode(o),t,n)}insertMarkup(e,t,n,r){const o=M(t,n),a=(s=e.frameReader.markupContent(r),$(t)?(ee.innerHTML=s||" ",ee):(Q.innerHTML=s||" ",Q.content));var s;let i=0;for(;a.firstChild;)H(a.firstChild,o,i++)}applyAttribute(e,t,n,r){const o=e.frameReader,a=o.attributeName(r),s=o.attributeEventHandlerId(r);if(s){const e=ie(a);this.eventDelegator.setListener(n,e,s,t)}else this.tryApplySpecialProperty(e,n,a,r)||n.setAttribute(a,o.attributeValue(r))}tryApplySpecialProperty(e,t,n,r){switch(n){case"value":return this.tryApplyValueProperty(e,t,r);case"checked":return this.tryApplyCheckedProperty(e,t,r);default:return!!n.startsWith(ne)&&(this.applyInternalAttribute(e,t,n.substring(ne.length),r),!0)}}applyInternalAttribute(e,t,n,r){const o=r?e.frameReader.attributeValue(r):null;if(n.startsWith(oe)){const e=ie(n.substring(oe.length));this.eventDelegator.setStopPropagation(t,e,null!==o)}else{if(!n.startsWith(re))throw new Error(`Unsupported internal attribute '${n}'`);{const e=ie(n.substring(re.length));this.eventDelegator.setPreventDefault(t,e,null!==o)}}}tryApplyValueProperty(e,t,n){const r=e.frameReader;let o=n?r.attributeValue(n):null;switch(o&&"INPUT"===t.tagName&&(o=function(e,t){switch(t.getAttribute("type")){case"time":return 8!==e.length||!e.endsWith("00")&&t.hasAttribute("step")?e:e.substring(0,5);case"datetime-local":return 19!==e.length||!e.endsWith("00")&&t.hasAttribute("step")?e:e.substring(0,16);default:return e}}(o,t)),t.tagName){case"INPUT":case"SELECT":case"TEXTAREA":return o&&t instanceof HTMLSelectElement&&ce(t)&&(o=JSON.parse(o)),ue(t,o),t._blazorDeferredValue=o,!0;case"OPTION":return o||""===o?t.setAttribute("value",o):t.removeAttribute("value"),this.trySetSelectValueFromOptionElement(t),!0;default:return!1}}tryApplyCheckedProperty(e,t,n){if("INPUT"===t.tagName){const r=n?e.frameReader.attributeValue(n):null;return t.checked=null!==r,!0}return!1}findClosestAncestorSelectElement(e){for(;e;){if(e instanceof HTMLSelectElement)return e;e=e.parentElement}return null}insertFrameRange(e,t,n,r,o,a,s){const i=r;for(let i=a;i<s;i++){const a=e.referenceFramesEntry(o,i);r+=this.insertFrame(e,t,n,r,o,a,i),i+=se(e,a)}return r-i}}function se(e,t){const r=e.frameReader;switch(r.frameType(t)){case n.component:case n.element:case n.region:return r.subtreeLength(t)-1;default:return 0}}function ie(e){if(e.startsWith("on"))return e.substring(2);throw new Error(`Attribute should be an event name, but doesn't start with 'on'. Value: '${e}'`)}function ce(e){return"select-multiple"===e.type}function le(e,t){e.value=t||""}function ue(e,t){e instanceof HTMLSelectElement?ce(e)?function(e,t){t||(t=[]);for(let n=0;n<e.options.length;n++)e.options[n].selected=-1!==t.indexOf(e.options[n].value)}(e,t):le(e,t):e.value=t}const de={};let fe=!1,he=!1,pe=!1,me=null;const be={listenForNavigationEvents:function(e){me=e,pe||(pe=!0,window.addEventListener("popstate",(()=>ve(!1))))},enableNavigationInterception:function(){he=!0},navigateTo:ye,getBaseURI:()=>document.baseURI,getLocationHref:()=>location.href};function ye(e,t,n=!1){const r=Ee(e),o=t instanceof Object?t:{forceLoad:t,replaceHistoryEntry:n};!o.forceLoad&&Ie(r)?ge(r,!1,o.replaceHistoryEntry):function(e,t){if(location.href===e){const t=e+"?";history.replaceState(null,"",t),location.replace(e)}else t?location.replace(e):location.href=e}(e,o.replaceHistoryEntry)}function ge(e,t,n){fe=!0,n?history.replaceState(null,"",e):history.pushState(null,"",e),ve(t)}async function ve(e){me&&await me(location.href,e)}let we;function Ee(e){return we=we||document.createElement("a"),we.href=e,we.href}function Se(e,t){return e?e.tagName===t?e:Se(e.parentElement,t):null}function Ie(e){const t=(n=document.baseURI).substring(0,n.lastIndexOf("/"));var n;const r=e.charAt(t.length);return e.startsWith(t)&&(""===r||"/"===r||"?"===r||"#"===r)}const Ce={focus:function(e,t){if(e instanceof HTMLElement)e.focus({preventScroll:t});else{if(!(e instanceof SVGElement))throw new Error("Unable to focus an invalid element.");if(!e.hasAttribute("tabindex"))throw new Error("Unable to focus an SVG element that does not have a tabindex.");e.focus({preventScroll:t})}},focusBySelector:function(e){const t=document.querySelector(e);t&&(t.hasAttribute("tabindex")||(t.tabIndex=-1),t.focus())}},Ae={init:function(e,t,n,r=50){const o=ke(t);(o||document.documentElement).style.overflowAnchor="none";const a=document.createRange();u(n.parentElement)&&(t.style.display="table-row",n.style.display="table-row");const s=new IntersectionObserver((function(r){r.forEach((r=>{var o;if(!r.isIntersecting)return;a.setStartAfter(t),a.setEndBefore(n);const s=a.getBoundingClientRect().height,i=null===(o=r.rootBounds)||void 0===o?void 0:o.height;r.target===t?e.invokeMethodAsync("OnSpacerBeforeVisible",r.intersectionRect.top-r.boundingClientRect.top,s,i):r.target===n&&n.offsetHeight>0&&e.invokeMethodAsync("OnSpacerAfterVisible",r.boundingClientRect.bottom-r.intersectionRect.bottom,s,i)}))}),{root:o,rootMargin:`${r}px`});s.observe(t),s.observe(n);const i=l(t),c=l(n);function l(e){const t={attributes:!0},n=new MutationObserver(((n,r)=>{u(e.parentElement)&&(r.disconnect(),e.style.display="table-row",r.observe(e,t)),s.unobserve(e),s.observe(e)}));return n.observe(e,t),n}function u(e){return null!==e&&(e instanceof HTMLTableElement&&""===e.style.display||"table"===e.style.display||e instanceof HTMLTableSectionElement&&""===e.style.display||"table-row-group"===e.style.display)}De[e._id]={intersectionObserver:s,mutationObserverBefore:i,mutationObserverAfter:c}},dispose:function(e){const t=De[e._id];t&&(t.intersectionObserver.disconnect(),t.mutationObserverBefore.disconnect(),t.mutationObserverAfter.disconnect(),e.dispose(),delete De[e._id])}},De={};function ke(e){return e?"visible"!==getComputedStyle(e).overflowY?e:ke(e.parentElement):null}const Re={getAndRemoveExistingTitle:function(){var e;const t=document.head?document.head.getElementsByTagName("title"):[];if(0===t.length)return null;let n=null;for(let r=t.length-1;r>=0;r--){const o=t[r],a=o.previousSibling;a instanceof Comment&&null!==U(a)||(null===n&&(n=o.textContent),null===(e=o.parentNode)||void 0===e||e.removeChild(o))}return n}},Te={init:function(e,t){t._blazorInputFileNextFileId=0,t.addEventListener("click",(function(){t.value=""})),t.addEventListener("change",(function(){t._blazorFilesById={};const n=Array.prototype.map.call(t.files,(function(e){const n={id:++t._blazorInputFileNextFileId,lastModified:new Date(e.lastModified).toISOString(),name:e.name,size:e.size,contentType:e.type,readPromise:void 0,arrayBuffer:void 0,blob:e};return t._blazorFilesById[n.id]=n,n}));e.invokeMethodAsync("NotifyChange",n)}))},toImageFile:async function(e,t,n,r,o){const a=Ne(e,t),s=await new Promise((function(e){const t=new Image;t.onload=function(){URL.revokeObjectURL(t.src),e(t)},t.onerror=function(){t.onerror=null,URL.revokeObjectURL(t.src)},t.src=URL.createObjectURL(a.blob)})),i=await new Promise((function(e){var t;const a=Math.min(1,r/s.width),i=Math.min(1,o/s.height),c=Math.min(a,i),l=document.createElement("canvas");l.width=Math.round(s.width*c),l.height=Math.round(s.height*c),null===(t=l.getContext("2d"))||void 0===t||t.drawImage(s,0,0,l.width,l.height),l.toBlob(e,n)})),c={id:++e._blazorInputFileNextFileId,lastModified:a.lastModified,name:a.name,size:(null==i?void 0:i.size)||0,contentType:n,blob:i||a.blob};return e._blazorFilesById[c.id]=c,c},readFileData:async function(e,t){return Ne(e,t).blob}};function Ne(e,t){const n=e._blazorFilesById[t];if(!n)throw new Error(`There is no file with ID ${t}. The file list may have changed. See https://aka.ms/aspnet/blazor-input-file-multiple-selections.`);return n}const _e=new Map,Oe={navigateTo:ye,registerCustomEventType:function(e,t){if(!t)throw new Error("The options parameter is required.");if(o.has(e))throw new Error(`The event '${e}' is already registered.`);if(t.browserEventName){const n=a.get(t.browserEventName);n?n.push(e):a.set(t.browserEventName,[e]),s.forEach((n=>n(e,t.browserEventName)))}o.set(e,t)},rootComponents:y,_internal:{navigationManager:be,domWrapper:Ce,Virtualize:Ae,PageTitle:Re,InputFile:Te,getJSDataStreamChunk:async function(e,t,n){return e instanceof Blob?await async function(e,t,n){const r=e.slice(t,t+n),o=await r.arrayBuffer();return new Uint8Array(o)}(e,t,n):function(e,t,n){return new Uint8Array(e.buffer,e.byteOffset+t,n)}(e,t,n)},receiveDotNetDataStream:function(t,n,r,o){let a=_e.get(t);if(!a){const n=new ReadableStream({start(e){_e.set(t,e),a=e}});e.jsCallDispatcher.supplyDotNetStream(t,n)}o?(a.error(o),_e.delete(t)):0===r?(a.close(),_e.delete(t)):a.enqueue(n.length===r?n:n.subarray(0,r))},attachWebRendererInterop:function(t,n,r,o){if(E.has(t))throw new Error(`Interop methods are already registered for renderer ${t}`);E.set(t,n),Object.keys(r).length>0&&function(t,n,r){if(p)throw new Error("Dynamic root components have already been enabled.");p=t,m=n;for(const[t,o]of Object.entries(r)){const r=e.jsCallDispatcher.findJSFunction(t,0);for(const e of o)r(e,n[e])}}(A(t),r,o),S()}}};window.Blazor=Oe;let Fe=!1;const je="function"==typeof TextDecoder?new TextDecoder("utf-8"):null,xe=je?je.decode.bind(je):function(e){let t=0;const n=e.length,r=[],o=[];for(;t<n;){const n=e[t++];if(0===n)break;if(0==(128&n))r.push(n);else if(192==(224&n)){const o=63&e[t++];r.push((31&n)<<6|o)}else if(224==(240&n)){const o=63&e[t++],a=63&e[t++];r.push((31&n)<<12|o<<6|a)}else if(240==(248&n)){let o=(7&n)<<18|(63&e[t++])<<12|(63&e[t++])<<6|63&e[t++];o>65535&&(o-=65536,r.push(o>>>10&1023|55296),o=56320|1023&o),r.push(o)}r.length>1024&&(o.push(String.fromCharCode.apply(null,r)),r.length=0)}return o.push(String.fromCharCode.apply(null,r)),o.join("")},Le=Math.pow(2,32),Pe=Math.pow(2,21)-1;function Me(e,t){return e[t]|e[t+1]<<8|e[t+2]<<16|e[t+3]<<24}function He(e,t){return e[t]+(e[t+1]<<8)+(e[t+2]<<16)+(e[t+3]<<24>>>0)}function Be(e,t){const n=He(e,t+4);if(n>Pe)throw new Error(`Cannot read uint64 with high order part ${n}, because the result would exceed Number.MAX_SAFE_INTEGER.`);return n*Le+He(e,t)}class Ue{constructor(e){this.batchData=e;const t=new Ke(e);this.arrayRangeReader=new Ve(e),this.arrayBuilderSegmentReader=new We(e),this.diffReader=new Je(e),this.editReader=new $e(e,t),this.frameReader=new ze(e,t)}updatedComponents(){return Me(this.batchData,this.batchData.length-20)}referenceFrames(){return Me(this.batchData,this.batchData.length-16)}disposedComponentIds(){return Me(this.batchData,this.batchData.length-12)}disposedEventHandlerIds(){return Me(this.batchData,this.batchData.length-8)}updatedComponentsEntry(e,t){const n=e+4*t;return Me(this.batchData,n)}referenceFramesEntry(e,t){return e+20*t}disposedComponentIdsEntry(e,t){const n=e+4*t;return Me(this.batchData,n)}disposedEventHandlerIdsEntry(e,t){const n=e+8*t;return Be(this.batchData,n)}}class Je{constructor(e){this.batchDataUint8=e}componentId(e){return Me(this.batchDataUint8,e)}edits(e){return e+4}editsEntry(e,t){return e+16*t}}class $e{constructor(e,t){this.batchDataUint8=e,this.stringReader=t}editType(e){return Me(this.batchDataUint8,e)}siblingIndex(e){return Me(this.batchDataUint8,e+4)}newTreeIndex(e){return Me(this.batchDataUint8,e+8)}moveToSiblingIndex(e){return Me(this.batchDataUint8,e+8)}removedAttributeName(e){const t=Me(this.batchDataUint8,e+12);return this.stringReader.readString(t)}}class ze{constructor(e,t){this.batchDataUint8=e,this.stringReader=t}frameType(e){return Me(this.batchDataUint8,e)}subtreeLength(e){return Me(this.batchDataUint8,e+4)}elementReferenceCaptureId(e){const t=Me(this.batchDataUint8,e+4);return this.stringReader.readString(t)}componentId(e){return Me(this.batchDataUint8,e+8)}elementName(e){const t=Me(this.batchDataUint8,e+8);return this.stringReader.readString(t)}textContent(e){const t=Me(this.batchDataUint8,e+4);return this.stringReader.readString(t)}markupContent(e){const t=Me(this.batchDataUint8,e+4);return this.stringReader.readString(t)}attributeName(e){const t=Me(this.batchDataUint8,e+4);return this.stringReader.readString(t)}attributeValue(e){const t=Me(this.batchDataUint8,e+8);return this.stringReader.readString(t)}attributeEventHandlerId(e){return Be(this.batchDataUint8,e+12)}}class Ke{constructor(e){this.batchDataUint8=e,this.stringTableStartIndex=Me(e,e.length-4)}readString(e){if(-1===e)return null;{const n=Me(this.batchDataUint8,this.stringTableStartIndex+4*e),r=function(e,t){let n=0,r=0;for(let o=0;o<4;o++){const a=e[t+o];if(n|=(127&a)<<r,a<128)break;r+=7}return n}(this.batchDataUint8,n),o=n+((t=r)<128?1:t<16384?2:t<2097152?3:4),a=new Uint8Array(this.batchDataUint8.buffer,this.batchDataUint8.byteOffset+o,r);return xe(a)}var t}}class Ve{constructor(e){this.batchDataUint8=e}count(e){return Me(this.batchDataUint8,e)}values(e){return e+4}}class We{constructor(e){this.batchDataUint8=e}offset(e){return 0}count(e){return Me(this.batchDataUint8,e)}values(e){return e+4}}const Xe="__bwv:";let Ye=!1;function Ge(e,t){tt("OnRenderCompleted",e,t)}function qe(e,t,n,r,o){tt("BeginInvokeDotNet",e?e.toString():null,t,n,r||0,o)}function Ze(e,t,n){tt("EndInvokeJS",e,t,n)}function Qe(e,t){const n=function(e){const t=new Array(e.length);for(let n=0;n<e.length;n++)t[n]=String.fromCharCode(e[n]);return btoa(t.join(""))}(t);tt("ReceiveByteArrayFromJS",e,n)}function et(e,t){return tt("OnLocationChanged",e,t),Promise.resolve()}function tt(e,...t){const n=function(e,t){return Ye?null:`__bwv:${JSON.stringify([e,...t])}`}(e,t);n&&window.external.sendMessage(n)}function nt(t,n){const r=rt(n);e.jsCallDispatcher.receiveByteArray(t,r)}function rt(e){const t=atob(e),n=t.length,r=new Uint8Array(n);for(let e=0;e<n;e++)r[e]=t.charCodeAt(e);return r}class ot{constructor(){this.afterStartedCallbacks=[]}async importInitializersAsync(e,t){await Promise.all(e.map((e=>async function(e,n){const r=function(e){const t=document.baseURI;return t.endsWith("/")?`${t}${e}`:`${t}/${e}`}(n),o=await import(r);if(void 0===o)return;const{beforeStart:a,afterStarted:s}=o;return s&&e.afterStartedCallbacks.push(s),a?a(...t):void 0}(this,e))))}async invokeAfterStartedCallbacks(e){await I,await Promise.all(this.afterStartedCallbacks.map((t=>t(e))))}}let at=!1;async function st(){if(at)throw new Error("Blazor has already started.");at=!0;const t=await async function(){const e=await fetch("_framework/blazor.modules.json",{method:"GET",credentials:"include",cache:"no-cache"}),t=await e.json(),n=new ot;return await n.importInitializersAsync(t,[]),n}();(function(){const t={AttachToDocument:(e,t)=>{!function(e,t,n){const r="::after";let o=!1;if(e.endsWith(r))e=e.slice(0,-r.length),o=!0;else if(e.endsWith("::before"))throw new Error("The '::before' selector is not supported.");const a=function(e){const t=h.get(e);if(t)return h.delete(e),t}(e)||document.querySelector(e);if(!a)throw new Error(`Could not find any element matching selector '${e}'.`);!function(e,t,n,r){let o=de[0];o||(o=new ae(0),de[0]=o),o.attachRootComponentToLogicalElement(n,t,r)}(0,P(a,!0),t,o)}(t,e)},RenderBatch:(e,t)=>{try{const n=rt(t);(function(e,t){const n=de[0];if(!n)throw new Error("There is no browser renderer with ID 0.");const r=t.arrayRangeReader,o=t.updatedComponents(),a=r.values(o),s=r.count(o),i=t.referenceFrames(),c=r.values(i),l=t.diffReader;for(let e=0;e<s;e++){const r=t.updatedComponentsEntry(a,e),o=l.componentId(r),s=l.edits(r);n.updateComponent(t,o,s,c)}const u=t.disposedComponentIds(),d=r.values(u),f=r.count(u);for(let e=0;e<f;e++){const r=t.disposedComponentIdsEntry(d,e);n.disposeComponent(r)}const h=t.disposedEventHandlerIds(),p=r.values(h),m=r.count(h);for(let e=0;e<m;e++){const r=t.disposedEventHandlerIdsEntry(p,e);n.disposeEventHandler(r)}fe&&(fe=!1,window.scrollTo&&window.scrollTo(0,0))})(0,new Ue(n)),Ge(e,null)}catch(t){Ge(e,t.toString())}},NotifyUnhandledException:(e,t)=>{Ye=!0,console.error(`${e}\n${t}`),function(){const e=document.querySelector("#blazor-error-ui");e&&(e.style.display="block"),Fe||(Fe=!0,document.querySelectorAll("#blazor-error-ui .reload").forEach((e=>{e.onclick=function(e){location.reload(),e.preventDefault()}})),document.querySelectorAll("#blazor-error-ui .dismiss").forEach((e=>{e.onclick=function(e){const t=document.querySelector("#blazor-error-ui");t&&(t.style.display="none"),e.preventDefault()}})))}()},BeginInvokeJS:e.jsCallDispatcher.beginInvokeJSFromDotNet,EndInvokeDotNet:e.jsCallDispatcher.endInvokeDotNetFromJS,SendByteArrayToJS:nt,Navigate:be.navigateTo};window.external.receiveMessage((e=>{const n=function(e){if(Ye||!e||!e.startsWith(Xe))return null;const t=e.substring(Xe.length),[n,...r]=JSON.parse(t);return{messageType:n,args:r}}(e);if(n){if(!Object.prototype.hasOwnProperty.call(t,n.messageType))throw new Error(`Unsupported IPC message type '${n.messageType}'`);t[n.messageType].apply(null,n.args)}}))})(),e.attachDispatcher({beginInvokeDotNetFromJS:qe,endInvokeJSFromDotNet:Ze,sendByteArray:Qe}),be.enableNavigationInterception(),be.listenForNavigationEvents(et),tt("AttachPage",be.getBaseURI(),be.getLocationHref()),await t.invokeAfterStartedCallbacks(Oe)}Oe.start=st,document&&document.currentScript&&"false"!==document.currentScript.getAttribute("autostart")&&st()})(); \ No newline at end of file
+(()=>{"use strict";var e,t,n;!function(e){window.DotNet=e;const t=[],n=new Map,r=new Map,o="__jsObjectId",a="__byte[]";class s{constructor(e){this._jsObject=e,this._cachedFunctions=new Map}findFunction(e){const t=this._cachedFunctions.get(e);if(t)return t;let n,r=this._jsObject;if(e.split(".").forEach((t=>{if(!(t in r))throw new Error(`Could not find '${e}' ('${t}' was undefined).`);n=r,r=r[t]})),r instanceof Function)return r=r.bind(n),this._cachedFunctions.set(e,r),r;throw new Error(`The value '${e}' is not a function.`)}getWrappedObject(){return this._jsObject}}const i={},c={0:new s(window)};c[0]._cachedFunctions.set("import",(e=>("string"==typeof e&&e.startsWith("./")&&(e=document.baseURI+e.substr(2)),import(e))));let l,u=1,d=1,f=null;function h(e){t.push(e)}function p(e){if(e&&"object"==typeof e){c[d]=new s(e);const t={[o]:d};return d++,t}throw new Error(`Cannot create a JSObjectReference from the value '${e}'.`)}function m(e){let t=-1;if(e instanceof ArrayBuffer&&(e=new Uint8Array(e)),e instanceof Blob)t=e.size;else{if(!(e.buffer instanceof ArrayBuffer))throw new Error("Supplied value is not a typed array or blob.");if(void 0===e.byteLength)throw new Error(`Cannot create a JSStreamReference from the value '${e}' as it doesn't have a byteLength.`);t=e.byteLength}const n={__jsStreamReferenceLength:t};try{const t=p(e);n.__jsObjectId=t.__jsObjectId}catch(t){throw new Error(`Cannot create a JSStreamReference from the value '${e}'.`)}return n}function b(e){return e?JSON.parse(e,((e,n)=>t.reduce(((t,n)=>n(e,t)),n))):null}function y(e,t,n,r){const o=v();if(o.invokeDotNetFromJS){const a=T(r),s=o.invokeDotNetFromJS(e,t,n,a);return s?b(s):null}throw new Error("The current dispatcher does not support synchronous calls from JS to .NET. Use invokeMethodAsync instead.")}function g(e,t,n,r){if(e&&n)throw new Error(`For instance method calls, assemblyName should be null. Received '${e}'.`);const o=u++,a=new Promise(((e,t)=>{i[o]={resolve:e,reject:t}}));try{const a=T(r);v().beginInvokeDotNetFromJS(o,e,t,n,a)}catch(e){w(o,!1,e)}return a}function v(){if(null!==f)return f;throw new Error("No .NET call dispatcher has been set.")}function w(e,t,n){if(!i.hasOwnProperty(e))throw new Error(`There is no pending async call with ID ${e}.`);const r=i[e];delete i[e],t?r.resolve(n):r.reject(n)}function E(e){return e instanceof Error?`${e.message}\n${e.stack}`:e?e.toString():"null"}function S(e,t){const n=c[t];if(n)return n.findFunction(e);throw new Error(`JS object instance with ID ${t} does not exist (has it been disposed?).`)}function I(e){delete c[e]}e.attachDispatcher=function(e){f=e},e.attachReviver=h,e.invokeMethod=function(e,t,...n){return y(e,t,null,n)},e.invokeMethodAsync=function(e,t,...n){return g(e,t,null,n)},e.createJSObjectReference=p,e.createJSStreamReference=m,e.disposeJSObjectReference=function(e){const t=e&&e.__jsObjectId;"number"==typeof t&&I(t)},function(e){e[e.Default=0]="Default",e[e.JSObjectReference=1]="JSObjectReference",e[e.JSStreamReference=2]="JSStreamReference",e[e.JSVoidResult=3]="JSVoidResult"}(l=e.JSCallResultType||(e.JSCallResultType={})),e.jsCallDispatcher={findJSFunction:S,disposeJSObjectReferenceById:I,invokeJSFromDotNet:(e,t,n,r)=>{const o=k(S(e,r).apply(null,b(t)),n);return null==o?null:T(o)},beginInvokeJSFromDotNet:(e,t,n,r,o)=>{const a=new Promise((e=>{e(S(t,o).apply(null,b(n)))}));e&&a.then((t=>T([e,!0,k(t,r)]))).then((t=>v().endInvokeJSFromDotNet(e,!0,t)),(t=>v().endInvokeJSFromDotNet(e,!1,JSON.stringify([e,!1,E(t)]))))},endInvokeDotNetFromJS:(e,t,n)=>{const r=t?b(n):new Error(n);w(parseInt(e,10),t,r)},receiveByteArray:(e,t)=>{n.set(e,t)},supplyDotNetStream:(e,t)=>{if(r.has(e)){const n=r.get(e);r.delete(e),n.resolve(t)}else{const n=new D;n.resolve(t),r.set(e,n)}}};class C{constructor(e){this._id=e}invokeMethod(e,...t){return y(null,e,this._id,t)}invokeMethodAsync(e,...t){return g(null,e,this._id,t)}dispose(){g(null,"__Dispose",this._id,null).catch((e=>console.error(e)))}serializeAsArg(){return{__dotNetObject:this._id}}}e.DotNetObject=C,h((function(e,t){if(t&&"object"==typeof t){if(t.hasOwnProperty("__dotNetObject"))return new C(t.__dotNetObject);if(t.hasOwnProperty(o)){const e=t.__jsObjectId,n=c[e];if(n)return n.getWrappedObject();throw new Error(`JS object instance with Id '${e}' does not exist. It may have been disposed.`)}if(t.hasOwnProperty(a)){const e=t["__byte[]"],r=n.get(e);if(void 0===r)throw new Error(`Byte array index '${e}' does not exist.`);return n.delete(e),r}if(t.hasOwnProperty("__dotNetStream"))return new A(t.__dotNetStream)}return t}));class A{constructor(e){if(r.has(e))this._streamPromise=r.get(e).streamPromise,r.delete(e);else{const t=new D;r.set(e,t),this._streamPromise=t.streamPromise}}stream(){return this._streamPromise}async arrayBuffer(){return new Response(await this.stream()).arrayBuffer()}}class D{constructor(){this.streamPromise=new Promise(((e,t)=>{this.resolve=e,this.reject=t}))}}function k(e,t){switch(t){case l.Default:return e;case l.JSObjectReference:return p(e);case l.JSStreamReference:return m(e);case l.JSVoidResult:return null;default:throw new Error(`Invalid JS call result type '${t}'.`)}}let R=0;function T(e){return R=0,JSON.stringify(e,N)}function N(e,t){if(t instanceof C)return t.serializeAsArg();if(t instanceof Uint8Array){f.sendByteArray(R,t);const e={[a]:R};return R++,e}return t}}(e||(e={})),function(e){e[e.prependFrame=1]="prependFrame",e[e.removeFrame=2]="removeFrame",e[e.setAttribute=3]="setAttribute",e[e.removeAttribute=4]="removeAttribute",e[e.updateText=5]="updateText",e[e.stepIn=6]="stepIn",e[e.stepOut=7]="stepOut",e[e.updateMarkup=8]="updateMarkup",e[e.permutationListEntry=9]="permutationListEntry",e[e.permutationListEnd=10]="permutationListEnd"}(t||(t={})),function(e){e[e.element=1]="element",e[e.text=2]="text",e[e.attribute=3]="attribute",e[e.component=4]="component",e[e.region=5]="region",e[e.elementReferenceCapture=6]="elementReferenceCapture",e[e.markup=8]="markup"}(n||(n={}));class r{constructor(e,t){this.componentId=e,this.fieldValue=t}static fromEvent(e,t){const n=t.target;if(n instanceof Element){const t=function(e){return e instanceof HTMLInputElement?e.type&&"checkbox"===e.type.toLowerCase()?{value:e.checked}:{value:e.value}:e instanceof HTMLSelectElement||e instanceof HTMLTextAreaElement?{value:e.value}:null}(n);if(t)return new r(e,t.value)}return null}}const o=new Map,a=new Map,s=[];function i(e){return o.get(e)}function c(e){const t=o.get(e);return(null==t?void 0:t.browserEventName)||e}function l(e,t){e.forEach((e=>o.set(e,t)))}function u(e){const t=[];for(let n=0;n<e.length;n++){const r=e[n];t.push({identifier:r.identifier,clientX:r.clientX,clientY:r.clientY,screenX:r.screenX,screenY:r.screenY,pageX:r.pageX,pageY:r.pageY})}return t}function d(e){return{detail:e.detail,screenX:e.screenX,screenY:e.screenY,clientX:e.clientX,clientY:e.clientY,offsetX:e.offsetX,offsetY:e.offsetY,pageX:e.pageX,pageY:e.pageY,button:e.button,buttons:e.buttons,ctrlKey:e.ctrlKey,shiftKey:e.shiftKey,altKey:e.altKey,metaKey:e.metaKey,type:e.type}}l(["input","change"],{createEventArgs:function(e){const t=e.target;if(function(e){return-1!==f.indexOf(e.getAttribute("type"))}(t)){const e=function(e){const t=e.value,n=e.type;switch(n){case"date":case"month":case"week":return t;case"datetime-local":return 16===t.length?t+":00":t;case"time":return 5===t.length?t+":00":t}throw new Error(`Invalid element type '${n}'.`)}(t);return{value:e}}if(function(e){return e instanceof HTMLSelectElement&&"select-multiple"===e.type}(t)){const e=t;return{value:Array.from(e.options).filter((e=>e.selected)).map((e=>e.value))}}{const e=function(e){return!!e&&"INPUT"===e.tagName&&"checkbox"===e.getAttribute("type")}(t);return{value:e?!!t.checked:t.value}}}}),l(["copy","cut","paste"],{createEventArgs:e=>({type:e.type})}),l(["drag","dragend","dragenter","dragleave","dragover","dragstart","drop"],{createEventArgs:e=>{return{...d(t=e),dataTransfer:t.dataTransfer?{dropEffect:t.dataTransfer.dropEffect,effectAllowed:t.dataTransfer.effectAllowed,files:Array.from(t.dataTransfer.files).map((e=>e.name)),items:Array.from(t.dataTransfer.items).map((e=>({kind:e.kind,type:e.type}))),types:t.dataTransfer.types}:null};var t}}),l(["focus","blur","focusin","focusout"],{createEventArgs:e=>({type:e.type})}),l(["keydown","keyup","keypress"],{createEventArgs:e=>{return{key:(t=e).key,code:t.code,location:t.location,repeat:t.repeat,ctrlKey:t.ctrlKey,shiftKey:t.shiftKey,altKey:t.altKey,metaKey:t.metaKey,type:t.type};var t}}),l(["contextmenu","click","mouseover","mouseout","mousemove","mousedown","mouseup","mouseleave","mouseenter","dblclick"],{createEventArgs:e=>d(e)}),l(["error"],{createEventArgs:e=>{return{message:(t=e).message,filename:t.filename,lineno:t.lineno,colno:t.colno,type:t.type};var t}}),l(["loadstart","timeout","abort","load","loadend","progress"],{createEventArgs:e=>{return{lengthComputable:(t=e).lengthComputable,loaded:t.loaded,total:t.total,type:t.type};var t}}),l(["touchcancel","touchend","touchmove","touchenter","touchleave","touchstart"],{createEventArgs:e=>{return{detail:(t=e).detail,touches:u(t.touches),targetTouches:u(t.targetTouches),changedTouches:u(t.changedTouches),ctrlKey:t.ctrlKey,shiftKey:t.shiftKey,altKey:t.altKey,metaKey:t.metaKey,type:t.type};var t}}),l(["gotpointercapture","lostpointercapture","pointercancel","pointerdown","pointerenter","pointerleave","pointermove","pointerout","pointerover","pointerup"],{createEventArgs:e=>{return{...d(t=e),pointerId:t.pointerId,width:t.width,height:t.height,pressure:t.pressure,tiltX:t.tiltX,tiltY:t.tiltY,pointerType:t.pointerType,isPrimary:t.isPrimary};var t}}),l(["wheel","mousewheel"],{createEventArgs:e=>{return{...d(t=e),deltaX:t.deltaX,deltaY:t.deltaY,deltaZ:t.deltaZ,deltaMode:t.deltaMode};var t}}),l(["toggle"],{createEventArgs:()=>({})});const f=["date","datetime-local","month","time","week"],h=new Map;let p,m,b=0;const y={async add(e,t,n){if(!n)throw new Error("initialParameters must be an object, even if empty.");const r="__bl-dynamic-root:"+(++b).toString();h.set(r,e);const o=await w().invokeMethodAsync("AddRootComponent",t,r),a=new v(o,m[t]);return await a.setParameters(n),a}};class g{invoke(e){return this._callback(e)}setCallback(t){this._selfJSObjectReference||(this._selfJSObjectReference=e.createJSObjectReference(this)),this._callback=t}getJSObjectReference(){return this._selfJSObjectReference}dispose(){this._selfJSObjectReference&&e.disposeJSObjectReference(this._selfJSObjectReference)}}class v{constructor(e,t){this._jsEventCallbackWrappers=new Map,this._componentId=e;for(const e of t)"eventcallback"===e.type&&this._jsEventCallbackWrappers.set(e.name.toLowerCase(),new g)}setParameters(e){const t={},n=Object.entries(e||{}),r=n.length;for(const[e,r]of n){const n=this._jsEventCallbackWrappers.get(e.toLowerCase());n&&r?(n.setCallback(r),t[e]=n.getJSObjectReference()):t[e]=r}return w().invokeMethodAsync("SetRootComponentParameters",this._componentId,r,t)}async dispose(){if(null!==this._componentId){await w().invokeMethodAsync("RemoveRootComponent",this._componentId),this._componentId=null;for(const e of this._jsEventCallbackWrappers.values())e.dispose()}}}function w(){if(!p)throw new Error("Dynamic root components have not been enabled in this application.");return p}const E=new Map;let S;const I=new Promise((e=>{S=e}));function C(e,t,n){return D(e,t.eventHandlerId,(()=>A(e).invokeMethodAsync("DispatchEventAsync",t,n)))}function A(e){const t=E.get(e);if(!t)throw new Error(`No interop methods are registered for renderer ${e}`);return t}let D=(e,t,n)=>n();const k=F(["abort","blur","canplay","canplaythrough","change","cuechange","durationchange","emptied","ended","error","focus","load","loadeddata","loadedmetadata","loadend","loadstart","mouseenter","mouseleave","pause","play","playing","progress","ratechange","reset","scroll","seeked","seeking","stalled","submit","suspend","timeupdate","toggle","unload","volumechange","waiting","DOMNodeInsertedIntoDocument","DOMNodeRemovedFromDocument"]),R={submit:!0},T=F(["click","dblclick","mousedown","mousemove","mouseup"]);class N{constructor(e){this.browserRendererId=e,this.afterClickCallbacks=[];const t=++N.nextEventDelegatorId;this.eventsCollectionKey=`_blazorEvents_${t}`,this.eventInfoStore=new _(this.onGlobalEvent.bind(this))}setListener(e,t,n,r){const o=this.getEventHandlerInfosForElement(e,!0),a=o.getHandler(t);if(a)this.eventInfoStore.update(a.eventHandlerId,n);else{const a={element:e,eventName:t,eventHandlerId:n,renderingComponentId:r};this.eventInfoStore.add(a),o.setHandler(t,a)}}getHandler(e){return this.eventInfoStore.get(e)}removeListener(e){const t=this.eventInfoStore.remove(e);if(t){const e=t.element,n=this.getEventHandlerInfosForElement(e,!1);n&&n.removeHandler(t.eventName)}}notifyAfterClick(e){this.afterClickCallbacks.push(e),this.eventInfoStore.addGlobalListener("click")}setStopPropagation(e,t,n){this.getEventHandlerInfosForElement(e,!0).stopPropagation(t,n)}setPreventDefault(e,t,n){this.getEventHandlerInfosForElement(e,!0).preventDefault(t,n)}onGlobalEvent(e){if(!(e.target instanceof Element))return;this.dispatchGlobalEventToAllElements(e.type,e);const t=(n=e.type,a.get(n));var n;t&&t.forEach((t=>this.dispatchGlobalEventToAllElements(t,e))),"click"===e.type&&this.afterClickCallbacks.forEach((t=>t(e)))}dispatchGlobalEventToAllElements(e,t){const n=t.composedPath();let o=n.shift(),a=null,s=!1;const c=Object.prototype.hasOwnProperty.call(k,e);let l=!1;for(;o;){const f=o,h=this.getEventHandlerInfosForElement(f,!1);if(h){const n=h.getHandler(e);if(n&&(u=f,d=t.type,!((u instanceof HTMLButtonElement||u instanceof HTMLInputElement||u instanceof HTMLTextAreaElement||u instanceof HTMLSelectElement)&&Object.prototype.hasOwnProperty.call(T,d)&&u.disabled))){if(!s){const n=i(e);a=(null==n?void 0:n.createEventArgs)?n.createEventArgs(t):{},s=!0}Object.prototype.hasOwnProperty.call(R,t.type)&&t.preventDefault(),C(this.browserRendererId,{eventHandlerId:n.eventHandlerId,eventName:e,eventFieldInfo:r.fromEvent(n.renderingComponentId,t)},a)}h.stopPropagation(e)&&(l=!0),h.preventDefault(e)&&t.preventDefault()}o=c||l?void 0:n.shift()}var u,d}getEventHandlerInfosForElement(e,t){return Object.prototype.hasOwnProperty.call(e,this.eventsCollectionKey)?e[this.eventsCollectionKey]:t?e[this.eventsCollectionKey]=new O:null}}N.nextEventDelegatorId=0;class _{constructor(e){this.globalListener=e,this.infosByEventHandlerId={},this.countByEventName={},s.push(this.handleEventNameAliasAdded.bind(this))}add(e){if(this.infosByEventHandlerId[e.eventHandlerId])throw new Error(`Event ${e.eventHandlerId} is already tracked`);this.infosByEventHandlerId[e.eventHandlerId]=e,this.addGlobalListener(e.eventName)}get(e){return this.infosByEventHandlerId[e]}addGlobalListener(e){if(e=c(e),Object.prototype.hasOwnProperty.call(this.countByEventName,e))this.countByEventName[e]++;else{this.countByEventName[e]=1;const t=Object.prototype.hasOwnProperty.call(k,e);document.addEventListener(e,this.globalListener,t)}}update(e,t){if(Object.prototype.hasOwnProperty.call(this.infosByEventHandlerId,t))throw new Error(`Event ${t} is already tracked`);const n=this.infosByEventHandlerId[e];delete this.infosByEventHandlerId[e],n.eventHandlerId=t,this.infosByEventHandlerId[t]=n}remove(e){const t=this.infosByEventHandlerId[e];if(t){delete this.infosByEventHandlerId[e];const n=c(t.eventName);0==--this.countByEventName[n]&&(delete this.countByEventName[n],document.removeEventListener(n,this.globalListener))}return t}handleEventNameAliasAdded(e,t){if(Object.prototype.hasOwnProperty.call(this.countByEventName,e)){const n=this.countByEventName[e];delete this.countByEventName[e],document.removeEventListener(e,this.globalListener),this.addGlobalListener(t),this.countByEventName[t]+=n-1}}}class O{constructor(){this.handlers={},this.preventDefaultFlags=null,this.stopPropagationFlags=null}getHandler(e){return Object.prototype.hasOwnProperty.call(this.handlers,e)?this.handlers[e]:null}setHandler(e,t){this.handlers[e]=t}removeHandler(e){delete this.handlers[e]}preventDefault(e,t){return void 0!==t&&(this.preventDefaultFlags=this.preventDefaultFlags||{},this.preventDefaultFlags[e]=t),!!this.preventDefaultFlags&&this.preventDefaultFlags[e]}stopPropagation(e,t){return void 0!==t&&(this.stopPropagationFlags=this.stopPropagationFlags||{},this.stopPropagationFlags[e]=t),!!this.stopPropagationFlags&&this.stopPropagationFlags[e]}}function F(e){const t={};return e.forEach((e=>{t[e]=!0})),t}const j=G("_blazorLogicalChildren"),x=G("_blazorLogicalParent"),L=G("_blazorLogicalEnd");function P(e,t){if(e.childNodes.length>0&&!t)throw new Error("New logical elements must start empty, or allowExistingContents must be true");return j in e||(e[j]=[]),e}function M(e,t){const n=document.createComment("!");return H(n,e,t),n}function H(e,t,n){const r=e;if(e instanceof Comment&&z(r)&&z(r).length>0)throw new Error("Not implemented: inserting non-empty logical container");if(U(r))throw new Error("Not implemented: moving existing logical children");const o=z(t);if(n<o.length){const t=o[n];t.parentNode.insertBefore(e,t),o.splice(n,0,r)}else X(e,t),o.push(r);r[x]=t,j in r||(r[j]=[])}function B(e,t){const n=z(e).splice(t,1)[0];if(n instanceof Comment){const e=z(n);if(e)for(;e.length>0;)B(n,0)}const r=n;r.parentNode.removeChild(r)}function U(e){return e[x]||null}function J(e,t){return z(e)[t]}function $(e){const t=V(e);return"http://www.w3.org/2000/svg"===t.namespaceURI&&"foreignObject"!==t.tagName}function z(e){return e[j]}function K(e,t){const n=z(e);t.forEach((e=>{e.moveRangeStart=n[e.fromSiblingIndex],e.moveRangeEnd=Y(e.moveRangeStart)})),t.forEach((t=>{const r=document.createComment("marker");t.moveToBeforeMarker=r;const o=n[t.toSiblingIndex+1];o?o.parentNode.insertBefore(r,o):X(r,e)})),t.forEach((e=>{const t=e.moveToBeforeMarker,n=t.parentNode,r=e.moveRangeStart,o=e.moveRangeEnd;let a=r;for(;a;){const e=a.nextSibling;if(n.insertBefore(a,t),a===o)break;a=e}n.removeChild(t)})),t.forEach((e=>{n[e.toSiblingIndex]=e.moveRangeStart}))}function V(e){if(e instanceof Element||e instanceof DocumentFragment)return e;if(e instanceof Comment)return e.parentNode;throw new Error("Not a valid logical element")}function W(e){const t=z(U(e));return t[Array.prototype.indexOf.call(t,e)+1]||null}function X(e,t){if(t instanceof Element||t instanceof DocumentFragment)t.appendChild(e);else{if(!(t instanceof Comment))throw new Error(`Cannot append node because the parent is not a valid logical element. Parent: ${t}`);{const n=W(t);n?n.parentNode.insertBefore(e,n):X(e,U(t))}}}function Y(e){if(e instanceof Element||e instanceof DocumentFragment)return e;const t=W(e);if(t)return t.previousSibling;{const t=U(e);return t instanceof Element||t instanceof DocumentFragment?t.lastChild:Y(t)}}function G(e){return"function"==typeof Symbol?Symbol():e}function q(e){return`_bl_${e}`}e.attachReviver(((e,t)=>t&&"object"==typeof t&&Object.prototype.hasOwnProperty.call(t,"__internalId")&&"string"==typeof t.__internalId?function(e){const t=`[${q(e)}]`;return document.querySelector(t)}(t.__internalId):t));const Z="_blazorDeferredValue",Q=document.createElement("template"),ee=document.createElementNS("http://www.w3.org/2000/svg","g"),te={},ne="__internal_",re="preventDefault_",oe="stopPropagation_";class ae{constructor(e){this.rootComponentIds=new Set,this.childComponentLocations={},this.eventDelegator=new N(e),this.eventDelegator.notifyAfterClick((e=>{if(!he)return;if(0!==e.button||function(e){return e.ctrlKey||e.shiftKey||e.altKey||e.metaKey}(e))return;if(e.defaultPrevented)return;const t=function(e){const t=!window._blazorDisableComposedPath&&e.composedPath&&e.composedPath();if(t){for(let e=0;e<t.length;e++){const n=t[e];if(n instanceof Element&&"A"===n.tagName)return n}return null}return Se(e.target,"A")}(e);if(t&&function(e){const t=e.getAttribute("target");return(!t||"_self"===t)&&e.hasAttribute("href")&&!e.hasAttribute("download")}(t)){const n=Ee(t.getAttribute("href"));Ie(n)&&(e.preventDefault(),ge(n,!0,!1))}}))}attachRootComponentToLogicalElement(e,t,n){this.attachComponentToElement(e,t),this.rootComponentIds.add(e),n||(te[e]=t)}updateComponent(e,t,n,r){var o;const a=this.childComponentLocations[t];if(!a)throw new Error(`No element is currently associated with component ${t}`);const s=te[t];if(s){const e=function(e){return e[L]||null}(s);delete te[t],e?function(e,t){const n=U(e);if(!n)throw new Error("Can't clear between nodes. The start node does not have a logical parent.");const r=z(n),o=r.indexOf(e)+1,a=r.indexOf(t);for(let e=o;e<=a;e++)B(n,o);e.textContent="!"}(s,e):function(e){let t;for(;t=e.firstChild;)e.removeChild(t)}(s)}const i=null===(o=V(a))||void 0===o?void 0:o.getRootNode(),c=i&&i.activeElement;this.applyEdits(e,t,a,0,n,r),c instanceof HTMLElement&&i&&i.activeElement!==c&&c.focus()}disposeComponent(e){this.rootComponentIds.delete(e)&&function(e){const t=z(e);for(;t.length;)B(e,0)}(this.childComponentLocations[e]),delete this.childComponentLocations[e]}disposeEventHandler(e){this.eventDelegator.removeListener(e)}attachComponentToElement(e,t){this.childComponentLocations[e]=t}applyEdits(e,n,r,o,a,s){let i,c=0,l=o;const u=e.arrayBuilderSegmentReader,d=e.editReader,f=e.frameReader,h=u.values(a),p=u.offset(a),m=p+u.count(a);for(let a=p;a<m;a++){const u=e.diffReader.editsEntry(h,a),p=d.editType(u);switch(p){case t.prependFrame:{const t=d.newTreeIndex(u),o=e.referenceFramesEntry(s,t),a=d.siblingIndex(u);this.insertFrame(e,n,r,l+a,s,o,t);break}case t.removeFrame:B(r,l+d.siblingIndex(u));break;case t.setAttribute:{const t=d.newTreeIndex(u),o=e.referenceFramesEntry(s,t),a=J(r,l+d.siblingIndex(u));if(!(a instanceof Element))throw new Error("Cannot set attribute on non-element child");this.applyAttribute(e,n,a,o);break}case t.removeAttribute:{const t=J(r,l+d.siblingIndex(u));if(!(t instanceof HTMLElement))throw new Error("Cannot remove attribute from non-element child");{const n=d.removedAttributeName(u);this.tryApplySpecialProperty(e,t,n,null)||t.removeAttribute(n)}break}case t.updateText:{const t=d.newTreeIndex(u),n=e.referenceFramesEntry(s,t),o=J(r,l+d.siblingIndex(u));if(!(o instanceof Text))throw new Error("Cannot set text content on non-text child");o.textContent=f.textContent(n);break}case t.updateMarkup:{const t=d.newTreeIndex(u),n=e.referenceFramesEntry(s,t),o=d.siblingIndex(u);B(r,l+o),this.insertMarkup(e,r,l+o,n);break}case t.stepIn:r=J(r,l+d.siblingIndex(u)),c++,l=0;break;case t.stepOut:r=U(r),c--,l=0===c?o:0;break;case t.permutationListEntry:i=i||[],i.push({fromSiblingIndex:l+d.siblingIndex(u),toSiblingIndex:l+d.moveToSiblingIndex(u)});break;case t.permutationListEnd:K(r,i),i=void 0;break;default:throw new Error(`Unknown edit type: ${p}`)}}}insertFrame(e,t,r,o,a,s,i){const c=e.frameReader,l=c.frameType(s);switch(l){case n.element:return this.insertElement(e,t,r,o,a,s,i),1;case n.text:return this.insertText(e,r,o,s),1;case n.attribute:throw new Error("Attribute frames should only be present as leading children of element frames.");case n.component:return this.insertComponent(e,r,o,s),1;case n.region:return this.insertFrameRange(e,t,r,o,a,i+1,i+c.subtreeLength(s));case n.elementReferenceCapture:if(r instanceof Element)return u=r,d=c.elementReferenceCaptureId(s),u.setAttribute(q(d),""),0;throw new Error("Reference capture frames can only be children of element frames.");case n.markup:return this.insertMarkup(e,r,o,s),1;default:throw new Error(`Unknown frame type: ${l}`)}var u,d}insertElement(e,t,r,o,a,s,i){const c=e.frameReader,l=c.elementName(s),u="svg"===l||$(r)?document.createElementNS("http://www.w3.org/2000/svg",l):document.createElement(l),d=P(u);let f=!1;const h=i+c.subtreeLength(s);for(let s=i+1;s<h;s++){const i=e.referenceFramesEntry(a,s);if(c.frameType(i)!==n.attribute){H(u,r,o),f=!0,this.insertFrameRange(e,t,d,0,a,s,h);break}this.applyAttribute(e,t,u,i)}f||H(u,r,o),u instanceof HTMLOptionElement?this.trySetSelectValueFromOptionElement(u):Z in u&&ue(u,u._blazorDeferredValue)}trySetSelectValueFromOptionElement(e){const t=this.findClosestAncestorSelectElement(e);if(!function(e){return!!e&&Z in e}(t))return!1;if(ce(t))e.selected=-1!==t._blazorDeferredValue.indexOf(e.value);else{if(t._blazorDeferredValue!==e.value)return!1;le(t,e.value),delete t._blazorDeferredValue}return!0}insertComponent(e,t,n,r){const o=M(t,n),a=e.frameReader.componentId(r);this.attachComponentToElement(a,o)}insertText(e,t,n,r){const o=e.frameReader.textContent(r);H(document.createTextNode(o),t,n)}insertMarkup(e,t,n,r){const o=M(t,n),a=(s=e.frameReader.markupContent(r),$(t)?(ee.innerHTML=s||" ",ee):(Q.innerHTML=s||" ",Q.content));var s;let i=0;for(;a.firstChild;)H(a.firstChild,o,i++)}applyAttribute(e,t,n,r){const o=e.frameReader,a=o.attributeName(r),s=o.attributeEventHandlerId(r);if(s){const e=ie(a);this.eventDelegator.setListener(n,e,s,t)}else this.tryApplySpecialProperty(e,n,a,r)||n.setAttribute(a,o.attributeValue(r))}tryApplySpecialProperty(e,t,n,r){switch(n){case"value":return this.tryApplyValueProperty(e,t,r);case"checked":return this.tryApplyCheckedProperty(e,t,r);default:return!!n.startsWith(ne)&&(this.applyInternalAttribute(e,t,n.substring(ne.length),r),!0)}}applyInternalAttribute(e,t,n,r){const o=r?e.frameReader.attributeValue(r):null;if(n.startsWith(oe)){const e=ie(n.substring(oe.length));this.eventDelegator.setStopPropagation(t,e,null!==o)}else{if(!n.startsWith(re))throw new Error(`Unsupported internal attribute '${n}'`);{const e=ie(n.substring(re.length));this.eventDelegator.setPreventDefault(t,e,null!==o)}}}tryApplyValueProperty(e,t,n){const r=e.frameReader;let o=n?r.attributeValue(n):null;switch(o&&"INPUT"===t.tagName&&(o=function(e,t){switch(t.getAttribute("type")){case"time":return 8!==e.length||!e.endsWith("00")&&t.hasAttribute("step")?e:e.substring(0,5);case"datetime-local":return 19!==e.length||!e.endsWith("00")&&t.hasAttribute("step")?e:e.substring(0,16);default:return e}}(o,t)),t.tagName){case"INPUT":case"SELECT":case"TEXTAREA":return o&&t instanceof HTMLSelectElement&&ce(t)&&(o=JSON.parse(o)),ue(t,o),t._blazorDeferredValue=o,!0;case"OPTION":return o||""===o?t.setAttribute("value",o):t.removeAttribute("value"),this.trySetSelectValueFromOptionElement(t),!0;default:return!1}}tryApplyCheckedProperty(e,t,n){if("INPUT"===t.tagName){const r=n?e.frameReader.attributeValue(n):null;return t.checked=null!==r,!0}return!1}findClosestAncestorSelectElement(e){for(;e;){if(e instanceof HTMLSelectElement)return e;e=e.parentElement}return null}insertFrameRange(e,t,n,r,o,a,s){const i=r;for(let i=a;i<s;i++){const a=e.referenceFramesEntry(o,i);r+=this.insertFrame(e,t,n,r,o,a,i),i+=se(e,a)}return r-i}}function se(e,t){const r=e.frameReader;switch(r.frameType(t)){case n.component:case n.element:case n.region:return r.subtreeLength(t)-1;default:return 0}}function ie(e){if(e.startsWith("on"))return e.substring(2);throw new Error(`Attribute should be an event name, but doesn't start with 'on'. Value: '${e}'`)}function ce(e){return"select-multiple"===e.type}function le(e,t){e.value=t||""}function ue(e,t){e instanceof HTMLSelectElement?ce(e)?function(e,t){t||(t=[]);for(let n=0;n<e.options.length;n++)e.options[n].selected=-1!==t.indexOf(e.options[n].value)}(e,t):le(e,t):e.value=t}const de={};let fe=!1,he=!1,pe=!1,me=null;const be={listenForNavigationEvents:function(e){me=e,pe||(pe=!0,window.addEventListener("popstate",(()=>ve(!1))))},enableNavigationInterception:function(){he=!0},navigateTo:ye,getBaseURI:()=>document.baseURI,getLocationHref:()=>location.href};function ye(e,t,n=!1){const r=Ee(e),o=t instanceof Object?t:{forceLoad:t,replaceHistoryEntry:n};!o.forceLoad&&Ie(r)?ge(r,!1,o.replaceHistoryEntry):function(e,t){if(location.href===e){const t=e+"?";history.replaceState(null,"",t),location.replace(e)}else t?location.replace(e):location.href=e}(e,o.replaceHistoryEntry)}function ge(e,t,n){fe=!0,n?history.replaceState(null,"",e):history.pushState(null,"",e),ve(t)}async function ve(e){me&&await me(location.href,e)}let we;function Ee(e){return we=we||document.createElement("a"),we.href=e,we.href}function Se(e,t){return e?e.tagName===t?e:Se(e.parentElement,t):null}function Ie(e){const t=(n=document.baseURI).substring(0,n.lastIndexOf("/"));var n;const r=e.charAt(t.length);return e.startsWith(t)&&(""===r||"/"===r||"?"===r||"#"===r)}const Ce={focus:function(e,t){if(e instanceof HTMLElement)e.focus({preventScroll:t});else{if(!(e instanceof SVGElement))throw new Error("Unable to focus an invalid element.");if(!e.hasAttribute("tabindex"))throw new Error("Unable to focus an SVG element that does not have a tabindex.");e.focus({preventScroll:t})}},focusBySelector:function(e){const t=document.querySelector(e);t&&(t.hasAttribute("tabindex")||(t.tabIndex=-1),t.focus())}},Ae={init:function(e,t,n,r=50){const o=ke(t);(o||document.documentElement).style.overflowAnchor="none";const a=document.createRange();u(n.parentElement)&&(t.style.display="table-row",n.style.display="table-row");const s=new IntersectionObserver((function(r){r.forEach((r=>{var o;if(!r.isIntersecting)return;a.setStartAfter(t),a.setEndBefore(n);const s=a.getBoundingClientRect().height,i=null===(o=r.rootBounds)||void 0===o?void 0:o.height;r.target===t?e.invokeMethodAsync("OnSpacerBeforeVisible",r.intersectionRect.top-r.boundingClientRect.top,s,i):r.target===n&&n.offsetHeight>0&&e.invokeMethodAsync("OnSpacerAfterVisible",r.boundingClientRect.bottom-r.intersectionRect.bottom,s,i)}))}),{root:o,rootMargin:`${r}px`});s.observe(t),s.observe(n);const i=l(t),c=l(n);function l(e){const t={attributes:!0},n=new MutationObserver(((n,r)=>{u(e.parentElement)&&(r.disconnect(),e.style.display="table-row",r.observe(e,t)),s.unobserve(e),s.observe(e)}));return n.observe(e,t),n}function u(e){return null!==e&&(e instanceof HTMLTableElement&&""===e.style.display||"table"===e.style.display||e instanceof HTMLTableSectionElement&&""===e.style.display||"table-row-group"===e.style.display)}De[e._id]={intersectionObserver:s,mutationObserverBefore:i,mutationObserverAfter:c}},dispose:function(e){const t=De[e._id];t&&(t.intersectionObserver.disconnect(),t.mutationObserverBefore.disconnect(),t.mutationObserverAfter.disconnect(),e.dispose(),delete De[e._id])}},De={};function ke(e){return e&&e!==document.body&&e!==document.documentElement?"visible"!==getComputedStyle(e).overflowY?e:ke(e.parentElement):null}const Re={getAndRemoveExistingTitle:function(){var e;const t=document.head?document.head.getElementsByTagName("title"):[];if(0===t.length)return null;let n=null;for(let r=t.length-1;r>=0;r--){const o=t[r],a=o.previousSibling;a instanceof Comment&&null!==U(a)||(null===n&&(n=o.textContent),null===(e=o.parentNode)||void 0===e||e.removeChild(o))}return n}},Te={init:function(e,t){t._blazorInputFileNextFileId=0,t.addEventListener("click",(function(){t.value=""})),t.addEventListener("change",(function(){t._blazorFilesById={};const n=Array.prototype.map.call(t.files,(function(e){const n={id:++t._blazorInputFileNextFileId,lastModified:new Date(e.lastModified).toISOString(),name:e.name,size:e.size,contentType:e.type,readPromise:void 0,arrayBuffer:void 0,blob:e};return t._blazorFilesById[n.id]=n,n}));e.invokeMethodAsync("NotifyChange",n)}))},toImageFile:async function(e,t,n,r,o){const a=Ne(e,t),s=await new Promise((function(e){const t=new Image;t.onload=function(){URL.revokeObjectURL(t.src),e(t)},t.onerror=function(){t.onerror=null,URL.revokeObjectURL(t.src)},t.src=URL.createObjectURL(a.blob)})),i=await new Promise((function(e){var t;const a=Math.min(1,r/s.width),i=Math.min(1,o/s.height),c=Math.min(a,i),l=document.createElement("canvas");l.width=Math.round(s.width*c),l.height=Math.round(s.height*c),null===(t=l.getContext("2d"))||void 0===t||t.drawImage(s,0,0,l.width,l.height),l.toBlob(e,n)})),c={id:++e._blazorInputFileNextFileId,lastModified:a.lastModified,name:a.name,size:(null==i?void 0:i.size)||0,contentType:n,blob:i||a.blob};return e._blazorFilesById[c.id]=c,c},readFileData:async function(e,t){return Ne(e,t).blob}};function Ne(e,t){const n=e._blazorFilesById[t];if(!n)throw new Error(`There is no file with ID ${t}. The file list may have changed. See https://aka.ms/aspnet/blazor-input-file-multiple-selections.`);return n}const _e=new Map,Oe={navigateTo:ye,registerCustomEventType:function(e,t){if(!t)throw new Error("The options parameter is required.");if(o.has(e))throw new Error(`The event '${e}' is already registered.`);if(t.browserEventName){const n=a.get(t.browserEventName);n?n.push(e):a.set(t.browserEventName,[e]),s.forEach((n=>n(e,t.browserEventName)))}o.set(e,t)},rootComponents:y,_internal:{navigationManager:be,domWrapper:Ce,Virtualize:Ae,PageTitle:Re,InputFile:Te,getJSDataStreamChunk:async function(e,t,n){return e instanceof Blob?await async function(e,t,n){const r=e.slice(t,t+n),o=await r.arrayBuffer();return new Uint8Array(o)}(e,t,n):function(e,t,n){return new Uint8Array(e.buffer,e.byteOffset+t,n)}(e,t,n)},receiveDotNetDataStream:function(t,n,r,o){let a=_e.get(t);if(!a){const n=new ReadableStream({start(e){_e.set(t,e),a=e}});e.jsCallDispatcher.supplyDotNetStream(t,n)}o?(a.error(o),_e.delete(t)):0===r?(a.close(),_e.delete(t)):a.enqueue(n.length===r?n:n.subarray(0,r))},attachWebRendererInterop:function(t,n,r,o){if(E.has(t))throw new Error(`Interop methods are already registered for renderer ${t}`);E.set(t,n),Object.keys(r).length>0&&function(t,n,r){if(p)throw new Error("Dynamic root components have already been enabled.");p=t,m=n;for(const[t,o]of Object.entries(r)){const r=e.jsCallDispatcher.findJSFunction(t,0);for(const e of o)r(e,n[e])}}(A(t),r,o),S()}}};window.Blazor=Oe;let Fe=!1;const je="function"==typeof TextDecoder?new TextDecoder("utf-8"):null,xe=je?je.decode.bind(je):function(e){let t=0;const n=e.length,r=[],o=[];for(;t<n;){const n=e[t++];if(0===n)break;if(0==(128&n))r.push(n);else if(192==(224&n)){const o=63&e[t++];r.push((31&n)<<6|o)}else if(224==(240&n)){const o=63&e[t++],a=63&e[t++];r.push((31&n)<<12|o<<6|a)}else if(240==(248&n)){let o=(7&n)<<18|(63&e[t++])<<12|(63&e[t++])<<6|63&e[t++];o>65535&&(o-=65536,r.push(o>>>10&1023|55296),o=56320|1023&o),r.push(o)}r.length>1024&&(o.push(String.fromCharCode.apply(null,r)),r.length=0)}return o.push(String.fromCharCode.apply(null,r)),o.join("")},Le=Math.pow(2,32),Pe=Math.pow(2,21)-1;function Me(e,t){return e[t]|e[t+1]<<8|e[t+2]<<16|e[t+3]<<24}function He(e,t){return e[t]+(e[t+1]<<8)+(e[t+2]<<16)+(e[t+3]<<24>>>0)}function Be(e,t){const n=He(e,t+4);if(n>Pe)throw new Error(`Cannot read uint64 with high order part ${n}, because the result would exceed Number.MAX_SAFE_INTEGER.`);return n*Le+He(e,t)}class Ue{constructor(e){this.batchData=e;const t=new Ke(e);this.arrayRangeReader=new Ve(e),this.arrayBuilderSegmentReader=new We(e),this.diffReader=new Je(e),this.editReader=new $e(e,t),this.frameReader=new ze(e,t)}updatedComponents(){return Me(this.batchData,this.batchData.length-20)}referenceFrames(){return Me(this.batchData,this.batchData.length-16)}disposedComponentIds(){return Me(this.batchData,this.batchData.length-12)}disposedEventHandlerIds(){return Me(this.batchData,this.batchData.length-8)}updatedComponentsEntry(e,t){const n=e+4*t;return Me(this.batchData,n)}referenceFramesEntry(e,t){return e+20*t}disposedComponentIdsEntry(e,t){const n=e+4*t;return Me(this.batchData,n)}disposedEventHandlerIdsEntry(e,t){const n=e+8*t;return Be(this.batchData,n)}}class Je{constructor(e){this.batchDataUint8=e}componentId(e){return Me(this.batchDataUint8,e)}edits(e){return e+4}editsEntry(e,t){return e+16*t}}class $e{constructor(e,t){this.batchDataUint8=e,this.stringReader=t}editType(e){return Me(this.batchDataUint8,e)}siblingIndex(e){return Me(this.batchDataUint8,e+4)}newTreeIndex(e){return Me(this.batchDataUint8,e+8)}moveToSiblingIndex(e){return Me(this.batchDataUint8,e+8)}removedAttributeName(e){const t=Me(this.batchDataUint8,e+12);return this.stringReader.readString(t)}}class ze{constructor(e,t){this.batchDataUint8=e,this.stringReader=t}frameType(e){return Me(this.batchDataUint8,e)}subtreeLength(e){return Me(this.batchDataUint8,e+4)}elementReferenceCaptureId(e){const t=Me(this.batchDataUint8,e+4);return this.stringReader.readString(t)}componentId(e){return Me(this.batchDataUint8,e+8)}elementName(e){const t=Me(this.batchDataUint8,e+8);return this.stringReader.readString(t)}textContent(e){const t=Me(this.batchDataUint8,e+4);return this.stringReader.readString(t)}markupContent(e){const t=Me(this.batchDataUint8,e+4);return this.stringReader.readString(t)}attributeName(e){const t=Me(this.batchDataUint8,e+4);return this.stringReader.readString(t)}attributeValue(e){const t=Me(this.batchDataUint8,e+8);return this.stringReader.readString(t)}attributeEventHandlerId(e){return Be(this.batchDataUint8,e+12)}}class Ke{constructor(e){this.batchDataUint8=e,this.stringTableStartIndex=Me(e,e.length-4)}readString(e){if(-1===e)return null;{const n=Me(this.batchDataUint8,this.stringTableStartIndex+4*e),r=function(e,t){let n=0,r=0;for(let o=0;o<4;o++){const a=e[t+o];if(n|=(127&a)<<r,a<128)break;r+=7}return n}(this.batchDataUint8,n),o=n+((t=r)<128?1:t<16384?2:t<2097152?3:4),a=new Uint8Array(this.batchDataUint8.buffer,this.batchDataUint8.byteOffset+o,r);return xe(a)}var t}}class Ve{constructor(e){this.batchDataUint8=e}count(e){return Me(this.batchDataUint8,e)}values(e){return e+4}}class We{constructor(e){this.batchDataUint8=e}offset(e){return 0}count(e){return Me(this.batchDataUint8,e)}values(e){return e+4}}const Xe="__bwv:";let Ye=!1;function Ge(e,t){tt("OnRenderCompleted",e,t)}function qe(e,t,n,r,o){tt("BeginInvokeDotNet",e?e.toString():null,t,n,r||0,o)}function Ze(e,t,n){tt("EndInvokeJS",e,t,n)}function Qe(e,t){const n=function(e){const t=new Array(e.length);for(let n=0;n<e.length;n++)t[n]=String.fromCharCode(e[n]);return btoa(t.join(""))}(t);tt("ReceiveByteArrayFromJS",e,n)}function et(e,t){return tt("OnLocationChanged",e,t),Promise.resolve()}function tt(e,...t){const n=function(e,t){return Ye?null:`__bwv:${JSON.stringify([e,...t])}`}(e,t);n&&window.external.sendMessage(n)}function nt(t,n){const r=rt(n);e.jsCallDispatcher.receiveByteArray(t,r)}function rt(e){const t=atob(e),n=t.length,r=new Uint8Array(n);for(let e=0;e<n;e++)r[e]=t.charCodeAt(e);return r}class ot{constructor(){this.afterStartedCallbacks=[]}async importInitializersAsync(e,t){await Promise.all(e.map((e=>async function(e,n){const r=function(e){const t=document.baseURI;return t.endsWith("/")?`${t}${e}`:`${t}/${e}`}(n),o=await import(r);if(void 0===o)return;const{beforeStart:a,afterStarted:s}=o;return s&&e.afterStartedCallbacks.push(s),a?a(...t):void 0}(this,e))))}async invokeAfterStartedCallbacks(e){await I,await Promise.all(this.afterStartedCallbacks.map((t=>t(e))))}}let at=!1;async function st(){if(at)throw new Error("Blazor has already started.");at=!0;const t=await async function(){const e=await fetch("_framework/blazor.modules.json",{method:"GET",credentials:"include",cache:"no-cache"}),t=await e.json(),n=new ot;return await n.importInitializersAsync(t,[]),n}();(function(){const t={AttachToDocument:(e,t)=>{!function(e,t,n){const r="::after";let o=!1;if(e.endsWith(r))e=e.slice(0,-r.length),o=!0;else if(e.endsWith("::before"))throw new Error("The '::before' selector is not supported.");const a=function(e){const t=h.get(e);if(t)return h.delete(e),t}(e)||document.querySelector(e);if(!a)throw new Error(`Could not find any element matching selector '${e}'.`);!function(e,t,n,r){let o=de[0];o||(o=new ae(0),de[0]=o),o.attachRootComponentToLogicalElement(n,t,r)}(0,P(a,!0),t,o)}(t,e)},RenderBatch:(e,t)=>{try{const n=rt(t);(function(e,t){const n=de[0];if(!n)throw new Error("There is no browser renderer with ID 0.");const r=t.arrayRangeReader,o=t.updatedComponents(),a=r.values(o),s=r.count(o),i=t.referenceFrames(),c=r.values(i),l=t.diffReader;for(let e=0;e<s;e++){const r=t.updatedComponentsEntry(a,e),o=l.componentId(r),s=l.edits(r);n.updateComponent(t,o,s,c)}const u=t.disposedComponentIds(),d=r.values(u),f=r.count(u);for(let e=0;e<f;e++){const r=t.disposedComponentIdsEntry(d,e);n.disposeComponent(r)}const h=t.disposedEventHandlerIds(),p=r.values(h),m=r.count(h);for(let e=0;e<m;e++){const r=t.disposedEventHandlerIdsEntry(p,e);n.disposeEventHandler(r)}fe&&(fe=!1,window.scrollTo&&window.scrollTo(0,0))})(0,new Ue(n)),Ge(e,null)}catch(t){Ge(e,t.toString())}},NotifyUnhandledException:(e,t)=>{Ye=!0,console.error(`${e}\n${t}`),function(){const e=document.querySelector("#blazor-error-ui");e&&(e.style.display="block"),Fe||(Fe=!0,document.querySelectorAll("#blazor-error-ui .reload").forEach((e=>{e.onclick=function(e){location.reload(),e.preventDefault()}})),document.querySelectorAll("#blazor-error-ui .dismiss").forEach((e=>{e.onclick=function(e){const t=document.querySelector("#blazor-error-ui");t&&(t.style.display="none"),e.preventDefault()}})))}()},BeginInvokeJS:e.jsCallDispatcher.beginInvokeJSFromDotNet,EndInvokeDotNet:e.jsCallDispatcher.endInvokeDotNetFromJS,SendByteArrayToJS:nt,Navigate:be.navigateTo};window.external.receiveMessage((e=>{const n=function(e){if(Ye||!e||!e.startsWith(Xe))return null;const t=e.substring(Xe.length),[n,...r]=JSON.parse(t);return{messageType:n,args:r}}(e);if(n){if(!Object.prototype.hasOwnProperty.call(t,n.messageType))throw new Error(`Unsupported IPC message type '${n.messageType}'`);t[n.messageType].apply(null,n.args)}}))})(),e.attachDispatcher({beginInvokeDotNetFromJS:qe,endInvokeJSFromDotNet:Ze,sendByteArray:Qe}),be.enableNavigationInterception(),be.listenForNavigationEvents(et),tt("AttachPage",be.getBaseURI(),be.getLocationHref()),await t.invokeAfterStartedCallbacks(Oe)}Oe.start=st,document&&document.currentScript&&"false"!==document.currentScript.getAttribute("autostart")&&st()})(); \ No newline at end of file
diff --git a/src/Components/Web.JS/src/Virtualize.ts b/src/Components/Web.JS/src/Virtualize.ts
index a6603f25fd..2fcd0f1d1c 100644
--- a/src/Components/Web.JS/src/Virtualize.ts
+++ b/src/Components/Web.JS/src/Virtualize.ts
@@ -9,7 +9,11 @@ export const Virtualize = {
const observersByDotNetId = {};
function findClosestScrollContainer(element: HTMLElement | null): HTMLElement | null {
- if (!element) {
+ // If we recurse up as far as body or the document root, return null so that the
+ // IntersectionObserver observes intersection with the top-level scroll viewport
+ // instead of the with body/documentElement which can be arbitrarily tall.
+ // See https://github.com/dotnet/aspnetcore/issues/37659 for more about what this fixes.
+ if (!element || element === document.body || element === document.documentElement) {
return null;
}
diff --git a/src/Components/Web/src/JSComponents/JSComponentConfigurationStore.cs b/src/Components/Web/src/JSComponents/JSComponentConfigurationStore.cs
index 38496b6f04..b286b071e1 100644
--- a/src/Components/Web/src/JSComponents/JSComponentConfigurationStore.cs
+++ b/src/Components/Web/src/JSComponents/JSComponentConfigurationStore.cs
@@ -4,6 +4,7 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Components.RenderTree;
using Microsoft.AspNetCore.Components.Web.Infrastructure;
+using Microsoft.AspNetCore.Internal;
namespace Microsoft.AspNetCore.Components.Web;
@@ -20,11 +21,11 @@ public sealed class JSComponentConfigurationStore
// without needing any changes on the downstream code that implements IJSComponentConfiguration,
// and without exposing any of the configuration storage across layers.
- internal Dictionary<string, Type> JSComponentTypesByIdentifier { get; } = new(StringComparer.Ordinal);
+ private readonly Dictionary<string, Type> _jsComponentTypesByIdentifier = new(StringComparer.Ordinal);
internal Dictionary<string, JSComponentParameter[]> JSComponentParametersByIdentifier { get; } = new(StringComparer.Ordinal);
internal Dictionary<string, List<string>> JSComponentIdentifiersByInitializer { get; } = new(StringComparer.Ordinal);
- internal void Add(Type componentType, string identifier)
+ internal void Add([DynamicallyAccessedMembers(LinkerFlags.Component)] Type componentType, string identifier)
{
var parameterTypes = JSComponentInterop.GetComponentParameters(componentType).ParameterInfoByName;
var parameters = new JSComponentParameter[parameterTypes.Count];
@@ -34,15 +35,24 @@ public sealed class JSComponentConfigurationStore
parameters[index++] = new JSComponentParameter(name, type.Type);
}
- JSComponentTypesByIdentifier.Add(identifier, componentType);
+ _jsComponentTypesByIdentifier.Add(identifier, componentType);
JSComponentParametersByIdentifier.Add(identifier, parameters);
}
+ [UnconditionalSuppressMessage("Trimming", "IL2067",
+ Justification = "Types added to dictionary are always correctly annotated.")]
+ internal bool TryGetComponentType(
+ string identifier,
+ [NotNullWhen(true)][DynamicallyAccessedMembers(LinkerFlags.Component)] out Type? componentType)
+ {
+ return _jsComponentTypesByIdentifier.TryGetValue(identifier, out componentType);
+ }
+
[DynamicDependency(DynamicallyAccessedMemberTypes.PublicProperties, typeof(JSComponentParameter))]
[DynamicDependency(nameof(WebRenderer.WebRendererInteropMethods.AddRootComponent), typeof(WebRenderer.WebRendererInteropMethods))]
[DynamicDependency(nameof(WebRenderer.WebRendererInteropMethods.SetRootComponentParameters), typeof(WebRenderer.WebRendererInteropMethods))]
[DynamicDependency(nameof(WebRenderer.WebRendererInteropMethods.RemoveRootComponent), typeof(WebRenderer.WebRendererInteropMethods))]
- internal void Add(Type componentType, string identifier, string javaScriptInitializer)
+ internal void Add([DynamicallyAccessedMembers(LinkerFlags.Component)] Type componentType, string identifier, string javaScriptInitializer)
{
Add(componentType, identifier);
diff --git a/src/Components/Web/src/JSComponents/JSComponentInterop.cs b/src/Components/Web/src/JSComponents/JSComponentInterop.cs
index d00bbf2baa..43132bd1e7 100644
--- a/src/Components/Web/src/JSComponents/JSComponentInterop.cs
+++ b/src/Components/Web/src/JSComponents/JSComponentInterop.cs
@@ -65,7 +65,7 @@ public class JSComponentInterop
/// </summary>
protected internal virtual int AddRootComponent(string identifier, string domElementSelector)
{
- if (!Configuration.JSComponentTypesByIdentifier.TryGetValue(identifier, out var componentType))
+ if (!Configuration.TryGetComponentType(identifier, out var componentType))
{
throw new ArgumentException($"There is no registered JS component with identifier '{identifier}'.");
}
diff --git a/src/Components/Web/src/PublicAPI.Unshipped.txt b/src/Components/Web/src/PublicAPI.Unshipped.txt
index 34c8afc7fb..ecfb9e5a98 100644
--- a/src/Components/Web/src/PublicAPI.Unshipped.txt
+++ b/src/Components/Web/src/PublicAPI.Unshipped.txt
@@ -1,3 +1,5 @@
#nullable enable
Microsoft.AspNetCore.Components.Forms.InputRadio<TValue>.Element.get -> Microsoft.AspNetCore.Components.ElementReference?
Microsoft.AspNetCore.Components.Forms.InputRadio<TValue>.Element.set -> void
+Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize<TItem>.SpacerElement.get -> string!
+Microsoft.AspNetCore.Components.Web.Virtualization.Virtualize<TItem>.SpacerElement.set -> void
diff --git a/src/Components/Web/src/Virtualization/Virtualize.cs b/src/Components/Web/src/Virtualization/Virtualize.cs
index 2e9779ad9a..b2418c29d6 100644
--- a/src/Components/Web/src/Virtualization/Virtualize.cs
+++ b/src/Components/Web/src/Virtualization/Virtualize.cs
@@ -96,6 +96,18 @@ public sealed class Virtualize<TItem> : ComponentBase, IVirtualizeJsCallbacks, I
public int OverscanCount { get; set; } = 3;
/// <summary>
+ /// Gets or sets the tag name of the HTML element that will be used as the virtualization spacer.
+ /// One such element will be rendered before the visible items, and one more after them, using
+ /// an explicit "height" style to control the scroll range.
+ ///
+ /// The default value is "div". If you are placing the <see cref="Virtualize{TItem}"/> instance inside
+ /// an element that requires a specific child tag name, consider setting that here. For example when
+ /// rendering inside a "tbody", consider setting <see cref="SpacerElement"/> to the value "tr".
+ /// </summary>
+ [Parameter]
+ public string SpacerElement { get; set; } = "div";
+
+ /// <summary>
/// Instructs the component to re-request data from its <see cref="ItemsProvider"/>.
/// This is useful if external data may have changed. There is no need to call this
/// when using <see cref="Items"/>.
@@ -178,7 +190,7 @@ public sealed class Virtualize<TItem> : ComponentBase, IVirtualizeJsCallbacks, I
throw oldRefreshException;
}
- builder.OpenElement(0, "div");
+ builder.OpenElement(0, SpacerElement);
builder.AddAttribute(1, "style", GetSpacerStyle(_itemsBefore));
builder.AddElementReferenceCapture(2, elementReference => _spacerBefore = elementReference);
builder.CloseElement();
@@ -235,7 +247,7 @@ public sealed class Virtualize<TItem> : ComponentBase, IVirtualizeJsCallbacks, I
var itemsAfter = Math.Max(0, _itemCount - _visibleItemCapacity - _itemsBefore);
- builder.OpenElement(6, "div");
+ builder.OpenElement(6, SpacerElement);
builder.AddAttribute(7, "style", GetSpacerStyle(itemsAfter));
builder.AddElementReferenceCapture(8, elementReference => _spacerAfter = elementReference);
diff --git a/src/Components/test/E2ETest/Tests/BindTest.cs b/src/Components/test/E2ETest/Tests/BindTest.cs
index 0e91ec522d..79c0fae459 100644
--- a/src/Components/test/E2ETest/Tests/BindTest.cs
+++ b/src/Components/test/E2ETest/Tests/BindTest.cs
@@ -719,7 +719,6 @@ public class BindTest : ServerTestBase<ToggleExecutionModeServerFixture<Program>
// For date comparisons, we parse (non-formatted) values to compare them. Client-side and server-side
// Blazor have different formatting behaviour by default.
[Fact]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/41425")]
public void CanBindTextboxDateTime()
{
var target = Browser.Exists(By.Id("textbox-datetime"));
@@ -748,7 +747,6 @@ public class BindTest : ServerTestBase<ToggleExecutionModeServerFixture<Program>
// For date comparisons, we parse (non-formatted) values to compare them. Client-side and server-side
// Blazor have different formatting behaviour by default.
[Fact]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/41425")]
public void CanBindTextboxNullableDateTime()
{
var target = Browser.Exists(By.Id("textbox-nullable-datetime"));
@@ -779,7 +777,6 @@ public class BindTest : ServerTestBase<ToggleExecutionModeServerFixture<Program>
// For date comparisons, we parse (non-formatted) values to compare them. Client-side and server-side
// Blazor have different formatting behaviour by default.
[Fact]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/41425")]
public void CanBindTextboxDateTimeOffset()
{
var target = Browser.Exists(By.Id("textbox-datetimeoffset"));
@@ -808,7 +805,6 @@ public class BindTest : ServerTestBase<ToggleExecutionModeServerFixture<Program>
// For date comparisons, we parse (non-formatted) values to compare them. Client-side and server-side
// Blazor have different formatting behaviour by default.
[Fact]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/41425")]
public void CanBindTextboxNullableDateTimeOffset()
{
var target = Browser.Exists(By.Id("textbox-nullable-datetimeoffset"));
@@ -839,7 +835,6 @@ public class BindTest : ServerTestBase<ToggleExecutionModeServerFixture<Program>
// For date comparisons, we parse (non-formatted) values to compare them. Client-side and server-side
// Blazor have different formatting behaviour by default.
[Fact]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/41425")]
public void CanBindTextboxDateOnly()
{
var target = Browser.Exists(By.Id("textbox-dateonly"));
@@ -868,7 +863,6 @@ public class BindTest : ServerTestBase<ToggleExecutionModeServerFixture<Program>
// For date comparisons, we parse (non-formatted) values to compare them. Client-side and server-side
// Blazor have different formatting behaviour by default.
[Fact]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/41425")]
public void CanBindTextboxNullableDateOnly()
{
var target = Browser.Exists(By.Id("textbox-nullable-dateonly"));
@@ -899,7 +893,6 @@ public class BindTest : ServerTestBase<ToggleExecutionModeServerFixture<Program>
// For date comparisons, we parse (non-formatted) values to compare them. Client-side and server-side
// Blazor have different formatting behaviour by default.
[Fact]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/41425")]
public void CanBindTextboxTimeOnly()
{
var target = Browser.Exists(By.Id("textbox-timeonly"));
@@ -928,7 +921,6 @@ public class BindTest : ServerTestBase<ToggleExecutionModeServerFixture<Program>
// For date comparisons, we parse (non-formatted) values to compare them. Client-side and server-side
// Blazor have different formatting behaviour by default.
[Fact]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/41425")]
public void CanBindTextboxNullableTimeOnly()
{
var target = Browser.Exists(By.Id("textbox-nullable-timeonly"));
@@ -1049,7 +1041,6 @@ public class BindTest : ServerTestBase<ToggleExecutionModeServerFixture<Program>
// Guess what! Client-side and server-side also understand timezones differently. So for now we're comparing
// the parsed output without consideration for the timezone
[Fact]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/41425")]
public void CanBindTextboxNullableDateTimeOffsetWithFormat()
{
var target = Browser.Exists(By.Id("textbox-nullable-datetimeoffset"));
@@ -1139,7 +1130,6 @@ public class BindTest : ServerTestBase<ToggleExecutionModeServerFixture<Program>
// For date comparisons, we parse (non-formatted) values to compare them. Client-side and server-side
// Blazor have different formatting behaviour by default.
[Fact]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/41425")]
public void CanBindTextboxTimeOnlyWithFormat()
{
var target = Browser.Exists(By.Id("textbox-timeonly-format"));
@@ -1199,7 +1189,6 @@ public class BindTest : ServerTestBase<ToggleExecutionModeServerFixture<Program>
// For date comparisons, we parse (non-formatted) values to compare them. Client-side and server-side
// Blazor have different formatting behaviour by default.
[Fact]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/41425")]
public void CanBindTextboxNullableDateTime_InvalidValue()
{
var target = Browser.Exists(By.Id("textbox-nullable-datetime-invalid"));
@@ -1236,7 +1225,6 @@ public class BindTest : ServerTestBase<ToggleExecutionModeServerFixture<Program>
// For date comparisons, we parse (non-formatted) values to compare them. Client-side and server-side
// Blazor have different formatting behaviour by default.
[Fact]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/41425")]
public void CanBindTextboxDateTimeOffset_InvalidValue()
{
var target = Browser.Exists(By.Id("textbox-datetimeoffset-invalid"));
@@ -1306,7 +1294,6 @@ public class BindTest : ServerTestBase<ToggleExecutionModeServerFixture<Program>
// For date comparisons, we parse (non-formatted) values to compare them. Client-side and server-side
// Blazor have different formatting behaviour by default.
[Fact]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/41425")]
public void CanBindTextboxNullableDateTimeOffsetWithFormat_InvalidValue()
{
var target = Browser.Exists(By.Id("textbox-nullable-datetimeoffset-format-invalid"));
@@ -1344,7 +1331,6 @@ public class BindTest : ServerTestBase<ToggleExecutionModeServerFixture<Program>
// For date comparisons, we parse (non-formatted) values to compare them. Client-side and server-side
// Blazor have different formatting behaviour by default.
[Fact]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/41425")]
public void CanBindTextboxNullableDateOnly_InvalidValue()
{
var target = Browser.Exists(By.Id("textbox-nullable-dateonly-invalid"));
@@ -1412,7 +1398,6 @@ public class BindTest : ServerTestBase<ToggleExecutionModeServerFixture<Program>
// For date comparisons, we parse (non-formatted) values to compare them. Client-side and server-side
// Blazor have different formatting behaviour by default.
[Fact]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/41425")]
public void CanBindTextboxNullableTimeOnly_InvalidValue()
{
var target = Browser.Exists(By.Id("textbox-nullable-timeonly-invalid"));
diff --git a/src/Components/test/E2ETest/Tests/CustomElementsTest.cs b/src/Components/test/E2ETest/Tests/CustomElementsTest.cs
new file mode 100644
index 0000000000..5dcdaf2a5a
--- /dev/null
+++ b/src/Components/test/E2ETest/Tests/CustomElementsTest.cs
@@ -0,0 +1,128 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using BasicTestApp;
+using Microsoft.AspNetCore.Components.E2ETest;
+using Microsoft.AspNetCore.Components.E2ETest.Infrastructure;
+using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures;
+using Microsoft.AspNetCore.E2ETesting;
+using OpenQA.Selenium;
+using Xunit.Abstractions;
+
+namespace Microsoft.AspNetCore.Components.E2ETest.Tests;
+
+public class CustomElementsTest : ServerTestBase<ToggleExecutionModeServerFixture<Program>>
+{
+ protected IWebElement app;
+
+ public CustomElementsTest(
+ BrowserFixture browserFixture,
+ ToggleExecutionModeServerFixture<Program> serverFixture,
+ ITestOutputHelper output)
+ : base(browserFixture, serverFixture, output)
+ {
+ }
+
+ protected override void InitializeAsyncCore()
+ {
+ Navigate(ServerPathBase, noReload: false);
+ app = Browser.MountTestComponent<CustomElementsComponent>();
+ }
+
+ [Fact]
+ public void CanAddAndRemoveCustomElements()
+ {
+ // Custom elements can be added.
+ app.FindElement(By.Id("add-custom-element")).Click();
+ Browser.Exists(By.Id("custom-element-0"));
+
+ app.FindElement(By.Id("add-custom-element")).Click();
+ Browser.Exists(By.Id("custom-element-1"));
+
+ // Custom elements are correctly removed.
+ app.FindElement(By.Id("remove-custom-element")).Click();
+ Browser.DoesNotExist(By.Id("custom-element-1"));
+
+ app.FindElement(By.Id("remove-custom-element")).Click();
+ Browser.DoesNotExist(By.Id("custom-element-0"));
+ }
+
+ [Fact]
+ public void CanUpdateSimpleParameters()
+ {
+ app.FindElement(By.Id("add-custom-element")).Click();
+ Browser.Exists(By.Id("custom-element-0"));
+
+ // Initial parameter values are correct.
+ ValidateSimpleParameterValues(id: 0, clickCount: 0);
+
+ app.FindElement(By.Id("increment-0")).Click();
+
+ // Parameter values have been updated.
+ ValidateSimpleParameterValues(id: 0, clickCount: 1);
+
+ void ValidateSimpleParameterValues(int id, int clickCount)
+ {
+ // Nullable parameters will be "null" every other click.
+ var doNullableParamsHaveValues = clickCount % 2 == 0;
+
+ var customElement = app.FindElement(By.Id($"custom-element-{id}"));
+
+ var expectedStringValue = $"Custom element {id} (Clicked {clickCount} times)";
+ Browser.Equal(expectedStringValue, () => customElement.FindElement(By.ClassName("string-param")).Text);
+
+ var expectedBoolValue = clickCount % 2 == 0 ? bool.TrueString : bool.FalseString;
+ Browser.Equal(expectedBoolValue, () => customElement.FindElement(By.ClassName("bool-param")).Text);
+
+ var expectedNullableBoolValue = doNullableParamsHaveValues ? expectedBoolValue : "null";
+ Browser.Equal(expectedNullableBoolValue, () => customElement.FindElement(By.ClassName("nullable-bool-param")).Text);
+
+ var expectedIntegerValue = clickCount.ToString(CultureInfo.InvariantCulture);
+ Browser.Equal(expectedIntegerValue, () => customElement.FindElement(By.ClassName("int-param")).Text);
+ Browser.Equal(expectedIntegerValue, () => customElement.FindElement(By.ClassName("long-param")).Text);
+
+ var expectedNullableIntegerValue = doNullableParamsHaveValues ? expectedIntegerValue : "null";
+ Browser.Equal(expectedNullableIntegerValue, () => customElement.FindElement(By.ClassName("nullable-int-param")).Text);
+ Browser.Equal(expectedNullableIntegerValue, () => customElement.FindElement(By.ClassName("nullable-long-param")).Text);
+
+ var expectedFloatValue = expectedIntegerValue + ".5";
+ Browser.Equal(expectedFloatValue, () => customElement.FindElement(By.ClassName("float-param")).Text);
+ Browser.Equal(expectedFloatValue, () => customElement.FindElement(By.ClassName("double-param")).Text);
+ Browser.Equal(expectedFloatValue, () => customElement.FindElement(By.ClassName("decimal-param")).Text);
+
+ var expectedNullableFloatValue = doNullableParamsHaveValues ? expectedFloatValue : "null";
+ Browser.Equal(expectedNullableFloatValue, () => customElement.FindElement(By.ClassName("nullable-float-param")).Text);
+ Browser.Equal(expectedNullableFloatValue, () => customElement.FindElement(By.ClassName("nullable-double-param")).Text);
+ Browser.Equal(expectedNullableFloatValue, () => customElement.FindElement(By.ClassName("nullable-decimal-param")).Text);
+ }
+ }
+
+ [Fact]
+ public void CanUpdateComplexParameters()
+ {
+ app.FindElement(By.Id("add-custom-element")).Click();
+ Browser.Exists(By.Id("custom-element-0"));
+
+ var incrementButton = app.FindElement(By.Id("increment-0"));
+ incrementButton.Click();
+ incrementButton.Click();
+
+ app.FindElement(By.Id("update-complex-parameters-0")).Click();
+
+ // The complex object parameter was updated.
+ var expectedComplexObjectValue = @"{ Property = ""Clicked 2 times"" }";
+ Browser.Equal(expectedComplexObjectValue, () => app.FindElement(By.Id("custom-element-0")).FindElement(By.ClassName("complex-type-param")).Text);
+
+ app.FindElement(By.Id("custom-element-0")).FindElement(By.ClassName("invoke-callback")).Click();
+
+ // The callback parameter was invoked.
+ var expectedMessage = "Callback with count = 2";
+ Browser.Equal(expectedMessage, () => app.FindElement(By.Id("message")).Text);
+ }
+}
diff --git a/src/Components/test/E2ETest/Tests/FormsInputDateTest.cs b/src/Components/test/E2ETest/Tests/FormsInputDateTest.cs
index e495a0528d..460eb5b87a 100644
--- a/src/Components/test/E2ETest/Tests/FormsInputDateTest.cs
+++ b/src/Components/test/E2ETest/Tests/FormsInputDateTest.cs
@@ -34,7 +34,6 @@ public class FormsInputDateTest : ServerTestBase<ToggleExecutionModeServerFixtur
}
[Fact]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/41425")]
public void InputDateInteractsWithEditContext_NonNullableDateTime()
{
var appElement = Browser.MountTestComponent<TypicalValidationComponent>();
@@ -67,7 +66,6 @@ public class FormsInputDateTest : ServerTestBase<ToggleExecutionModeServerFixtur
}
[Fact]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/41425")]
public void InputDateInteractsWithEditContext_NullableDateTimeOffset()
{
var appElement = Browser.MountTestComponent<TypicalValidationComponent>();
@@ -91,7 +89,6 @@ public class FormsInputDateTest : ServerTestBase<ToggleExecutionModeServerFixtur
}
[Fact]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/41425")]
public void InputDateInteractsWithEditContext_TimeInput()
{
var appElement = Browser.MountTestComponent<TypicalValidationComponent>();
@@ -146,7 +143,6 @@ public class FormsInputDateTest : ServerTestBase<ToggleExecutionModeServerFixtur
}
[Fact]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/41425")]
public void InputDateInteractsWithEditContext_MonthInput()
{
var appElement = Browser.MountTestComponent<TypicalValidationComponent>();
@@ -176,7 +172,6 @@ public class FormsInputDateTest : ServerTestBase<ToggleExecutionModeServerFixtur
}
[Fact]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/41425")]
public void InputDateInteractsWithEditContext_DateTimeLocalInput()
{
var appElement = Browser.MountTestComponent<TypicalValidationComponent>();
@@ -213,7 +208,6 @@ public class FormsInputDateTest : ServerTestBase<ToggleExecutionModeServerFixtur
}
[Fact]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/41425")]
public void InputDateInteractsWithEditContext_DateTimeLocalInput_Step()
{
var appElement = Browser.MountTestComponent<TypicalValidationComponent>();
diff --git a/src/Components/test/E2ETest/Tests/GlobalizationTest.cs b/src/Components/test/E2ETest/Tests/GlobalizationTest.cs
index 96c83d8572..1aa3ff04e5 100644
--- a/src/Components/test/E2ETest/Tests/GlobalizationTest.cs
+++ b/src/Components/test/E2ETest/Tests/GlobalizationTest.cs
@@ -25,7 +25,6 @@ public abstract class GlobalizationTest<TServerFixture> : ServerTestBase<TServer
[Theory]
[InlineData("en-US")]
[InlineData("fr-FR")]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/41425")]
public virtual void CanSetCultureAndParseCultureSensitiveNumbersAndDates(string culture)
{
var cultureInfo = CultureInfo.GetCultureInfo(culture);
@@ -96,7 +95,6 @@ public abstract class GlobalizationTest<TServerFixture> : ServerTestBase<TServer
[Theory]
[InlineData("en-US")]
[InlineData("fr-FR")]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/41425")]
public void CanSetCultureAndParseCultureInvariantNumbersAndDatesWithInputFields(string culture)
{
var cultureInfo = CultureInfo.GetCultureInfo(culture);
@@ -154,7 +152,6 @@ public abstract class GlobalizationTest<TServerFixture> : ServerTestBase<TServer
[Theory]
[InlineData("en-US")]
[InlineData("fr-FR")]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/41425")]
public void CanSetCultureAndParseCultureInvariantNumbersAndDatesWithFormComponents(string culture)
{
var cultureInfo = CultureInfo.GetCultureInfo(culture);
diff --git a/src/Components/test/E2ETest/Tests/RoutingTest.cs b/src/Components/test/E2ETest/Tests/RoutingTest.cs
index a28d0d9e0a..36697e4cee 100644
--- a/src/Components/test/E2ETest/Tests/RoutingTest.cs
+++ b/src/Components/test/E2ETest/Tests/RoutingTest.cs
@@ -832,7 +832,6 @@ public class RoutingTest : ServerTestBase<ToggleExecutionModeServerFixture<Progr
}
[Fact]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/41425")]
public void CanArriveAtQueryStringPageWithDateTimeQuery()
{
var dateTime = new DateTime(2000, 1, 2, 3, 4, 5, 6);
diff --git a/src/Components/test/E2ETest/Tests/VirtualizationTest.cs b/src/Components/test/E2ETest/Tests/VirtualizationTest.cs
index 1ba1c0174d..b954498b48 100644
--- a/src/Components/test/E2ETest/Tests/VirtualizationTest.cs
+++ b/src/Components/test/E2ETest/Tests/VirtualizationTest.cs
@@ -239,6 +239,30 @@ public class VirtualizationTest : ServerTestBase<ToggleExecutionModeServerFixtur
}
[Fact]
+ public void CanRenderHtmlTable()
+ {
+ Browser.MountTestComponent<VirtualizationTable>();
+ var expectedInitialSpacerStyle = "height: 0px; flex-shrink: 0;";
+ var topSpacer = Browser.Exists(By.CssSelector("#virtualized-table > tbody > :first-child"));
+ var bottomSpacer = Browser.Exists(By.CssSelector("#virtualized-table > tbody > :last-child"));
+
+ // We can override the tag name of the spacer
+ Assert.Equal("tr", topSpacer.TagName.ToLowerInvariant());
+ Assert.Equal("tr", bottomSpacer.TagName.ToLowerInvariant());
+ Assert.Contains(expectedInitialSpacerStyle, topSpacer.GetAttribute("style"));
+
+ // Check scrolling document element works
+ Browser.DoesNotExist(By.Id("row-999"));
+ Browser.ExecuteJavaScript("window.scrollTo(0, document.body.scrollHeight);");
+ var lastElement = Browser.Exists(By.Id("row-999"));
+ Browser.True(() => lastElement.Displayed);
+
+ // Validate that the top spacer has expanded, and bottom one has collapsed
+ Browser.False(() => topSpacer.GetAttribute("style").Contains(expectedInitialSpacerStyle));
+ Assert.Contains(expectedInitialSpacerStyle, bottomSpacer.GetAttribute("style"));
+ }
+
+ [Fact]
public void CanMutateDataInPlace_Sync()
{
Browser.MountTestComponent<VirtualizationDataChanges>();
diff --git a/src/Components/test/E2ETest/yarn.lock b/src/Components/test/E2ETest/yarn.lock
index c24b1a9c72..1f4d5572af 100644
--- a/src/Components/test/E2ETest/yarn.lock
+++ b/src/Components/test/E2ETest/yarn.lock
@@ -4,19 +4,19 @@
"@sindresorhus/is@^4.0.0":
version "4.6.0"
- resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz"
+ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f"
integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==
"@szmarczak/http-timer@^4.0.5":
version "4.0.6"
- resolved "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz"
+ resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807"
integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==
dependencies:
defer-to-connect "^2.0.0"
"@types/cacheable-request@^6.0.1":
version "6.0.2"
- resolved "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.2.tgz"
+ resolved "https://registry.yarnpkg.com/@types/cacheable-request/-/cacheable-request-6.0.2.tgz#c324da0197de0a98a2312156536ae262429ff6b9"
integrity sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==
dependencies:
"@types/http-cache-semantics" "*"
@@ -26,20 +26,25 @@
"@types/http-cache-semantics@*":
version "4.0.1"
- resolved "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz"
+ resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812"
integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==
+"@types/json-buffer@~3.0.0":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/@types/json-buffer/-/json-buffer-3.0.0.tgz#85c1ff0f0948fc159810d4b5be35bf8c20875f64"
+ integrity sha512-3YP80IxxFJB4b5tYC2SUPwkg0XQLiu0nWvhRgEatgjf+29IcWO9X1k8xRv5DGssJ/lCrjYTjQPcobJr2yWIVuQ==
+
"@types/keyv@*":
- version "3.1.3"
- resolved "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.3.tgz"
- integrity sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==
+ version "3.1.4"
+ resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6"
+ integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==
dependencies:
"@types/node" "*"
"@types/node@*":
- version "17.0.21"
- resolved "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz"
- integrity sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==
+ version "18.0.0"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.0.tgz#67c7b724e1bcdd7a8821ce0d5ee184d3b4dd525a"
+ integrity sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA==
"@types/node@^13.1.7":
version "13.13.52"
@@ -48,7 +53,7 @@
"@types/responselike@*", "@types/responselike@^1.0.0":
version "1.0.0"
- resolved "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz"
+ resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29"
integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==
dependencies:
"@types/node" "*"
@@ -124,12 +129,12 @@ buffer@^5.5.0:
cacheable-lookup@^5.0.3:
version "5.0.4"
- resolved "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz"
+ resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005"
integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==
cacheable-request@^7.0.2:
version "7.0.2"
- resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.2.tgz"
+ resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-7.0.2.tgz#ea0d0b889364a25854757301ca12b2da77f91d27"
integrity sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==
dependencies:
clone-response "^1.0.2"
@@ -142,8 +147,8 @@ cacheable-request@^7.0.2:
clone-response@^1.0.2:
version "1.0.2"
- resolved "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz"
- integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=
+ resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b"
+ integrity sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==
dependencies:
mimic-response "^1.0.0"
@@ -152,6 +157,14 @@ commander@^7.2.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
+compress-brotli@^1.3.8:
+ version "1.3.8"
+ resolved "https://registry.yarnpkg.com/compress-brotli/-/compress-brotli-1.3.8.tgz#0c0a60c97a989145314ec381e84e26682e7b38db"
+ integrity sha512-lVcQsjhxhIXsuupfy9fmZUFtAIdBmXA7EGY6GBdgZ++qkM9zG4YFT8iU7FoBxzryNDMOpD1HIFHUSX4D87oqhQ==
+ dependencies:
+ "@types/json-buffer" "~3.0.0"
+ json-buffer "~3.0.1"
+
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
@@ -175,14 +188,14 @@ debug@4, debug@^4.3.1:
decompress-response@^6.0.0:
version "6.0.0"
- resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz"
+ resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc"
integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==
dependencies:
mimic-response "^3.1.0"
defer-to-connect@^2.0.0:
version "2.0.1"
- resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz"
+ resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587"
integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==
diff@^4.0.1:
@@ -225,7 +238,7 @@ fs.realpath@^1.0.0:
get-stream@^5.1.0:
version "5.2.0"
- resolved "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz"
+ resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==
dependencies:
pump "^3.0.0"
@@ -243,9 +256,9 @@ glob@^7.1.3:
path-is-absolute "^1.0.0"
got@^11.8.2:
- version "11.8.3"
- resolved "https://registry.npmjs.org/got/-/got-11.8.3.tgz"
- integrity sha512-7gtQ5KiPh1RtGS9/Jbv1ofDpBFuq42gyfEib+ejaRBJuj/3tQFeR5+gw57e4ipaU8c/rCjvX6fkQz2lyDlGAOg==
+ version "11.8.5"
+ resolved "https://registry.yarnpkg.com/got/-/got-11.8.5.tgz#ce77d045136de56e8f024bebb82ea349bc730046"
+ integrity sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ==
dependencies:
"@sindresorhus/is" "^4.0.0"
"@szmarczak/http-timer" "^4.0.5"
@@ -266,12 +279,12 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0:
http-cache-semantics@^4.0.0:
version "4.1.0"
- resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz"
+ resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==
http2-wrapper@^1.0.0-beta.5.2:
version "1.0.3"
- resolved "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz"
+ resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d"
integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==
dependencies:
quick-lru "^5.1.1"
@@ -313,9 +326,9 @@ isexe@^2.0.0:
resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz"
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
-json-buffer@3.0.1:
+json-buffer@3.0.1, json-buffer@~3.0.1:
version "3.0.1"
- resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz"
+ resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==
jsonfile@^6.0.1:
@@ -328,10 +341,11 @@ jsonfile@^6.0.1:
graceful-fs "^4.1.6"
keyv@^4.0.0:
- version "4.1.1"
- resolved "https://registry.npmjs.org/keyv/-/keyv-4.1.1.tgz"
- integrity sha512-tGv1yP6snQVDSM4X6yxrv2zzq/EvpW+oYiUz6aueW1u9CtS8RzUQYxxmFwgZlO2jSgCxQbchhxaqXXp2hnKGpQ==
+ version "4.3.2"
+ resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.3.2.tgz#e839df676a0c7ee594c8835e7c1c83742558e5c2"
+ integrity sha512-kn8WmodVBe12lmHpA6W8OY7SNh6wVR+Z+wZESF4iF5FCazaVXGWOtnbnvX0tMQ1bO+/TmOD9LziuYMvrIIs0xw==
dependencies:
+ compress-brotli "^1.3.8"
json-buffer "3.0.1"
lodash.mapvalues@^4.6.0:
@@ -351,7 +365,7 @@ lodash@>=4.17.21, lodash@^4.16.6, lodash@^4.17.14:
lowercase-keys@^2.0.0:
version "2.0.0"
- resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz"
+ resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479"
integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==
make-error@^1.1.1:
@@ -361,12 +375,12 @@ make-error@^1.1.1:
mimic-response@^1.0.0:
version "1.0.1"
- resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz"
+ resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
mimic-response@^3.1.0:
version "3.1.0"
- resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz"
+ resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9"
integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==
minimatch@^3.0.4:
@@ -393,7 +407,7 @@ ms@2.1.2:
normalize-url@^6.0.1:
version "6.1.0"
- resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz"
+ resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a"
integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==
once@^1.3.0, once@^1.3.1, once@^1.4.0:
@@ -405,7 +419,7 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0:
p-cancelable@^2.0.0:
version "2.1.1"
- resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz"
+ resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf"
integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==
path-is-absolute@^1.0.0:
@@ -430,7 +444,7 @@ progress@2.0.3:
pump@^3.0.0:
version "3.0.0"
- resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz"
+ resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
dependencies:
end-of-stream "^1.1.0"
@@ -438,7 +452,7 @@ pump@^3.0.0:
quick-lru@^5.1.1:
version "5.1.1"
- resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz"
+ resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932"
integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==
readable-stream@^3.1.1, readable-stream@^3.4.0:
@@ -452,12 +466,12 @@ readable-stream@^3.1.1, readable-stream@^3.4.0:
resolve-alpn@^1.0.0:
version "1.2.1"
- resolved "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz"
+ resolved "https://registry.yarnpkg.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9"
integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==
responselike@^2.0.0:
version "2.0.0"
- resolved "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz"
+ resolved "https://registry.yarnpkg.com/responselike/-/responselike-2.0.0.tgz#26391bcc3174f750f9a79eacc40a12a5c42d7723"
integrity sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==
dependencies:
lowercase-keys "^2.0.0"
@@ -583,8 +597,8 @@ which@^2.0.1, which@^2.0.2:
wrappy@1:
version "1.0.2"
- resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
- integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
+ resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+ integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
yauzl@^2.10.0:
version "2.10.0"
diff --git a/src/Components/test/testassets/BasicTestApp/BasicTestApp.csproj b/src/Components/test/testassets/BasicTestApp/BasicTestApp.csproj
index f81ddcae75..bc6052a3bc 100644
--- a/src/Components/test/testassets/BasicTestApp/BasicTestApp.csproj
+++ b/src/Components/test/testassets/BasicTestApp/BasicTestApp.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
+<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
@@ -22,6 +22,7 @@
<Reference Include="System.ComponentModel" />
<Reference Include="System.Net.Http.Json" />
<Reference Include="Microsoft.AspNetCore.Components.WebAssembly" />
+ <Reference Include="Microsoft.AspNetCore.Components.CustomElements" />
<Reference Include="Microsoft.AspNetCore.Components.Authorization" />
<Reference Include="Microsoft.AspNetCore.SignalR.Client" />
<Reference Include="Microsoft.Extensions.Logging.Configuration" />
diff --git a/src/Components/test/testassets/BasicTestApp/CustomElementParameterTypes.razor b/src/Components/test/testassets/BasicTestApp/CustomElementParameterTypes.razor
new file mode 100644
index 0000000000..c5905a8051
--- /dev/null
+++ b/src/Components/test/testassets/BasicTestApp/CustomElementParameterTypes.razor
@@ -0,0 +1,48 @@
+@using Microsoft.JSInterop
+
+<button class="invoke-callback" @onclick="InvokeCallbackAsync">Invoke callback</button><br />
+<strong>@nameof(StringParam)</strong>: <span class="string-param">@StringParam</span><br />
+<strong>@nameof(BoolParam)</strong>: <span class="bool-param">@BoolParam</span><br />
+<strong>@nameof(IntParam)</strong>: <span class="int-param">@IntParam</span><br />
+<strong>@nameof(LongParam)</strong>: <span class="long-param">@LongParam</span><br />
+<strong>@nameof(FloatParam)</strong>: <span class="float-param">@FloatParam</span><br />
+<strong>@nameof(DoubleParam)</strong>: <span class="double-param">@DoubleParam</span><br />
+<strong>@nameof(DecimalParam)</strong>: <span class="decimal-param">@DecimalParam</span><br />
+<strong>@nameof(NullableBoolParam)</strong>: <span class="nullable-bool-param">@(NullableBoolParam?.ToString() ?? "null")</span><br />
+<strong>@nameof(NullableIntParam)</strong>: <span class="nullable-int-param">@(NullableIntParam?.ToString() ?? "null")</span><br />
+<strong>@nameof(NullableLongParam)</strong>: <span class="nullable-long-param">@(NullableLongParam?.ToString() ?? "null")</span><br />
+<strong>@nameof(NullableFloatParam)</strong>: <span class="nullable-float-param">@(NullableFloatParam?.ToString() ?? "null")</span><br />
+<strong>@nameof(NullableDoubleParam)</strong>: <span class="nullable-double-param">@(NullableDoubleParam?.ToString() ?? "null")</span><br />
+<strong>@nameof(NullableDecimalParam)</strong>: <span class="nullable-decimal-param">@(NullableDecimalParam?.ToString() ?? "null")</span><br />
+<strong>@nameof(ComplexTypeParam)</strong>: <span class="complex-type-param">@ComplexTypeParam</span><br />
+
+@code {
+ [Parameter] public string StringParam { get; set; }
+ [Parameter] public bool BoolParam { get; set; }
+ [Parameter] public int IntParam { get; set; }
+ [Parameter] public long LongParam { get; set; }
+ [Parameter] public float FloatParam { get; set; }
+ [Parameter] public double DoubleParam { get; set; }
+ [Parameter] public decimal DecimalParam { get; set; }
+ [Parameter] public bool? NullableBoolParam { get; set; }
+ [Parameter] public int? NullableIntParam { get; set; }
+ [Parameter] public long? NullableLongParam { get; set; }
+ [Parameter] public float? NullableFloatParam { get; set; }
+ [Parameter] public double? NullableDoubleParam { get; set; }
+ [Parameter] public decimal? NullableDecimalParam { get; set; }
+ [Parameter] public MyComplexType ComplexTypeParam { get; set; }
+ [Parameter] public EventCallback CallbackParam { get; set; }
+
+ private async Task InvokeCallbackAsync()
+ {
+ await CallbackParam.InvokeAsync();
+ }
+
+ public class MyComplexType
+ {
+ public string Property { get; set; }
+
+ public override string ToString()
+ => $@"{{ {nameof(Property)} = ""{Property}"" }}";
+ }
+}
diff --git a/src/Components/test/testassets/BasicTestApp/CustomElementsComponent.razor b/src/Components/test/testassets/BasicTestApp/CustomElementsComponent.razor
new file mode 100644
index 0000000000..f88ab8d3f0
--- /dev/null
+++ b/src/Components/test/testassets/BasicTestApp/CustomElementsComponent.razor
@@ -0,0 +1,75 @@
+@using Microsoft.JSInterop
+@inject IJSRuntime JSRuntime
+
+<h3>CustomElementsComponent</h3>
+
+<p id="message"></p>
+
+<button id="add-custom-element" @onclick="AddCustomElement">Add custom element</button>
+<button id="remove-custom-element" @onclick="RemoveCustomElement">Remove custom element</button>
+
+@for (var i = 0; i < _customElements.Count; i++)
+{
+ var customElement = _customElements[i];
+ var clickCount = customElement.ClickCount;
+ <p>
+ <button id="increment-@i" @onclick="customElement.IncrementCount">Increment</button><br />
+ <button id="update-complex-parameters-@i" @onclick="customElement.UpdateComplexParameters">Update complex parameters</button><br />
+ <my-custom-element
+ @ref="@customElement.Reference"
+ id="custom-element-@i"
+ string-param="Custom element @i (Clicked @clickCount times)"
+ bool-param="@((clickCount % 2 == 0).ToString())"
+ int-param="@clickCount"
+ long-param="@clickCount"
+ float-param="@(clickCount + 0.5)"
+ double-param="@(clickCount + 0.5)"
+ decimal-param="@(clickCount + 0.5)"
+ nullable-bool-param="@(clickCount % 2 == 0 ? bool.TrueString : null)"
+ nullable-int-param="@(clickCount % 2 == 0 ? clickCount : null)"
+ nullable-long-param="@(clickCount % 2 == 0 ? clickCount : null)"
+ nullable-float-param="@(clickCount % 2 == 0 ? clickCount + 0.5 : null)"
+ nullable-double-param="@(clickCount % 2 == 0 ? clickCount + 0.5 : null)"
+ nullable-decimal-param="@(clickCount % 2 == 0 ? clickCount + 0.5 : null)" />
+ </p>
+}
+
+@code {
+ private List<CustomElementWrapper> _customElements = new();
+
+ private void AddCustomElement()
+ {
+ _customElements.Add(new(JSRuntime));
+ }
+
+ private void RemoveCustomElement()
+ {
+ if (_customElements.Count != 0)
+ {
+ _customElements.RemoveAt(_customElements.Count - 1);
+ }
+ }
+
+ private class CustomElementWrapper
+ {
+ private readonly IJSRuntime _jsRuntime;
+
+ public ElementReference Reference { get; set; }
+ public int ClickCount { get; private set; }
+
+ public CustomElementWrapper(IJSRuntime jsRuntime)
+ {
+ _jsRuntime = jsRuntime;
+ }
+
+ public void IncrementCount()
+ {
+ ClickCount++;
+ }
+
+ public async Task UpdateComplexParameters()
+ {
+ await _jsRuntime.InvokeVoidAsync("customElementFunctions.updateComplexProperties", Reference, ClickCount);
+ }
+ }
+}
diff --git a/src/Components/test/testassets/BasicTestApp/Index.razor b/src/Components/test/testassets/BasicTestApp/Index.razor
index 87da2d67e4..5cd0688202 100644
--- a/src/Components/test/testassets/BasicTestApp/Index.razor
+++ b/src/Components/test/testassets/BasicTestApp/Index.razor
@@ -18,6 +18,7 @@
<option value="BasicTestApp.CounterComponentUsingChild">Counter using child component</option>
<option value="BasicTestApp.CounterComponentWrapper">Counter wrapped in parent</option>
<option value="BasicTestApp.CulturePicker">Culture Picker</option>
+ <option value="BasicTestApp.CustomElementsComponent">Custom elements</option>
<option value="BasicTestApp.DataDashComponent">data-* attribute rendering</option>
<option value="BasicTestApp.DispatchingComponent">Dispatching to sync context</option>
<option value="BasicTestApp.DuplicateAttributesComponent">Duplicate attributes</option>
@@ -100,6 +101,7 @@
<option value="BasicTestApp.TouchEventComponent">Touch events</option>
<option value="BasicTestApp.VirtualizationComponent">Virtualization</option>
<option value="BasicTestApp.VirtualizationDataChanges">Virtualization data changes</option>
+ <option value="BasicTestApp.VirtualizationTable">Virtualization HTML table</option>
<option value="BasicTestApp.HotReload.RenderOnHotReload">Render on hot reload</option>
</select>
diff --git a/src/Components/test/testassets/BasicTestApp/Program.cs b/src/Components/test/testassets/BasicTestApp/Program.cs
index b669190d7d..0cef496473 100644
--- a/src/Components/test/testassets/BasicTestApp/Program.cs
+++ b/src/Components/test/testassets/BasicTestApp/Program.cs
@@ -28,6 +28,7 @@ public class Program
builder.RootComponents.RegisterForJavaScript<JavaScriptRootComponentParameterTypes>(
"component-with-many-parameters",
javaScriptInitializer: "myJsRootComponentInitializers.testInitializer");
+ builder.RootComponents.RegisterCustomElement<CustomElementParameterTypes>("my-custom-element");
builder.Services.AddSingleton(new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddSingleton<AuthenticationStateProvider, ServerAuthenticationStateProvider>();
diff --git a/src/Components/test/testassets/BasicTestApp/VirtualizationTable.razor b/src/Components/test/testassets/BasicTestApp/VirtualizationTable.razor
new file mode 100644
index 0000000000..a928ec8089
--- /dev/null
+++ b/src/Components/test/testassets/BasicTestApp/VirtualizationTable.razor
@@ -0,0 +1,28 @@
+<p>This is to show we can use an HTML table with Virtualize, despite it having particular rules about the element hierarchy.</p>
+<p>We're also using the document root as the scroll container. Other tests cover having a different scroll container, such as a div with overflow:scroll.</p>
+
+<table id="virtualized-table">
+ <thead style="position: sticky; top: 0; background-color: silver">
+ <tr>
+ <th>Item</th>
+ <th>Another col</th>
+ </tr>
+ </thead>
+ <tbody>
+ <Virtualize Items="@fixedItems" ItemSize="30" SpacerElement="tr">
+ <tr @key="context" style="height: 30px;" id="row-@context">
+ <td>Item @context</td>
+ <td>Another value</td>
+ </tr>
+ </Virtualize>
+ </tbody>
+</table>
+
+<style>
+ /* Represents https://github.com/dotnet/aspnetcore/issues/37659 */
+ html, body { overflow-y: scroll }
+</style>
+
+@code {
+ List<int> fixedItems = Enumerable.Range(0, 1000).ToList();
+}
diff --git a/src/Components/test/testassets/BasicTestApp/wwwroot/index.html b/src/Components/test/testassets/BasicTestApp/wwwroot/index.html
index 02bdad9151..9048aa6427 100644
--- a/src/Components/test/testassets/BasicTestApp/wwwroot/index.html
+++ b/src/Components/test/testassets/BasicTestApp/wwwroot/index.html
@@ -31,6 +31,7 @@
<script src="js/webComponentPerformingJsInterop.js"></script>
<script src="js/customLinkElement.js"></script>
<script src="js/jsRootComponentInitializers.js"></script>
+ <script src="js/customElementTests.js"></script>
<script>
// Used by ElementRefComponent
@@ -51,6 +52,9 @@
<!-- Used by ExternalContentPackage -->
<script src="_content/TestContentPackage/prompt.js"></script>
+
+ <!-- Used by CustomElementsComponent -->
+ <script src="_content/Microsoft.AspNetCore.Components.CustomElements/BlazorCustomElements.js"></script>
</body>
</html>
diff --git a/src/Components/test/testassets/BasicTestApp/wwwroot/js/customElementTests.js b/src/Components/test/testassets/BasicTestApp/wwwroot/js/customElementTests.js
new file mode 100644
index 0000000000..1eafa26512
--- /dev/null
+++ b/src/Components/test/testassets/BasicTestApp/wwwroot/js/customElementTests.js
@@ -0,0 +1,10 @@
+window.customElementFunctions = {
+ updateComplexProperties: function (element, count) {
+ element.complexTypeParam = {
+ property: `Clicked ${count} times`,
+ };
+ element.callbackParam = () => {
+ document.getElementById('message').innerText = `Callback with count = ${count}`;
+ };
+ }
+};
diff --git a/src/Framework/AspNetCoreAnalyzers/src/Analyzers/DiagnosticDescriptors.cs b/src/Framework/AspNetCoreAnalyzers/src/Analyzers/DiagnosticDescriptors.cs
index 58e7b8fa07..12505fe547 100644
--- a/src/Framework/AspNetCoreAnalyzers/src/Analyzers/DiagnosticDescriptors.cs
+++ b/src/Framework/AspNetCoreAnalyzers/src/Analyzers/DiagnosticDescriptors.cs
@@ -80,6 +80,15 @@ internal static class DiagnosticDescriptors
isEnabledByDefault: true,
helpLinkUri: "https://aka.ms/aspnet/analyzers");
+ internal static readonly DiagnosticDescriptor DoNotUseHostConfigureLogging = new(
+ "ASP0011",
+ "Suggest using builder.Logging over Host.ConfigureLogging or WebHost.ConfigureLogging",
+ "Suggest using builder.Logging instead of {0}",
+ "Usage",
+ DiagnosticSeverity.Warning,
+ isEnabledByDefault: true,
+ helpLinkUri: "https://aka.ms/aspnet/analyzers");
+
internal static readonly DiagnosticDescriptor DisallowConfigureAppConfigureHostBuilder = new(
"ASP0013",
"Suggest using WebApplicationBuilder.Configuration over Configure methods",
@@ -88,5 +97,4 @@ internal static class DiagnosticDescriptors
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
helpLinkUri: "https://aka.ms/aspnet/analyzers");
-
}
diff --git a/src/Framework/AspNetCoreAnalyzers/src/Analyzers/WebApplicationBuilder/WebApplicationBuilderAnalyzer.cs b/src/Framework/AspNetCoreAnalyzers/src/Analyzers/WebApplicationBuilder/WebApplicationBuilderAnalyzer.cs
index 91dd7181a5..a538dbc22a 100644
--- a/src/Framework/AspNetCoreAnalyzers/src/Analyzers/WebApplicationBuilder/WebApplicationBuilderAnalyzer.cs
+++ b/src/Framework/AspNetCoreAnalyzers/src/Analyzers/WebApplicationBuilder/WebApplicationBuilderAnalyzer.cs
@@ -21,7 +21,8 @@ public class WebApplicationBuilderAnalyzer : DiagnosticAnalyzer
DiagnosticDescriptors.DoNotUseConfigureWebHostWithConfigureHostBuilder,
DiagnosticDescriptors.DoNotUseConfigureWithConfigureWebHostBuilder,
DiagnosticDescriptors.DoNotUseUseStartupWithConfigureWebHostBuilder,
- DiagnosticDescriptors.DisallowConfigureAppConfigureHostBuilder,
+ DiagnosticDescriptors.DoNotUseHostConfigureLogging,
+ DiagnosticDescriptors.DisallowConfigureAppConfigureHostBuilder
});
public override void Initialize(AnalysisContext context)
@@ -45,6 +46,11 @@ public class WebApplicationBuilderAnalyzer : DiagnosticAnalyzer
wellKnownTypes.HostingAbstractionsWebHostBuilderExtensions,
wellKnownTypes.WebHostBuilderExtensions,
};
+ INamedTypeSymbol[] configureLoggingTypes =
+ {
+ wellKnownTypes.HostingHostBuilderExtensions,
+ wellKnownTypes.WebHostBuilderExtensions
+ };
INamedTypeSymbol[] configureAppTypes =
{
wellKnownTypes.ConfigureHostBuilder,
@@ -106,7 +112,39 @@ public class WebApplicationBuilderAnalyzer : DiagnosticAnalyzer
DiagnosticDescriptors.DoNotUseUseStartupWithConfigureWebHostBuilder,
invocation));
}
+
+ //var builder = WebApplication.CreateBuilder(args);
+ //builder.Host.ConfigureLogging(x => {})
+ if (IsDisallowedMethod(
+ operationAnalysisContext,
+ invocation,
+ targetMethod,
+ wellKnownTypes.ConfigureHostBuilder,
+ "ConfigureLogging",
+ configureLoggingTypes))
+ {
+ operationAnalysisContext.ReportDiagnostic(
+ CreateDiagnostic(
+ DiagnosticDescriptors.DoNotUseHostConfigureLogging,
+ invocation));
+ }
+ //var builder = WebApplication.CreateBuilder(args);
+ //builder.WebHost.ConfigureLogging(x => {})
+ if (IsDisallowedMethod(
+ operationAnalysisContext,
+ invocation,
+ targetMethod,
+ wellKnownTypes.ConfigureWebHostBuilder,
+ "ConfigureLogging",
+ configureLoggingTypes))
+ {
+ operationAnalysisContext.ReportDiagnostic(
+ CreateDiagnostic(
+ DiagnosticDescriptors.DoNotUseHostConfigureLogging,
+ invocation));
+ }
+
// var builder = WebApplication.CreateBuilder();
// builder.WebHost.ConfigureAppConfiguration(builder => {});
if (IsDisallowedMethod(
diff --git a/src/Framework/AspNetCoreAnalyzers/src/Analyzers/WebApplicationBuilder/WellKnownTypes.cs b/src/Framework/AspNetCoreAnalyzers/src/Analyzers/WebApplicationBuilder/WellKnownTypes.cs
index fb36c53015..0e2f1102d4 100644
--- a/src/Framework/AspNetCoreAnalyzers/src/Analyzers/WebApplicationBuilder/WellKnownTypes.cs
+++ b/src/Framework/AspNetCoreAnalyzers/src/Analyzers/WebApplicationBuilder/WellKnownTypes.cs
@@ -55,7 +55,7 @@ internal sealed class WellKnownTypes
GenericHostWebHostBuilderExtensions = genericHostWebHostBuilderExtensions,
HostingAbstractionsWebHostBuilderExtensions = hostingAbstractionsWebHostBuilderExtensions,
WebHostBuilderExtensions = webHostBuilderExtensions,
- HostingHostBuilderExtensions = hostingHostBuilderExtensions,
+ HostingHostBuilderExtensions = hostingHostBuilderExtensions
};
return true;
diff --git a/src/Framework/AspNetCoreAnalyzers/test/WebApplicationBuilder/DisallowConfigureHostLoggingTest.cs b/src/Framework/AspNetCoreAnalyzers/test/WebApplicationBuilder/DisallowConfigureHostLoggingTest.cs
new file mode 100644
index 0000000000..38934ee8d1
--- /dev/null
+++ b/src/Framework/AspNetCoreAnalyzers/test/WebApplicationBuilder/DisallowConfigureHostLoggingTest.cs
@@ -0,0 +1,278 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+using System.Globalization;
+using Microsoft.AspNetCore.Analyzer.Testing;
+using Microsoft.CodeAnalysis;
+
+namespace Microsoft.AspNetCore.Analyzers.WebApplicationBuilder;
+public partial class DisallowConfigureHostLoggingTest
+{
+ private TestDiagnosticAnalyzerRunner Runner { get; } = new(new WebApplicationBuilderAnalyzer());
+
+ [Fact]
+ public async Task DoesNotWarnWhenBuilderLoggingIsUsed()
+ {
+ //arrange
+ var source = @"
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+var builder = WebApplication.CreateBuilder(args);
+builder.Logging.AddJsonConsole();
+";
+ //act
+ var diagnostics = await Runner.GetDiagnosticsAsync(source);
+ //assert
+ Assert.Empty(diagnostics);
+ }
+
+ [Fact]
+ public async Task DoesNotWarnWhenBuilderLoggingIsUsed_InMain()
+ {
+ //arrange
+ var source = @"
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+public static class Program
+{
+ public static void Main (string[] args)
+ {
+ var builder = WebApplication.CreateBuilder(args);
+ builder.Logging.AddJsonConsole();
+ }
+}
+public class Startup { }
+";
+ //act
+ var diagnostics = await Runner.GetDiagnosticsAsync(source);
+ //assert
+ Assert.Empty(diagnostics);
+ }
+
+ [Fact]
+ public async Task WarnsWhenBuilderLoggingIsNotUsed_Host()
+ {
+ //arrange
+ var source = TestSource.Read(@"
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+var builder = WebApplication.CreateBuilder(args);
+builder.Host./*MM*/ConfigureLogging(logging =>
+{
+logging.AddJsonConsole();
+});
+");
+ //act
+ var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
+ //assert
+ var diagnostic = Assert.Single(diagnostics);
+ Assert.Same(DiagnosticDescriptors.DoNotUseHostConfigureLogging, diagnostic.Descriptor);
+ AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location);
+ Assert.Equal("Suggest using builder.Logging instead of ConfigureLogging", diagnostic.GetMessage(CultureInfo.InvariantCulture));
+ }
+
+ [Fact]
+ public async Task WarnsWhenBuilderLoggingIsNotUsed_WebHost()
+ {
+ //arrange
+ var source = TestSource.Read(@"
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Microsoft.AspNetCore.Hosting;
+var builder = WebApplication.CreateBuilder(args);
+builder.WebHost./*MM*/ConfigureLogging(logging =>
+{
+logging.AddJsonConsole();
+});
+");
+ //act
+ var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
+ //assert
+ var diagnostic = Assert.Single(diagnostics);
+ Assert.Same(DiagnosticDescriptors.DoNotUseHostConfigureLogging, diagnostic.Descriptor);
+ AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location);
+ Assert.Equal("Suggest using builder.Logging instead of ConfigureLogging", diagnostic.GetMessage(CultureInfo.InvariantCulture));
+ }
+
+ [Fact]
+ public async Task WarnsWhenBuilderLoggingIsNotUsed_OnDifferentLine_Host()
+ {
+ //arrange
+ var source = TestSource.Read(@"
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+var builder = WebApplication.CreateBuilder(args);
+builder.Host.
+ /*MM*/ConfigureLogging(logging =>
+{
+ logging.AddJsonConsole();
+});
+");
+ //act
+ var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
+ //assert
+ var diagnostic = Assert.Single(diagnostics);
+ Assert.Same(DiagnosticDescriptors.DoNotUseHostConfigureLogging, diagnostic.Descriptor);
+ AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location);
+ Assert.Equal("Suggest using builder.Logging instead of ConfigureLogging", diagnostic.GetMessage(CultureInfo.InvariantCulture));
+ }
+
+ [Fact]
+ public async Task WarnsWhenBuilderLoggingIsNotUsed_OnDifferentLine_WebHost()
+ {
+ //arrange
+ var source = TestSource.Read(@"
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Microsoft.AspNetCore.Hosting;
+var builder = WebApplication.CreateBuilder(args);
+builder.WebHost.
+ /*MM*/ConfigureLogging(logging =>
+{
+logging.AddJsonConsole();
+});
+");
+ //act
+ var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
+ //assert
+ var diagnostic = Assert.Single(diagnostics);
+ Assert.Same(DiagnosticDescriptors.DoNotUseHostConfigureLogging, diagnostic.Descriptor);
+ AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location);
+ Assert.Equal("Suggest using builder.Logging instead of ConfigureLogging", diagnostic.GetMessage(CultureInfo.InvariantCulture));
+ }
+
+ [Fact]
+ public async Task WarnsWhenBuilderLoggingIsNotUsed_InMain_Host()
+ {
+ //arrange
+ var source = TestSource.Read(@"
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+public static class Program
+{
+ public static void Main (string[] args)
+ {
+ var builder = WebApplication.CreateBuilder(args);
+ builder.Host./*MM*/ConfigureLogging(logging =>
+ {
+ logging.AddJsonConsole();
+ });
+ }
+}
+public class Startup { }
+");
+ //act
+ var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
+ //assert
+ var diagnostic = Assert.Single(diagnostics);
+ Assert.Same(DiagnosticDescriptors.DoNotUseHostConfigureLogging, diagnostic.Descriptor);
+ AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location);
+ Assert.Equal("Suggest using builder.Logging instead of ConfigureLogging", diagnostic.GetMessage(CultureInfo.InvariantCulture));
+ }
+
+ [Fact]
+ public async Task WarnsWhenBuilderLoggingIsNotUsed_InMain_WebHost()
+ {
+ //arrange
+ var source = TestSource.Read(@"
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Microsoft.AspNetCore.Hosting;
+public static class Program
+{
+ public static void Main (string[] args)
+ {
+ var builder = WebApplication.CreateBuilder(args);
+ builder.WebHost./*MM*/ConfigureLogging(logging =>
+ {
+ logging.AddJsonConsole();
+ });
+ }
+}
+public class Startup { }
+");
+ //act
+ var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
+ //assert
+ var diagnostic = Assert.Single(diagnostics);
+ Assert.Same(DiagnosticDescriptors.DoNotUseHostConfigureLogging, diagnostic.Descriptor);
+ AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location);
+ Assert.Equal("Suggest using builder.Logging instead of ConfigureLogging", diagnostic.GetMessage(CultureInfo.InvariantCulture));
+ }
+
+ [Fact]
+ public async Task WarnsWhenBuilderLoggingIsNotUsed_WhenChained_WebHost()
+ {
+ //arrange
+ var source = TestSource.Read(@"
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Microsoft.AspNetCore.Hosting;
+var builder = WebApplication.CreateBuilder(args);
+builder.WebHost.
+ /*MM*/ConfigureLogging(logging => { })
+ .ConfigureServices(services => { });
+");
+ //act
+ var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
+ //assert
+ var diagnostic = Assert.Single(diagnostics);
+ Assert.Same(DiagnosticDescriptors.DoNotUseHostConfigureLogging, diagnostic.Descriptor);
+ AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location);
+ Assert.Equal("Suggest using builder.Logging instead of ConfigureLogging", diagnostic.GetMessage(CultureInfo.InvariantCulture));
+ }
+
+ [Fact]
+ public async Task WarnsTwiceWhenBuilderLoggingIsNotUsed_Host()
+ {
+ //arrange
+ var source = TestSource.Read(@"
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+var builder = WebApplication.CreateBuilder(args);
+builder.Host./*MM1*/ConfigureLogging(logging =>
+{
+logging.AddJsonConsole();
+});
+builder.Host./*MM2*/ConfigureLogging(logging =>
+{
+logging.AddJsonConsole();
+});
+");
+ //act
+ var diagnostics = await Runner.GetDiagnosticsAsync(source.Source);
+ //assert
+ Assert.Equal(2, diagnostics.Length);
+ var diagnostic1 = diagnostics[0];
+ var diagnostic2 = diagnostics[1];
+
+ Assert.Same(DiagnosticDescriptors.DoNotUseHostConfigureLogging, diagnostic1.Descriptor);
+ AnalyzerAssert.DiagnosticLocation(source.MarkerLocations["MM1"], diagnostic1.Location);
+ Assert.Equal("Suggest using builder.Logging instead of ConfigureLogging", diagnostic1.GetMessage(CultureInfo.InvariantCulture));
+
+ Assert.Same(DiagnosticDescriptors.DoNotUseHostConfigureLogging, diagnostic2.Descriptor);
+ AnalyzerAssert.DiagnosticLocation(source.MarkerLocations["MM2"], diagnostic2.Location);
+ Assert.Equal("Suggest using builder.Logging instead of ConfigureLogging", diagnostic2.GetMessage(CultureInfo.InvariantCulture));
+ }
+
+}
+
diff --git a/src/Framework/Framework.slnf b/src/Framework/Framework.slnf
index e242406826..d3f3abdc9f 100644
--- a/src/Framework/Framework.slnf
+++ b/src/Framework/Framework.slnf
@@ -56,6 +56,7 @@
"src\\Middleware\\HttpsPolicy\\src\\Microsoft.AspNetCore.HttpsPolicy.csproj",
"src\\Middleware\\Localization.Routing\\src\\Microsoft.AspNetCore.Localization.Routing.csproj",
"src\\Middleware\\Localization\\src\\Microsoft.AspNetCore.Localization.csproj",
+ "src\\Middleware\\OutputCaching\\src\\Microsoft.AspNetCore.OutputCaching.csproj",
"src\\Middleware\\ResponseCaching.Abstractions\\src\\Microsoft.AspNetCore.ResponseCaching.Abstractions.csproj",
"src\\Middleware\\ResponseCaching\\src\\Microsoft.AspNetCore.ResponseCaching.csproj",
"src\\Middleware\\ResponseCompression\\src\\Microsoft.AspNetCore.ResponseCompression.csproj",
diff --git a/src/Framework/test/TestData.cs b/src/Framework/test/TestData.cs
index b36cb3fec2..28aaa88dcc 100644
--- a/src/Framework/test/TestData.cs
+++ b/src/Framework/test/TestData.cs
@@ -73,6 +73,7 @@ public static class TestData
"Microsoft.AspNetCore.Mvc.RazorPages",
"Microsoft.AspNetCore.Mvc.TagHelpers",
"Microsoft.AspNetCore.Mvc.ViewFeatures",
+ "Microsoft.AspNetCore.OutputCaching",
"Microsoft.AspNetCore.Razor",
"Microsoft.AspNetCore.Razor.Runtime",
"Microsoft.AspNetCore.RequestDecompression",
@@ -209,6 +210,7 @@ public static class TestData
{ "Microsoft.AspNetCore.Mvc.RazorPages", "7.0.0.0" },
{ "Microsoft.AspNetCore.Mvc.TagHelpers", "7.0.0.0" },
{ "Microsoft.AspNetCore.Mvc.ViewFeatures", "7.0.0.0" },
+ { "Microsoft.AspNetCore.OutputCaching", "7.0.0.0" },
{ "Microsoft.AspNetCore.Razor", "7.0.0.0" },
{ "Microsoft.AspNetCore.Razor.Runtime", "7.0.0.0" },
{ "Microsoft.AspNetCore.RequestDecompression", "7.0.0.0" },
diff --git a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/GrpcJsonTranscodingServiceExtensions.cs b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/GrpcJsonTranscodingServiceExtensions.cs
index 98934e6355..817bd9aeda 100644
--- a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/GrpcJsonTranscodingServiceExtensions.cs
+++ b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/GrpcJsonTranscodingServiceExtensions.cs
@@ -17,34 +17,34 @@ public static class GrpcJsonTranscodingServiceExtensions
/// <summary>
/// Adds gRPC JSON transcoding services to the specified <see cref="IGrpcServerBuilder" />.
/// </summary>
- /// <param name="grpcBuilder">The <see cref="IGrpcServerBuilder"/>.</param>
+ /// <param name="builder">The <see cref="IGrpcServerBuilder"/>.</param>
/// <returns>The same instance of the <see cref="IGrpcServerBuilder"/> for chaining.</returns>
- public static IGrpcServerBuilder AddJsonTranscoding(this IGrpcServerBuilder grpcBuilder)
+ public static IGrpcServerBuilder AddJsonTranscoding(this IGrpcServerBuilder builder)
{
- if (grpcBuilder == null)
+ if (builder == null)
{
- throw new ArgumentNullException(nameof(grpcBuilder));
+ throw new ArgumentNullException(nameof(builder));
}
- grpcBuilder.Services.TryAddEnumerable(ServiceDescriptor.Singleton(typeof(IServiceMethodProvider<>), typeof(JsonTranscodingServiceMethodProvider<>)));
+ builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton(typeof(IServiceMethodProvider<>), typeof(JsonTranscodingServiceMethodProvider<>)));
- return grpcBuilder;
+ return builder;
}
/// <summary>
/// Adds gRPC JSON transcoding services to the specified <see cref="IGrpcServerBuilder" />.
/// </summary>
- /// <param name="grpcBuilder">The <see cref="IGrpcServerBuilder"/>.</param>
+ /// <param name="builder">The <see cref="IGrpcServerBuilder"/>.</param>
/// <param name="configureOptions">An <see cref="Action{GrpcJsonTranscodingOptions}"/> to configure the provided <see cref="GrpcJsonTranscodingOptions"/>.</param>
/// <returns>The same instance of the <see cref="IGrpcServerBuilder"/> for chaining.</returns>
- public static IGrpcServerBuilder AddJsonTranscoding(this IGrpcServerBuilder grpcBuilder, Action<GrpcJsonTranscodingOptions> configureOptions)
+ public static IGrpcServerBuilder AddJsonTranscoding(this IGrpcServerBuilder builder, Action<GrpcJsonTranscodingOptions> configureOptions)
{
- if (grpcBuilder == null)
+ if (builder == null)
{
- throw new ArgumentNullException(nameof(grpcBuilder));
+ throw new ArgumentNullException(nameof(builder));
}
- grpcBuilder.Services.Configure(configureOptions);
- return grpcBuilder.AddJsonTranscoding();
+ builder.Services.Configure(configureOptions);
+ return builder.AddJsonTranscoding();
}
}
diff --git a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/Binding/JsonTranscodingProviderServiceBinder.cs b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/Binding/JsonTranscodingProviderServiceBinder.cs
index 3941ea7b5e..4ea56f445d 100644
--- a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/Binding/JsonTranscodingProviderServiceBinder.cs
+++ b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/Binding/JsonTranscodingProviderServiceBinder.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics.CodeAnalysis;
+using System.Linq;
using Google.Api;
using Google.Protobuf.Reflection;
using Grpc.AspNetCore.Server;
@@ -228,20 +229,15 @@ internal sealed partial class JsonTranscodingProviderServiceBinder<TService> : S
private static (RoutePattern routePattern, CallHandlerDescriptorInfo descriptorInfo) ParseRoute(string pattern, string body, string responseBody, MethodDescriptor methodDescriptor)
{
- if (!pattern.StartsWith('/'))
- {
- // This validation is consistent with grpc-gateway code generation.
- // We should match their validation to be a good member of the eco-system.
- throw new InvalidOperationException($"Path template '{pattern}' must start with a '/'.");
- }
+ var httpRoutePattern = HttpRoutePattern.Parse(pattern);
+ var adapter = JsonTranscodingRouteAdapter.Parse(httpRoutePattern);
- var routePattern = RoutePatternFactory.Parse(pattern);
- return (RoutePatternFactory.Parse(pattern), CreateDescriptorInfo(body, responseBody, methodDescriptor, routePattern));
+ return (RoutePatternFactory.Parse(adapter.ResolvedRouteTemplate), CreateDescriptorInfo(body, responseBody, methodDescriptor, adapter));
}
- private static CallHandlerDescriptorInfo CreateDescriptorInfo(string body, string responseBody, MethodDescriptor methodDescriptor, RoutePattern routePattern)
+ private static CallHandlerDescriptorInfo CreateDescriptorInfo(string body, string responseBody, MethodDescriptor methodDescriptor, JsonTranscodingRouteAdapter routeAdapter)
{
- var routeParameterDescriptors = ServiceDescriptorHelpers.ResolveRouteParameterDescriptors(routePattern, methodDescriptor.InputType);
+ var routeParameterDescriptors = ServiceDescriptorHelpers.ResolveRouteParameterDescriptors(routeAdapter.HttpRoutePattern.Variables.Select(v => v.FieldPath).ToList(), methodDescriptor.InputType);
var bodyDescriptor = ServiceDescriptorHelpers.ResolveBodyDescriptor(body, typeof(TService), methodDescriptor);
@@ -260,7 +256,8 @@ internal sealed partial class JsonTranscodingProviderServiceBinder<TService> : S
bodyDescriptor?.Descriptor,
bodyDescriptor?.IsDescriptorRepeated ?? false,
bodyDescriptor?.FieldDescriptors,
- routeParameterDescriptors);
+ routeParameterDescriptors,
+ routeAdapter);
return descriptorInfo;
}
diff --git a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/CallHandlers/CallHandlerDescriptorInfo.cs b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/CallHandlers/CallHandlerDescriptorInfo.cs
index fc31a28f01..022ff483fd 100644
--- a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/CallHandlers/CallHandlerDescriptorInfo.cs
+++ b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/CallHandlers/CallHandlerDescriptorInfo.cs
@@ -15,13 +15,15 @@ internal sealed class CallHandlerDescriptorInfo
MessageDescriptor? bodyDescriptor,
bool bodyDescriptorRepeated,
List<FieldDescriptor>? bodyFieldDescriptors,
- Dictionary<string, List<FieldDescriptor>> routeParameterDescriptors)
+ Dictionary<string, List<FieldDescriptor>> routeParameterDescriptors,
+ JsonTranscodingRouteAdapter routeAdapter)
{
ResponseBodyDescriptor = responseBodyDescriptor;
BodyDescriptor = bodyDescriptor;
BodyDescriptorRepeated = bodyDescriptorRepeated;
BodyFieldDescriptors = bodyFieldDescriptors;
RouteParameterDescriptors = routeParameterDescriptors;
+ RouteAdapter = routeAdapter;
if (BodyFieldDescriptors != null)
{
BodyFieldDescriptorsPath = string.Join('.', BodyFieldDescriptors.Select(d => d.Name));
@@ -35,6 +37,7 @@ internal sealed class CallHandlerDescriptorInfo
public bool BodyDescriptorRepeated { get; }
public List<FieldDescriptor>? BodyFieldDescriptors { get; }
public Dictionary<string, List<FieldDescriptor>> RouteParameterDescriptors { get; }
+ public JsonTranscodingRouteAdapter RouteAdapter { get; }
public ConcurrentDictionary<string, List<FieldDescriptor>?> PathDescriptorsCache { get; }
public string? BodyFieldDescriptorsPath { get; }
}
diff --git a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/CallHandlers/ServerCallHandlerBase.cs b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/CallHandlers/ServerCallHandlerBase.cs
index aeb030dd77..5cfe984ac1 100644
--- a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/CallHandlers/ServerCallHandlerBase.cs
+++ b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/CallHandlers/ServerCallHandlerBase.cs
@@ -36,6 +36,11 @@ internal abstract class ServerCallHandlerBase<TService, TRequest, TResponse>
public Task HandleCallAsync(HttpContext httpContext)
{
+ foreach (var rewriteAction in DescriptorInfo.RouteAdapter.RewriteVariableActions)
+ {
+ rewriteAction(httpContext);
+ }
+
var serverCallContext = new JsonTranscodingServerCallContext(httpContext, MethodInvoker.Options, MethodInvoker.Method, DescriptorInfo, Logger);
httpContext.Features.Set<IServerCallContextFeature>(serverCallContext);
diff --git a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/JsonRequestHelpers.cs b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/JsonRequestHelpers.cs
index 3507363c96..1dff5bb15d 100644
--- a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/JsonRequestHelpers.cs
+++ b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/JsonRequestHelpers.cs
@@ -338,7 +338,7 @@ internal static class JsonRequestHelpers
{
return serverCallContext.DescriptorInfo.PathDescriptorsCache.GetOrAdd(path, p =>
{
- ServiceDescriptorHelpers.TryResolveDescriptors(requestMessage.Descriptor, p, out var pathDescriptors);
+ ServiceDescriptorHelpers.TryResolveDescriptors(requestMessage.Descriptor, p.Split('.'), out var pathDescriptors);
return pathDescriptors;
});
}
diff --git a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/JsonTranscodingRouteAdapter.cs b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/JsonTranscodingRouteAdapter.cs
new file mode 100644
index 0000000000..af9d3b8dbd
--- /dev/null
+++ b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Internal/JsonTranscodingRouteAdapter.cs
@@ -0,0 +1,210 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Globalization;
+using System.Linq;
+using Grpc.Shared;
+using Microsoft.AspNetCore.Http;
+
+namespace Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal;
+
+/// <summary>
+/// Routes on HTTP rule are similar to ASP.NET Core routes but add and remove some features.
+/// - Constraints aren't supported.
+/// - Optional parameters aren't supported.
+/// - Parameters spanning multiple segments are supported.
+///
+/// The purpose of this type is to add support for parameters spanning multiple segments and
+/// anonymous any or catch-all segments. This type transforms an HTTP route into an ASP.NET Core
+/// route by rewritting it to a compatible format and providing actions to reconstruct parameters
+/// that span multiple segments.
+///
+/// For example, consider a multi-segment parameter route:
+/// - Before: /v1/{book.name=shelves/*/books/*}
+/// - After: /v1/shelves/{__Complex_book.name_2}/books/{__Complex_book.name_4}
+///
+/// It is rewritten so that any * or ** segments become ASP.NET Core route parameters. These parameter
+/// names are never used by the user, and instead they're reconstructed into the final value by the
+/// adapter and then added to the HttpRequest.RouteValues collection.
+/// - Request URL: /v1/shelves/example-shelf/books/example-book
+/// - Route parameter: book.name = shelves/example-self/books/example-book
+/// </summary>
+internal sealed class JsonTranscodingRouteAdapter
+{
+ public HttpRoutePattern HttpRoutePattern { get; }
+ public string ResolvedRouteTemplate { get; }
+ public List<Action<HttpContext>> RewriteVariableActions { get; }
+
+ private JsonTranscodingRouteAdapter(HttpRoutePattern httpRoutePattern, string resolvedRoutePattern, List<Action<HttpContext>> rewriteVariableActions)
+ {
+ HttpRoutePattern = httpRoutePattern;
+ ResolvedRouteTemplate = resolvedRoutePattern;
+ RewriteVariableActions = rewriteVariableActions;
+ }
+
+ public static JsonTranscodingRouteAdapter Parse(HttpRoutePattern pattern)
+ {
+ var rewriteActions = new List<Action<HttpContext>>();
+
+ var tempSegments = pattern.Segments.ToList();
+ var i = 0;
+ while (i < tempSegments.Count)
+ {
+ var segmentVariable = GetVariable(pattern, i);
+ if (segmentVariable != null)
+ {
+ var fullPath = string.Join(".", segmentVariable.FieldPath);
+
+ var segmentCount = segmentVariable.EndSegment - segmentVariable.StartSegment;
+ if (segmentCount == 1)
+ {
+ // Single segment parameter. Include in route with its default name.
+ tempSegments[i] = segmentVariable.HasCatchAllPath
+ ? $"{{**{fullPath}}}"
+ : $"{{{fullPath}}}";
+ i++;
+ }
+ else
+ {
+ var routeParameterParts = new List<string>();
+ var routeValueFormatTemplateParts = new List<string>();
+ var variableParts = new List<string>();
+ var haveCatchAll = false;
+ var catchAllSuffix = string.Empty;
+
+ while (i < segmentVariable.EndSegment && !haveCatchAll)
+ {
+ var segment = tempSegments[i];
+ var segmentType = GetSegmentType(segment);
+ switch (segmentType)
+ {
+ case SegmentType.Literal:
+ routeValueFormatTemplateParts.Add(segment);
+ break;
+ case SegmentType.Any:
+ {
+ var parameterName = $"__Complex_{fullPath}_{i}";
+ tempSegments[i] = $"{{{parameterName}}}";
+
+ routeValueFormatTemplateParts.Add($"{{{variableParts.Count}}}");
+ variableParts.Add(parameterName);
+ break;
+ }
+ case SegmentType.CatchAll:
+ {
+ var parameterName = $"__Complex_{fullPath}_{i}";
+ var suffix = string.Join("/", tempSegments.Skip(i + 1));
+ catchAllSuffix = string.Join("/", tempSegments.Skip(i + segmentCount - 1));
+
+ // It's possible to have multiple routes with catch-all parameters that have different suffixes.
+ // For example:
+ // - /{name=v1/**/b}/one
+ // - /{name=v1/**/b}/two
+ // The suffix is added as a route constraint to avoid matching multiple routes to a request.
+ var constraint = suffix.Length > 0 ? $":regex({suffix}$)" : string.Empty;
+ tempSegments[i] = $"{{**{parameterName}{constraint}}}";
+
+ routeValueFormatTemplateParts.Add($"{{{variableParts.Count}}}");
+ variableParts.Add(parameterName);
+ haveCatchAll = true;
+
+ // Remove remaining segments. They have been added in the route constraint.
+ while (i < tempSegments.Count - 1)
+ {
+ tempSegments.RemoveAt(tempSegments.Count - 1);
+ }
+ break;
+ }
+ }
+ i++;
+ }
+
+ var routeValueFormatTemplate = string.Join("/", routeValueFormatTemplateParts);
+
+ // Add an action to reconstruct the multiple segment parameter from ASP.NET Core
+ // request route values. This should be called when the request is received.
+ rewriteActions.Add(context =>
+ {
+ var values = new object?[variableParts.Count];
+ for (var i = 0; i < values.Length; i++)
+ {
+ values[i] = context.Request.RouteValues[variableParts[i]];
+ }
+ var finalValue = string.Format(CultureInfo.InvariantCulture, routeValueFormatTemplate, values);
+
+ // Catch-all route parameter is always the last parameter. The original HTTP pattern could specify a
+ // literal suffix after the catch-all, e.g. /{param=**}/suffix. Because ASP.NET Core routing provides
+ // the entire remainder of the URL in the route value, we must trim the suffix from that route value.
+ if (!string.IsNullOrEmpty(catchAllSuffix))
+ {
+ finalValue = finalValue.Substring(0, finalValue.Length - catchAllSuffix.Length - 1);
+ }
+ context.Request.RouteValues[fullPath] = finalValue;
+ });
+ }
+ }
+ else
+ {
+ // HTTP route can match any value in a segment without a parameter.
+ // For example, v1/*/books. Add a parameter to match this behavior logic.
+ // Parameter value is never used.
+
+ var segmentType = GetSegmentType(tempSegments[i]);
+ switch (segmentType)
+ {
+ case SegmentType.Literal:
+ // Literal is unchanged.
+ break;
+ case SegmentType.Any:
+ // Ignore any segment value.
+ tempSegments[i] = $"{{__Discard_{i}}}";
+ break;
+ case SegmentType.CatchAll:
+ // Ignore remaining segment values.
+ tempSegments[i] = $"{{**__Discard_{i}}}";
+ break;
+ }
+
+ i++;
+ }
+ }
+
+ return new JsonTranscodingRouteAdapter(pattern, "/" + string.Join("/", tempSegments), rewriteActions);
+ }
+
+ private static SegmentType GetSegmentType(string segment)
+ {
+ if (segment.StartsWith("**", StringComparison.Ordinal))
+ {
+ return SegmentType.CatchAll;
+ }
+ else if (segment.StartsWith("*", StringComparison.Ordinal))
+ {
+ return SegmentType.Any;
+ }
+ else
+ {
+ return SegmentType.Literal;
+ }
+ }
+
+ private enum SegmentType
+ {
+ Literal,
+ Any,
+ CatchAll
+ }
+
+ private static HttpRouteVariable? GetVariable(HttpRoutePattern pattern, int i)
+ {
+ foreach (var variable in pattern.Variables)
+ {
+ if (i >= variable.StartSegment && i < variable.EndSegment)
+ {
+ return variable;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Microsoft.AspNetCore.Grpc.JsonTranscoding.csproj b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Microsoft.AspNetCore.Grpc.JsonTranscoding.csproj
index 956777c1e3..260bb02b3b 100644
--- a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Microsoft.AspNetCore.Grpc.JsonTranscoding.csproj
+++ b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/Microsoft.AspNetCore.Grpc.JsonTranscoding.csproj
@@ -23,6 +23,8 @@
<Compile Include="..\Shared\AuthContextHelpers.cs" Link="Internal\Shared\AuthContextHelpers.cs" />
<Compile Include="..\Shared\ServiceDescriptorHelpers.cs" Link="Internal\Shared\ServiceDescriptorHelpers.cs" />
<Compile Include="..\Shared\X509CertificateHelpers.cs" Link="Internal\Shared\X509CertificateHelpers.cs" />
+ <Compile Include="..\Shared\HttpRoutePattern.cs" Link="Internal\Shared\HttpRoutePattern.cs" />
+ <Compile Include="..\Shared\HttpRoutePatternParser.cs" Link="Internal\Shared\HttpRoutePatternParser.cs" />
<Compile Include="$(SharedSourceRoot)ValueTaskExtensions\**\*.cs" LinkBase="Internal\Shared" />
<Protobuf Include="Internal\Protos\errors.proto" Access="Internal" />
diff --git a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/PublicAPI.Unshipped.txt b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/PublicAPI.Unshipped.txt
index 6ec9d5a61a..d448a37e8e 100644
--- a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/PublicAPI.Unshipped.txt
+++ b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.JsonTranscoding/PublicAPI.Unshipped.txt
@@ -20,5 +20,5 @@ Microsoft.AspNetCore.Grpc.JsonTranscoding.GrpcJsonTranscodingOptions.JsonSetting
Microsoft.AspNetCore.Grpc.JsonTranscoding.GrpcJsonTranscodingOptions.TypeRegistry.get -> Google.Protobuf.Reflection.TypeRegistry!
Microsoft.AspNetCore.Grpc.JsonTranscoding.GrpcJsonTranscodingOptions.TypeRegistry.set -> void
Microsoft.Extensions.DependencyInjection.GrpcJsonTranscodingServiceExtensions
-static Microsoft.Extensions.DependencyInjection.GrpcJsonTranscodingServiceExtensions.AddJsonTranscoding(this Grpc.AspNetCore.Server.IGrpcServerBuilder! grpcBuilder) -> Grpc.AspNetCore.Server.IGrpcServerBuilder!
-static Microsoft.Extensions.DependencyInjection.GrpcJsonTranscodingServiceExtensions.AddJsonTranscoding(this Grpc.AspNetCore.Server.IGrpcServerBuilder! grpcBuilder, System.Action<Microsoft.AspNetCore.Grpc.JsonTranscoding.GrpcJsonTranscodingOptions!>! configureOptions) -> Grpc.AspNetCore.Server.IGrpcServerBuilder!
+static Microsoft.Extensions.DependencyInjection.GrpcJsonTranscodingServiceExtensions.AddJsonTranscoding(this Grpc.AspNetCore.Server.IGrpcServerBuilder! builder) -> Grpc.AspNetCore.Server.IGrpcServerBuilder!
+static Microsoft.Extensions.DependencyInjection.GrpcJsonTranscodingServiceExtensions.AddJsonTranscoding(this Grpc.AspNetCore.Server.IGrpcServerBuilder! builder, System.Action<Microsoft.AspNetCore.Grpc.JsonTranscoding.GrpcJsonTranscodingOptions!>! configureOptions) -> Grpc.AspNetCore.Server.IGrpcServerBuilder!
diff --git a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.Swagger/Internal/GrpcJsonTranscodingDescriptionProvider.cs b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.Swagger/Internal/GrpcJsonTranscodingDescriptionProvider.cs
index 1cc787de88..cff40e9d68 100644
--- a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.Swagger/Internal/GrpcJsonTranscodingDescriptionProvider.cs
+++ b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.Swagger/Internal/GrpcJsonTranscodingDescriptionProvider.cs
@@ -84,7 +84,8 @@ internal sealed class GrpcJsonTranscodingDescriptionProvider : IApiDescriptionPr
}
var methodMetadata = routeEndpoint.Metadata.GetMetadata<GrpcMethodMetadata>()!;
- var routeParameters = ServiceDescriptorHelpers.ResolveRouteParameterDescriptors(routeEndpoint.RoutePattern, methodDescriptor.InputType);
+ var httpRoutePattern = HttpRoutePattern.Parse(pattern);
+ var routeParameters = ServiceDescriptorHelpers.ResolveRouteParameterDescriptors(httpRoutePattern.Variables.Select(v => v.FieldPath).ToList(), methodDescriptor.InputType);
foreach (var routeParameter in routeParameters)
{
diff --git a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.Swagger/Microsoft.AspNetCore.Grpc.Swagger.csproj b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.Swagger/Microsoft.AspNetCore.Grpc.Swagger.csproj
index 4ad6d024b9..f102223658 100644
--- a/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.Swagger/Microsoft.AspNetCore.Grpc.Swagger.csproj
+++ b/src/Grpc/JsonTranscoding/src/Microsoft.AspNetCore.Grpc.Swagger/Microsoft.AspNetCore.Grpc.Swagger.csproj
@@ -11,6 +11,8 @@
<InternalsVisibleTo Include="Microsoft.AspNetCore.Grpc.Swagger.Tests" />
<Compile Include="..\Shared\ServiceDescriptorHelpers.cs" Link="Internal\Shared\ServiceDescriptorHelpers.cs" />
+ <Compile Include="..\Shared\HttpRoutePattern.cs" Link="Internal\Shared\HttpRoutePattern.cs" />
+ <Compile Include="..\Shared\HttpRoutePatternParser.cs" Link="Internal\Shared\HttpRoutePatternParser.cs" />
<Reference Include="Microsoft.AspNetCore.Grpc.JsonTranscoding" />
<Reference Include="Swashbuckle.AspNetCore" />
diff --git a/src/Grpc/JsonTranscoding/src/Shared/HttpRoutePattern.cs b/src/Grpc/JsonTranscoding/src/Shared/HttpRoutePattern.cs
new file mode 100644
index 0000000000..0110856608
--- /dev/null
+++ b/src/Grpc/JsonTranscoding/src/Shared/HttpRoutePattern.cs
@@ -0,0 +1,35 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Grpc.Shared;
+
+internal sealed class HttpRoutePattern
+{
+ public List<string> Segments { get; }
+ public string? Verb { get; }
+ public List<HttpRouteVariable> Variables { get; }
+
+ private HttpRoutePattern(List<string> segments, string? verb, List<HttpRouteVariable> variables)
+ {
+ Segments = segments;
+ Verb = verb;
+ Variables = variables;
+ }
+
+ public static HttpRoutePattern Parse(string pattern)
+ {
+ var p = new HttpRoutePatternParser(pattern);
+ p.Parse();
+
+ return new HttpRoutePattern(p.Segments, p.Verb, p.Variables);
+ }
+}
+
+internal sealed class HttpRouteVariable
+{
+ public int Index { get; set; }
+ public int StartSegment { get; set; }
+ public int EndSegment { get; set; }
+ public List<string> FieldPath { get; } = new List<string>();
+ public bool HasCatchAllPath { get; set; }
+}
diff --git a/src/Grpc/JsonTranscoding/src/Shared/HttpRoutePatternParser.cs b/src/Grpc/JsonTranscoding/src/Shared/HttpRoutePatternParser.cs
new file mode 100644
index 0000000000..d32e4d863e
--- /dev/null
+++ b/src/Grpc/JsonTranscoding/src/Shared/HttpRoutePatternParser.cs
@@ -0,0 +1,349 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+
+namespace Grpc.Shared;
+
+// HTTP Template Grammar:
+//
+// Template = "/" | "/" Segments [ Verb ] ;
+// Segments = Segment { "/" Segment } ;
+// Segment = "*" | "**" | LITERAL | Variable ;
+// Variable = "{" FieldPath [ "=" Segments ] "}" ;
+// FieldPath = IDENT { "." IDENT } ;
+// Verb = ":" LITERAL ;
+internal class HttpRoutePatternParser
+{
+ private readonly string _input;
+
+ // Token delimiter indexes
+ private int _tokenStart;
+ private int _tokenEnd;
+
+ private bool _inVariable;
+
+ private readonly List<string> _segments;
+ private string? _verb;
+ private readonly List<HttpRouteVariable> _variables;
+ private bool _hasCatchAllSegment;
+
+ public List<string> Segments => _segments;
+ public string? Verb => _verb;
+ public List<HttpRouteVariable> Variables => _variables;
+
+ public HttpRoutePatternParser(string input)
+ {
+ _input = input;
+ _segments = new List<string>();
+ _variables = new List<HttpRouteVariable>();
+ }
+
+ public void Parse()
+ {
+ try
+ {
+ ParseTemplate();
+
+ if (_tokenStart < _input.Length)
+ {
+ throw new InvalidOperationException("Path template wasn't parsed to the end.");
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new InvalidOperationException($"Error parsing path template '{_input}'.", ex);
+ }
+ }
+
+ // Template = "/" Segments [ Verb ] ;
+ private void ParseTemplate()
+ {
+ if (!Consume('/'))
+ {
+ throw new InvalidOperationException("Path template must start with a '/'.");
+ }
+ ParseSegments();
+
+ if (EnsureCurrent())
+ {
+ if (CurrentChar != ':')
+ {
+ throw new InvalidOperationException("Path segment must end with a '/'.");
+ }
+ ParseVerb();
+ }
+ }
+
+ // Segments = Segment { "/" Segment } ;
+ private void ParseSegments()
+ {
+ while (true)
+ {
+ if (!ParseSegment())
+ {
+ // Support '/' template.
+ if (_segments.Count > 0)
+ {
+ throw new InvalidOperationException("Route template shouldn't end with a '/'.");
+ }
+ }
+ if (!Consume('/'))
+ {
+ break;
+ }
+ }
+ }
+
+ // Segment = "*" | "**" | LITERAL | Variable ;
+ private bool ParseSegment()
+ {
+ if (!EnsureCurrent())
+ {
+ return false;
+ }
+ switch (CurrentChar)
+ {
+ case '*':
+ {
+ if (_hasCatchAllSegment)
+ {
+ throw new InvalidOperationException("Only literal segments can follow a catch-all segment.");
+ }
+
+ ConsumeAndAssert('*');
+
+ // Check for '**'
+ if (Consume('*'))
+ {
+ _segments.Add("**");
+ _hasCatchAllSegment = true;
+ if (_inVariable)
+ {
+ CurrentVariable.HasCatchAllPath = true;
+ }
+ return true;
+ }
+ else
+ {
+ _segments.Add("*");
+ return true;
+ }
+ }
+
+ case '{':
+ if (_hasCatchAllSegment)
+ {
+ throw new InvalidOperationException("Only literal segments can follow a catch-all segment.");
+ }
+
+ ParseVariable();
+ return true;
+ default:
+ ParseLiteralSegment();
+ return true;
+ }
+ }
+
+ // Variable = "{" FieldPath [ "=" Segments ] "}" ;
+ private void ParseVariable()
+ {
+ ConsumeAndAssert('{');
+ StartVariable();
+ ParseFieldPath();
+ if (Consume('='))
+ {
+ ParseSegments();
+ }
+ else
+ {
+ _segments.Add("*");
+ }
+ EndVariable();
+ ConsumeAndAssert('}');
+ }
+
+ private void ParseLiteralSegment()
+ {
+ if (!TryParseLiteral(out var literal))
+ {
+ throw new InvalidOperationException("Empty literal segment.");
+ }
+ _segments.Add(literal);
+ }
+
+ // FieldPath = IDENT { "." IDENT } ;
+ private void ParseFieldPath()
+ {
+ do
+ {
+ if (!ParseIdentifier())
+ {
+ throw new InvalidOperationException("Incomplete or empty field path.");
+ }
+ }
+ while (Consume('.'));
+ }
+
+ // Verb = ":" LITERAL ;
+ private void ParseVerb()
+ {
+ ConsumeAndAssert(':');
+ if (!TryParseLiteral(out _verb))
+ {
+ throw new InvalidOperationException("Empty verb.");
+ }
+ }
+
+ private bool ParseIdentifier()
+ {
+ var identifier = string.Empty;
+ var hasEndChar = false;
+
+ while (!hasEndChar && NextChar())
+ {
+ var c = CurrentChar;
+ switch (c)
+ {
+ case '.':
+ case '}':
+ case '=':
+ hasEndChar = true;
+ break;
+ default:
+ Consume(c);
+ identifier += c;
+ break;
+ }
+ }
+
+ if (string.IsNullOrEmpty(identifier))
+ {
+ return false;
+ }
+
+ CurrentVariable.FieldPath.Add(identifier);
+ return true;
+ }
+
+ private bool TryParseLiteral([NotNullWhen(true)] out string? literal)
+ {
+ literal = null;
+
+ if (!EnsureCurrent())
+ {
+ return false;
+ }
+
+ // Initialize to false in case we encounter an empty literal.
+ var result = false;
+
+ while (true)
+ {
+ var c = CurrentChar;
+ switch (c)
+ {
+ case '/':
+ case ':':
+ case '}':
+ if (!result)
+ {
+ throw new InvalidOperationException("Path template has an empty segment.");
+ }
+ return result;
+ default:
+ Consume(c);
+ literal += c;
+ break;
+ }
+
+ result = true;
+
+ if (!NextChar())
+ {
+ break;
+ }
+ }
+
+ return result;
+ }
+
+ private void ConsumeAndAssert(char? c)
+ {
+ if (!Consume(c))
+ {
+ throw new InvalidOperationException($"Expected '{c}' when parsing path template.");
+ }
+ }
+
+ private bool Consume(char? c)
+ {
+ if (!EnsureCurrent())
+ {
+ return false;
+ }
+ if (CurrentChar != c)
+ {
+ return false;
+ }
+ _tokenStart++;
+ return true;
+ }
+
+ private bool EnsureCurrent() => _tokenStart < _tokenEnd || NextChar();
+
+ private bool NextChar()
+ {
+ if (_tokenEnd < _input.Length)
+ {
+ _tokenEnd++;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ private char? CurrentChar => _tokenStart < _tokenEnd && _tokenEnd <= _input.Length ? _input[_tokenEnd - 1] : null;
+
+ private HttpRouteVariable CurrentVariable
+ {
+ get
+ {
+ if (!_inVariable || _variables.LastOrDefault() is not HttpRouteVariable variable)
+ {
+ throw new InvalidOperationException("Unexpected error when updating variable.");
+ }
+
+ return variable;
+ }
+
+ }
+
+ private void StartVariable()
+ {
+ if (_inVariable)
+ {
+ throw new InvalidOperationException("Variable can't be nested.");
+ }
+
+ _variables.Add(new HttpRouteVariable());
+ _inVariable = true;
+ CurrentVariable.StartSegment = _segments.Count;
+ CurrentVariable.HasCatchAllPath = false;
+ }
+
+ private void EndVariable()
+ {
+ CurrentVariable.EndSegment = _segments.Count;
+
+ Debug.Assert(CurrentVariable.FieldPath.Any());
+ Debug.Assert(CurrentVariable.StartSegment < CurrentVariable.EndSegment);
+ Debug.Assert(CurrentVariable.EndSegment <= _segments.Count);
+
+ _inVariable = false;
+ }
+}
diff --git a/src/Grpc/JsonTranscoding/src/Shared/ServiceDescriptorHelpers.cs b/src/Grpc/JsonTranscoding/src/Shared/ServiceDescriptorHelpers.cs
index f18b0a2aec..07b68b74d6 100644
--- a/src/Grpc/JsonTranscoding/src/Shared/ServiceDescriptorHelpers.cs
+++ b/src/Grpc/JsonTranscoding/src/Shared/ServiceDescriptorHelpers.cs
@@ -25,6 +25,7 @@ using Google.Api;
using Google.Protobuf;
using Google.Protobuf.Reflection;
using Google.Protobuf.WellKnownTypes;
+using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal;
using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal.Json;
using Microsoft.AspNetCore.Routing.Patterns;
using Microsoft.Extensions.Primitives;
@@ -65,28 +66,13 @@ internal static class ServiceDescriptorHelpers
throw new InvalidOperationException($"Get not find Descriptor property on {serviceReflectionType.Name}.");
}
- public static bool TryResolveDescriptors(MessageDescriptor messageDescriptor, string variable, [NotNullWhen(true)]out List<FieldDescriptor>? fieldDescriptors)
+ public static bool TryResolveDescriptors(MessageDescriptor messageDescriptor, IList<string> path, [NotNullWhen(true)]out List<FieldDescriptor>? fieldDescriptors)
{
fieldDescriptors = null;
- var path = variable.AsSpan();
MessageDescriptor? currentDescriptor = messageDescriptor;
- while (path.Length > 0)
+ foreach (var fieldName in path)
{
- var separator = path.IndexOf('.');
-
- string fieldName;
- if (separator != -1)
- {
- fieldName = path.Slice(0, separator).ToString();
- path = path.Slice(separator + 1);
- }
- else
- {
- fieldName = path.ToString();
- path = ReadOnlySpan<char>.Empty;
- }
-
var field = currentDescriptor?.FindFieldByName(fieldName);
if (field == null)
{
@@ -108,7 +94,6 @@ internal static class ServiceDescriptorHelpers
{
currentDescriptor = null;
}
-
}
return fieldDescriptors != null;
@@ -303,17 +288,18 @@ internal static class ServiceDescriptorHelpers
}
}
- public static Dictionary<string, List<FieldDescriptor>> ResolveRouteParameterDescriptors(RoutePattern pattern, MessageDescriptor messageDescriptor)
+ public static Dictionary<string, List<FieldDescriptor>> ResolveRouteParameterDescriptors(List<List<string>> parameters, MessageDescriptor messageDescriptor)
{
var routeParameterDescriptors = new Dictionary<string, List<FieldDescriptor>>(StringComparer.Ordinal);
- foreach (var routeParameter in pattern.Parameters)
+ foreach (var routeParameter in parameters)
{
- if (!TryResolveDescriptors(messageDescriptor, routeParameter.Name, out var fieldDescriptors))
+ var completeFieldPath = string.Join(".", routeParameter);
+ if (!TryResolveDescriptors(messageDescriptor, routeParameter, out var fieldDescriptors))
{
- throw new InvalidOperationException($"Couldn't find matching field for route parameter '{routeParameter.Name}' on {messageDescriptor.Name}.");
+ throw new InvalidOperationException($"Couldn't find matching field for route parameter '{completeFieldPath}' on {messageDescriptor.Name}.");
}
- routeParameterDescriptors.Add(routeParameter.Name, fieldDescriptors);
+ routeParameterDescriptors.Add(completeFieldPath, fieldDescriptors);
}
return routeParameterDescriptors;
@@ -325,7 +311,8 @@ internal static class ServiceDescriptorHelpers
{
if (!string.Equals(body, "*", StringComparison.Ordinal))
{
- if (!TryResolveDescriptors(methodDescriptor.InputType, body, out var bodyFieldDescriptors))
+ var bodyFieldPath = body.Split('.');
+ if (!TryResolveDescriptors(methodDescriptor.InputType, bodyFieldPath, out var bodyFieldDescriptors))
{
throw new InvalidOperationException($"Couldn't find matching field for body '{body}' on {methodDescriptor.InputType.Name}.");
}
diff --git a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.IntegrationTests/Infrastructure/TestHelpers.cs b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.IntegrationTests/Infrastructure/TestHelpers.cs
index c2319e38bf..adc3c9e111 100644
--- a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.IntegrationTests/Infrastructure/TestHelpers.cs
+++ b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.IntegrationTests/Infrastructure/TestHelpers.cs
@@ -5,6 +5,8 @@ using System.Net;
using Google.Protobuf.Reflection;
using Grpc.AspNetCore.Server;
using Grpc.Core.Interceptors;
+using Grpc.Shared;
+using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal;
using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal.CallHandlers;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
@@ -67,6 +69,7 @@ internal static class TestHelpers
bodyDescriptor,
bodyDescriptorRepeated ?? false,
bodyFieldDescriptors,
- routeParameterDescriptors ?? new Dictionary<string, List<FieldDescriptor>>());
+ routeParameterDescriptors ?? new Dictionary<string, List<FieldDescriptor>>(),
+ JsonTranscodingRouteAdapter.Parse(HttpRoutePattern.Parse("/")));
}
}
diff --git a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.IntegrationTests/RouteTests.cs b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.IntegrationTests/RouteTests.cs
new file mode 100644
index 0000000000..6ccaeffd65
--- /dev/null
+++ b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.IntegrationTests/RouteTests.cs
@@ -0,0 +1,108 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Text;
+using System.Text.Json;
+using Grpc.Core;
+using IntegrationTestsWebsite;
+using Microsoft.AspNetCore.Grpc.JsonTranscoding.IntegrationTests.Infrastructure;
+using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal;
+using Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests.Infrastructure;
+using Microsoft.AspNetCore.Testing;
+using Xunit.Abstractions;
+
+namespace Microsoft.AspNetCore.Grpc.JsonTranscoding.IntegrationTests;
+
+public class RouteTests : IntegrationTestBase
+{
+ public RouteTests(GrpcTestFixture<Startup> fixture, ITestOutputHelper outputHelper)
+ : base(fixture, outputHelper)
+ {
+ }
+
+ [Fact]
+ public async Task ComplexParameter_MatchUrl_SuccessResult()
+ {
+ // Arrange
+ Task<HelloReply> UnaryMethod(HelloRequest request, ServerCallContext context)
+ {
+ return Task.FromResult(new HelloReply { Message = $"Hello {request.Name}!" });
+ }
+ var method = Fixture.DynamicGrpc.AddUnaryMethod<HelloRequest, HelloReply>(
+ UnaryMethod,
+ Greeter.Descriptor.FindMethodByName("SayHelloComplex"));
+
+ var client = new HttpClient(Fixture.Handler) { BaseAddress = new Uri("http://localhost") };
+
+ // Act
+ var response = await client.GetAsync("/v1/greeter/from/test").DefaultTimeout();
+ var responseStream = await response.Content.ReadAsStreamAsync();
+ using var result = await JsonDocument.ParseAsync(responseStream);
+
+ // Assert
+ Assert.Equal("Hello from/test!", result.RootElement.GetProperty("message").GetString());
+ }
+
+ [Fact]
+ public async Task MultipleComplexCatchAll_MatchUrl_SuccessResult()
+ {
+ // Arrange
+ Task<HelloReply> UnaryMethod1(HelloRequest request, ServerCallContext context)
+ {
+ return Task.FromResult(new HelloReply { Message = $"One - Hello {request.Name}!" });
+ }
+ Task<HelloReply> UnaryMethod2(HelloRequest request, ServerCallContext context)
+ {
+ return Task.FromResult(new HelloReply { Message = $"Two - Hello {request.Name}!" });
+ }
+ var method1 = Fixture.DynamicGrpc.AddUnaryMethod<HelloRequest, HelloReply>(
+ UnaryMethod1,
+ Greeter.Descriptor.FindMethodByName("SayHelloComplexCatchAll1"));
+ var method2 = Fixture.DynamicGrpc.AddUnaryMethod<HelloRequest, HelloReply>(
+ UnaryMethod2,
+ Greeter.Descriptor.FindMethodByName("SayHelloComplexCatchAll2"));
+
+ var client = new HttpClient(Fixture.Handler) { BaseAddress = new Uri("http://localhost") };
+
+ // Act 1
+ var response1 = await client.GetAsync("/v1/greeter/test1/b/c/d/one").DefaultTimeout();
+ var responseStream1 = await response1.Content.ReadAsStreamAsync();
+ using var result1 = await JsonDocument.ParseAsync(responseStream1);
+
+ // Assert 1
+ Assert.Equal("One - Hello v1/greeter/test1/b/c!", result1.RootElement.GetProperty("message").GetString());
+
+ // Act 2
+ var response2 = await client.GetAsync("/v1/greeter/test2/b/c/d/two").DefaultTimeout();
+ var responseStream2 = await response2.Content.ReadAsStreamAsync();
+ using var result2 = await JsonDocument.ParseAsync(responseStream2);
+
+ // Assert 2
+ Assert.Equal("Two - Hello v1/greeter/test2/b/c!", result2.RootElement.GetProperty("message").GetString());
+ }
+
+ [Fact]
+ public async Task ComplexCatchAllParameter_NestedField_MatchUrl_SuccessResult()
+ {
+ // Arrange
+ Task<HelloReply> UnaryMethod(ComplextHelloRequest request, ServerCallContext context)
+ {
+ return Task.FromResult(new HelloReply { Message = $"Hello {request.Name.FirstName} {request.Name.LastName}!" });
+ }
+ var method = Fixture.DynamicGrpc.AddUnaryMethod<ComplextHelloRequest, HelloReply>(
+ UnaryMethod,
+ Greeter.Descriptor.FindMethodByName("SayHelloComplexCatchAll3"));
+
+ var client = new HttpClient(Fixture.Handler) { BaseAddress = new Uri("http://localhost") };
+
+ // Act
+ var response = await client.GetAsync("/v1/last_name/complex_greeter/test2/b/c/d/two").DefaultTimeout();
+ var responseStream = await response.Content.ReadAsStreamAsync();
+ using var result = await JsonDocument.ParseAsync(responseStream);
+
+ // Assert
+ Assert.Equal("Hello complex_greeter/test2/b last_name!", result.RootElement.GetProperty("message").GetString());
+ }
+}
diff --git a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/HttpRoutePatternParserTests.cs b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/HttpRoutePatternParserTests.cs
new file mode 100644
index 0000000000..3fb4a80d1f
--- /dev/null
+++ b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/HttpRoutePatternParserTests.cs
@@ -0,0 +1,325 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Grpc.Shared;
+using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal;
+
+namespace Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests;
+
+public class HttpRoutePatternParserTests
+{
+ [Fact]
+ public void ParseMultipleVariables()
+ {
+ var pattern = HttpRoutePattern.Parse("/shelves/{shelf}/books/{book}");
+ Assert.Null(pattern.Verb);
+ Assert.Collection(
+ pattern.Segments,
+ s => Assert.Equal("shelves", s),
+ s => Assert.Equal("*", s),
+ s => Assert.Equal("books", s),
+ s => Assert.Equal("*", s));
+ Assert.Collection(
+ pattern.Variables,
+ v =>
+ {
+ Assert.Equal(1, v.StartSegment);
+ Assert.Equal(2, v.EndSegment);
+ Assert.Equal("shelf", string.Join(".", v.FieldPath));
+ Assert.False(v.HasCatchAllPath);
+ },
+ v =>
+ {
+ Assert.Equal(3, v.StartSegment);
+ Assert.Equal(4, v.EndSegment);
+ Assert.Equal("book", string.Join(".", v.FieldPath));
+ Assert.False(v.HasCatchAllPath);
+ });
+ }
+
+ [Fact]
+ public void ParseComplexVariable()
+ {
+ var pattern = HttpRoutePattern.Parse("/v1/{book.name=shelves/*/books/*}");
+ Assert.Null(pattern.Verb);
+ Assert.Collection(
+ pattern.Segments,
+ s => Assert.Equal("v1", s),
+ s => Assert.Equal("shelves", s),
+ s => Assert.Equal("*", s),
+ s => Assert.Equal("books", s),
+ s => Assert.Equal("*", s));
+ Assert.Collection(
+ pattern.Variables,
+ v =>
+ {
+ Assert.Equal(1, v.StartSegment);
+ Assert.Equal(5, v.EndSegment);
+ Assert.Equal("book.name", string.Join(".", v.FieldPath));
+ Assert.False(v.HasCatchAllPath);
+ });
+ }
+
+ [Fact]
+ public void ParseCatchAllSegment()
+ {
+ var pattern = HttpRoutePattern.Parse("/shelves/**");
+ Assert.Collection(
+ pattern.Segments,
+ s => Assert.Equal("shelves", s),
+ s => Assert.Equal("**", s));
+ Assert.Empty(pattern.Variables);
+ }
+
+ [Fact]
+ public void ParseCatchAllSegment2()
+ {
+ var pattern = HttpRoutePattern.Parse("/**");
+ Assert.Collection(
+ pattern.Segments,
+ s => Assert.Equal("**", s));
+ Assert.Empty(pattern.Variables);
+ }
+
+ [Fact]
+ public void ParseAnySegment()
+ {
+ var pattern = HttpRoutePattern.Parse("/*");
+ Assert.Collection(
+ pattern.Segments,
+ s => Assert.Equal("*", s));
+ Assert.Empty(pattern.Variables);
+ }
+
+ [Fact]
+ public void ParseSlash()
+ {
+ var pattern = HttpRoutePattern.Parse("/");
+ Assert.Empty(pattern.Segments);
+ Assert.Empty(pattern.Variables);
+ }
+
+ [Fact]
+ public void ParseVerb()
+ {
+ var pattern = HttpRoutePattern.Parse("/a:foo");
+ Assert.Equal("foo", pattern.Verb);
+ Assert.Collection(
+ pattern.Segments,
+ s => Assert.Equal("a", s));
+ Assert.Empty(pattern.Variables);
+ }
+
+ [Fact]
+ public void ParseAnyAndCatchAllSegment()
+ {
+ var pattern = HttpRoutePattern.Parse("/*/**");
+ Assert.Collection(
+ pattern.Segments,
+ s => Assert.Equal("*", s),
+ s => Assert.Equal("**", s));
+ Assert.Empty(pattern.Variables);
+ }
+
+ [Fact]
+ public void ParseAnyAndCatchAllSegment2()
+ {
+ var pattern = HttpRoutePattern.Parse("/*/a/**");
+ Assert.Collection(
+ pattern.Segments,
+ s => Assert.Equal("*", s),
+ s => Assert.Equal("a", s),
+ s => Assert.Equal("**", s));
+ Assert.Empty(pattern.Variables);
+ }
+
+ [Fact]
+ public void ParseNestedFieldPath()
+ {
+ var pattern = HttpRoutePattern.Parse("/a/{a.b.c}");
+ Assert.Collection(
+ pattern.Segments,
+ s => Assert.Equal("a", s),
+ s => Assert.Equal("*", s));
+ Assert.Collection(
+ pattern.Variables,
+ v =>
+ {
+ Assert.Equal(1, v.StartSegment);
+ Assert.Equal(2, v.EndSegment);
+ Assert.Equal("a.b.c", string.Join(".", v.FieldPath));
+ Assert.False(v.HasCatchAllPath);
+ });
+ }
+
+ [Fact]
+ public void ParseComplexNestedFieldPath()
+ {
+ var pattern = HttpRoutePattern.Parse("/a/{a.b.c=*}");
+ Assert.Collection(
+ pattern.Segments,
+ s => Assert.Equal("a", s),
+ s => Assert.Equal("*", s));
+ Assert.Collection(
+ pattern.Variables,
+ v =>
+ {
+ Assert.Equal(1, v.StartSegment);
+ Assert.Equal(2, v.EndSegment);
+ Assert.Equal("a.b.c", string.Join(".", v.FieldPath));
+ Assert.False(v.HasCatchAllPath);
+ });
+ }
+
+ [Fact]
+ public void ParseComplexCatchAll()
+ {
+ var pattern = HttpRoutePattern.Parse("/a/{b=**}");
+ Assert.Collection(
+ pattern.Segments,
+ s => Assert.Equal("a", s),
+ s => Assert.Equal("**", s));
+ Assert.Collection(
+ pattern.Variables,
+ v =>
+ {
+ Assert.Equal(1, v.StartSegment);
+ Assert.Equal(2, v.EndSegment);
+ Assert.Equal("b", string.Join(".", v.FieldPath));
+ Assert.True(v.HasCatchAllPath);
+ });
+ }
+
+ [Fact]
+ public void ParseComplexPrefixSegment()
+ {
+ var pattern = HttpRoutePattern.Parse("/a/{b=c/*}");
+ Assert.Collection(
+ pattern.Segments,
+ s => Assert.Equal("a", s),
+ s => Assert.Equal("c", s),
+ s => Assert.Equal("*", s));
+ Assert.Collection(
+ pattern.Variables,
+ v =>
+ {
+ Assert.Equal(1, v.StartSegment);
+ Assert.Equal(3, v.EndSegment);
+ Assert.Equal("b", string.Join(".", v.FieldPath));
+ Assert.False(v.HasCatchAllPath);
+ });
+ }
+
+ [Fact]
+ public void ParseComplexPrefixSuffixSegment()
+ {
+ var pattern = HttpRoutePattern.Parse("/a/{b=c/*/d}");
+ Assert.Collection(
+ pattern.Segments,
+ s => Assert.Equal("a", s),
+ s => Assert.Equal("c", s),
+ s => Assert.Equal("*", s),
+ s => Assert.Equal("d", s));
+ Assert.Collection(
+ pattern.Variables,
+ v =>
+ {
+ Assert.Equal(1, v.StartSegment);
+ Assert.Equal(4, v.EndSegment);
+ Assert.Equal("b", string.Join(".", v.FieldPath));
+ Assert.False(v.HasCatchAllPath);
+ });
+ }
+
+ [Fact]
+ public void ParseComplexPathCatchAll()
+ {
+ var pattern = HttpRoutePattern.Parse("/a/{b=c/**}");
+ Assert.Collection(
+ pattern.Segments,
+ s => Assert.Equal("a", s),
+ s => Assert.Equal("c", s),
+ s => Assert.Equal("**", s));
+ Assert.Collection(
+ pattern.Variables,
+ v =>
+ {
+ Assert.Equal(1, v.StartSegment);
+ Assert.Equal(3, v.EndSegment);
+ Assert.Equal("b", string.Join(".", v.FieldPath));
+ Assert.True(v.HasCatchAllPath);
+ });
+ }
+
+ [Fact]
+ public void ParseComplexPrefixSuffixCatchAll()
+ {
+ var pattern = HttpRoutePattern.Parse("/{x.y.z=a/**/b}/c/d");
+ Assert.Collection(
+ pattern.Segments,
+ s => Assert.Equal("a", s),
+ s => Assert.Equal("**", s),
+ s => Assert.Equal("b", s),
+ s => Assert.Equal("c", s),
+ s => Assert.Equal("d", s));
+ Assert.Collection(
+ pattern.Variables,
+ v =>
+ {
+ Assert.Equal(0, v.StartSegment);
+ Assert.Equal(3, v.EndSegment);
+ Assert.Equal("x.y.z", string.Join(".", v.FieldPath));
+ Assert.True(v.HasCatchAllPath);
+ });
+ }
+
+ [Fact]
+ public void ParseCatchAllVerb()
+ {
+ var pattern = HttpRoutePattern.Parse("/a/{b=*}/**:verb");
+ Assert.Equal("verb", pattern.Verb);
+ Assert.Collection(
+ pattern.Segments,
+ s => Assert.Equal("a", s),
+ s => Assert.Equal("*", s),
+ s => Assert.Equal("**", s));
+ Assert.Collection(
+ pattern.Variables,
+ v =>
+ {
+ Assert.Equal(1, v.StartSegment);
+ Assert.Equal(2, v.EndSegment);
+ Assert.Equal("b", string.Join(".", v.FieldPath));
+ Assert.False(v.HasCatchAllPath);
+ });
+ }
+
+ [Theory]
+ [InlineData("", "Path template must start with a '/'.")]
+ [InlineData("//", "Path template has an empty segment.")]
+ [InlineData("/{}", "Incomplete or empty field path.")]
+ [InlineData("/a/", "Route template shouldn't end with a '/'.")]
+ [InlineData(":verb", "Path template must start with a '/'.")]
+ [InlineData(":", "Path template must start with a '/'.")]
+ [InlineData("/:", "Path template has an empty segment.")]
+ [InlineData("/{var}:", "Empty verb.")]
+ [InlineData("/{", "Incomplete or empty field path.")]
+ [InlineData("/a{x}", "Path segment must end with a '/'.")]
+ [InlineData("/{x}a", "Path segment must end with a '/'.")]
+ [InlineData("/{x}{y}", "Path segment must end with a '/'.")]
+ [InlineData("/{var=a/{nested=b}}", "Variable can't be nested.")]
+ [InlineData("/{x=**}/*", "Only literal segments can follow a catch-all segment.")]
+ [InlineData("/{x=}", "Path template has an empty segment.")]
+ [InlineData("/**/*", "Only literal segments can follow a catch-all segment.")]
+ [InlineData("/{x", "Expected '}' when parsing path template.")]
+ public void Error(string pattern, string errorMessage)
+ {
+ var ex = Assert.Throws<InvalidOperationException>(() => HttpRoutePattern.Parse(pattern));
+ Assert.Equal(errorMessage, ex.InnerException!.Message);
+ }
+}
diff --git a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/Infrastructure/TestHelpers.cs b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/Infrastructure/TestHelpers.cs
index 03a26331d6..85550a7a82 100644
--- a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/Infrastructure/TestHelpers.cs
+++ b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/Infrastructure/TestHelpers.cs
@@ -5,6 +5,8 @@ using System.Net;
using Google.Protobuf.Reflection;
using Grpc.AspNetCore.Server;
using Grpc.Core.Interceptors;
+using Grpc.Shared;
+using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal;
using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal.CallHandlers;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
@@ -68,6 +70,7 @@ internal static class TestHelpers
bodyDescriptor,
bodyDescriptorRepeated ?? false,
bodyFieldDescriptors,
- routeParameterDescriptors ?? new Dictionary<string, List<FieldDescriptor>>());
+ routeParameterDescriptors ?? new Dictionary<string, List<FieldDescriptor>>(),
+ JsonTranscodingRouteAdapter.Parse(HttpRoutePattern.Parse("/")));
}
}
diff --git a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/JsonTranscodingRouteAdapterTests.cs b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/JsonTranscodingRouteAdapterTests.cs
new file mode 100644
index 0000000000..c27ffe47e6
--- /dev/null
+++ b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/JsonTranscodingRouteAdapterTests.cs
@@ -0,0 +1,223 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Grpc.Shared;
+using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.Extensions.FileSystemGlobbing.Internal;
+
+namespace Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests;
+
+public class JsonTranscodingRouteAdapterTests
+{
+ [Fact]
+ public void ParseMultipleVariables()
+ {
+ var pattern = HttpRoutePattern.Parse("/shelves/{shelf}/books/{book}");
+ var adapter = JsonTranscodingRouteAdapter.Parse(pattern);
+
+ Assert.Equal("/shelves/{shelf}/books/{book}", adapter.ResolvedRouteTemplate);
+ Assert.Empty(adapter.RewriteVariableActions);
+ }
+
+ [Fact]
+ public void ParseComplexVariable()
+ {
+ var route = HttpRoutePattern.Parse("/v1/{book.name=shelves/*/books/*}");
+ var adapter = JsonTranscodingRouteAdapter.Parse(route);
+
+ Assert.Equal("/v1/shelves/{__Complex_book.name_2}/books/{__Complex_book.name_4}", adapter.ResolvedRouteTemplate);
+ Assert.Single(adapter.RewriteVariableActions);
+
+ var httpContext = new DefaultHttpContext();
+ httpContext.Request.RouteValues = new RouteValueDictionary
+ {
+ { "__Complex_book.name_2", "first" },
+ { "__Complex_book.name_4", "second" }
+ };
+
+ adapter.RewriteVariableActions[0](httpContext);
+
+ Assert.Equal("shelves/first/books/second", httpContext.Request.RouteValues["book.name"]);
+ }
+
+ [Fact]
+ public void ParseCatchAllSegment()
+ {
+ var pattern = HttpRoutePattern.Parse("/shelves/**");
+ var adapter = JsonTranscodingRouteAdapter.Parse(pattern);
+
+ Assert.Equal("/shelves/{**__Discard_1}", adapter.ResolvedRouteTemplate);
+ Assert.Empty(adapter.RewriteVariableActions);
+ }
+
+ [Fact]
+ public void ParseAnySegment()
+ {
+ var pattern = HttpRoutePattern.Parse("/*")!;
+ var adapter = JsonTranscodingRouteAdapter.Parse(pattern);
+
+ Assert.Equal("/{__Discard_0}", adapter.ResolvedRouteTemplate);
+ Assert.Empty(adapter.RewriteVariableActions);
+ }
+
+ [Fact]
+ public void ParseVerb()
+ {
+ var pattern = HttpRoutePattern.Parse("/a:foo");
+ var adapter = JsonTranscodingRouteAdapter.Parse(pattern);
+
+ Assert.Equal("/a", adapter.ResolvedRouteTemplate);
+ Assert.Empty(adapter.RewriteVariableActions);
+ }
+
+ [Fact]
+ public void ParseAnyAndCatchAllSegment()
+ {
+ var pattern = HttpRoutePattern.Parse("/*/**");
+ var adapter = JsonTranscodingRouteAdapter.Parse(pattern);
+
+ Assert.Equal("/{__Discard_0}/{**__Discard_1}", adapter.ResolvedRouteTemplate);
+ Assert.Empty(adapter.RewriteVariableActions);
+ }
+
+ [Fact]
+ public void ParseAnyAndCatchAllSegment2()
+ {
+ var pattern = HttpRoutePattern.Parse("/*/a/**");
+ var adapter = JsonTranscodingRouteAdapter.Parse(pattern);
+
+ Assert.Equal("/{__Discard_0}/a/{**__Discard_2}", adapter.ResolvedRouteTemplate);
+ Assert.Empty(adapter.RewriteVariableActions);
+ }
+
+ [Fact]
+ public void ParseNestedFieldPath()
+ {
+ var pattern = HttpRoutePattern.Parse("/a/{a.b.c}");
+ var adapter = JsonTranscodingRouteAdapter.Parse(pattern);
+
+ Assert.Equal("/a/{a.b.c}", adapter.ResolvedRouteTemplate);
+ Assert.Empty(adapter.RewriteVariableActions);
+ }
+
+ [Fact]
+ public void ParseComplexNestedFieldPath()
+ {
+ var pattern = HttpRoutePattern.Parse("/a/{a.b.c=*}");
+ var adapter = JsonTranscodingRouteAdapter.Parse(pattern);
+
+ Assert.Equal("/a/{a.b.c}", adapter.ResolvedRouteTemplate);
+ Assert.Empty(adapter.RewriteVariableActions);
+ }
+
+ [Fact]
+ public void ParseComplexCatchAll()
+ {
+ var pattern = HttpRoutePattern.Parse("/a/{b=**}");
+ var adapter = JsonTranscodingRouteAdapter.Parse(pattern);
+
+ Assert.Equal("/a/{**b}", adapter.ResolvedRouteTemplate);
+ Assert.Empty(adapter.RewriteVariableActions);
+ }
+
+ [Fact]
+ public void ParseComplexPrefixSuffixCatchAll()
+ {
+ var pattern = HttpRoutePattern.Parse("/{x.y.z=a/**/b}/c/d");
+ var adapter = JsonTranscodingRouteAdapter.Parse(pattern);
+
+ Assert.Equal("/a/{**__Complex_x.y.z_1:regex(b/c/d$)}", adapter.ResolvedRouteTemplate);
+
+ var httpContext = new DefaultHttpContext();
+ httpContext.Request.RouteValues = new RouteValueDictionary
+ {
+ { "__Complex_x.y.z_1", "my/value/b/c/d" }
+ };
+
+ adapter.RewriteVariableActions[0](httpContext);
+
+ Assert.Equal("a/my/value/b", httpContext.Request.RouteValues["x.y.z"]);
+ }
+
+ [Fact]
+ public void ParseComplexPrefixSegment()
+ {
+ var pattern = HttpRoutePattern.Parse("/a/{b=c/*}");
+ var adapter = JsonTranscodingRouteAdapter.Parse(pattern);
+
+ Assert.Equal("/a/c/{__Complex_b_2}", adapter.ResolvedRouteTemplate);
+
+ var httpContext = new DefaultHttpContext();
+ httpContext.Request.RouteValues = new RouteValueDictionary
+ {
+ { "__Complex_b_2", "value" }
+ };
+
+ adapter.RewriteVariableActions[0](httpContext);
+
+ Assert.Equal("c/value", httpContext.Request.RouteValues["b"]);
+ }
+
+ [Fact]
+ public void ParseComplexPrefixSuffixSegment()
+ {
+ var pattern = HttpRoutePattern.Parse("/a/{b=c/*/d}");
+ var adapter = JsonTranscodingRouteAdapter.Parse(pattern);
+
+ Assert.Equal("/a/c/{__Complex_b_2}/d", adapter.ResolvedRouteTemplate);
+
+ var httpContext = new DefaultHttpContext();
+ httpContext.Request.RouteValues = new RouteValueDictionary
+ {
+ { "__Complex_b_2", "value" }
+ };
+
+ adapter.RewriteVariableActions[0](httpContext);
+
+ Assert.Equal("c/value/d", httpContext.Request.RouteValues["b"]);
+ }
+
+ [Fact]
+ public void ParseComplexPathCatchAll()
+ {
+ var pattern = HttpRoutePattern.Parse("/a/{b=c/**}");
+ var adapter = JsonTranscodingRouteAdapter.Parse(pattern);
+
+ Assert.Equal("/a/c/{**__Complex_b_2}", adapter.ResolvedRouteTemplate);
+
+ var httpContext = new DefaultHttpContext();
+ httpContext.Request.RouteValues = new RouteValueDictionary
+ {
+ { "__Complex_b_2", "value" }
+ };
+
+ adapter.RewriteVariableActions[0](httpContext);
+
+ Assert.Equal("c/value", httpContext.Request.RouteValues["b"]);
+ }
+
+ [Fact]
+ public void ParseManyVariables()
+ {
+ var pattern = HttpRoutePattern.Parse("/{a}/{b}/{c}");
+ var adapter = JsonTranscodingRouteAdapter.Parse(pattern);
+
+ Assert.Equal("/{a}/{b}/{c}", adapter.ResolvedRouteTemplate);
+ }
+
+ [Fact]
+ public void ParseCatchAllVerb()
+ {
+ var pattern = HttpRoutePattern.Parse("/a/{b=*}/**:verb");
+ var adapter = JsonTranscodingRouteAdapter.Parse(pattern);
+
+ Assert.Equal("/a/{b}/{**__Discard_2}", adapter.ResolvedRouteTemplate);
+ }
+}
diff --git a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/JsonTranscodingServerCallContextTests.cs b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/JsonTranscodingServerCallContextTests.cs
index 2e3d6def02..9365412e9d 100644
--- a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/JsonTranscodingServerCallContextTests.cs
+++ b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/JsonTranscodingServerCallContextTests.cs
@@ -5,6 +5,7 @@ using System.Net;
using Google.Protobuf.Reflection;
using Grpc.AspNetCore.Server;
using Grpc.Core;
+using Grpc.Shared;
using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal;
using Microsoft.AspNetCore.Grpc.JsonTranscoding.Internal.CallHandlers;
using Microsoft.AspNetCore.Http;
@@ -99,7 +100,8 @@ public class JsonTranscodingServerCallContextTests
null,
false,
null,
- new Dictionary<string, List<FieldDescriptor>>()),
+ new Dictionary<string, List<FieldDescriptor>>(),
+ JsonTranscodingRouteAdapter.Parse(HttpRoutePattern.Parse("/")!)),
NullLogger.Instance);
}
}
diff --git a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/JsonTranscodingServiceMethodProviderTests.cs b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/JsonTranscodingServiceMethodProviderTests.cs
index 470db8b06d..4c1e3c65e9 100644
--- a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/JsonTranscodingServiceMethodProviderTests.cs
+++ b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/JsonTranscodingServiceMethodProviderTests.cs
@@ -173,7 +173,8 @@ public class JsonTranscodingServiceMethodProviderTests
// Assert
Assert.Equal("Error binding gRPC service 'JsonTranscodingInvalidPatternGreeterService'.", ex.Message);
Assert.Equal("Error binding BadPattern on JsonTranscodingInvalidPatternGreeterService to HTTP API.", ex.InnerException!.InnerException!.Message);
- Assert.Equal("Path template 'v1/greeter/{name}' must start with a '/'.", ex.InnerException!.InnerException!.InnerException!.Message);
+ Assert.Equal("Error parsing path template 'v1/greeter/{name}'.", ex.InnerException!.InnerException!.InnerException!.Message);
+ Assert.Equal("Path template must start with a '/'.", ex.InnerException!.InnerException!.InnerException!.InnerException!.Message);
}
private static RouteEndpoint FindGrpcEndpoint(IReadOnlyList<Endpoint> endpoints, string methodName)
diff --git a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/UnaryServerCallHandlerTests.cs b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/UnaryServerCallHandlerTests.cs
index 21855fb25f..722a9410eb 100644
--- a/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/UnaryServerCallHandlerTests.cs
+++ b/src/Grpc/JsonTranscoding/test/Microsoft.AspNetCore.Grpc.JsonTranscoding.Tests/UnaryServerCallHandlerTests.cs
@@ -222,7 +222,7 @@ public class UnaryServerCallHandlerTests : LoggedTest
return Task.FromResult(new HelloReply { Message = $"Hello {r.Name}" });
};
- ServiceDescriptorHelpers.TryResolveDescriptors(HelloRequest.Descriptor, "sub", out var bodyFieldDescriptors);
+ ServiceDescriptorHelpers.TryResolveDescriptors(HelloRequest.Descriptor, new[] { "sub" }, out var bodyFieldDescriptors);
var descriptorInfo = TestHelpers.CreateDescriptorInfo(
bodyDescriptor: HelloRequest.Types.SubMessage.Descriptor,
@@ -264,7 +264,7 @@ public class UnaryServerCallHandlerTests : LoggedTest
return Task.FromResult(new HelloReply { Message = $"Hello {r.Name}" });
};
- ServiceDescriptorHelpers.TryResolveDescriptors(HelloRequest.Descriptor, "repeated_strings", out var bodyFieldDescriptors);
+ ServiceDescriptorHelpers.TryResolveDescriptors(HelloRequest.Descriptor, new[] { "repeated_strings" }, out var bodyFieldDescriptors);
var descriptorInfo = TestHelpers.CreateDescriptorInfo(
bodyDescriptor: HelloRequest.Types.SubMessage.Descriptor,
@@ -318,7 +318,7 @@ public class UnaryServerCallHandlerTests : LoggedTest
return Task.FromResult(new HelloReply { Message = $"Hello {r.Name}" });
};
- ServiceDescriptorHelpers.TryResolveDescriptors(HelloRequest.Descriptor, "sub.subfields", out var bodyFieldDescriptors);
+ ServiceDescriptorHelpers.TryResolveDescriptors(HelloRequest.Descriptor, new[] { "sub", "subfields" }, out var bodyFieldDescriptors);
var descriptorInfo = TestHelpers.CreateDescriptorInfo(
bodyDescriptor: HelloRequest.Types.SubMessage.Descriptor,
@@ -463,7 +463,7 @@ public class UnaryServerCallHandlerTests : LoggedTest
return Task.FromResult(new HelloReply());
};
- ServiceDescriptorHelpers.TryResolveDescriptors(HelloRequest.Descriptor, "repeated_strings", out var bodyFieldDescriptors);
+ ServiceDescriptorHelpers.TryResolveDescriptors(HelloRequest.Descriptor, new[] { "repeated_strings" }, out var bodyFieldDescriptors);
var descriptorInfo = TestHelpers.CreateDescriptorInfo(
bodyDescriptor: HelloRequest.Types.SubMessage.Descriptor,
@@ -503,7 +503,7 @@ public class UnaryServerCallHandlerTests : LoggedTest
invoker,
descriptorInfo: TestHelpers.CreateDescriptorInfo(bodyDescriptor: HelloRequest.Descriptor));
var httpContext = TestHelpers.CreateHttpContext();
- httpContext.Request.Body = new MemoryStream("{}"u8);
+ httpContext.Request.Body = new MemoryStream("{}"u8.ToArray());
httpContext.Request.ContentType = contentType;
// Act
await unaryServerCallHandler.HandleCallAsync(httpContext);
@@ -707,7 +707,7 @@ public class UnaryServerCallHandlerTests : LoggedTest
var httpContext = TestHelpers.CreateHttpContext();
httpContext.Request.ContentType = "application/json";
- httpContext.Request.Body = new MemoryStream("null"u8);
+ httpContext.Request.Body = new MemoryStream("null"u8.ToArray());
// Act
await unaryServerCallHandler.HandleCallAsync(httpContext);
@@ -733,7 +733,7 @@ public class UnaryServerCallHandlerTests : LoggedTest
return Task.FromResult(new HelloReply());
};
- Assert.True(ServiceDescriptorHelpers.TryResolveDescriptors(HelloRequest.Descriptor, "wrappers.float_value", out var bodyFieldDescriptors));
+ Assert.True(ServiceDescriptorHelpers.TryResolveDescriptors(HelloRequest.Descriptor, new[] { "wrappers", "float_value" }, out var bodyFieldDescriptors));
var descriptorInfo = TestHelpers.CreateDescriptorInfo(
bodyDescriptor: FloatValue.Descriptor,
@@ -795,7 +795,7 @@ public class UnaryServerCallHandlerTests : LoggedTest
return Task.FromResult(new HelloReply { Message = $"Hello {r.Name}" });
};
- ServiceDescriptorHelpers.TryResolveDescriptors(HttpBodySubField.Descriptor, "sub", out var bodyFieldDescriptors);
+ ServiceDescriptorHelpers.TryResolveDescriptors(HttpBodySubField.Descriptor, new[] { "sub" }, out var bodyFieldDescriptors);
var descriptorInfo = TestHelpers.CreateDescriptorInfo(
bodyDescriptor: HttpBody.Descriptor,
@@ -834,7 +834,7 @@ public class UnaryServerCallHandlerTests : LoggedTest
return Task.FromResult(new HelloReply { Message = $"Hello {r.Name}" });
};
- ServiceDescriptorHelpers.TryResolveDescriptors(NestedHttpBodySubField.Descriptor, "sub.sub", out var bodyFieldDescriptors);
+ ServiceDescriptorHelpers.TryResolveDescriptors(NestedHttpBodySubField.Descriptor, new[] { "sub", "sub" }, out var bodyFieldDescriptors);
var descriptorInfo = TestHelpers.CreateDescriptorInfo(
bodyDescriptor: HttpBody.Descriptor,
diff --git a/src/Grpc/JsonTranscoding/test/testassets/IntegrationTestsWebsite/Protos/greet.proto b/src/Grpc/JsonTranscoding/test/testassets/IntegrationTestsWebsite/Protos/greet.proto
index 97f8543145..3038d68f95 100644
--- a/src/Grpc/JsonTranscoding/test/testassets/IntegrationTestsWebsite/Protos/greet.proto
+++ b/src/Grpc/JsonTranscoding/test/testassets/IntegrationTestsWebsite/Protos/greet.proto
@@ -20,6 +20,26 @@ service Greeter {
body: "*"
};
}
+ rpc SayHelloComplex (HelloRequest) returns (HelloReply) {
+ option (google.api.http) = {
+ get: "/v1/greeter/{name=from/*}"
+ };
+ }
+ rpc SayHelloComplexCatchAll1 (HelloRequest) returns (HelloReply) {
+ option (google.api.http) = {
+ get: "/{name=v1/greeter/**/b}/c/d/one"
+ };
+ }
+ rpc SayHelloComplexCatchAll2 (HelloRequest) returns (HelloReply) {
+ option (google.api.http) = {
+ get: "/{name=v1/greeter/**/b}/c/d/two"
+ };
+ }
+ rpc SayHelloComplexCatchAll3 (ComplextHelloRequest) returns (HelloReply) {
+ option (google.api.http) = {
+ get: "/v1/{name.last_name}/{name.first_name=complex_greeter/**/b}/c/d/two"
+ };
+ }
}
// The request message containing the user's name.
@@ -27,6 +47,14 @@ message HelloRequest {
string name = 1;
}
+message ComplextHelloRequest {
+ NameDetail name = 1;
+ message NameDetail {
+ string first_name = 1;
+ string last_name = 2;
+ }
+}
+
// The response message containing the greetings.
message HelloReply {
string message = 1;
diff --git a/src/Hosting/Hosting/src/Internal/WebHost.cs b/src/Hosting/Hosting/src/Internal/WebHost.cs
index 44fbc1bacb..b2350b6ad0 100644
--- a/src/Hosting/Hosting/src/Internal/WebHost.cs
+++ b/src/Hosting/Hosting/src/Internal/WebHost.cs
@@ -371,7 +371,7 @@ internal sealed partial class WebHost : IWebHost, IAsyncDisposable
[LoggerMessage(4, LogLevel.Debug, "Hosting started", EventName = "Started")]
public static partial void Started(ILogger logger);
- [LoggerMessage(5, LogLevel.Debug, "Hosting shutdown", EventName = "Started")]
+ [LoggerMessage(5, LogLevel.Debug, "Hosting shutdown", EventName = "Shutdown")]
public static partial void Shutdown(ILogger logger);
[LoggerMessage(12, LogLevel.Debug, "Server shutdown exception", EventName = "ServerShutdownException")]
diff --git a/src/Hosting/test/FunctionalTests/LinkedApplicationTests.cs b/src/Hosting/test/FunctionalTests/LinkedApplicationTests.cs
index cb853ec24b..d9f48a1ee8 100644
--- a/src/Hosting/test/FunctionalTests/LinkedApplicationTests.cs
+++ b/src/Hosting/test/FunctionalTests/LinkedApplicationTests.cs
@@ -8,6 +8,7 @@ namespace Microsoft.AspNetCore.Hosting.FunctionalTests;
public class LinkedApplicationTests : LoggedTest
{
+ [Fact]
[QuarantinedTest("https://github.com/dotnet/aspnetcore-internal/issues/4030")]
public async Task LinkedApplicationWorks()
{
diff --git a/src/Http/Authentication.Core/src/AuthenticationService.cs b/src/Http/Authentication.Core/src/AuthenticationService.cs
index e949edcfac..aa35ea3060 100644
--- a/src/Http/Authentication.Core/src/AuthenticationService.cs
+++ b/src/Http/Authentication.Core/src/AuthenticationService.cs
@@ -14,6 +14,7 @@ namespace Microsoft.AspNetCore.Authentication;
public class AuthenticationService : IAuthenticationService
{
private HashSet<ClaimsPrincipal>? _transformCache;
+ private const string defaultSchemesOptionsMsg = "The default schemes can be set using either AddAuthentication(string defaultScheme) or AddAuthentication(Action<AuthenticationOptions> configureOptions) or by setting the Authentication:DefaultScheme property in configuration.";
/// <summary>
/// Constructor.
@@ -64,7 +65,7 @@ public class AuthenticationService : IAuthenticationService
scheme = defaultScheme?.Name;
if (scheme == null)
{
- throw new InvalidOperationException($"No authenticationScheme was specified, and there was no DefaultAuthenticateScheme found. The default schemes can be set using either AddAuthentication(string defaultScheme) or AddAuthentication(Action<AuthenticationOptions> configureOptions).");
+ throw new InvalidOperationException($"No authenticationScheme was specified, and there was no DefaultAuthenticateScheme found. {defaultSchemesOptionsMsg}");
}
}
@@ -112,7 +113,7 @@ public class AuthenticationService : IAuthenticationService
scheme = defaultChallengeScheme?.Name;
if (scheme == null)
{
- throw new InvalidOperationException($"No authenticationScheme was specified, and there was no DefaultChallengeScheme found. The default schemes can be set using either AddAuthentication(string defaultScheme) or AddAuthentication(Action<AuthenticationOptions> configureOptions).");
+ throw new InvalidOperationException($"No authenticationScheme was specified, and there was no DefaultChallengeScheme found. {defaultSchemesOptionsMsg}");
}
}
@@ -140,7 +141,7 @@ public class AuthenticationService : IAuthenticationService
scheme = defaultForbidScheme?.Name;
if (scheme == null)
{
- throw new InvalidOperationException($"No authenticationScheme was specified, and there was no DefaultForbidScheme found. The default schemes can be set using either AddAuthentication(string defaultScheme) or AddAuthentication(Action<AuthenticationOptions> configureOptions).");
+ throw new InvalidOperationException($"No authenticationScheme was specified, and there was no DefaultForbidScheme found. {defaultSchemesOptionsMsg}");
}
}
@@ -186,7 +187,7 @@ public class AuthenticationService : IAuthenticationService
scheme = defaultScheme?.Name;
if (scheme == null)
{
- throw new InvalidOperationException($"No authenticationScheme was specified, and there was no DefaultSignInScheme found. The default schemes can be set using either AddAuthentication(string defaultScheme) or AddAuthentication(Action<AuthenticationOptions> configureOptions).");
+ throw new InvalidOperationException($"No authenticationScheme was specified, and there was no DefaultSignInScheme found. {defaultSchemesOptionsMsg}");
}
}
@@ -220,7 +221,7 @@ public class AuthenticationService : IAuthenticationService
scheme = defaultScheme?.Name;
if (scheme == null)
{
- throw new InvalidOperationException($"No authenticationScheme was specified, and there was no DefaultSignOutScheme found. The default schemes can be set using either AddAuthentication(string defaultScheme) or AddAuthentication(Action<AuthenticationOptions> configureOptions).");
+ throw new InvalidOperationException($"No authenticationScheme was specified, and there was no DefaultSignOutScheme found. {defaultSchemesOptionsMsg}");
}
}
diff --git a/src/Http/Routing/src/EndpointMiddleware.cs b/src/Http/Routing/src/EndpointMiddleware.cs
index d04af317c0..339772e5f8 100644
--- a/src/Http/Routing/src/EndpointMiddleware.cs
+++ b/src/Http/Routing/src/EndpointMiddleware.cs
@@ -31,41 +31,44 @@ internal sealed partial class EndpointMiddleware
public Task Invoke(HttpContext httpContext)
{
var endpoint = httpContext.GetEndpoint();
- if (endpoint?.RequestDelegate != null)
+ if (endpoint is not null)
{
if (!_routeOptions.SuppressCheckForUnhandledSecurityMetadata)
{
- if (endpoint.Metadata.GetMetadata<IAuthorizeData>() != null &&
+ if (endpoint.Metadata.GetMetadata<IAuthorizeData>() is not null &&
!httpContext.Items.ContainsKey(AuthorizationMiddlewareInvokedKey))
{
ThrowMissingAuthMiddlewareException(endpoint);
}
- if (endpoint.Metadata.GetMetadata<ICorsMetadata>() != null &&
+ if (endpoint.Metadata.GetMetadata<ICorsMetadata>() is not null &&
!httpContext.Items.ContainsKey(CorsMiddlewareInvokedKey))
{
ThrowMissingCorsMiddlewareException(endpoint);
}
}
- Log.ExecutingEndpoint(_logger, endpoint);
-
- try
+ if (endpoint.RequestDelegate is not null)
{
- var requestTask = endpoint.RequestDelegate(httpContext);
- if (!requestTask.IsCompletedSuccessfully)
+ Log.ExecutingEndpoint(_logger, endpoint);
+
+ try
{
- return AwaitRequestTask(endpoint, requestTask, _logger);
+ var requestTask = endpoint.RequestDelegate(httpContext);
+ if (!requestTask.IsCompletedSuccessfully)
+ {
+ return AwaitRequestTask(endpoint, requestTask, _logger);
+ }
}
- }
- catch (Exception exception)
- {
+ catch (Exception exception)
+ {
+ Log.ExecutedEndpoint(_logger, endpoint);
+ return Task.FromException(exception);
+ }
+
Log.ExecutedEndpoint(_logger, endpoint);
- return Task.FromException(exception);
+ return Task.CompletedTask;
}
-
- Log.ExecutedEndpoint(_logger, endpoint);
- return Task.CompletedTask;
}
return _next(httpContext);
diff --git a/src/Http/Routing/test/UnitTests/EndpointMiddlewareTest.cs b/src/Http/Routing/test/UnitTests/EndpointMiddlewareTest.cs
index 16c412e710..3356a1b6ef 100644
--- a/src/Http/Routing/test/UnitTests/EndpointMiddlewareTest.cs
+++ b/src/Http/Routing/test/UnitTests/EndpointMiddlewareTest.cs
@@ -121,6 +121,35 @@ public class EndpointMiddlewareTest
}
[Fact]
+ public async Task Invoke_WithEndpointWithNullRequestDelegate_ThrowsIfAuthAttributesWereFound_ButAuthMiddlewareNotInvoked()
+ {
+ // Arrange
+ var expected = "Endpoint Test contains authorization metadata, but a middleware was not found that supports authorization." +
+ Environment.NewLine +
+ "Configure your application startup by adding app.UseAuthorization() in the application startup code. " +
+ "If there are calls to app.UseRouting() and app.UseEndpoints(...), the call to app.UseAuthorization() must go between them.";
+ var httpContext = new DefaultHttpContext
+ {
+ RequestServices = new ServiceProvider()
+ };
+
+ RequestDelegate throwIfCalled = (c) =>
+ {
+ throw new InvalidTimeZoneException("Should not be called");
+ };
+
+ httpContext.SetEndpoint(new Endpoint(requestDelegate: null, new EndpointMetadataCollection(Mock.Of<IAuthorizeData>()), "Test"));
+
+ var middleware = new EndpointMiddleware(NullLogger<EndpointMiddleware>.Instance, throwIfCalled, RouteOptions);
+
+ // Act & Assert
+ var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => middleware.Invoke(httpContext));
+
+ // Assert
+ Assert.Equal(expected, ex.Message);
+ }
+
+ [Fact]
public async Task Invoke_WithEndpoint_WorksIfAuthAttributesWereFound_AndAuthMiddlewareInvoked()
{
// Arrange
diff --git a/src/Identity/UI/src/Microsoft.AspNetCore.Identity.UI.csproj b/src/Identity/UI/src/Microsoft.AspNetCore.Identity.UI.csproj
index e5c0ef0f56..847aa53906 100644
--- a/src/Identity/UI/src/Microsoft.AspNetCore.Identity.UI.csproj
+++ b/src/Identity/UI/src/Microsoft.AspNetCore.Identity.UI.csproj
@@ -13,7 +13,6 @@
<StaticWebAssetBasePath>Identity</StaticWebAssetBasePath>
<ProvideApplicationPartFactoryAttributeTypeName>Microsoft.AspNetCore.Mvc.ApplicationParts.NullApplicationPartFactory, Microsoft.AspNetCore.Mvc.Core</ProvideApplicationPartFactoryAttributeTypeName>
- <GenerateStaticWebAssetsPackTargetsDependsOn>_GenerateIdentityUIPackItems;$(GenerateStaticWebAssetsPackTargetsDependsOn)</GenerateStaticWebAssetsPackTargetsDependsOn>
<DisableStaticWebAssetsBuildPropsFileGeneration>true</DisableStaticWebAssetsBuildPropsFileGeneration>
<StaticWebAssetsDisableProjectBuildPropsFileGeneration>true</StaticWebAssetsDisableProjectBuildPropsFileGeneration>
<StaticWebAssetsDisableProjectBuildMultiTargetingPropsFileGeneration>true</StaticWebAssetsDisableProjectBuildMultiTargetingPropsFileGeneration>
@@ -74,7 +73,7 @@
</ItemGroup>
</Target>
- <Target Name="_GenerateIdentityUIPackItems">
+ <Target Name="_GenerateIdentityUIPackItems" BeforeTargets="GenerateStaticWebAssetsPackFiles">
<ItemGroup>
<V4AssetsCandidates Include="assets\V4\**" />
<V4AssetsCandidates>
@@ -96,25 +95,19 @@
<GenerateStaticWebAsssetsPropsFile StaticWebAssets="@(V5Assets)" PackagePathPrefix="staticwebassets/V5" TargetPropsFilePath="$(IntermediateOutputPath)IdentityUI.V5.targets" />
<ComputeStaticWebAssetsTargetPaths Assets="@(V4Assets)" PathPrefix="staticwebassets/V4" AdjustPathsForPack="true">
- <Output TaskParameter="AssetsWithTargetPath" ItemName="_PackV4Asset" />
+ <Output TaskParameter="AssetsWithTargetPath" ItemName="_PackStaticWebAssetWithTargetPath" />
</ComputeStaticWebAssetsTargetPaths>
<ComputeStaticWebAssetsTargetPaths Assets="@(V5Assets)" PathPrefix="staticwebassets/V5" AdjustPathsForPack="true">
- <Output TaskParameter="AssetsWithTargetPath" ItemName="_PackV5Asset" />
+ <Output TaskParameter="AssetsWithTargetPath" ItemName="_PackStaticWebAssetWithTargetPath" />
</ComputeStaticWebAssetsTargetPaths>
<ItemGroup>
- <TfmSpecificPackageFile Include="$(IntermediateOutputPath)IdentityUI.V4.targets">
+ <StaticWebAssetPackageFile Include="$(IntermediateOutputPath)IdentityUI.V4.targets">
<PackagePath>build\Microsoft.AspNetCore.StaticWebAssets.V4.targets</PackagePath>
- </TfmSpecificPackageFile>
- <TfmSpecificPackageFile Include="%(_PackV4Asset.Identity)">
- <PackagePath>%(_PackV4Asset.TargetPath)</PackagePath>
- </TfmSpecificPackageFile>
- <TfmSpecificPackageFile Include="$(IntermediateOutputPath)IdentityUI.V5.targets">
+ </StaticWebAssetPackageFile>
+ <StaticWebAssetPackageFile Include="$(IntermediateOutputPath)IdentityUI.V5.targets">
<PackagePath>build\Microsoft.AspNetCore.StaticWebAssets.V5.targets</PackagePath>
- </TfmSpecificPackageFile>
- <TfmSpecificPackageFile Include="%(_PackV5Asset.Identity)">
- <PackagePath>%(_PackV5Asset.TargetPath)</PackagePath>
- </TfmSpecificPackageFile>
+ </StaticWebAssetPackageFile>
</ItemGroup>
</Target>
</Project>
diff --git a/src/Installers/Windows/AspNetCoreModule-Setup/CustomAction/aspnetcoreCA.vcxproj b/src/Installers/Windows/AspNetCoreModule-Setup/CustomAction/aspnetcoreCA.vcxproj
index 677c5fcfae..3672d1c285 100644
--- a/src/Installers/Windows/AspNetCoreModule-Setup/CustomAction/aspnetcoreCA.vcxproj
+++ b/src/Installers/Windows/AspNetCoreModule-Setup/CustomAction/aspnetcoreCA.vcxproj
@@ -49,16 +49,11 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup>
<ConfigurationType>DynamicLibrary</ConfigurationType>
- <PlatformToolsetVersion>v142</PlatformToolsetVersion>
- <PlatformToolset>$(PlatformToolsetVersion)</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
- <PropertyGroup Label="Configuration">
- <VCToolsVersion>14.29.30133</VCToolsVersion>
- </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<PropertyGroup>
<AdditionalIncludeDirectories>$(IIS-Common)version;$(IIS-Common)Include;$(IIS-Setup)iisca\lib;$(WIX)sdk\$(WixPlatformToolset)\inc;$(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
diff --git a/src/Installers/Windows/AspNetCoreModule-Setup/IIS-Setup/IIS-Common/lib/IISSetup.CommonLib.vcxproj b/src/Installers/Windows/AspNetCoreModule-Setup/IIS-Setup/IIS-Common/lib/IISSetup.CommonLib.vcxproj
index 5721139576..d19c8d3f97 100644
--- a/src/Installers/Windows/AspNetCoreModule-Setup/IIS-Setup/IIS-Common/lib/IISSetup.CommonLib.vcxproj
+++ b/src/Installers/Windows/AspNetCoreModule-Setup/IIS-Setup/IIS-Common/lib/IISSetup.CommonLib.vcxproj
@@ -34,8 +34,6 @@
<Keyword>Win32Proj</Keyword>
<RootNamespace>Lib</RootNamespace>
<ProjectName>CommonLib</ProjectName>
- <PlatformToolsetVersion Condition=" '$(VisualStudioVersion)' == '17.0' ">v143</PlatformToolsetVersion>
- <PlatformToolsetVersion Condition=" '$(PlatformToolsetVersion)' == '' ">v142</PlatformToolsetVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup>
diff --git a/src/Installers/Windows/AspNetCoreModule-Setup/IIS-Setup/iisca/lib/iisca.vcxproj b/src/Installers/Windows/AspNetCoreModule-Setup/IIS-Setup/iisca/lib/iisca.vcxproj
index 7b3ee0ebbb..4d3383bd08 100644
--- a/src/Installers/Windows/AspNetCoreModule-Setup/IIS-Setup/iisca/lib/iisca.vcxproj
+++ b/src/Installers/Windows/AspNetCoreModule-Setup/IIS-Setup/iisca/lib/iisca.vcxproj
@@ -76,8 +76,6 @@
<ProjectGuid>{7324770c-0871-4d73-be3d-5e2f3e9e1b1e}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>iisca</RootNamespace>
- <WindowsTargetPlatformVersion Condition=" '$(WindowsTargetPlatformVersion)' == '' ">10.0.18362.0</WindowsTargetPlatformVersion>
- <PlatformToolsetVersion>v142</PlatformToolsetVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup>
@@ -86,9 +84,6 @@
<PlatformToolset>$(PlatformToolsetVersion)</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
- <PropertyGroup Label="Configuration">
- <VCToolsVersion>14.29.30133</VCToolsVersion>
- </PropertyGroup>
<PropertyGroup>
<AdditionalIncludeDirectories>$(IIS-Common)version;$(IIS-Common)Include;$(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PrecompiledHeader>Create</PrecompiledHeader>
diff --git a/src/Installers/Windows/AspNetCoreModule-Setup/build/settings/common.props b/src/Installers/Windows/AspNetCoreModule-Setup/build/settings/common.props
index 43b3f6b7e4..081bc01fd5 100644
--- a/src/Installers/Windows/AspNetCoreModule-Setup/build/settings/common.props
+++ b/src/Installers/Windows/AspNetCoreModule-Setup/build/settings/common.props
@@ -14,8 +14,6 @@
<!-- General properties -->
<PropertyGroup>
- <IisOobWinSdkVersion Condition="'$(IisOobWinSdkVersion)' == ''">10.0.18362.0</IisOobWinSdkVersion>
- <WindowsTargetPlatformVersion Condition="'$(WindowsTargetPlatformVersion)' == ''">$(IisOobWinSdkVersion)</WindowsTargetPlatformVersion>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
diff --git a/src/Middleware/CORS/test/testassets/CorsMiddlewareWebSite/Properties/launchSettings.json b/src/Middleware/CORS/test/testassets/CorsMiddlewareWebSite/Properties/launchSettings.json
index 485cac49a9..3528609bc2 100644
--- a/src/Middleware/CORS/test/testassets/CorsMiddlewareWebSite/Properties/launchSettings.json
+++ b/src/Middleware/CORS/test/testassets/CorsMiddlewareWebSite/Properties/launchSettings.json
@@ -9,4 +9,4 @@
"applicationUrl": "https://localhost:61226;http://localhost:61227"
}
}
-} \ No newline at end of file
+}
diff --git a/src/Middleware/Diagnostics/test/testassets/DatabaseErrorPageSample/Properties/launchSettings.json b/src/Middleware/Diagnostics/test/testassets/DatabaseErrorPageSample/Properties/launchSettings.json
index 3fbc5274ba..b2929adc68 100644
--- a/src/Middleware/Diagnostics/test/testassets/DatabaseErrorPageSample/Properties/launchSettings.json
+++ b/src/Middleware/Diagnostics/test/testassets/DatabaseErrorPageSample/Properties/launchSettings.json
@@ -9,4 +9,4 @@
"applicationUrl": "https://localhost:61218;http://localhost:61219"
}
}
-} \ No newline at end of file
+}
diff --git a/src/Middleware/Diagnostics/test/testassets/DeveloperExceptionPageSample/Properties/launchSettings.json b/src/Middleware/Diagnostics/test/testassets/DeveloperExceptionPageSample/Properties/launchSettings.json
index 8f4f5d821b..919228c334 100644
--- a/src/Middleware/Diagnostics/test/testassets/DeveloperExceptionPageSample/Properties/launchSettings.json
+++ b/src/Middleware/Diagnostics/test/testassets/DeveloperExceptionPageSample/Properties/launchSettings.json
@@ -9,4 +9,4 @@
"applicationUrl": "https://localhost:61228;http://localhost:61229"
}
}
-} \ No newline at end of file
+}
diff --git a/src/Middleware/Diagnostics/test/testassets/ExceptionHandlerSample/Properties/launchSettings.json b/src/Middleware/Diagnostics/test/testassets/ExceptionHandlerSample/Properties/launchSettings.json
index c69c9dd556..86c5743f7e 100644
--- a/src/Middleware/Diagnostics/test/testassets/ExceptionHandlerSample/Properties/launchSettings.json
+++ b/src/Middleware/Diagnostics/test/testassets/ExceptionHandlerSample/Properties/launchSettings.json
@@ -9,4 +9,4 @@
"applicationUrl": "https://localhost:61220;http://localhost:61221"
}
}
-} \ No newline at end of file
+}
diff --git a/src/Middleware/Diagnostics/test/testassets/StatusCodePagesSample/Properties/launchSettings.json b/src/Middleware/Diagnostics/test/testassets/StatusCodePagesSample/Properties/launchSettings.json
index f27a4afd71..97b0bd2ba9 100644
--- a/src/Middleware/Diagnostics/test/testassets/StatusCodePagesSample/Properties/launchSettings.json
+++ b/src/Middleware/Diagnostics/test/testassets/StatusCodePagesSample/Properties/launchSettings.json
@@ -9,4 +9,4 @@
"applicationUrl": "https://localhost:61230;http://localhost:61231"
}
}
-} \ No newline at end of file
+}
diff --git a/src/Middleware/Diagnostics/test/testassets/WelcomePageSample/Properties/launchSettings.json b/src/Middleware/Diagnostics/test/testassets/WelcomePageSample/Properties/launchSettings.json
index b82719da65..10feca9e72 100644
--- a/src/Middleware/Diagnostics/test/testassets/WelcomePageSample/Properties/launchSettings.json
+++ b/src/Middleware/Diagnostics/test/testassets/WelcomePageSample/Properties/launchSettings.json
@@ -9,4 +9,4 @@
"applicationUrl": "https://localhost:61223;http://localhost:61225"
}
}
-} \ No newline at end of file
+}
diff --git a/src/Middleware/HttpLogging/src/W3CLoggingMiddleware.cs b/src/Middleware/HttpLogging/src/W3CLoggingMiddleware.cs
index de9f9334d2..90fe835629 100644
--- a/src/Middleware/HttpLogging/src/W3CLoggingMiddleware.cs
+++ b/src/Middleware/HttpLogging/src/W3CLoggingMiddleware.cs
@@ -82,10 +82,8 @@ internal sealed class W3CLoggingMiddleware
{
var options = _options.CurrentValue;
- var additionalHeadersLength = _additionalRequestHeaders.Count;
-
var elements = new string[_fieldsLength];
- var additionalHeaderElements = new string[additionalHeadersLength];
+ var additionalHeaderElements = new string[_additionalRequestHeaders.Count];
// Whether any of the requested fields actually had content
bool shouldLog = false;
@@ -154,11 +152,9 @@ internal sealed class W3CLoggingMiddleware
if ((W3CLoggingFields.RequestHeaders & options.LoggingFields) != W3CLoggingFields.None)
{
- var headers = request.Headers;
-
if (options.LoggingFields.HasFlag(W3CLoggingFields.Host))
{
- if (headers.TryGetValue(HeaderNames.Host, out var host))
+ if (request.Headers.TryGetValue(HeaderNames.Host, out var host))
{
shouldLog |= AddToList(elements, _hostIndex, host.ToString());
}
@@ -166,7 +162,7 @@ internal sealed class W3CLoggingMiddleware
if (options.LoggingFields.HasFlag(W3CLoggingFields.Referer))
{
- if (headers.TryGetValue(HeaderNames.Referer, out var referer))
+ if (request.Headers.TryGetValue(HeaderNames.Referer, out var referer))
{
shouldLog |= AddToList(elements, _refererIndex, referer.ToString());
}
@@ -174,7 +170,7 @@ internal sealed class W3CLoggingMiddleware
if (options.LoggingFields.HasFlag(W3CLoggingFields.UserAgent))
{
- if (headers.TryGetValue(HeaderNames.UserAgent, out var agent))
+ if (request.Headers.TryGetValue(HeaderNames.UserAgent, out var agent))
{
shouldLog |= AddToList(elements, _userAgentIndex, agent.ToString());
}
@@ -182,23 +178,23 @@ internal sealed class W3CLoggingMiddleware
if (options.LoggingFields.HasFlag(W3CLoggingFields.Cookie))
{
- if (headers.TryGetValue(HeaderNames.Cookie, out var cookie))
+ if (request.Headers.TryGetValue(HeaderNames.Cookie, out var cookie))
{
shouldLog |= AddToList(elements, _cookieIndex, cookie.ToString());
}
}
+ }
+ }
- if (_additionalRequestHeaders.Count != 0)
- {
- var additionalRequestHeaders = _additionalRequestHeaders.ToList();
+ if (_additionalRequestHeaders.Count != 0)
+ {
+ var additionalRequestHeaders = _additionalRequestHeaders.ToList();
- for (var i = 0; i < additionalHeadersLength; i++)
- {
- if (headers.TryGetValue(additionalRequestHeaders[i], out var headerValue))
- {
- shouldLog |= AddToList(additionalHeaderElements, i, headerValue.ToString());
- }
- }
+ for (var i = 0; i < additionalRequestHeaders.Count; i++)
+ {
+ if (context.Request.Headers.TryGetValue(additionalRequestHeaders[i], out var headerValue))
+ {
+ shouldLog |= AddToList(additionalHeaderElements, i, headerValue.ToString());
}
}
}
diff --git a/src/Middleware/HttpLogging/test/W3CLoggingMiddlewareTests.cs b/src/Middleware/HttpLogging/test/W3CLoggingMiddlewareTests.cs
index d9be128515..988631cd96 100644
--- a/src/Middleware/HttpLogging/test/W3CLoggingMiddlewareTests.cs
+++ b/src/Middleware/HttpLogging/test/W3CLoggingMiddlewareTests.cs
@@ -148,6 +148,42 @@ public class W3CLoggingMiddlewareTests
}
[Fact]
+ public async Task LogsAdditionalRequestHeaders_WithNoOtherOptions()
+ {
+ var options = CreateOptionsAccessor();
+ options.CurrentValue.AdditionalRequestHeaders.Add("x-forwarded-for");
+ options.CurrentValue.LoggingFields = W3CLoggingFields.None;
+
+ var logger = Helpers.CreateTestW3CLogger(options);
+
+ var middleware = new W3CLoggingMiddleware(
+ c =>
+ {
+ c.Response.StatusCode = 200;
+ return Task.CompletedTask;
+ },
+ options,
+ logger);
+
+ var httpContext = new DefaultHttpContext();
+ httpContext.Request.Protocol = "HTTP/1.0";
+ httpContext.Request.Headers["Cookie"] = "Snickerdoodle";
+ httpContext.Request.Headers["x-forwarded-for"] = "1.3.3.7, 2001:db8:85a3:8d3:1319:8a2e:370:7348";
+ httpContext.Response.StatusCode = 200;
+
+ var now = DateTime.UtcNow;
+ await middleware.Invoke(httpContext);
+ await logger.Processor.WaitForWrites(4).DefaultTimeout();
+
+ var lines = logger.Processor.Lines;
+ Assert.Equal("#Version: 1.0", lines[0]);
+
+ Assert.StartsWith("#Start-Date: ", lines[1]);
+ Assert.Equal("#Fields: cs(x-forwarded-for)", lines[2]);
+ Assert.Equal("1.3.3.7,+2001:db8:85a3:8d3:1319:8a2e:370:7348", lines[3]);
+ }
+
+ [Fact]
public async Task OmitsDuplicateAdditionalRequestHeaders()
{
var options = CreateOptionsAccessor();
diff --git a/src/Middleware/Localization/testassets/LocalizationWebsite/Properties/launchSettings.json b/src/Middleware/Localization/testassets/LocalizationWebsite/Properties/launchSettings.json
index 3b71d90077..7494427a69 100644
--- a/src/Middleware/Localization/testassets/LocalizationWebsite/Properties/launchSettings.json
+++ b/src/Middleware/Localization/testassets/LocalizationWebsite/Properties/launchSettings.json
@@ -9,4 +9,4 @@
"applicationUrl": "https://localhost:61222;http://localhost:61224"
}
}
-} \ No newline at end of file
+}
diff --git a/src/Middleware/Middleware.slnf b/src/Middleware/Middleware.slnf
index 6033a2555e..c75854876f 100644
--- a/src/Middleware/Middleware.slnf
+++ b/src/Middleware/Middleware.slnf
@@ -76,6 +76,9 @@
"src\\Middleware\\MiddlewareAnalysis\\samples\\MiddlewareAnalysisSample\\MiddlewareAnalysisSample.csproj",
"src\\Middleware\\MiddlewareAnalysis\\src\\Microsoft.AspNetCore.MiddlewareAnalysis.csproj",
"src\\Middleware\\MiddlewareAnalysis\\test\\Microsoft.AspNetCore.MiddlewareAnalysis.Tests.csproj",
+ "src\\Middleware\\OutputCaching\\samples\\OutputCachingSample\\OutputCachingSample.csproj",
+ "src\\Middleware\\OutputCaching\\src\\Microsoft.AspNetCore.OutputCaching.csproj",
+ "src\\Middleware\\OutputCaching\\test\\Microsoft.AspNetCore.OutputCaching.Tests.csproj",
"src\\Middleware\\RateLimiting\\src\\Microsoft.AspNetCore.RateLimiting.csproj",
"src\\Middleware\\RateLimiting\\test\\Microsoft.AspNetCore.RateLimiting.Tests.csproj",
"src\\Middleware\\RequestDecompression\\perf\\Microbenchmarks\\Microsoft.AspNetCore.RequestDecompression.Microbenchmarks.csproj",
diff --git a/src/Middleware/OutputCaching/OutputCaching.slnf b/src/Middleware/OutputCaching/OutputCaching.slnf
new file mode 100644
index 0000000000..7d85ca7205
--- /dev/null
+++ b/src/Middleware/OutputCaching/OutputCaching.slnf
@@ -0,0 +1,10 @@
+{
+ "solution": {
+ "path": "..\\..\\..\\AspNetCore.sln",
+ "projects": [
+ "src\\Middleware\\OutputCaching\\samples\\OutputCachingSample\\OutputCachingSample.csproj",
+ "src\\Middleware\\OutputCaching\\src\\Microsoft.AspNetCore.OutputCaching.csproj",
+ "src\\Middleware\\OutputCaching\\test\\Microsoft.AspNetCore.OutputCaching.Tests.csproj"
+ ]
+ }
+} \ No newline at end of file
diff --git a/src/Middleware/OutputCaching/samples/OutputCachingSample/.vscode/launch.json b/src/Middleware/OutputCaching/samples/OutputCachingSample/.vscode/launch.json
new file mode 100644
index 0000000000..93e3163e87
--- /dev/null
+++ b/src/Middleware/OutputCaching/samples/OutputCachingSample/.vscode/launch.json
@@ -0,0 +1,35 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ // Use IntelliSense to find out which attributes exist for C# debugging
+ // Use hover for the description of the existing attributes
+ // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
+ "name": ".NET Core Launch (web)",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ // If you have changed target frameworks, make sure to update the program path.
+ "program": "${workspaceFolder}/bin/Debug/net7.0/OutputCachingSample.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}",
+ "stopAtEntry": false,
+ // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser
+ "serverReadyAction": {
+ "action": "openExternally",
+ "pattern": "\\bNow listening on:\\s+(https?://\\S+)"
+ },
+ "env": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "sourceFileMap": {
+ "/Views": "${workspaceFolder}/Views"
+ }
+ },
+ {
+ "name": ".NET Core Attach",
+ "type": "coreclr",
+ "request": "attach"
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/Middleware/OutputCaching/samples/OutputCachingSample/.vscode/tasks.json b/src/Middleware/OutputCaching/samples/OutputCachingSample/.vscode/tasks.json
new file mode 100644
index 0000000000..5b41ee7a09
--- /dev/null
+++ b/src/Middleware/OutputCaching/samples/OutputCachingSample/.vscode/tasks.json
@@ -0,0 +1,41 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "build",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "build",
+ "${workspaceFolder}/OutputCachingSample.csproj",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "publish",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "publish",
+ "${workspaceFolder}/OutputCachingSample.csproj",
+ "/property:GenerateFullPaths=true",
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "problemMatcher": "$msCompile"
+ },
+ {
+ "label": "watch",
+ "command": "dotnet",
+ "type": "process",
+ "args": [
+ "watch",
+ "run",
+ "--project",
+ "${workspaceFolder}/OutputCachingSample.csproj"
+ ],
+ "problemMatcher": "$msCompile"
+ }
+ ]
+} \ No newline at end of file
diff --git a/src/Middleware/OutputCaching/samples/OutputCachingSample/Gravatar.cs b/src/Middleware/OutputCaching/samples/OutputCachingSample/Gravatar.cs
new file mode 100644
index 0000000000..08eccafbb9
--- /dev/null
+++ b/src/Middleware/OutputCaching/samples/OutputCachingSample/Gravatar.cs
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+public static class Gravatar
+{
+ public static async Task WriteGravatar(HttpContext context)
+ {
+ const string type = "monsterid"; // identicon, monsterid, wavatar
+ const int size = 200;
+ var hash = Guid.NewGuid().ToString("n");
+
+ context.Response.StatusCode = 200;
+ context.Response.ContentType = "text/html";
+ await context.Response.WriteAsync($"<img src=\"https://www.gravatar.com/avatar/{hash}?s={size}&d={type}\"/>");
+ await context.Response.WriteAsync($"<pre>Generated at {DateTime.Now:hh:mm:ss.ff}</pre>");
+ }
+}
diff --git a/src/Middleware/OutputCaching/samples/OutputCachingSample/OutputCachingSample.csproj b/src/Middleware/OutputCaching/samples/OutputCachingSample/OutputCachingSample.csproj
new file mode 100644
index 0000000000..8e76982e4d
--- /dev/null
+++ b/src/Middleware/OutputCaching/samples/OutputCachingSample/OutputCachingSample.csproj
@@ -0,0 +1,14 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+
+ <PropertyGroup>
+ <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
+ <ImplicitUsings>enable</ImplicitUsings>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Reference Include="Microsoft.AspNetCore" />
+ <!-- Mvc.Core is referenced only for its attributes -->
+ <Reference Include="Microsoft.AspNetCore.Mvc.Core" />
+ </ItemGroup>
+
+</Project> \ No newline at end of file
diff --git a/src/Middleware/OutputCaching/samples/OutputCachingSample/Properties/launchSettings.json b/src/Middleware/OutputCaching/samples/OutputCachingSample/Properties/launchSettings.json
new file mode 100644
index 0000000000..b544fed195
--- /dev/null
+++ b/src/Middleware/OutputCaching/samples/OutputCachingSample/Properties/launchSettings.json
@@ -0,0 +1,27 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:54270/",
+ "sslPort": 44398
+ }
+ },
+ "profiles": {
+ "OutputCachingSample": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "https://localhost:5001;http://localhost:5000"
+ },
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/src/Middleware/OutputCaching/samples/OutputCachingSample/README.md b/src/Middleware/OutputCaching/samples/OutputCachingSample/README.md
new file mode 100644
index 0000000000..f88770eaaf
--- /dev/null
+++ b/src/Middleware/OutputCaching/samples/OutputCachingSample/README.md
@@ -0,0 +1,6 @@
+ASP.NET Core Output Caching Sample
+===================================
+
+This sample illustrates the usage of ASP.NET Core output caching middleware. The application sends a `Hello World!` message and the current time. A different cache entry is created for each variation of the query string.
+
+When running the sample, a response will be served from cache when possible and will be stored for up to 10 seconds.
diff --git a/src/Middleware/OutputCaching/samples/OutputCachingSample/Startup.cs b/src/Middleware/OutputCaching/samples/OutputCachingSample/Startup.cs
new file mode 100644
index 0000000000..ca91981bc6
--- /dev/null
+++ b/src/Middleware/OutputCaching/samples/OutputCachingSample/Startup.cs
@@ -0,0 +1,75 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Globalization;
+using Microsoft.AspNetCore.OutputCaching;
+
+var builder = WebApplication.CreateBuilder(args);
+
+builder.Services.AddOutputCache(options =>
+{
+ // Define policies for all requests which are not configured per endpoint or per request
+ options.AddBasePolicy(builder => builder.With(c => c.HttpContext.Request.Path.StartsWithSegments("/js")).Expire(TimeSpan.FromDays(1)));
+ options.AddBasePolicy(builder => builder.With(c => c.HttpContext.Request.Path.StartsWithSegments("/js")).NoCache());
+
+ options.AddPolicy("NoCache", b => b.NoCache());
+});
+
+var app = builder.Build();
+
+app.UseOutputCache();
+
+app.MapGet("/", Gravatar.WriteGravatar);
+
+app.MapGet("/cached", Gravatar.WriteGravatar).CacheOutput();
+
+app.MapGet("/nocache", Gravatar.WriteGravatar).CacheOutput(x => x.NoCache());
+
+app.MapGet("/profile", Gravatar.WriteGravatar).CacheOutput("NoCache");
+
+app.MapGet("/attribute", [OutputCache(PolicyName = "NoCache")] () => Gravatar.WriteGravatar);
+
+var blog = app.MapGroup("blog").CacheOutput(x => x.Tag("blog"));
+blog.MapGet("/", Gravatar.WriteGravatar);
+blog.MapGet("/post/{id}", Gravatar.WriteGravatar).CacheOutput(x => x.Tag("blog", "byid")); // Calling CacheOutput() here overwrites the group's policy
+
+app.MapPost("/purge/{tag}", async (IOutputCacheStore cache, string tag) =>
+{
+ // POST such that the endpoint is not cached itself
+
+ await cache.EvictByTagAsync(tag, default);
+});
+
+// Cached entries will vary by culture, but any other additional query is ignored and returns the same cached content
+app.MapGet("/query", Gravatar.WriteGravatar).CacheOutput(p => p.VaryByQuery("culture"));
+
+app.MapGet("/vary", Gravatar.WriteGravatar).CacheOutput(c => c.VaryByValue((context) => new KeyValuePair<string, string>("time", (DateTime.Now.Second % 2).ToString(CultureInfo.InvariantCulture))));
+
+long requests = 0;
+
+// Locking is enabled by default
+app.MapGet("/lock", async (context) =>
+{
+ await Task.Delay(1000);
+ await context.Response.WriteAsync($"<pre>{requests++}</pre>");
+}).CacheOutput(p => p.AllowLocking(false).Expire(TimeSpan.FromMilliseconds(1)));
+
+// Etag
+app.MapGet("/etag", async (context) =>
+{
+ // If the client sends an If-None-Match header with the etag value, the server
+ // returns 304 if the cache entry is fresh instead of the full response
+
+ var etag = $"\"{Guid.NewGuid():n}\"";
+ context.Response.Headers.ETag = etag;
+
+ await Gravatar.WriteGravatar(context);
+
+ var cacheContext = context.Features.Get<IOutputCacheFeature>()?.Context;
+
+}).CacheOutput();
+
+// When the request header If-Modified-Since is provided, return 304 if the cached entry is older
+app.MapGet("/ims", Gravatar.WriteGravatar).CacheOutput();
+
+await app.RunAsync();
diff --git a/src/Middleware/OutputCaching/samples/OutputCachingSample/appsettings.Development.json b/src/Middleware/OutputCaching/samples/OutputCachingSample/appsettings.Development.json
new file mode 100644
index 0000000000..4e8090a0ee
--- /dev/null
+++ b/src/Middleware/OutputCaching/samples/OutputCachingSample/appsettings.Development.json
@@ -0,0 +1,10 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information",
+ "Microsoft.AspNetCore.OutputCaching": "Debug"
+ }
+ }
+}
diff --git a/src/Middleware/OutputCaching/samples/OutputCachingSample/appsettings.json b/src/Middleware/OutputCaching/samples/OutputCachingSample/appsettings.json
new file mode 100644
index 0000000000..d9d9a9bff6
--- /dev/null
+++ b/src/Middleware/OutputCaching/samples/OutputCachingSample/appsettings.json
@@ -0,0 +1,10 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/src/Middleware/OutputCaching/src/CacheEntryHelpers.cs b/src/Middleware/OutputCaching/src/CacheEntryHelpers.cs
new file mode 100644
index 0000000000..e7fdcf1bcb
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/CacheEntryHelpers.cs
@@ -0,0 +1,59 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.Extensions.Primitives;
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+internal static class CacheEntryHelpers
+{
+ internal static long EstimateCachedResponseSize(OutputCacheEntry cachedResponse)
+ {
+ if (cachedResponse == null)
+ {
+ return 0L;
+ }
+
+ checked
+ {
+ // StatusCode
+ long size = sizeof(int);
+
+ // Headers
+ if (cachedResponse.Headers != null)
+ {
+ foreach (var item in cachedResponse.Headers)
+ {
+ size += (item.Key.Length * sizeof(char)) + EstimateStringValuesSize(item.Value);
+ }
+ }
+
+ // Body
+ if (cachedResponse.Body != null)
+ {
+ size += cachedResponse.Body.Length;
+ }
+
+ return size;
+ }
+ }
+
+ internal static long EstimateStringValuesSize(StringValues stringValues)
+ {
+ checked
+ {
+ var size = 0L;
+
+ for (var i = 0; i < stringValues.Count; i++)
+ {
+ var stringValue = stringValues[i];
+ if (!string.IsNullOrEmpty(stringValue))
+ {
+ size += stringValue.Length * sizeof(char);
+ }
+ }
+
+ return size;
+ }
+ }
+}
diff --git a/src/Middleware/OutputCaching/src/CacheVaryByRules.cs b/src/Middleware/OutputCaching/src/CacheVaryByRules.cs
new file mode 100644
index 0000000000..28caea7ace
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/CacheVaryByRules.cs
@@ -0,0 +1,37 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Linq;
+using Microsoft.Extensions.Primitives;
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+/// <summary>
+/// Represents vary-by rules.
+/// </summary>
+public sealed class CacheVaryByRules
+{
+ private Dictionary<string, string>? _varyByCustom;
+
+ internal bool HasVaryByCustom => _varyByCustom != null && _varyByCustom.Any();
+
+ /// <summary>
+ /// Gets a dictionary of key-pair values to vary the cache by.
+ /// </summary>
+ public IDictionary<string, string> VaryByCustom => _varyByCustom ??= new();
+
+ /// <summary>
+ /// Gets or sets the list of headers to vary by.
+ /// </summary>
+ public StringValues Headers { get; set; }
+
+ /// <summary>
+ /// Gets or sets the list of query string keys to vary by.
+ /// </summary>
+ public StringValues QueryKeys { get; set; }
+
+ /// <summary>
+ /// Gets or sets a prefix to vary by.
+ /// </summary>
+ public StringValues VaryByPrefix { get; set; }
+}
diff --git a/src/Middleware/OutputCaching/src/CachedResponseBody.cs b/src/Middleware/OutputCaching/src/CachedResponseBody.cs
new file mode 100644
index 0000000000..dd46a5ece7
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/CachedResponseBody.cs
@@ -0,0 +1,53 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.IO.Pipelines;
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+/// <summary>
+/// Represents a cached response body.
+/// </summary>
+internal sealed class CachedResponseBody
+{
+ /// <summary>
+ /// Creates a new <see cref="CachedResponseBody"/> instance.
+ /// </summary>
+ /// <param name="segments">The segments.</param>
+ /// <param name="length">The length.</param>
+ public CachedResponseBody(List<byte[]> segments, long length)
+ {
+ ArgumentNullException.ThrowIfNull(segments);
+
+ Segments = segments;
+ Length = length;
+ }
+
+ /// <summary>
+ /// Gets the segments of the body.
+ /// </summary>
+ public List<byte[]> Segments { get; }
+
+ /// <summary>
+ /// Gets the length of the body.
+ /// </summary>
+ public long Length { get; }
+
+ /// <summary>
+ /// Copies the body to a <see cref="PipeWriter"/>.
+ /// </summary>
+ /// <param name="destination">The destination</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns></returns>
+ public async Task CopyToAsync(PipeWriter destination, CancellationToken cancellationToken)
+ {
+ ArgumentNullException.ThrowIfNull(destination);
+
+ foreach (var segment in Segments)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ await destination.WriteAsync(segment, cancellationToken);
+ }
+ }
+}
diff --git a/src/Middleware/OutputCaching/src/DispatcherExtensions.cs b/src/Middleware/OutputCaching/src/DispatcherExtensions.cs
new file mode 100644
index 0000000000..173b7147d1
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/DispatcherExtensions.cs
@@ -0,0 +1,89 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Concurrent;
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+internal sealed class WorkDispatcher<TKey, TValue> where TKey : notnull
+{
+ private readonly ConcurrentDictionary<TKey, Task<TValue?>> _workers = new();
+
+ public async Task<TValue?> ScheduleAsync(TKey key, Func<TKey, Task<TValue?>> valueFactory)
+ {
+ ArgumentNullException.ThrowIfNull(key);
+
+ while (true)
+ {
+ if (_workers.TryGetValue(key, out var task))
+ {
+ return await task;
+ }
+
+ // This is the task that we'll return to all waiters. We'll complete it when the factory is complete
+ var tcs = new TaskCompletionSource<TValue?>(TaskCreationOptions.RunContinuationsAsynchronously);
+
+ if (_workers.TryAdd(key, tcs.Task))
+ {
+ try
+ {
+ var value = await valueFactory(key);
+ tcs.TrySetResult(value);
+ return await tcs.Task;
+ }
+ catch (Exception ex)
+ {
+ // Make sure all waiters see the exception
+ tcs.SetException(ex);
+
+ throw;
+ }
+ finally
+ {
+ // We remove the entry if the factory failed so it's not a permanent failure
+ // and future gets can retry (this could be a pluggable policy)
+ _workers.TryRemove(key, out _);
+ }
+ }
+ }
+ }
+
+ public async Task<TValue?> ScheduleAsync<TState>(TKey key, TState state, Func<TKey, TState, Task<TValue?>> valueFactory)
+ {
+ ArgumentNullException.ThrowIfNull(key);
+
+ while (true)
+ {
+ if (_workers.TryGetValue(key, out var task))
+ {
+ return await task;
+ }
+
+ // This is the task that we'll return to all waiters. We'll complete it when the factory is complete
+ var tcs = new TaskCompletionSource<TValue?>(TaskCreationOptions.RunContinuationsAsynchronously);
+
+ if (_workers.TryAdd(key, tcs.Task))
+ {
+ try
+ {
+ var value = await valueFactory(key, state);
+ tcs.TrySetResult(value);
+ return await tcs.Task;
+ }
+ catch (Exception ex)
+ {
+ // Make sure all waiters see the exception
+ tcs.SetException(ex);
+
+ throw;
+ }
+ finally
+ {
+ // We remove the entry if the factory failed so it's not a permanent failure
+ // and future gets can retry (this could be a pluggable policy)
+ _workers.TryRemove(key, out _);
+ }
+ }
+ }
+ }
+}
diff --git a/src/Middleware/OutputCaching/src/IOutputCacheFeature.cs b/src/Middleware/OutputCaching/src/IOutputCacheFeature.cs
new file mode 100644
index 0000000000..db651bd7c0
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/IOutputCacheFeature.cs
@@ -0,0 +1,15 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+/// <summary>
+/// A feature for configuring additional output cache options on the HTTP response.
+/// </summary>
+public interface IOutputCacheFeature
+{
+ /// <summary>
+ /// Gets the cache context.
+ /// </summary>
+ OutputCacheContext Context { get; }
+}
diff --git a/src/Middleware/OutputCaching/src/IOutputCacheKeyProvider.cs b/src/Middleware/OutputCaching/src/IOutputCacheKeyProvider.cs
new file mode 100644
index 0000000000..e86cf6c679
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/IOutputCacheKeyProvider.cs
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+internal interface IOutputCacheKeyProvider
+{
+ /// <summary>
+ /// Create a key for storing cached responses.
+ /// </summary>
+ /// <param name="context">The <see cref="OutputCacheContext"/>.</param>
+ /// <returns>The created key.</returns>
+ string CreateStorageKey(OutputCacheContext context);
+}
diff --git a/src/Middleware/OutputCaching/src/IOutputCachePolicy.cs b/src/Middleware/OutputCaching/src/IOutputCachePolicy.cs
new file mode 100644
index 0000000000..aae03b76db
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/IOutputCachePolicy.cs
@@ -0,0 +1,30 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+/// <summary>
+/// An implementation of this interface can update how the current request is cached.
+/// </summary>
+public interface IOutputCachePolicy
+{
+ /// <summary>
+ /// Updates the <see cref="OutputCacheContext"/> before the cache middleware is invoked.
+ /// At that point the cache middleware can still be enabled or disabled for the request.
+ /// </summary>
+ /// <param name="context">The current request's cache context.</param>
+ ValueTask CacheRequestAsync(OutputCacheContext context, CancellationToken cancellation);
+
+ /// <summary>
+ /// Updates the <see cref="OutputCacheContext"/> before the cached response is used.
+ /// At that point the freshness of the cached response can be updated.
+ /// </summary>
+ /// <param name="context">The current request's cache context.</param>
+ ValueTask ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellation);
+
+ /// <summary>
+ /// Updates the <see cref="OutputCacheContext"/> before the response is served and can be cached.
+ /// At that point cacheability of the response can be updated.
+ /// </summary>
+ ValueTask ServeResponseAsync(OutputCacheContext context, CancellationToken cancellation);
+}
diff --git a/src/Middleware/OutputCaching/src/IOutputCacheStore.cs b/src/Middleware/OutputCaching/src/IOutputCacheStore.cs
new file mode 100644
index 0000000000..ffba017ef6
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/IOutputCacheStore.cs
@@ -0,0 +1,36 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+/// <summary>
+/// Represents a store for cached responses.
+/// </summary>
+public interface IOutputCacheStore
+{
+ /// <summary>
+ /// Evicts cached responses by tag.
+ /// </summary>
+ /// <param name="tag">The tag to evict.</param>
+ /// <param name="cancellationToken">Indicates that the operation should be cancelled.</param>
+ ValueTask EvictByTagAsync(string tag, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Gets the cached response for the given key, if it exists.
+ /// If no cached response exists for the given key, <c>null</c> is returned.
+ /// </summary>
+ /// <param name="key">The cache key to look up.</param>
+ /// <param name="cancellationToken">Indicates that the operation should be cancelled.</param>
+ /// <returns>The response cache entry if it exists; otherwise <c>null</c>.</returns>
+ ValueTask<byte[]?> GetAsync(string key, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Stores the given response in the response cache.
+ /// </summary>
+ /// <param name="key">The cache key to store the response under.</param>
+ /// <param name="value">The response cache entry to store.</param>
+ /// <param name="tags">The tags associated with the cache entry to store.</param>
+ /// <param name="validFor">The amount of time the entry will be kept in the cache before expiring, relative to now.</param>
+ /// <param name="cancellationToken">Indicates that the operation should be cancelled.</param>
+ ValueTask SetAsync(string key, byte[] value, string[]? tags, TimeSpan validFor, CancellationToken cancellationToken);
+}
diff --git a/src/Middleware/OutputCaching/src/ISystemClock.cs b/src/Middleware/OutputCaching/src/ISystemClock.cs
new file mode 100644
index 0000000000..8e2ead45b9
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/ISystemClock.cs
@@ -0,0 +1,15 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+/// <summary>
+/// Abstracts the system clock to facilitate testing.
+/// </summary>
+internal interface ISystemClock
+{
+ /// <summary>
+ /// Retrieves the current system time in UTC.
+ /// </summary>
+ DateTimeOffset UtcNow { get; }
+}
diff --git a/src/Middleware/OutputCaching/src/LoggerExtensions.cs b/src/Middleware/OutputCaching/src/LoggerExtensions.cs
new file mode 100644
index 0000000000..642f7252a6
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/LoggerExtensions.cs
@@ -0,0 +1,70 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.Extensions.Logging;
+using Microsoft.Net.Http.Headers;
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+/// <summary>
+/// Defines the logger messages produced by output caching
+/// </summary>
+internal static partial class LoggerExtensions
+{
+ [LoggerMessage(1, LogLevel.Debug, "The request cannot be served from cache because it uses the HTTP method: {Method}.",
+ EventName = "RequestMethodNotCacheable")]
+ internal static partial void RequestMethodNotCacheable(this ILogger logger, string method);
+
+ [LoggerMessage(2, LogLevel.Debug, "The request cannot be served from cache because it contains an 'Authorization' header.",
+ EventName = "RequestWithAuthorizationNotCacheable")]
+ internal static partial void RequestWithAuthorizationNotCacheable(this ILogger logger);
+
+ [LoggerMessage(3, LogLevel.Debug, "Response is not cacheable because it contains a 'SetCookie' header.", EventName = "ResponseWithSetCookieNotCacheable")]
+ internal static partial void ResponseWithSetCookieNotCacheable(this ILogger logger);
+
+ [LoggerMessage(4, LogLevel.Debug, "Response is not cacheable because its status code {StatusCode} does not indicate success.",
+ EventName = "ResponseWithUnsuccessfulStatusCodeNotCacheable")]
+ internal static partial void ResponseWithUnsuccessfulStatusCodeNotCacheable(this ILogger logger, int statusCode);
+
+ [LoggerMessage(5, LogLevel.Debug, "The 'IfNoneMatch' header of the request contains a value of *.", EventName = "NotModifiedIfNoneMatchStar")]
+ internal static partial void NotModifiedIfNoneMatchStar(this ILogger logger);
+
+ [LoggerMessage(6, LogLevel.Debug, "The ETag {ETag} in the 'IfNoneMatch' header matched the ETag of a cached entry.",
+ EventName = "NotModifiedIfNoneMatchMatched")]
+ internal static partial void NotModifiedIfNoneMatchMatched(this ILogger logger, EntityTagHeaderValue etag);
+
+ [LoggerMessage(7, LogLevel.Debug, "The last modified date of {LastModified} is before the date {IfModifiedSince} specified in the 'IfModifiedSince' header.",
+ EventName = "NotModifiedIfModifiedSinceSatisfied")]
+ internal static partial void NotModifiedIfModifiedSinceSatisfied(this ILogger logger, DateTimeOffset lastModified, DateTimeOffset ifModifiedSince);
+
+ [LoggerMessage(8, LogLevel.Information, "The content requested has not been modified.", EventName = "NotModifiedServed")]
+ internal static partial void NotModifiedServed(this ILogger logger);
+
+ [LoggerMessage(9, LogLevel.Information, "Serving response from cache.", EventName = "CachedResponseServed")]
+ internal static partial void CachedResponseServed(this ILogger logger);
+
+ [LoggerMessage(10, LogLevel.Information, "No cached response available for this request and the 'only-if-cached' cache directive was specified.",
+ EventName = "GatewayTimeoutServed")]
+ internal static partial void GatewayTimeoutServed(this ILogger logger);
+
+ [LoggerMessage(11, LogLevel.Information, "No cached response available for this request.", EventName = "NoResponseServed")]
+ internal static partial void NoResponseServed(this ILogger logger);
+
+ [LoggerMessage(12, LogLevel.Debug, "Vary by rules were updated. Headers: {Headers}, Query keys: {QueryKeys}", EventName = "VaryByRulesUpdated")]
+ internal static partial void VaryByRulesUpdated(this ILogger logger, string headers, string queryKeys);
+
+ [LoggerMessage(13, LogLevel.Information, "The response has been cached.", EventName = "ResponseCached")]
+ internal static partial void ResponseCached(this ILogger logger);
+
+ [LoggerMessage(14, LogLevel.Information, "The response could not be cached for this request.", EventName = "ResponseNotCached")]
+ internal static partial void ResponseNotCached(this ILogger logger);
+
+ [LoggerMessage(15, LogLevel.Warning, "The response could not be cached for this request because the 'Content-Length' did not match the body length.",
+ EventName = "ResponseContentLengthMismatchNotCached")]
+ internal static partial void ResponseContentLengthMismatchNotCached(this ILogger logger);
+
+ [LoggerMessage(16, LogLevel.Debug, "The response time of the entry is {ResponseTime} and has exceeded its expiry date.",
+ EventName = "ExpirationExpiresExceeded")]
+ internal static partial void ExpirationExpiresExceeded(this ILogger logger, DateTimeOffset responseTime);
+
+}
diff --git a/src/Middleware/OutputCaching/src/Memory/MemoryOutputCacheStore.cs b/src/Middleware/OutputCaching/src/Memory/MemoryOutputCacheStore.cs
new file mode 100644
index 0000000000..3f1df73c7b
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/Memory/MemoryOutputCacheStore.cs
@@ -0,0 +1,96 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.Extensions.Caching.Memory;
+
+namespace Microsoft.AspNetCore.OutputCaching.Memory;
+
+internal sealed class MemoryOutputCacheStore : IOutputCacheStore
+{
+ private readonly IMemoryCache _cache;
+ private readonly Dictionary<string, HashSet<string>> _taggedEntries = new();
+ private readonly object _tagsLock = new();
+
+ internal MemoryOutputCacheStore(IMemoryCache cache)
+ {
+ ArgumentNullException.ThrowIfNull(cache);
+
+ _cache = cache;
+ }
+
+ public ValueTask EvictByTagAsync(string tag, CancellationToken cancellationToken)
+ {
+ ArgumentNullException.ThrowIfNull(tag);
+
+ lock (_tagsLock)
+ {
+ if (_taggedEntries.TryGetValue(tag, out var keys))
+ {
+ foreach (var key in keys)
+ {
+ _cache.Remove(key);
+ }
+
+ _taggedEntries.Remove(tag);
+ }
+ }
+
+ return ValueTask.CompletedTask;
+ }
+
+ /// <inheritdoc />
+ public ValueTask<byte[]?> GetAsync(string key, CancellationToken cancellationToken)
+ {
+ ArgumentNullException.ThrowIfNull(key);
+
+ var entry = _cache.Get(key) as byte[];
+ return ValueTask.FromResult(entry);
+ }
+
+ /// <inheritdoc />
+ public ValueTask SetAsync(string key, byte[] value, string[]? tags, TimeSpan validFor, CancellationToken cancellationToken)
+ {
+ ArgumentNullException.ThrowIfNull(key);
+ ArgumentNullException.ThrowIfNull(value);
+
+ if (tags != null)
+ {
+ // Lock with SetEntry() to prevent EvictByTagAsync() from trying to remove a tag whose entry hasn't been added yet.
+ // It might be acceptable to not lock SetEntry() since in this case Remove(key) would just no-op and the user retry to evict.
+
+ lock (_tagsLock)
+ {
+ foreach (var tag in tags)
+ {
+ if (!_taggedEntries.TryGetValue(tag, out var keys))
+ {
+ keys = new HashSet<string>();
+ _taggedEntries[tag] = keys;
+ }
+
+ keys.Add(key);
+ }
+
+ SetEntry();
+ }
+ }
+ else
+ {
+ SetEntry();
+ }
+
+ void SetEntry()
+ {
+ _cache.Set(
+ key,
+ value,
+ new MemoryCacheEntryOptions
+ {
+ AbsoluteExpirationRelativeToNow = validFor,
+ Size = value.Length
+ });
+ }
+
+ return ValueTask.CompletedTask;
+ }
+}
diff --git a/src/Middleware/OutputCaching/src/Microsoft.AspNetCore.OutputCaching.csproj b/src/Middleware/OutputCaching/src/Microsoft.AspNetCore.OutputCaching.csproj
new file mode 100644
index 0000000000..1396a66e27
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/Microsoft.AspNetCore.OutputCaching.csproj
@@ -0,0 +1,26 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <Description>ASP.NET Core middleware for caching HTTP responses on the server.</Description>
+ <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
+ <IsAspNetCoreApp>true</IsAspNetCoreApp>
+ <IsPackable>false</IsPackable>
+ <IsTrimmable>true</IsTrimmable>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <InternalsVisibleTo Include="Microsoft.AspNetCore.OutputCaching.Tests" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <Reference Include="Microsoft.AspNetCore.Http.Extensions" />
+ <Reference Include="Microsoft.AspNetCore.Http" />
+ <Reference Include="Microsoft.Extensions.Caching.Memory" />
+ <Reference Include="Microsoft.Extensions.Logging.Abstractions" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <Compile Include="$(RepoRoot)src\Shared\TaskToApm.cs" Link="Streams\TaskToApm.cs" />
+ </ItemGroup>
+
+</Project>
diff --git a/src/Middleware/OutputCaching/src/OutputCacheApplicationBuilderExtensions.cs b/src/Middleware/OutputCaching/src/OutputCacheApplicationBuilderExtensions.cs
new file mode 100644
index 0000000000..4d6caa87d0
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/OutputCacheApplicationBuilderExtensions.cs
@@ -0,0 +1,23 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.AspNetCore.OutputCaching;
+
+namespace Microsoft.AspNetCore.Builder;
+
+/// <summary>
+/// Extension methods for adding the <see cref="OutputCacheMiddleware"/> to an application.
+/// </summary>
+public static class OutputCacheApplicationBuilderExtensions
+{
+ /// <summary>
+ /// Adds the <see cref="OutputCacheMiddleware"/> for caching HTTP responses.
+ /// </summary>
+ /// <param name="app">The <see cref="IApplicationBuilder"/>.</param>
+ public static IApplicationBuilder UseOutputCache(this IApplicationBuilder app)
+ {
+ ArgumentNullException.ThrowIfNull(app);
+
+ return app.UseMiddleware<OutputCacheMiddleware>();
+ }
+}
diff --git a/src/Middleware/OutputCaching/src/OutputCacheAttribute.cs b/src/Middleware/OutputCaching/src/OutputCacheAttribute.cs
new file mode 100644
index 0000000000..ab48a03556
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/OutputCacheAttribute.cs
@@ -0,0 +1,88 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+/// <summary>
+/// Specifies the parameters necessary for setting appropriate headers in output caching.
+/// </summary>
+/// <remarks>
+/// This attribute requires the output cache middleware.
+/// </remarks>
+[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
+public sealed class OutputCacheAttribute : Attribute
+{
+ // A nullable-int cannot be used as an Attribute parameter.
+ // Hence this nullable-int is present to back the Duration property.
+ // The same goes for nullable-ResponseCacheLocation and nullable-bool.
+ private int? _duration;
+ private bool? _noCache;
+
+ private IOutputCachePolicy? _builtPolicy;
+
+ /// <summary>
+ /// Gets or sets the duration in seconds for which the response is cached.
+ /// </summary>
+ public int Duration
+ {
+ get => _duration ?? 0;
+ init => _duration = value;
+ }
+
+ /// <summary>
+ /// Gets or sets the value which determines whether the reponse should be cached or not.
+ /// When set to <see langword="true"/>, the response won't be cached.
+ /// </summary>
+ public bool NoStore
+ {
+ get => _noCache ?? false;
+ init => _noCache = value;
+ }
+
+ /// <summary>
+ /// Gets or sets the query keys to vary by.
+ /// </summary>
+ public string[]? VaryByQueryKeys { get; init; }
+
+ /// <summary>
+ /// Gets or sets the headers to vary by.
+ /// </summary>
+ public string[]? VaryByHeaders { get; init; }
+
+ /// <summary>
+ /// Gets or sets the value of the cache policy name.
+ /// </summary>
+ public string? PolicyName { get; init; }
+
+ internal IOutputCachePolicy BuildPolicy()
+ {
+ if (_builtPolicy != null)
+ {
+ return _builtPolicy;
+ }
+
+ var builder = new OutputCachePolicyBuilder();
+
+ if (PolicyName != null)
+ {
+ builder.AddPolicy(new NamedPolicy(PolicyName));
+ }
+
+ if (_noCache != null && _noCache.Value)
+ {
+ builder.NoCache();
+ }
+
+ if (VaryByQueryKeys != null)
+ {
+ builder.VaryByQuery(VaryByQueryKeys);
+ }
+
+ if (_duration != null)
+ {
+ builder.Expire(TimeSpan.FromSeconds(_duration.Value));
+ }
+
+ return _builtPolicy = builder.Build();
+ }
+}
diff --git a/src/Middleware/OutputCaching/src/OutputCacheContext.cs b/src/Middleware/OutputCaching/src/OutputCacheContext.cs
new file mode 100644
index 0000000000..667ca5dcce
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/OutputCacheContext.cs
@@ -0,0 +1,85 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+/// <summary>
+/// Represent the current cache context for the request.
+/// </summary>
+public sealed class OutputCacheContext
+{
+ internal OutputCacheContext(HttpContext httpContext, IOutputCacheStore store, OutputCacheOptions options, ILogger logger)
+ {
+ HttpContext = httpContext;
+ Logger = logger;
+ Store = store;
+ Options = options;
+ }
+
+ /// <summary>
+ /// Determines whether the output caching logic should be configured for the incoming HTTP request.
+ /// </summary>
+ public bool EnableOutputCaching { get; set; }
+
+ /// <summary>
+ /// Determines whether a cache lookup is allowed for the incoming HTTP request.
+ /// </summary>
+ public bool AllowCacheLookup { get; set; }
+
+ /// <summary>
+ /// Determines whether storage of the response is allowed for the incoming HTTP request.
+ /// </summary>
+ public bool AllowCacheStorage { get; set; }
+
+ /// <summary>
+ /// Determines whether the request should be locked.
+ /// </summary>
+ public bool AllowLocking { get; set; }
+
+ /// <summary>
+ /// Gets the <see cref="HttpContext"/>.
+ /// </summary>
+ public HttpContext HttpContext { get; }
+
+ /// <summary>
+ /// Gets the response time.
+ /// </summary>
+ public DateTimeOffset? ResponseTime { get; internal set; }
+
+ /// <summary>
+ /// Gets the <see cref="CacheVaryByRules"/> instance.
+ /// </summary>
+ public CacheVaryByRules CacheVaryByRules { get; set; } = new();
+
+ /// <summary>
+ /// Gets the tags of the cached response.
+ /// </summary>
+ public HashSet<string> Tags { get; } = new();
+
+ /// <summary>
+ /// Gets or sets the amount of time the response should be cached for.
+ /// </summary>
+ public TimeSpan? ResponseExpirationTimeSpan { get; set; }
+
+ internal string CacheKey { get; set; } = default!;
+
+ internal TimeSpan CachedResponseValidFor { get; set; }
+
+ internal bool IsCacheEntryFresh { get; set; }
+
+ internal TimeSpan CachedEntryAge { get; set; }
+
+ internal OutputCacheEntry CachedResponse { get; set; } = default!;
+
+ internal bool ResponseStarted { get; set; }
+
+ internal Stream OriginalResponseStream { get; set; } = default!;
+
+ internal OutputCacheStream OutputCacheStream { get; set; } = default!;
+ internal ILogger Logger { get; }
+ internal OutputCacheOptions Options { get; }
+ internal IOutputCacheStore Store { get; }
+}
diff --git a/src/Middleware/OutputCaching/src/OutputCacheEntry.cs b/src/Middleware/OutputCaching/src/OutputCacheEntry.cs
new file mode 100644
index 0000000000..325f2e6607
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/OutputCacheEntry.cs
@@ -0,0 +1,34 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.AspNetCore.Http;
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+internal sealed class OutputCacheEntry
+{
+ /// <summary>
+ /// Gets the created date and time of the cache entry.
+ /// </summary>
+ public DateTimeOffset Created { get; set; }
+
+ /// <summary>
+ /// Gets the status code of the cache entry.
+ /// </summary>
+ public int StatusCode { get; set; }
+
+ /// <summary>
+ /// Gets the headers of the cache entry.
+ /// </summary>
+ public HeaderDictionary Headers { get; set; } = default!;
+
+ /// <summary>
+ /// Gets the body of the cache entry.
+ /// </summary>
+ public CachedResponseBody Body { get; set; } = default!;
+
+ /// <summary>
+ /// Gets the tags of the cache entry.
+ /// </summary>
+ public string[] Tags { get; set; } = Array.Empty<string>();
+}
diff --git a/src/Middleware/OutputCaching/src/OutputCacheEntryFormatter.cs b/src/Middleware/OutputCaching/src/OutputCacheEntryFormatter.cs
new file mode 100644
index 0000000000..b6dbef1c2c
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/OutputCacheEntryFormatter.cs
@@ -0,0 +1,81 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Linq;
+using System.Text.Json;
+using Microsoft.AspNetCore.OutputCaching.Serialization;
+
+namespace Microsoft.AspNetCore.OutputCaching;
+/// <summary>
+/// Formats <see cref="OutputCacheEntry"/> instance to match structures supported by the <see cref="IOutputCacheStore"/> implementations.
+/// </summary>
+internal static class OutputCacheEntryFormatter
+{
+ public static async ValueTask<OutputCacheEntry?> GetAsync(string key, IOutputCacheStore store, CancellationToken cancellationToken)
+ {
+ ArgumentNullException.ThrowIfNull(key);
+
+ var content = await store.GetAsync(key, cancellationToken);
+
+ if (content == null)
+ {
+ return null;
+ }
+
+ var formatter = JsonSerializer.Deserialize(content, FormatterEntrySerializerContext.Default.FormatterEntry);
+
+ if (formatter == null)
+ {
+ return null;
+ }
+
+ var outputCacheEntry = new OutputCacheEntry
+ {
+ StatusCode = formatter.StatusCode,
+ Created = formatter.Created,
+ Tags = formatter.Tags,
+ Headers = new(),
+ Body = new CachedResponseBody(formatter.Body, formatter.Body.Sum(x => x.Length))
+ };
+
+ if (formatter.Headers != null)
+ {
+ foreach (var header in formatter.Headers)
+ {
+ outputCacheEntry.Headers.TryAdd(header.Key, header.Value);
+ }
+ }
+
+ return outputCacheEntry;
+ }
+
+ public static async ValueTask StoreAsync(string key, OutputCacheEntry value, TimeSpan duration, IOutputCacheStore store, CancellationToken cancellationToken)
+ {
+ ArgumentNullException.ThrowIfNull(value);
+ ArgumentNullException.ThrowIfNull(value.Body);
+ ArgumentNullException.ThrowIfNull(value.Headers);
+
+ var formatterEntry = new FormatterEntry
+ {
+ StatusCode = value.StatusCode,
+ Created = value.Created,
+ Tags = value.Tags,
+ Body = value.Body.Segments
+ };
+
+ if (value.Headers != null)
+ {
+ formatterEntry.Headers = new();
+ foreach (var header in value.Headers)
+ {
+ formatterEntry.Headers.TryAdd(header.Key, header.Value.ToArray());
+ }
+ }
+
+ using var bufferStream = new MemoryStream();
+
+ JsonSerializer.Serialize(bufferStream, formatterEntry, FormatterEntrySerializerContext.Default.FormatterEntry);
+
+ await store.SetAsync(key, bufferStream.ToArray(), value.Tags ?? Array.Empty<string>(), duration, cancellationToken);
+ }
+}
diff --git a/src/Middleware/OutputCaching/src/OutputCacheFeature.cs b/src/Middleware/OutputCaching/src/OutputCacheFeature.cs
new file mode 100644
index 0000000000..b1ce32b97a
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/OutputCacheFeature.cs
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+internal sealed class OutputCacheFeature : IOutputCacheFeature
+{
+ public OutputCacheFeature(OutputCacheContext context)
+ {
+ Context = context;
+ }
+
+ public OutputCacheContext Context { get; }
+}
diff --git a/src/Middleware/OutputCaching/src/OutputCacheKeyProvider.cs b/src/Middleware/OutputCaching/src/OutputCacheKeyProvider.cs
new file mode 100644
index 0000000000..dc8cce4afc
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/OutputCacheKeyProvider.cs
@@ -0,0 +1,190 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Linq;
+using System.Text;
+using Microsoft.Extensions.ObjectPool;
+using Microsoft.Extensions.Options;
+using Microsoft.Extensions.Primitives;
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+internal sealed class OutputCacheKeyProvider : IOutputCacheKeyProvider
+{
+ // Use the record separator for delimiting components of the cache key to avoid possible collisions
+ private const char KeyDelimiter = '\x1e';
+ // Use the unit separator for delimiting subcomponents of the cache key to avoid possible collisions
+ private const char KeySubDelimiter = '\x1f';
+
+ private readonly ObjectPool<StringBuilder> _builderPool;
+ private readonly OutputCacheOptions _options;
+
+ internal OutputCacheKeyProvider(ObjectPoolProvider poolProvider, IOptions<OutputCacheOptions> options)
+ {
+ ArgumentNullException.ThrowIfNull(poolProvider);
+ ArgumentNullException.ThrowIfNull(options);
+
+ _builderPool = poolProvider.CreateStringBuilderPool();
+ _options = options.Value;
+ }
+
+ // GET<delimiter>SCHEME<delimiter>HOST:PORT/PATHBASE/PATH<delimiter>H<delimiter>HeaderName=HeaderValue<delimiter>Q<delimiter>QueryName=QueryValue1<subdelimiter>QueryValue2
+ public string CreateStorageKey(OutputCacheContext context)
+ {
+ ArgumentNullException.ThrowIfNull(_builderPool);
+
+ var varyByRules = context.CacheVaryByRules;
+ if (varyByRules == null)
+ {
+ throw new InvalidOperationException($"{nameof(CacheVaryByRules)} must not be null on the {nameof(OutputCacheContext)}");
+ }
+
+ var request = context.HttpContext.Request;
+ var builder = _builderPool.Get();
+
+ try
+ {
+ builder
+ .AppendUpperInvariant(request.Method)
+ .Append(KeyDelimiter)
+ .AppendUpperInvariant(request.Scheme)
+ .Append(KeyDelimiter)
+ .AppendUpperInvariant(request.Host.Value);
+
+ if (_options.UseCaseSensitivePaths)
+ {
+ builder
+ .Append(request.PathBase.Value)
+ .Append(request.Path.Value);
+ }
+ else
+ {
+ builder
+ .AppendUpperInvariant(request.PathBase.Value)
+ .AppendUpperInvariant(request.Path.Value);
+ }
+
+ // Vary by prefix and custom
+ var prefixCount = varyByRules?.VaryByPrefix.Count ?? 0;
+ if (prefixCount > 0)
+ {
+ // Append a group separator for the header segment of the cache key
+ builder.Append(KeyDelimiter)
+ .Append('C');
+
+ for (var i = 0; i < prefixCount; i++)
+ {
+ var value = varyByRules?.VaryByPrefix[i] ?? string.Empty;
+ builder.Append(KeyDelimiter).Append(value);
+ }
+ }
+
+ // Vary by headers
+ var headersCount = varyByRules?.Headers.Count ?? 0;
+ if (headersCount > 0)
+ {
+ // Append a group separator for the header segment of the cache key
+ builder.Append(KeyDelimiter)
+ .Append('H');
+
+ var requestHeaders = context.HttpContext.Request.Headers;
+ for (var i = 0; i < headersCount; i++)
+ {
+ var header = varyByRules!.Headers[i] ?? string.Empty;
+ var headerValues = requestHeaders[header];
+ builder.Append(KeyDelimiter)
+ .Append(header)
+ .Append('=');
+
+ var headerValuesArray = headerValues.ToArray();
+ Array.Sort(headerValuesArray, StringComparer.Ordinal);
+
+ for (var j = 0; j < headerValuesArray.Length; j++)
+ {
+ builder.Append(headerValuesArray[j]);
+ }
+ }
+ }
+
+ // Vary by query keys
+ if (varyByRules?.QueryKeys.Count > 0)
+ {
+ // Append a group separator for the query key segment of the cache key
+ builder.Append(KeyDelimiter)
+ .Append('Q');
+
+ if (varyByRules.QueryKeys.Count == 1 && string.Equals(varyByRules.QueryKeys[0], "*", StringComparison.Ordinal) && context.HttpContext.Request.Query.Count > 0)
+ {
+ // Vary by all available query keys
+ var queryArray = context.HttpContext.Request.Query.ToArray();
+ // Query keys are aggregated case-insensitively whereas the query values are compared ordinally.
+ Array.Sort(queryArray, QueryKeyComparer.OrdinalIgnoreCase);
+
+ for (var i = 0; i < queryArray.Length; i++)
+ {
+ builder.Append(KeyDelimiter)
+ .AppendUpperInvariant(queryArray[i].Key)
+ .Append('=');
+
+ var queryValueArray = queryArray[i].Value.ToArray();
+ Array.Sort(queryValueArray, StringComparer.Ordinal);
+
+ for (var j = 0; j < queryValueArray.Length; j++)
+ {
+ if (j > 0)
+ {
+ builder.Append(KeySubDelimiter);
+ }
+
+ builder.Append(queryValueArray[j]);
+ }
+ }
+ }
+ else
+ {
+ for (var i = 0; i < varyByRules.QueryKeys.Count; i++)
+ {
+ var queryKey = varyByRules.QueryKeys[i] ?? string.Empty;
+ var queryKeyValues = context.HttpContext.Request.Query[queryKey];
+ builder.Append(KeyDelimiter)
+ .Append(queryKey)
+ .Append('=');
+
+ var queryValueArray = queryKeyValues.ToArray();
+ Array.Sort(queryValueArray, StringComparer.Ordinal);
+
+ for (var j = 0; j < queryValueArray.Length; j++)
+ {
+ if (j > 0)
+ {
+ builder.Append(KeySubDelimiter);
+ }
+
+ builder.Append(queryValueArray[j]);
+ }
+ }
+ }
+ }
+
+ return builder.ToString();
+ }
+ finally
+ {
+ _builderPool.Return(builder);
+ }
+ }
+
+ private sealed class QueryKeyComparer : IComparer<KeyValuePair<string, StringValues>>
+ {
+ private readonly StringComparer _stringComparer;
+
+ public static QueryKeyComparer OrdinalIgnoreCase { get; } = new QueryKeyComparer(StringComparer.OrdinalIgnoreCase);
+
+ public QueryKeyComparer(StringComparer stringComparer)
+ {
+ _stringComparer = stringComparer;
+ }
+
+ public int Compare(KeyValuePair<string, StringValues> x, KeyValuePair<string, StringValues> y) => _stringComparer.Compare(x.Key, y.Key);
+ }
+}
diff --git a/src/Middleware/OutputCaching/src/OutputCacheMiddleware.cs b/src/Middleware/OutputCaching/src/OutputCacheMiddleware.cs
new file mode 100644
index 0000000000..a526358e11
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/OutputCacheMiddleware.cs
@@ -0,0 +1,620 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Linq;
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.ObjectPool;
+using Microsoft.Extensions.Options;
+using Microsoft.Extensions.Primitives;
+using Microsoft.Net.Http.Headers;
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+/// <summary>
+/// Enable HTTP response caching.
+/// </summary>
+internal sealed class OutputCacheMiddleware
+{
+ // see https://tools.ietf.org/html/rfc7232#section-4.1
+ private static readonly string[] HeadersToIncludeIn304 =
+ new[] { "Cache-Control", "Content-Location", "Date", "ETag", "Expires", "Vary" };
+
+ private readonly RequestDelegate _next;
+ private readonly OutputCacheOptions _options;
+ private readonly ILogger _logger;
+ private readonly IOutputCacheStore _store;
+ private readonly IOutputCacheKeyProvider _keyProvider;
+ private readonly WorkDispatcher<string, OutputCacheEntry?> _outputCacheEntryDispatcher;
+ private readonly WorkDispatcher<string, OutputCacheEntry?> _requestDispatcher;
+
+ /// <summary>
+ /// Creates a new <see cref="OutputCacheMiddleware"/>.
+ /// </summary>
+ /// <param name="next">The <see cref="RequestDelegate"/> representing the next middleware in the pipeline.</param>
+ /// <param name="options">The options for this middleware.</param>
+ /// <param name="loggerFactory">The <see cref="ILoggerFactory"/> used for logging.</param>
+ /// <param name="outputCache">The <see cref="IOutputCacheStore"/> store.</param>
+ /// <param name="poolProvider">The <see cref="ObjectPoolProvider"/> used for creating <see cref="ObjectPool"/> instances.</param>
+ public OutputCacheMiddleware(
+ RequestDelegate next,
+ IOptions<OutputCacheOptions> options,
+ ILoggerFactory loggerFactory,
+ IOutputCacheStore outputCache,
+ ObjectPoolProvider poolProvider
+ )
+ : this(
+ next,
+ options,
+ loggerFactory,
+ outputCache,
+ new OutputCacheKeyProvider(poolProvider, options))
+ { }
+
+ // for testing
+ internal OutputCacheMiddleware(
+ RequestDelegate next,
+ IOptions<OutputCacheOptions> options,
+ ILoggerFactory loggerFactory,
+ IOutputCacheStore cache,
+ IOutputCacheKeyProvider keyProvider)
+ {
+ ArgumentNullException.ThrowIfNull(next);
+ ArgumentNullException.ThrowIfNull(options);
+ ArgumentNullException.ThrowIfNull(loggerFactory);
+ ArgumentNullException.ThrowIfNull(cache);
+ ArgumentNullException.ThrowIfNull(keyProvider);
+
+ _next = next;
+ _options = options.Value;
+ _logger = loggerFactory.CreateLogger<OutputCacheMiddleware>();
+ _store = cache;
+ _keyProvider = keyProvider;
+ _outputCacheEntryDispatcher = new();
+ _requestDispatcher = new();
+ }
+
+ /// <summary>
+ /// Invokes the logic of the middleware.
+ /// </summary>
+ /// <param name="httpContext">The <see cref="HttpContext"/>.</param>
+ /// <returns>A <see cref="Task"/> that completes when the middleware has completed processing.</returns>
+ public Task Invoke(HttpContext httpContext)
+ {
+ // Skip the middleware if there is no policy for the current request
+ if (!TryGetRequestPolicies(httpContext, out var policies))
+ {
+ return _next(httpContext);
+ }
+
+ return InvokeAwaited(httpContext, policies);
+ }
+
+ private async Task InvokeAwaited(HttpContext httpContext, IReadOnlyList<IOutputCachePolicy> policies)
+ {
+ var context = new OutputCacheContext(httpContext, _store, _options, _logger);
+
+ // Add IOutputCacheFeature
+ AddOutputCacheFeature(context);
+
+ try
+ {
+ foreach (var policy in policies)
+ {
+ await policy.CacheRequestAsync(context, httpContext.RequestAborted);
+ }
+
+ // Should we attempt any caching logic?
+ if (context.EnableOutputCaching)
+ {
+ // Can this request be served from cache?
+ if (context.AllowCacheLookup)
+ {
+ if (await TryServeFromCacheAsync(context, policies))
+ {
+ return;
+ }
+ }
+
+ // Should we store the response to this request?
+ if (context.AllowCacheStorage)
+ {
+ // It is also a pre-condition to reponse locking
+
+ var executed = false;
+
+ if (context.AllowLocking)
+ {
+ var cacheEntry = await _requestDispatcher.ScheduleAsync(context.CacheKey, key => ExecuteResponseAsync());
+
+ // The current request was processed, nothing more to do
+ if (executed)
+ {
+ return;
+ }
+
+ // If the result was processed by another request, try to serve it from cache entry (no lookup)
+ if (await TryServeCachedResponseAsync(context, cacheEntry, policies))
+ {
+ return;
+ }
+
+ // If the cache entry couldn't be served, continue to processing the request as usual
+ }
+
+ await ExecuteResponseAsync();
+
+ async Task<OutputCacheEntry?> ExecuteResponseAsync()
+ {
+ // Hook up to listen to the response stream
+ ShimResponseStream(context);
+
+ try
+ {
+ await _next(httpContext);
+
+ // The next middleware might change the policy
+ foreach (var policy in policies)
+ {
+ await policy.ServeResponseAsync(context, httpContext.RequestAborted);
+ }
+
+ // If there was no response body, check the response headers now. We can cache things like redirects.
+ StartResponse(context);
+
+ // Finalize the cache entry
+ await FinalizeCacheBodyAsync(context);
+
+ executed = true;
+ }
+ finally
+ {
+ UnshimResponseStream(context);
+ }
+
+ return context.CachedResponse;
+ }
+
+ return;
+ }
+ }
+
+ await _next(httpContext);
+ }
+ finally
+ {
+ RemoveOutputCacheFeature(httpContext);
+ }
+ }
+
+ internal bool TryGetRequestPolicies(HttpContext httpContext, out IReadOnlyList<IOutputCachePolicy> policies)
+ {
+ policies = Array.Empty<IOutputCachePolicy>();
+ List<IOutputCachePolicy>? result = null;
+
+ if (_options.BasePolicies != null)
+ {
+ result = new();
+ result.AddRange(_options.BasePolicies);
+ }
+
+ var metadata = httpContext.GetEndpoint()?.Metadata;
+
+ var policy = metadata?.GetMetadata<IOutputCachePolicy>();
+
+ if (policy != null)
+ {
+ result ??= new();
+ result.Add(policy);
+ }
+
+ var attribute = metadata?.GetMetadata<OutputCacheAttribute>();
+
+ if (attribute != null)
+ {
+ result ??= new();
+ result.Add(attribute.BuildPolicy());
+ }
+
+ if (result != null)
+ {
+ policies = result;
+ return true;
+ }
+
+ return false;
+ }
+
+ internal async Task<bool> TryServeCachedResponseAsync(OutputCacheContext context, OutputCacheEntry? cacheEntry, IReadOnlyList<IOutputCachePolicy> policies)
+ {
+ if (cacheEntry == null)
+ {
+ return false;
+ }
+
+ context.CachedResponse = cacheEntry;
+ context.ResponseTime = _options.SystemClock.UtcNow;
+ var cacheEntryAge = context.ResponseTime.Value - context.CachedResponse.Created;
+ context.CachedEntryAge = cacheEntryAge > TimeSpan.Zero ? cacheEntryAge : TimeSpan.Zero;
+
+ foreach (var policy in policies)
+ {
+ await policy.ServeFromCacheAsync(context, context.HttpContext.RequestAborted);
+ }
+
+ context.IsCacheEntryFresh = true;
+
+ // Validate expiration
+ if (context.CachedEntryAge <= TimeSpan.Zero)
+ {
+ context.Logger.ExpirationExpiresExceeded(context.ResponseTime!.Value);
+ context.IsCacheEntryFresh = false;
+ }
+
+ if (context.IsCacheEntryFresh)
+ {
+ var cachedResponseHeaders = context.CachedResponse.Headers;
+
+ // Check conditional request rules
+ if (ContentIsNotModified(context))
+ {
+ _logger.NotModifiedServed();
+ context.HttpContext.Response.StatusCode = StatusCodes.Status304NotModified;
+
+ if (cachedResponseHeaders != null)
+ {
+ foreach (var key in HeadersToIncludeIn304)
+ {
+ if (cachedResponseHeaders.TryGetValue(key, out var values))
+ {
+ context.HttpContext.Response.Headers[key] = values;
+ }
+ }
+ }
+ }
+ else
+ {
+ var response = context.HttpContext.Response;
+ // Copy the cached status code and response headers
+ response.StatusCode = context.CachedResponse.StatusCode;
+ foreach (var header in context.CachedResponse.Headers)
+ {
+ response.Headers[header.Key] = header.Value;
+ }
+
+ // Note: int64 division truncates result and errors may be up to 1 second. This reduction in
+ // accuracy of age calculation is considered appropriate since it is small compared to clock
+ // skews and the "Age" header is an estimate of the real age of cached content.
+ response.Headers.Age = HeaderUtilities.FormatNonNegativeInt64(context.CachedEntryAge.Ticks / TimeSpan.TicksPerSecond);
+
+ // Copy the cached response body
+ var body = context.CachedResponse.Body;
+ if (body.Length > 0)
+ {
+ try
+ {
+ await body.CopyToAsync(response.BodyWriter, context.HttpContext.RequestAborted);
+ }
+ catch (OperationCanceledException)
+ {
+ context.HttpContext.Abort();
+ }
+ }
+ _logger.CachedResponseServed();
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ internal async Task<bool> TryServeFromCacheAsync(OutputCacheContext cacheContext, IReadOnlyList<IOutputCachePolicy> policies)
+ {
+ CreateCacheKey(cacheContext);
+
+ // Locking cache lookups by default
+ // TODO: should it be part of the cache implementations or can we assume all caches would benefit from it?
+ // It makes sense for caches that use IO (disk, network) or need to deserialize the state but could also be a global option
+
+ var cacheEntry = await _outputCacheEntryDispatcher.ScheduleAsync(cacheContext.CacheKey, cacheContext, static async (key, cacheContext) => await OutputCacheEntryFormatter.GetAsync(key, cacheContext.Store, cacheContext.HttpContext.RequestAborted));
+
+ if (await TryServeCachedResponseAsync(cacheContext, cacheEntry, policies))
+ {
+ return true;
+ }
+
+ if (HeaderUtilities.ContainsCacheDirective(cacheContext.HttpContext.Request.Headers.CacheControl, CacheControlHeaderValue.OnlyIfCachedString))
+ {
+ _logger.GatewayTimeoutServed();
+ cacheContext.HttpContext.Response.StatusCode = StatusCodes.Status504GatewayTimeout;
+ return true;
+ }
+
+ _logger.NoResponseServed();
+ return false;
+ }
+
+ internal void CreateCacheKey(OutputCacheContext context)
+ {
+ if (!string.IsNullOrEmpty(context.CacheKey))
+ {
+ return;
+ }
+
+ var varyHeaders = context.CacheVaryByRules.Headers;
+ var varyQueryKeys = context.CacheVaryByRules.QueryKeys;
+ var varyByCustomKeys = context.CacheVaryByRules.VaryByCustom;
+ var varyByPrefix = context.CacheVaryByRules.VaryByPrefix;
+
+ // Check if any vary rules exist
+ if (!StringValues.IsNullOrEmpty(varyHeaders) || !StringValues.IsNullOrEmpty(varyQueryKeys) || !StringValues.IsNullOrEmpty(varyByPrefix) || varyByCustomKeys?.Count > 0)
+ {
+ // Normalize order and casing of vary by rules
+ var normalizedVaryHeaders = GetOrderCasingNormalizedStringValues(varyHeaders);
+ var normalizedVaryQueryKeys = GetOrderCasingNormalizedStringValues(varyQueryKeys);
+ var normalizedVaryByCustom = GetOrderCasingNormalizedDictionary(varyByCustomKeys);
+
+ // Update vary rules with normalized values
+ context.CacheVaryByRules = new CacheVaryByRules
+ {
+ VaryByPrefix = varyByPrefix + normalizedVaryByCustom,
+ Headers = normalizedVaryHeaders,
+ QueryKeys = normalizedVaryQueryKeys
+ };
+
+ // TODO: Add same condition on LogLevel in Response Caching
+ // Always overwrite the CachedVaryByRules to update the expiry information
+ if (_logger.IsEnabled(LogLevel.Debug))
+ {
+ _logger.VaryByRulesUpdated(normalizedVaryHeaders.ToString(), normalizedVaryQueryKeys.ToString());
+ }
+ }
+
+ context.CacheKey = _keyProvider.CreateStorageKey(context);
+ }
+
+ /// <summary>
+ /// Finalize cache headers.
+ /// </summary>
+ /// <param name="context"></param>
+ internal void FinalizeCacheHeaders(OutputCacheContext context)
+ {
+ if (context.AllowCacheStorage)
+ {
+ // Create the cache entry now
+ var response = context.HttpContext.Response;
+ var headers = response.Headers;
+
+ context.CachedResponseValidFor = context.ResponseExpirationTimeSpan ?? _options.DefaultExpirationTimeSpan;
+
+ // Setting the date on the raw response headers.
+ headers.Date = HeaderUtilities.FormatDate(context.ResponseTime!.Value);
+
+ // Store the response on the state
+ context.CachedResponse = new OutputCacheEntry
+ {
+ Created = context.ResponseTime!.Value,
+ StatusCode = response.StatusCode,
+ Headers = new HeaderDictionary(),
+ Tags = context.Tags.ToArray()
+ };
+
+ foreach (var header in headers)
+ {
+ if (!string.Equals(header.Key, HeaderNames.Age, StringComparison.OrdinalIgnoreCase))
+ {
+ context.CachedResponse.Headers[header.Key] = header.Value;
+ }
+ }
+
+ return;
+ }
+
+ context.OutputCacheStream.DisableBuffering();
+ }
+
+ /// <summary>
+ /// Stores the response body
+ /// </summary>
+ internal async ValueTask FinalizeCacheBodyAsync(OutputCacheContext context)
+ {
+ if (context.AllowCacheStorage && context.OutputCacheStream.BufferingEnabled)
+ {
+ // If AllowCacheLookup is false, the cache key was not created
+ CreateCacheKey(context);
+
+ var contentLength = context.HttpContext.Response.ContentLength;
+ var cachedResponseBody = context.OutputCacheStream.GetCachedResponseBody();
+ if (!contentLength.HasValue || contentLength == cachedResponseBody.Length
+ || (cachedResponseBody.Length == 0
+ && HttpMethods.IsHead(context.HttpContext.Request.Method)))
+ {
+ var response = context.HttpContext.Response;
+ // Add a content-length if required
+ if (!response.ContentLength.HasValue && StringValues.IsNullOrEmpty(response.Headers.TransferEncoding))
+ {
+ context.CachedResponse.Headers.ContentLength = cachedResponseBody.Length;
+ }
+
+ context.CachedResponse.Body = cachedResponseBody;
+ _logger.ResponseCached();
+
+ if (string.IsNullOrEmpty(context.CacheKey))
+ {
+ throw new InvalidOperationException("Cache key must be defined");
+ }
+
+ await OutputCacheEntryFormatter.StoreAsync(context.CacheKey, context.CachedResponse, context.CachedResponseValidFor, _store, context.HttpContext.RequestAborted);
+ }
+ else
+ {
+ _logger.ResponseContentLengthMismatchNotCached();
+ }
+ }
+ else
+ {
+ _logger.ResponseNotCached();
+ }
+ }
+
+ /// <summary>
+ /// Mark the response as started and set the response time if no response was started yet.
+ /// </summary>
+ /// <param name="context"></param>
+ /// <returns><c>true</c> if the response was not started before this call; otherwise <c>false</c>.</returns>
+ private bool OnStartResponse(OutputCacheContext context)
+ {
+ if (!context.ResponseStarted)
+ {
+ context.ResponseStarted = true;
+ context.ResponseTime = _options.SystemClock.UtcNow;
+
+ return true;
+ }
+ return false;
+ }
+
+ internal void StartResponse(OutputCacheContext context)
+ {
+ if (OnStartResponse(context))
+ {
+ FinalizeCacheHeaders(context);
+ }
+ }
+
+ internal static void AddOutputCacheFeature(OutputCacheContext context)
+ {
+ if (context.HttpContext.Features.Get<IOutputCacheFeature>() != null)
+ {
+ throw new InvalidOperationException($"Another instance of {nameof(OutputCacheFeature)} already exists. Only one instance of {nameof(OutputCacheMiddleware)} can be configured for an application.");
+ }
+
+ context.HttpContext.Features.Set<IOutputCacheFeature>(new OutputCacheFeature(context));
+ }
+
+ internal void ShimResponseStream(OutputCacheContext context)
+ {
+ // Shim response stream
+ context.OriginalResponseStream = context.HttpContext.Response.Body;
+ context.OutputCacheStream = new OutputCacheStream(
+ context.OriginalResponseStream,
+ _options.MaximumBodySize,
+ StreamUtilities.BodySegmentSize,
+ () => StartResponse(context));
+ context.HttpContext.Response.Body = context.OutputCacheStream;
+ }
+
+ internal static void RemoveOutputCacheFeature(HttpContext context) =>
+ context.Features.Set<IOutputCacheFeature?>(null);
+
+ internal static void UnshimResponseStream(OutputCacheContext context)
+ {
+ // Unshim response stream
+ context.HttpContext.Response.Body = context.OriginalResponseStream;
+
+ // Remove IOutputCachingFeature
+ RemoveOutputCacheFeature(context.HttpContext);
+ }
+
+ internal static bool ContentIsNotModified(OutputCacheContext context)
+ {
+ var cachedResponseHeaders = context.CachedResponse.Headers;
+ var ifNoneMatchHeader = context.HttpContext.Request.Headers.IfNoneMatch;
+
+ if (!StringValues.IsNullOrEmpty(ifNoneMatchHeader))
+ {
+ if (ifNoneMatchHeader.Count == 1 && StringSegment.Equals(ifNoneMatchHeader[0], EntityTagHeaderValue.Any.Tag, StringComparison.OrdinalIgnoreCase))
+ {
+ context.Logger.NotModifiedIfNoneMatchStar();
+ return true;
+ }
+
+ if (!StringValues.IsNullOrEmpty(cachedResponseHeaders[HeaderNames.ETag])
+ && EntityTagHeaderValue.TryParse(cachedResponseHeaders[HeaderNames.ETag].ToString(), out var eTag)
+ && EntityTagHeaderValue.TryParseList(ifNoneMatchHeader, out var ifNoneMatchEtags))
+ {
+ for (var i = 0; i < ifNoneMatchEtags?.Count; i++)
+ {
+ var requestETag = ifNoneMatchEtags[i];
+ if (eTag.Compare(requestETag, useStrongComparison: false))
+ {
+ context.Logger.NotModifiedIfNoneMatchMatched(requestETag);
+ return true;
+ }
+ }
+ }
+ }
+ else
+ {
+ var ifModifiedSince = context.HttpContext.Request.Headers.IfModifiedSince;
+ if (!StringValues.IsNullOrEmpty(ifModifiedSince))
+ {
+ if (!HeaderUtilities.TryParseDate(cachedResponseHeaders[HeaderNames.LastModified].ToString(), out var modified) &&
+ !HeaderUtilities.TryParseDate(cachedResponseHeaders[HeaderNames.Date].ToString(), out modified))
+ {
+ return false;
+ }
+
+ if (HeaderUtilities.TryParseDate(ifModifiedSince.ToString(), out var modifiedSince) &&
+ modified <= modifiedSince)
+ {
+ context.Logger.NotModifiedIfModifiedSinceSatisfied(modified, modifiedSince);
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ // Normalize order and casing
+ internal static StringValues GetOrderCasingNormalizedStringValues(StringValues stringValues)
+ {
+ if (stringValues.Count == 0)
+ {
+ return StringValues.Empty;
+ }
+ else if (stringValues.Count == 1)
+ {
+ return new StringValues(stringValues.ToString().ToUpperInvariant());
+ }
+ else
+ {
+ var originalArray = stringValues.ToArray();
+ var newArray = new string[originalArray.Length];
+
+ for (var i = 0; i < originalArray.Length; i++)
+ {
+ newArray[i] = originalArray[i]!.ToUpperInvariant();
+ }
+
+ // Since the casing has already been normalized, use Ordinal comparison
+ Array.Sort(newArray, StringComparer.Ordinal);
+
+ return new StringValues(newArray);
+ }
+ }
+
+ internal static StringValues GetOrderCasingNormalizedDictionary(IDictionary<string, string>? dictionary)
+ {
+ const char KeySubDelimiter = '\x1f';
+
+ if (dictionary == null || dictionary.Count == 0)
+ {
+ return StringValues.Empty;
+ }
+
+ var newArray = new string[dictionary.Count];
+
+ var i = 0;
+ foreach (var (key, value) in dictionary)
+ {
+ newArray[i++] = $"{key.ToUpperInvariant()}{KeySubDelimiter}{value}";
+ }
+
+ // Since the casing has already been normalized, use Ordinal comparison
+ Array.Sort(newArray, StringComparer.Ordinal);
+
+ return new StringValues(newArray);
+ }
+}
diff --git a/src/Middleware/OutputCaching/src/OutputCacheOptions.cs b/src/Middleware/OutputCaching/src/OutputCacheOptions.cs
new file mode 100644
index 0000000000..fdd316d4d3
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/OutputCacheOptions.cs
@@ -0,0 +1,96 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.ComponentModel;
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+/// <summary>
+/// Options for configuring the <see cref="OutputCacheMiddleware"/>.
+/// </summary>
+public class OutputCacheOptions
+{
+ /// <summary>
+ /// The size limit for the output cache middleware in bytes. The default is set to 100 MB.
+ /// When this limit is exceeded, no new responses will be cached until older entries are
+ /// evicted.
+ /// </summary>
+ public long SizeLimit { get; set; } = 100 * 1024 * 1024;
+
+ /// <summary>
+ /// The largest cacheable size for the response body in bytes. The default is set to 64 MB.
+ /// If the response body exceeds this limit, it will not be cached by the <see cref="OutputCacheMiddleware"/>.
+ /// </summary>
+ public long MaximumBodySize { get; set; } = 64 * 1024 * 1024;
+
+ /// <summary>
+ /// The duration a response is cached when no specific value is defined by a policy. The default is set to 60 seconds.
+ /// </summary>
+ public TimeSpan DefaultExpirationTimeSpan { get; set; } = TimeSpan.FromSeconds(60);
+
+ /// <summary>
+ /// <c>true</c> if request paths are case-sensitive; otherwise <c>false</c>. The default is to treat paths as case-insensitive.
+ /// </summary>
+ public bool UseCaseSensitivePaths { get; set; }
+
+ /// <summary>
+ /// Gets the application <see cref="IServiceProvider"/>.
+ /// </summary>
+ public IServiceProvider ApplicationServices { get; internal set; } = default!;
+
+ internal Dictionary<string, IOutputCachePolicy>? NamedPolicies { get; set; }
+
+ internal List<IOutputCachePolicy>? BasePolicies { get; set; }
+
+ /// <summary>
+ /// For testing purposes only.
+ /// </summary>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ internal ISystemClock SystemClock { get; set; } = new SystemClock();
+
+ /// <summary>
+ /// Defines a <see cref="IOutputCachePolicy"/> which can be referenced by name.
+ /// </summary>
+ /// <param name="name">The name of the policy.</param>
+ /// <param name="policy">The policy to add</param>
+ public void AddPolicy(string name, IOutputCachePolicy policy)
+ {
+ NamedPolicies ??= new Dictionary<string, IOutputCachePolicy>(StringComparer.OrdinalIgnoreCase);
+ NamedPolicies[name] = policy;
+ }
+
+ /// <summary>
+ /// Defines a <see cref="IOutputCachePolicy"/> which can be referenced by name.
+ /// </summary>
+ /// <param name="name">The name of the policy.</param>
+ /// <param name="build">an action on <see cref="OutputCachePolicyBuilder"/>.</param>
+ public void AddPolicy(string name, Action<OutputCachePolicyBuilder> build)
+ {
+ var builder = new OutputCachePolicyBuilder();
+ build(builder);
+ NamedPolicies ??= new Dictionary<string, IOutputCachePolicy>(StringComparer.OrdinalIgnoreCase);
+ NamedPolicies[name] = builder.Build();
+ }
+
+ /// <summary>
+ /// Adds an <see cref="IOutputCachePolicy"/> instance to base policies.
+ /// </summary>
+ /// <param name="policy">The policy to add</param>
+ public void AddBasePolicy(IOutputCachePolicy policy)
+ {
+ BasePolicies ??= new();
+ BasePolicies.Add(policy);
+ }
+
+ /// <summary>
+ /// Builds and adds an <see cref="IOutputCachePolicy"/> instance to base policies.
+ /// </summary>
+ /// <param name="build">an action on <see cref="OutputCachePolicyBuilder"/>.</param>
+ public void AddBasePolicy(Action<OutputCachePolicyBuilder> build)
+ {
+ var builder = new OutputCachePolicyBuilder();
+ build(builder);
+ BasePolicies ??= new();
+ BasePolicies.Add(builder.Build());
+ }
+}
diff --git a/src/Middleware/OutputCaching/src/OutputCacheOptionsSetup.cs b/src/Middleware/OutputCaching/src/OutputCacheOptionsSetup.cs
new file mode 100644
index 0000000000..76353e2d79
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/OutputCacheOptionsSetup.cs
@@ -0,0 +1,21 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.Extensions.Options;
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+internal sealed class OutputCacheOptionsSetup : IConfigureOptions<OutputCacheOptions>
+{
+ private readonly IServiceProvider _services;
+
+ public OutputCacheOptionsSetup(IServiceProvider services)
+ {
+ _services = services;
+ }
+
+ public void Configure(OutputCacheOptions options)
+ {
+ options.ApplicationServices = _services;
+ }
+}
diff --git a/src/Middleware/OutputCaching/src/OutputCachePolicyBuilder.cs b/src/Middleware/OutputCaching/src/OutputCachePolicyBuilder.cs
new file mode 100644
index 0000000000..e2c2e59403
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/OutputCachePolicyBuilder.cs
@@ -0,0 +1,244 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.OutputCaching.Policies;
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+/// <summary>
+/// Provides helper methods to create custom policies.
+/// </summary>
+public sealed class OutputCachePolicyBuilder
+{
+ private const DynamicallyAccessedMemberTypes ActivatorAccessibility = DynamicallyAccessedMemberTypes.PublicConstructors;
+
+ private IOutputCachePolicy? _builtPolicy;
+ private readonly List<IOutputCachePolicy> _policies = new();
+ private List<Func<OutputCacheContext, CancellationToken, Task<bool>>>? _requirements;
+
+ /// <summary>
+ /// Creates a new <see cref="OutputCachePolicyBuilder"/> instance.
+ /// </summary>
+ public OutputCachePolicyBuilder()
+ {
+ _builtPolicy = null;
+ _policies.Add(DefaultPolicy.Instance);
+ }
+
+ internal OutputCachePolicyBuilder AddPolicy(IOutputCachePolicy policy)
+ {
+ _builtPolicy = null;
+ _policies.Add(policy);
+ return this;
+ }
+
+ /// <summary>
+ /// Adds a dynamically resolved policy.
+ /// </summary>
+ /// <param name="policyType">The type of policy to add</param>
+ public OutputCachePolicyBuilder AddPolicy([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type policyType)
+ {
+ return AddPolicy(new TypedPolicy(policyType));
+ }
+
+ /// <summary>
+ /// Adds a dynamically resolved policy.
+ /// </summary>
+ /// <typeparam name="T">The policy type.</typeparam>
+ public OutputCachePolicyBuilder AddPolicy<[DynamicallyAccessedMembers(ActivatorAccessibility)] T>() where T : IOutputCachePolicy
+ {
+ return AddPolicy(typeof(T));
+ }
+
+ /// <summary>
+ /// Adds a requirement to the current policy.
+ /// </summary>
+ /// <param name="predicate">The predicate applied to the policy.</param>
+ public OutputCachePolicyBuilder With(Func<OutputCacheContext, CancellationToken, Task<bool>> predicate)
+ {
+ ArgumentNullException.ThrowIfNull(predicate);
+
+ _builtPolicy = null;
+ _requirements ??= new();
+ _requirements.Add(predicate);
+ return this;
+ }
+
+ /// <summary>
+ /// Adds a requirement to the current policy.
+ /// </summary>
+ /// <param name="predicate">The predicate applied to the policy.</param>
+ public OutputCachePolicyBuilder With(Func<OutputCacheContext, bool> predicate)
+ {
+ ArgumentNullException.ThrowIfNull(predicate);
+
+ _builtPolicy = null;
+ _requirements ??= new();
+ _requirements.Add((c, t) => Task.FromResult(predicate(c)));
+ return this;
+ }
+
+ /// <summary>
+ /// Adds a policy to vary the cached responses by query strings.
+ /// </summary>
+ /// <param name="queryKeys">The query keys to vary the cached responses by. Leave empty to ignore all query strings.</param>
+ /// <remarks>
+ /// By default all query keys vary the cache entries. However when specific query keys are specified only these are then taken into account.
+ /// </remarks>
+ public OutputCachePolicyBuilder VaryByQuery(params string[] queryKeys)
+ {
+ ArgumentNullException.ThrowIfNull(queryKeys);
+
+ return AddPolicy(new VaryByQueryPolicy(queryKeys));
+ }
+
+ /// <summary>
+ /// Adds a policy to vary the cached responses by header.
+ /// </summary>
+ /// <param name="headers">The headers to vary the cached responses by.</param>
+ public OutputCachePolicyBuilder VaryByHeader(params string[] headers)
+ {
+ ArgumentNullException.ThrowIfNull(headers);
+
+ return AddPolicy(new VaryByHeaderPolicy(headers));
+ }
+
+ /// <summary>
+ /// Adds a policy to vary the cached responses by custom values.
+ /// </summary>
+ /// <param name="varyBy">The value to vary the cached responses by.</param>
+ public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, CancellationToken, ValueTask<string>> varyBy)
+ {
+ ArgumentNullException.ThrowIfNull(varyBy);
+
+ return AddPolicy(new VaryByValuePolicy(varyBy));
+ }
+
+ /// <summary>
+ /// Adds a policy to vary the cached responses by custom key/value.
+ /// </summary>
+ /// <param name="varyBy">The key/value to vary the cached responses by.</param>
+ public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, CancellationToken, ValueTask<KeyValuePair<string, string>>> varyBy)
+ {
+ ArgumentNullException.ThrowIfNull(varyBy);
+
+ return AddPolicy(new VaryByValuePolicy(varyBy));
+ }
+
+ /// <summary>
+ /// Adds a policy to vary the cached responses by custom values.
+ /// </summary>
+ /// <param name="varyBy">The value to vary the cached responses by.</param>
+ public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, string> varyBy)
+ {
+ ArgumentNullException.ThrowIfNull(varyBy);
+
+ return AddPolicy(new VaryByValuePolicy(varyBy));
+ }
+
+ /// <summary>
+ /// Adds a policy to vary the cached responses by custom key/value.
+ /// </summary>
+ /// <param name="varyBy">The key/value to vary the cached responses by.</param>
+ public OutputCachePolicyBuilder VaryByValue(Func<HttpContext, KeyValuePair<string, string>> varyBy)
+ {
+ ArgumentNullException.ThrowIfNull(varyBy);
+
+ return AddPolicy(new VaryByValuePolicy(varyBy));
+ }
+
+ /// <summary>
+ /// Adds a policy to tag the cached response.
+ /// </summary>
+ /// <param name="tags">The tags to add to the cached reponse.</param>
+ public OutputCachePolicyBuilder Tag(params string[] tags)
+ {
+ ArgumentNullException.ThrowIfNull(tags);
+
+ return AddPolicy(new TagsPolicy(tags));
+ }
+
+ /// <summary>
+ /// Adds a policy to change the cached response expiration.
+ /// </summary>
+ /// <param name="expiration">The expiration of the cached reponse.</param>
+ public OutputCachePolicyBuilder Expire(TimeSpan expiration)
+ {
+ return AddPolicy(new ExpirationPolicy(expiration));
+ }
+
+ /// <summary>
+ /// Adds a policy to change the request locking strategy.
+ /// </summary>
+ /// <param name="lockResponse">Whether the request should be locked.</param>
+ public OutputCachePolicyBuilder AllowLocking(bool lockResponse = true)
+ {
+ return AddPolicy(lockResponse ? LockingPolicy.Enabled : LockingPolicy.Disabled);
+ }
+
+ /// <summary>
+ /// Clears the current policies.
+ /// </summary>
+ /// <remarks>It also removed the default cache policy.</remarks>
+ public OutputCachePolicyBuilder Clear()
+ {
+ _builtPolicy = null;
+ if (_requirements != null)
+ {
+ _requirements.Clear();
+ }
+ _policies.Clear();
+ return this;
+ }
+
+ /// <summary>
+ /// Clears the policies and adds one preventing any caching logic to happen.
+ /// </summary>
+ /// <remarks>
+ /// The cache key will never be computed.
+ /// </remarks>
+ public OutputCachePolicyBuilder NoCache()
+ {
+ _policies.Clear();
+ return AddPolicy(EnableCachePolicy.Disabled);
+ }
+
+ /// <summary>
+ /// Creates the <see cref="IOutputCachePolicy"/>.
+ /// </summary>
+ /// <returns>The<see cref="IOutputCachePolicy"/> instance.</returns>
+ internal IOutputCachePolicy Build()
+ {
+ if (_builtPolicy != null)
+ {
+ return _builtPolicy;
+ }
+
+ var policies = _policies.Count == 1
+ ? _policies[0]
+ : new CompositePolicy(_policies.ToArray())
+ ;
+
+ // If the policy was built with requirements, wrap it
+ if (_requirements != null && _requirements.Any())
+ {
+ policies = new PredicatePolicy(async c =>
+ {
+ foreach (var r in _requirements)
+ {
+ if (!await r(c, c.HttpContext.RequestAborted))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }, policies);
+ }
+
+ return _builtPolicy = policies;
+ }
+}
diff --git a/src/Middleware/OutputCaching/src/OutputCacheServiceCollectionExtensions.cs b/src/Middleware/OutputCaching/src/OutputCacheServiceCollectionExtensions.cs
new file mode 100644
index 0000000000..b0184e1339
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/OutputCacheServiceCollectionExtensions.cs
@@ -0,0 +1,58 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.AspNetCore.OutputCaching;
+using Microsoft.AspNetCore.OutputCaching.Memory;
+using Microsoft.Extensions.Caching.Memory;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Extensions.ObjectPool;
+using Microsoft.Extensions.Options;
+
+namespace Microsoft.Extensions.DependencyInjection;
+
+/// <summary>
+/// Extension methods for the OutputCaching middleware.
+/// </summary>
+public static class OutputCacheServiceCollectionExtensions
+{
+ /// <summary>
+ /// Add output caching services.
+ /// </summary>
+ /// <param name="services">The <see cref="IServiceCollection"/> for adding services.</param>
+ /// <returns></returns>
+ public static IServiceCollection AddOutputCache(this IServiceCollection services)
+ {
+ ArgumentNullException.ThrowIfNull(services);
+
+ services.AddTransient<IConfigureOptions<OutputCacheOptions>, OutputCacheOptionsSetup>();
+
+ services.TryAddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
+
+ services.TryAddSingleton<IOutputCacheStore>(sp =>
+ {
+ var outputCacheOptions = sp.GetRequiredService<IOptions<OutputCacheOptions>>();
+ return new MemoryOutputCacheStore(new MemoryCache(new MemoryCacheOptions
+ {
+ SizeLimit = outputCacheOptions.Value.SizeLimit
+ }));
+ });
+ return services;
+ }
+
+ /// <summary>
+ /// Add output caching services and configure the related options.
+ /// </summary>
+ /// <param name="services">The <see cref="IServiceCollection"/> for adding services.</param>
+ /// <param name="configureOptions">A delegate to configure the <see cref="OutputCacheOptions"/>.</param>
+ /// <returns></returns>
+ public static IServiceCollection AddOutputCache(this IServiceCollection services, Action<OutputCacheOptions> configureOptions)
+ {
+ ArgumentNullException.ThrowIfNull(services);
+ ArgumentNullException.ThrowIfNull(configureOptions);
+
+ services.Configure(configureOptions);
+ services.AddOutputCache();
+
+ return services;
+ }
+}
diff --git a/src/Middleware/OutputCaching/src/Policies/CompositePolicy.cs b/src/Middleware/OutputCaching/src/Policies/CompositePolicy.cs
new file mode 100644
index 0000000000..b82b54386d
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/Policies/CompositePolicy.cs
@@ -0,0 +1,48 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.OutputCaching.Policies;
+
+/// <summary>
+/// A composite policy.
+/// </summary>
+internal sealed class CompositePolicy : IOutputCachePolicy
+{
+ private readonly IOutputCachePolicy[] _policies;
+
+ /// <summary>
+ /// Creates a new instance of <see cref="CompositePolicy"/>
+ /// </summary>
+ /// <param name="policies">The policies to include.</param>
+ public CompositePolicy(params IOutputCachePolicy[] policies)
+ {
+ _policies = policies;
+ }
+
+ /// <inheritdoc/>
+ async ValueTask IOutputCachePolicy.CacheRequestAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ foreach (var policy in _policies)
+ {
+ await policy.CacheRequestAsync(context, cancellationToken);
+ }
+ }
+
+ /// <inheritdoc/>
+ async ValueTask IOutputCachePolicy.ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ foreach (var policy in _policies)
+ {
+ await policy.ServeFromCacheAsync(context, cancellationToken);
+ }
+ }
+
+ /// <inheritdoc/>
+ async ValueTask IOutputCachePolicy.ServeResponseAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ foreach (var policy in _policies)
+ {
+ await policy.ServeResponseAsync(context, cancellationToken);
+ }
+ }
+}
diff --git a/src/Middleware/OutputCaching/src/Policies/DefaultPolicy.cs b/src/Middleware/OutputCaching/src/Policies/DefaultPolicy.cs
new file mode 100644
index 0000000000..c17a700b0f
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/Policies/DefaultPolicy.cs
@@ -0,0 +1,87 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.AspNetCore.Http;
+using Microsoft.Extensions.Primitives;
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+/// <summary>
+/// A policy which caches un-authenticated, GET and HEAD, 200 responses.
+/// </summary>
+internal sealed class DefaultPolicy : IOutputCachePolicy
+{
+ public static readonly DefaultPolicy Instance = new();
+
+ private DefaultPolicy()
+ {
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.CacheRequestAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ var attemptOutputCaching = AttemptOutputCaching(context);
+ context.EnableOutputCaching = true;
+ context.AllowCacheLookup = attemptOutputCaching;
+ context.AllowCacheStorage = attemptOutputCaching;
+ context.AllowLocking = true;
+
+ // Vary by any query by default
+ context.CacheVaryByRules.QueryKeys = "*";
+
+ return ValueTask.CompletedTask;
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ return ValueTask.CompletedTask;
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.ServeResponseAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ var response = context.HttpContext.Response;
+
+ // Verify existence of cookie headers
+ if (!StringValues.IsNullOrEmpty(response.Headers.SetCookie))
+ {
+ context.Logger.ResponseWithSetCookieNotCacheable();
+ context.AllowCacheStorage = false;
+ return ValueTask.CompletedTask;
+ }
+
+ // Check response code
+ if (response.StatusCode != StatusCodes.Status200OK)
+ {
+ context.Logger.ResponseWithUnsuccessfulStatusCodeNotCacheable(response.StatusCode);
+ context.AllowCacheStorage = false;
+ return ValueTask.CompletedTask;
+ }
+
+ return ValueTask.CompletedTask;
+ }
+
+ private static bool AttemptOutputCaching(OutputCacheContext context)
+ {
+ // Check if the current request fulfisls the requirements to be cached
+
+ var request = context.HttpContext.Request;
+
+ // Verify the method
+ if (!HttpMethods.IsGet(request.Method) && !HttpMethods.IsHead(request.Method))
+ {
+ context.Logger.RequestMethodNotCacheable(request.Method);
+ return false;
+ }
+
+ // Verify existence of authorization headers
+ if (!StringValues.IsNullOrEmpty(request.Headers.Authorization) || request.HttpContext.User?.Identity?.IsAuthenticated == true)
+ {
+ context.Logger.RequestWithAuthorizationNotCacheable();
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/src/Middleware/OutputCaching/src/Policies/EnableCachePolicy.cs b/src/Middleware/OutputCaching/src/Policies/EnableCachePolicy.cs
new file mode 100644
index 0000000000..3ebb8d4764
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/Policies/EnableCachePolicy.cs
@@ -0,0 +1,37 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+/// <summary>
+/// A policy that enables caching
+/// </summary>
+internal sealed class EnableCachePolicy : IOutputCachePolicy
+{
+ public static readonly EnableCachePolicy Enabled = new();
+ public static readonly EnableCachePolicy Disabled = new();
+
+ private EnableCachePolicy()
+ {
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.CacheRequestAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ context.EnableOutputCaching = this == Enabled;
+
+ return ValueTask.CompletedTask;
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.ServeResponseAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ return ValueTask.CompletedTask;
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ return ValueTask.CompletedTask;
+ }
+}
diff --git a/src/Middleware/OutputCaching/src/Policies/ExpirationPolicy.cs b/src/Middleware/OutputCaching/src/Policies/ExpirationPolicy.cs
new file mode 100644
index 0000000000..5a9f963f63
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/Policies/ExpirationPolicy.cs
@@ -0,0 +1,41 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+/// <summary>
+/// A policy that defines a custom expiration duration.
+/// </summary>
+internal sealed class ExpirationPolicy : IOutputCachePolicy
+{
+ private readonly TimeSpan _expiration;
+
+ /// <summary>
+ /// Creates a new <see cref="ExpirationPolicy"/> instance.
+ /// </summary>
+ /// <param name="expiration">The expiration duration.</param>
+ public ExpirationPolicy(TimeSpan expiration)
+ {
+ _expiration = expiration;
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.CacheRequestAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ context.ResponseExpirationTimeSpan = _expiration;
+
+ return ValueTask.CompletedTask;
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ return ValueTask.CompletedTask;
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.ServeResponseAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ return ValueTask.CompletedTask;
+ }
+}
diff --git a/src/Middleware/OutputCaching/src/Policies/LockingPolicy.cs b/src/Middleware/OutputCaching/src/Policies/LockingPolicy.cs
new file mode 100644
index 0000000000..504ac4e6a2
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/Policies/LockingPolicy.cs
@@ -0,0 +1,47 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+/// <summary>
+/// A policy that changes the locking behavior.
+/// </summary>
+internal sealed class LockingPolicy : IOutputCachePolicy
+{
+ private readonly bool _lockResponse;
+
+ private LockingPolicy(bool lockResponse)
+ {
+ _lockResponse = lockResponse;
+ }
+
+ /// <summary>
+ /// A policy that enables locking.
+ /// </summary>
+ public static readonly LockingPolicy Enabled = new(true);
+
+ /// <summary>
+ /// A policy that disables locking.
+ /// </summary>
+ public static readonly LockingPolicy Disabled = new(false);
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.CacheRequestAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ context.AllowLocking = _lockResponse;
+
+ return ValueTask.CompletedTask;
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ return ValueTask.CompletedTask;
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.ServeResponseAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ return ValueTask.CompletedTask;
+ }
+}
diff --git a/src/Middleware/OutputCaching/src/Policies/NamedPolicy.cs b/src/Middleware/OutputCaching/src/Policies/NamedPolicy.cs
new file mode 100644
index 0000000000..4967d8b1b3
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/Policies/NamedPolicy.cs
@@ -0,0 +1,69 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+/// <summary>
+/// A named policy.
+/// </summary>
+internal sealed class NamedPolicy : IOutputCachePolicy
+{
+ private readonly string _policyName;
+
+ /// <summary>
+ /// Create a new <see cref="NamedPolicy"/> instance.
+ /// </summary>
+ /// <param name="policyName">The name of the profile.</param>
+ public NamedPolicy(string policyName)
+ {
+ _policyName = policyName;
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.ServeResponseAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ var policy = GetProfilePolicy(context);
+
+ if (policy == null)
+ {
+ return ValueTask.CompletedTask;
+ }
+
+ return policy.ServeResponseAsync(context, cancellationToken);
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ var policy = GetProfilePolicy(context);
+
+ if (policy == null)
+ {
+ return ValueTask.CompletedTask;
+ }
+
+ return policy.ServeFromCacheAsync(context, cancellationToken);
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.CacheRequestAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ var policy = GetProfilePolicy(context);
+
+ if (policy == null)
+ {
+ return ValueTask.CompletedTask;
+ }
+
+ return policy.CacheRequestAsync(context, cancellationToken); ;
+ }
+
+ internal IOutputCachePolicy? GetProfilePolicy(OutputCacheContext context)
+ {
+ var policies = context.Options.NamedPolicies;
+
+ return policies != null && policies.TryGetValue(_policyName, out var cacheProfile)
+ ? cacheProfile
+ : null;
+ }
+}
diff --git a/src/Middleware/OutputCaching/src/Policies/NoLookupPolicy.cs b/src/Middleware/OutputCaching/src/Policies/NoLookupPolicy.cs
new file mode 100644
index 0000000000..d6fee5da0d
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/Policies/NoLookupPolicy.cs
@@ -0,0 +1,36 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+/// <summary>
+/// A policy that prevents the response from being served from cache.
+/// </summary>
+internal sealed class NoLookupPolicy : IOutputCachePolicy
+{
+ public static NoLookupPolicy Instance = new();
+
+ private NoLookupPolicy()
+ {
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.ServeResponseAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ context.AllowCacheLookup = false;
+
+ return ValueTask.CompletedTask;
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ return ValueTask.CompletedTask;
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.CacheRequestAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ return ValueTask.CompletedTask;
+ }
+}
diff --git a/src/Middleware/OutputCaching/src/Policies/NoStorePolicy.cs b/src/Middleware/OutputCaching/src/Policies/NoStorePolicy.cs
new file mode 100644
index 0000000000..11a22b6c5e
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/Policies/NoStorePolicy.cs
@@ -0,0 +1,36 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+/// <summary>
+/// A policy that prevents the response from being cached.
+/// </summary>
+internal sealed class NoStorePolicy : IOutputCachePolicy
+{
+ public static NoStorePolicy Instance = new();
+
+ private NoStorePolicy()
+ {
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.ServeResponseAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ context.AllowCacheStorage = false;
+
+ return ValueTask.CompletedTask;
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ return ValueTask.CompletedTask;
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.CacheRequestAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ return ValueTask.CompletedTask;
+ }
+}
diff --git a/src/Middleware/OutputCaching/src/Policies/OutputCacheConventionBuilderExtensions.cs b/src/Middleware/OutputCaching/src/Policies/OutputCacheConventionBuilderExtensions.cs
new file mode 100644
index 0000000000..050fbeb84e
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/Policies/OutputCacheConventionBuilderExtensions.cs
@@ -0,0 +1,81 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.OutputCaching;
+
+namespace Microsoft.Extensions.DependencyInjection;
+
+/// <summary>
+/// A set of endpoint extension methods.
+/// </summary>
+public static class OutputCacheConventionBuilderExtensions
+{
+ /// <summary>
+ /// Marks an endpoint to be cached with the default policy.
+ /// </summary>
+ public static TBuilder CacheOutput<TBuilder>(this TBuilder builder) where TBuilder : IEndpointConventionBuilder
+ {
+ ArgumentNullException.ThrowIfNull(builder);
+
+ // Enable caching if this method is invoked on an endpoint, extra policies can disable it
+
+ builder.Add(endpointBuilder =>
+ {
+ endpointBuilder.Metadata.Add(DefaultPolicy.Instance);
+ });
+ return builder;
+ }
+
+ /// <summary>
+ /// Marks an endpoint to be cached with the specified policy.
+ /// </summary>
+ public static TBuilder CacheOutput<TBuilder>(this TBuilder builder, IOutputCachePolicy policy) where TBuilder : IEndpointConventionBuilder
+ {
+ ArgumentNullException.ThrowIfNull(builder);
+
+ // Enable caching if this method is invoked on an endpoint, extra policies can disable it
+
+ builder.Add(endpointBuilder =>
+ {
+ endpointBuilder.Metadata.Add(policy);
+ });
+ return builder;
+ }
+
+ /// <summary>
+ /// Marks an endpoint to be cached using the specified policy builder.
+ /// </summary>
+ public static TBuilder CacheOutput<TBuilder>(this TBuilder builder, Action<OutputCachePolicyBuilder> policy) where TBuilder : IEndpointConventionBuilder
+ {
+ ArgumentNullException.ThrowIfNull(builder);
+
+ var outputCachePolicyBuilder = new OutputCachePolicyBuilder();
+
+ policy?.Invoke(outputCachePolicyBuilder);
+
+ builder.Add(endpointBuilder =>
+ {
+ endpointBuilder.Metadata.Add(outputCachePolicyBuilder.Build());
+ });
+
+ return builder;
+ }
+
+ /// <summary>
+ /// Marks an endpoint to be cached using a named policy.
+ /// </summary>
+ public static TBuilder CacheOutput<TBuilder>(this TBuilder builder, string policyName) where TBuilder : IEndpointConventionBuilder
+ {
+ ArgumentNullException.ThrowIfNull(builder);
+
+ var policy = new NamedPolicy(policyName);
+
+ builder.Add(endpointBuilder =>
+ {
+ endpointBuilder.Metadata.Add(policy);
+ });
+
+ return builder;
+ }
+}
diff --git a/src/Middleware/OutputCaching/src/Policies/PredicatePolicy.cs b/src/Middleware/OutputCaching/src/Policies/PredicatePolicy.cs
new file mode 100644
index 0000000000..d58c67fa57
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/Policies/PredicatePolicy.cs
@@ -0,0 +1,76 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.OutputCaching.Policies;
+
+/// <summary>
+/// A policy that adds a requirement to another policy.
+/// </summary>
+internal sealed class PredicatePolicy : IOutputCachePolicy
+{
+ // TODO: Accept a non async predicate too?
+
+ private readonly Func<OutputCacheContext, ValueTask<bool>> _predicate;
+ private readonly IOutputCachePolicy _policy;
+
+ /// <summary>
+ /// Creates a new <see cref="PredicatePolicy"/> instance.
+ /// </summary>
+ /// <param name="asyncPredicate">The predicate.</param>
+ /// <param name="policy">The policy.</param>
+ public PredicatePolicy(Func<OutputCacheContext, ValueTask<bool>> asyncPredicate, IOutputCachePolicy policy)
+ {
+ _predicate = asyncPredicate;
+ _policy = policy;
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.CacheRequestAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ return ExecuteAwaited(static (policy, context, cancellationToken) => policy.CacheRequestAsync(context, cancellationToken), _policy, context, cancellationToken);
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ return ExecuteAwaited(static (policy, context, cancellationToken) => policy.ServeFromCacheAsync(context, cancellationToken), _policy, context, cancellationToken);
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.ServeResponseAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ return ExecuteAwaited(static (policy, context, cancellationToken) => policy.ServeResponseAsync(context, cancellationToken), _policy, context, cancellationToken);
+ }
+
+ private ValueTask ExecuteAwaited(Func<IOutputCachePolicy, OutputCacheContext, CancellationToken, ValueTask> action, IOutputCachePolicy policy, OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ ArgumentNullException.ThrowIfNull(action);
+
+ if (_predicate == null)
+ {
+ return action(policy, context, cancellationToken);
+ }
+
+ var task = _predicate(context);
+
+ if (task.IsCompletedSuccessfully)
+ {
+ if (task.Result)
+ {
+ return action(policy, context, cancellationToken);
+ }
+
+ return ValueTask.CompletedTask;
+ }
+
+ return Awaited(task);
+
+ async ValueTask Awaited(ValueTask<bool> task)
+ {
+ if (await task)
+ {
+ await action(policy, context, cancellationToken);
+ }
+ }
+ }
+}
diff --git a/src/Middleware/OutputCaching/src/Policies/TagsPolicy.cs b/src/Middleware/OutputCaching/src/Policies/TagsPolicy.cs
new file mode 100644
index 0000000000..070e2a66b1
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/Policies/TagsPolicy.cs
@@ -0,0 +1,44 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+/// <summary>
+/// A policy that defines custom tags on the cache entry.
+/// </summary>
+internal sealed class TagsPolicy : IOutputCachePolicy
+{
+ private readonly string[] _tags;
+
+ /// <summary>
+ /// Creates a new <see cref="TagsPolicy"/> instance.
+ /// </summary>
+ /// <param name="tags">The tags.</param>
+ public TagsPolicy(params string[] tags)
+ {
+ _tags = tags;
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.CacheRequestAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ foreach (var tag in _tags)
+ {
+ context.Tags.Add(tag);
+ }
+
+ return ValueTask.CompletedTask;
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ return ValueTask.CompletedTask;
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.ServeResponseAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ return ValueTask.CompletedTask;
+ }
+}
diff --git a/src/Middleware/OutputCaching/src/Policies/TypedPolicy.cs b/src/Middleware/OutputCaching/src/Policies/TypedPolicy.cs
new file mode 100644
index 0000000000..d01f60a243
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/Policies/TypedPolicy.cs
@@ -0,0 +1,52 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Microsoft.AspNetCore.OutputCaching.Policies;
+
+/// <summary>
+/// A type base policy.
+/// </summary>
+internal sealed class TypedPolicy : IOutputCachePolicy
+{
+ private IOutputCachePolicy? _instance;
+
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
+ private readonly Type _policyType;
+
+ /// <summary>
+ /// Creates a new instance of <see cref="TypedPolicy"/>
+ /// </summary>
+ /// <param name="policyType">The type of policy.</param>
+ public TypedPolicy([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type policyType)
+ {
+ ArgumentNullException.ThrowIfNull(policyType);
+
+ _policyType = policyType;
+ }
+
+ private IOutputCachePolicy? CreatePolicy(OutputCacheContext context)
+ {
+ return _instance ??= ActivatorUtilities.CreateInstance(context.Options.ApplicationServices, _policyType) as IOutputCachePolicy;
+ }
+
+ /// <inheritdoc/>
+ ValueTask IOutputCachePolicy.CacheRequestAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ return CreatePolicy(context)?.CacheRequestAsync(context, cancellationToken) ?? ValueTask.CompletedTask;
+ }
+
+ /// <inheritdoc/>
+ ValueTask IOutputCachePolicy.ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ return CreatePolicy(context)?.ServeFromCacheAsync(context, cancellationToken) ?? ValueTask.CompletedTask;
+ }
+
+ /// <inheritdoc/>
+ ValueTask IOutputCachePolicy.ServeResponseAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ return CreatePolicy(context)?.ServeResponseAsync(context, cancellationToken) ?? ValueTask.CompletedTask;
+ }
+}
diff --git a/src/Middleware/OutputCaching/src/Policies/VaryByHeaderPolicy.cs b/src/Middleware/OutputCaching/src/Policies/VaryByHeaderPolicy.cs
new file mode 100644
index 0000000000..35ed3ca16a
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/Policies/VaryByHeaderPolicy.cs
@@ -0,0 +1,68 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.Extensions.Primitives;
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+/// <summary>
+/// When applied, the cached content will be different for every value of the provided headers.
+/// </summary>
+internal sealed class VaryByHeaderPolicy : IOutputCachePolicy
+{
+ private readonly StringValues _headers;
+
+ /// <summary>
+ /// Creates a policy that doesn't vary the cached content based on headers.
+ /// </summary>
+ public VaryByHeaderPolicy()
+ {
+ }
+
+ /// <summary>
+ /// Creates a policy that varies the cached content based on the specified header.
+ /// </summary>
+ public VaryByHeaderPolicy(string header)
+ {
+ ArgumentNullException.ThrowIfNull(header);
+
+ _headers = header;
+ }
+
+ /// <summary>
+ /// Creates a policy that varies the cached content based on the specified query string keys.
+ /// </summary>
+ public VaryByHeaderPolicy(params string[] headers)
+ {
+ ArgumentNullException.ThrowIfNull(headers);
+
+ _headers = headers;
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.CacheRequestAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ // No vary by header?
+ if (_headers.Count == 0)
+ {
+ context.CacheVaryByRules.Headers = _headers;
+ return ValueTask.CompletedTask;
+ }
+
+ context.CacheVaryByRules.Headers = StringValues.Concat(context.CacheVaryByRules.Headers, _headers);
+
+ return ValueTask.CompletedTask;
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ return ValueTask.CompletedTask;
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.ServeResponseAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ return ValueTask.CompletedTask;
+ }
+}
diff --git a/src/Middleware/OutputCaching/src/Policies/VaryByQueryPolicy.cs b/src/Middleware/OutputCaching/src/Policies/VaryByQueryPolicy.cs
new file mode 100644
index 0000000000..e415459644
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/Policies/VaryByQueryPolicy.cs
@@ -0,0 +1,72 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.Extensions.Primitives;
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+/// <summary>
+/// When applied, the cached content will be different for every value of the provided query string keys.
+/// It also disables the default behavior which is to vary on all query string keys.
+/// </summary>
+internal sealed class VaryByQueryPolicy : IOutputCachePolicy
+{
+ private readonly StringValues _queryKeys;
+
+ /// <summary>
+ /// Creates a policy that doesn't vary the cached content based on query string.
+ /// </summary>
+ public VaryByQueryPolicy()
+ {
+ }
+
+ /// <summary>
+ /// Creates a policy that varies the cached content based on the specified query string key.
+ /// </summary>
+ public VaryByQueryPolicy(string queryKey)
+ {
+ _queryKeys = queryKey;
+ }
+
+ /// <summary>
+ /// Creates a policy that varies the cached content based on the specified query string keys.
+ /// </summary>
+ public VaryByQueryPolicy(params string[] queryKeys)
+ {
+ _queryKeys = queryKeys;
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.CacheRequestAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ // No vary by query?
+ if (_queryKeys.Count == 0)
+ {
+ context.CacheVaryByRules.QueryKeys = _queryKeys;
+ return ValueTask.CompletedTask;
+ }
+
+ // If the current key is "*" (default) replace it
+ if (context.CacheVaryByRules.QueryKeys.Count == 1 && string.Equals(context.CacheVaryByRules.QueryKeys[0], "*", StringComparison.Ordinal))
+ {
+ context.CacheVaryByRules.QueryKeys = _queryKeys;
+ return ValueTask.CompletedTask;
+ }
+
+ context.CacheVaryByRules.QueryKeys = StringValues.Concat(context.CacheVaryByRules.QueryKeys, _queryKeys);
+
+ return ValueTask.CompletedTask;
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ return ValueTask.CompletedTask;
+ }
+
+ /// <inheritdoc />
+ ValueTask IOutputCachePolicy.ServeResponseAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ return ValueTask.CompletedTask;
+ }
+}
diff --git a/src/Middleware/OutputCaching/src/Policies/VaryByValuePolicy.cs b/src/Middleware/OutputCaching/src/Policies/VaryByValuePolicy.cs
new file mode 100644
index 0000000000..5de5366307
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/Policies/VaryByValuePolicy.cs
@@ -0,0 +1,82 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.AspNetCore.Http;
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+/// <summary>
+/// When applied, the cached content will be different for every provided value.
+/// </summary>
+internal sealed class VaryByValuePolicy : IOutputCachePolicy
+{
+ private readonly Action<HttpContext, CacheVaryByRules>? _varyBy;
+ private readonly Func<HttpContext, CacheVaryByRules, CancellationToken, ValueTask>? _varyByAsync;
+
+ /// <summary>
+ /// Creates a policy that doesn't vary the cached content based on values.
+ /// </summary>
+ public VaryByValuePolicy()
+ {
+ }
+
+ /// <summary>
+ /// Creates a policy that vary the cached content based on the specified value.
+ /// </summary>
+ public VaryByValuePolicy(Func<HttpContext, string> varyBy)
+ {
+ _varyBy = (context, rules) => rules.VaryByPrefix += varyBy(context);
+ }
+
+ /// <summary>
+ /// Creates a policy that vary the cached content based on the specified value.
+ /// </summary>
+ public VaryByValuePolicy(Func<HttpContext, CancellationToken, ValueTask<string>> varyBy)
+ {
+ _varyByAsync = async (context, rules, token) => rules.VaryByPrefix += await varyBy(context, token);
+ }
+
+ /// <summary>
+ /// Creates a policy that vary the cached content based on the specified value.
+ /// </summary>
+ public VaryByValuePolicy(Func<HttpContext, KeyValuePair<string, string>> varyBy)
+ {
+ _varyBy = (context, rules) =>
+ {
+ var result = varyBy(context);
+ rules.VaryByCustom?.TryAdd(result.Key, result.Value);
+ };
+ }
+
+ /// <summary>
+ /// Creates a policy that vary the cached content based on the specified value.
+ /// </summary>
+ public VaryByValuePolicy(Func<HttpContext, CancellationToken, ValueTask<KeyValuePair<string, string>>> varyBy)
+ {
+ _varyBy = async (context, rules) =>
+ {
+ var result = await varyBy(context, context.RequestAborted);
+ rules.VaryByCustom?.TryAdd(result.Key, result.Value);
+ };
+ }
+
+ /// <inheritdoc/>
+ ValueTask IOutputCachePolicy.CacheRequestAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ _varyBy?.Invoke(context.HttpContext, context.CacheVaryByRules);
+
+ return _varyByAsync?.Invoke(context.HttpContext, context.CacheVaryByRules, context.HttpContext.RequestAborted) ?? ValueTask.CompletedTask;
+ }
+
+ /// <inheritdoc/>
+ ValueTask IOutputCachePolicy.ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ return ValueTask.CompletedTask;
+ }
+
+ /// <inheritdoc/>
+ ValueTask IOutputCachePolicy.ServeResponseAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ return ValueTask.CompletedTask;
+ }
+}
diff --git a/src/Middleware/OutputCaching/src/PublicAPI.Shipped.txt b/src/Middleware/OutputCaching/src/PublicAPI.Shipped.txt
new file mode 100644
index 0000000000..7dc5c58110
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/PublicAPI.Shipped.txt
@@ -0,0 +1 @@
+#nullable enable
diff --git a/src/Middleware/OutputCaching/src/PublicAPI.Unshipped.txt b/src/Middleware/OutputCaching/src/PublicAPI.Unshipped.txt
new file mode 100644
index 0000000000..779e48f353
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/PublicAPI.Unshipped.txt
@@ -0,0 +1,90 @@
+#nullable enable
+Microsoft.AspNetCore.Builder.OutputCacheApplicationBuilderExtensions
+Microsoft.AspNetCore.OutputCaching.CacheVaryByRules
+Microsoft.AspNetCore.OutputCaching.CacheVaryByRules.CacheVaryByRules() -> void
+Microsoft.AspNetCore.OutputCaching.CacheVaryByRules.Headers.get -> Microsoft.Extensions.Primitives.StringValues
+Microsoft.AspNetCore.OutputCaching.CacheVaryByRules.Headers.set -> void
+Microsoft.AspNetCore.OutputCaching.CacheVaryByRules.QueryKeys.get -> Microsoft.Extensions.Primitives.StringValues
+Microsoft.AspNetCore.OutputCaching.CacheVaryByRules.QueryKeys.set -> void
+Microsoft.AspNetCore.OutputCaching.CacheVaryByRules.VaryByCustom.get -> System.Collections.Generic.IDictionary<string!, string!>!
+Microsoft.AspNetCore.OutputCaching.CacheVaryByRules.VaryByPrefix.get -> Microsoft.Extensions.Primitives.StringValues
+Microsoft.AspNetCore.OutputCaching.CacheVaryByRules.VaryByPrefix.set -> void
+Microsoft.AspNetCore.OutputCaching.IOutputCacheFeature
+Microsoft.AspNetCore.OutputCaching.IOutputCacheFeature.Context.get -> Microsoft.AspNetCore.OutputCaching.OutputCacheContext!
+Microsoft.AspNetCore.OutputCaching.IOutputCachePolicy.CacheRequestAsync(Microsoft.AspNetCore.OutputCaching.OutputCacheContext! context, System.Threading.CancellationToken cancellation) -> System.Threading.Tasks.ValueTask
+Microsoft.AspNetCore.OutputCaching.IOutputCachePolicy.ServeFromCacheAsync(Microsoft.AspNetCore.OutputCaching.OutputCacheContext! context, System.Threading.CancellationToken cancellation) -> System.Threading.Tasks.ValueTask
+Microsoft.AspNetCore.OutputCaching.IOutputCachePolicy.ServeResponseAsync(Microsoft.AspNetCore.OutputCaching.OutputCacheContext! context, System.Threading.CancellationToken cancellation) -> System.Threading.Tasks.ValueTask
+Microsoft.AspNetCore.OutputCaching.IOutputCacheStore
+Microsoft.AspNetCore.OutputCaching.IOutputCacheStore.EvictByTagAsync(string! tag, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask
+Microsoft.AspNetCore.OutputCaching.IOutputCachePolicy
+Microsoft.AspNetCore.OutputCaching.IOutputCacheStore.GetAsync(string! key, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask<byte[]?>
+Microsoft.AspNetCore.OutputCaching.IOutputCacheStore.SetAsync(string! key, byte[]! value, string![]? tags, System.TimeSpan validFor, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask
+Microsoft.AspNetCore.OutputCaching.OutputCacheAttribute
+Microsoft.AspNetCore.OutputCaching.OutputCacheAttribute.Duration.get -> int
+Microsoft.AspNetCore.OutputCaching.OutputCacheAttribute.Duration.init -> void
+Microsoft.AspNetCore.OutputCaching.OutputCacheAttribute.NoStore.get -> bool
+Microsoft.AspNetCore.OutputCaching.OutputCacheAttribute.NoStore.init -> void
+Microsoft.AspNetCore.OutputCaching.OutputCacheAttribute.OutputCacheAttribute() -> void
+Microsoft.AspNetCore.OutputCaching.OutputCacheAttribute.PolicyName.get -> string?
+Microsoft.AspNetCore.OutputCaching.OutputCacheAttribute.PolicyName.init -> void
+Microsoft.AspNetCore.OutputCaching.OutputCacheAttribute.VaryByHeaders.get -> string![]?
+Microsoft.AspNetCore.OutputCaching.OutputCacheAttribute.VaryByHeaders.init -> void
+Microsoft.AspNetCore.OutputCaching.OutputCacheAttribute.VaryByQueryKeys.get -> string![]?
+Microsoft.AspNetCore.OutputCaching.OutputCacheAttribute.VaryByQueryKeys.init -> void
+Microsoft.AspNetCore.OutputCaching.OutputCacheContext.EnableOutputCaching.get -> bool
+Microsoft.AspNetCore.OutputCaching.OutputCacheContext.EnableOutputCaching.set -> void
+Microsoft.AspNetCore.OutputCaching.OutputCacheOptions.AddBasePolicy(Microsoft.AspNetCore.OutputCaching.IOutputCachePolicy! policy) -> void
+Microsoft.AspNetCore.OutputCaching.OutputCacheOptions.AddBasePolicy(System.Action<Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder!>! build) -> void
+Microsoft.AspNetCore.OutputCaching.OutputCacheOptions.AddPolicy(string! name, Microsoft.AspNetCore.OutputCaching.IOutputCachePolicy! policy) -> void
+Microsoft.AspNetCore.OutputCaching.OutputCacheOptions.AddPolicy(string! name, System.Action<Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder!>! build) -> void
+Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder
+Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder.AddPolicy(System.Type! policyType) -> Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder!
+Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder.AddPolicy<T>() -> Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder!
+Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder.AllowLocking(bool lockResponse = true) -> Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder!
+Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder.Clear() -> Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder!
+Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder.Expire(System.TimeSpan expiration) -> Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder!
+Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder.NoCache() -> Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder!
+Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder.OutputCachePolicyBuilder() -> void
+Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder.Tag(params string![]! tags) -> Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder!
+Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder.VaryByHeader(params string![]! headers) -> Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder!
+Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder.VaryByQuery(params string![]! queryKeys) -> Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder!
+Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder.VaryByValue(System.Func<Microsoft.AspNetCore.Http.HttpContext!, System.Collections.Generic.KeyValuePair<string!, string!>>! varyBy) -> Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder!
+Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder.VaryByValue(System.Func<Microsoft.AspNetCore.Http.HttpContext!, string!>! varyBy) -> Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder!
+Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder.VaryByValue(System.Func<Microsoft.AspNetCore.Http.HttpContext!, System.Threading.CancellationToken, System.Threading.Tasks.ValueTask<string!>>! varyBy) -> Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder!
+Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder.VaryByValue(System.Func<Microsoft.AspNetCore.Http.HttpContext!, System.Threading.CancellationToken, System.Threading.Tasks.ValueTask<System.Collections.Generic.KeyValuePair<string!, string!>>>! varyBy) -> Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder!
+Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder.With(System.Func<Microsoft.AspNetCore.OutputCaching.OutputCacheContext!, System.Threading.CancellationToken, System.Threading.Tasks.Task<bool>!>! predicate) -> Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder!
+Microsoft.AspNetCore.OutputCaching.OutputCacheContext
+Microsoft.AspNetCore.OutputCaching.OutputCacheContext.AllowCacheLookup.get -> bool
+Microsoft.AspNetCore.OutputCaching.OutputCacheContext.AllowCacheLookup.set -> void
+Microsoft.AspNetCore.OutputCaching.OutputCacheContext.AllowCacheStorage.get -> bool
+Microsoft.AspNetCore.OutputCaching.OutputCacheContext.AllowCacheStorage.set -> void
+Microsoft.AspNetCore.OutputCaching.OutputCacheContext.AllowLocking.get -> bool
+Microsoft.AspNetCore.OutputCaching.OutputCacheContext.AllowLocking.set -> void
+Microsoft.AspNetCore.OutputCaching.OutputCacheContext.ResponseExpirationTimeSpan.get -> System.TimeSpan?
+Microsoft.AspNetCore.OutputCaching.OutputCacheContext.ResponseExpirationTimeSpan.set -> void
+Microsoft.AspNetCore.OutputCaching.OutputCacheContext.ResponseTime.get -> System.DateTimeOffset?
+Microsoft.AspNetCore.OutputCaching.OutputCacheOptions
+Microsoft.AspNetCore.OutputCaching.OutputCacheOptions.ApplicationServices.get -> System.IServiceProvider!
+Microsoft.AspNetCore.OutputCaching.OutputCacheOptions.DefaultExpirationTimeSpan.get -> System.TimeSpan
+Microsoft.AspNetCore.OutputCaching.OutputCacheOptions.DefaultExpirationTimeSpan.set -> void
+Microsoft.AspNetCore.OutputCaching.OutputCacheOptions.MaximumBodySize.get -> long
+Microsoft.AspNetCore.OutputCaching.OutputCacheOptions.MaximumBodySize.set -> void
+Microsoft.AspNetCore.OutputCaching.OutputCacheOptions.OutputCacheOptions() -> void
+Microsoft.AspNetCore.OutputCaching.OutputCacheOptions.SizeLimit.get -> long
+Microsoft.AspNetCore.OutputCaching.OutputCacheOptions.SizeLimit.set -> void
+Microsoft.AspNetCore.OutputCaching.OutputCacheOptions.UseCaseSensitivePaths.get -> bool
+Microsoft.AspNetCore.OutputCaching.OutputCacheOptions.UseCaseSensitivePaths.set -> void
+Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder.With(System.Func<Microsoft.AspNetCore.OutputCaching.OutputCacheContext!, bool>! predicate) -> Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder!
+Microsoft.Extensions.DependencyInjection.OutputCacheConventionBuilderExtensions
+Microsoft.Extensions.DependencyInjection.OutputCacheServiceCollectionExtensions
+static Microsoft.AspNetCore.Builder.OutputCacheApplicationBuilderExtensions.UseOutputCache(this Microsoft.AspNetCore.Builder.IApplicationBuilder! app) -> Microsoft.AspNetCore.Builder.IApplicationBuilder!
+static Microsoft.Extensions.DependencyInjection.OutputCacheConventionBuilderExtensions.CacheOutput<TBuilder>(this TBuilder builder) -> TBuilder
+static Microsoft.Extensions.DependencyInjection.OutputCacheConventionBuilderExtensions.CacheOutput<TBuilder>(this TBuilder builder, Microsoft.AspNetCore.OutputCaching.IOutputCachePolicy! policy) -> TBuilder
+static Microsoft.Extensions.DependencyInjection.OutputCacheConventionBuilderExtensions.CacheOutput<TBuilder>(this TBuilder builder, string! policyName) -> TBuilder
+static Microsoft.Extensions.DependencyInjection.OutputCacheConventionBuilderExtensions.CacheOutput<TBuilder>(this TBuilder builder, System.Action<Microsoft.AspNetCore.OutputCaching.OutputCachePolicyBuilder!>! policy) -> TBuilder
+static Microsoft.Extensions.DependencyInjection.OutputCacheServiceCollectionExtensions.AddOutputCache(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
+static Microsoft.Extensions.DependencyInjection.OutputCacheServiceCollectionExtensions.AddOutputCache(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action<Microsoft.AspNetCore.OutputCaching.OutputCacheOptions!>! configureOptions) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
+Microsoft.AspNetCore.OutputCaching.OutputCacheContext.CacheVaryByRules.get -> Microsoft.AspNetCore.OutputCaching.CacheVaryByRules!
+Microsoft.AspNetCore.OutputCaching.OutputCacheContext.CacheVaryByRules.set -> void
+Microsoft.AspNetCore.OutputCaching.OutputCacheContext.HttpContext.get -> Microsoft.AspNetCore.Http.HttpContext!
+Microsoft.AspNetCore.OutputCaching.OutputCacheContext.Tags.get -> System.Collections.Generic.HashSet<string!>!
diff --git a/src/Middleware/OutputCaching/src/Resources.resx b/src/Middleware/OutputCaching/src/Resources.resx
new file mode 100644
index 0000000000..3a19868a73
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/Resources.resx
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+ <!--
+ Microsoft ResX Schema
+
+ Version 2.0
+
+ The primary goals of this format is to allow a simple XML format
+ that is mostly human readable. The generation and parsing of the
+ various data types are done through the TypeConverter classes
+ associated with the data types.
+
+ Example:
+
+ ... ado.net/XML headers & schema ...
+ <resheader name="resmimetype">text/microsoft-resx</resheader>
+ <resheader name="version">2.0</resheader>
+ <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+ <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+ <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+ <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+ <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+ <value>[base64 mime encoded serialized .NET Framework object]</value>
+ </data>
+ <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+ <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+ <comment>This is a comment</comment>
+ </data>
+
+ There are any number of "resheader" rows that contain simple
+ name/value pairs.
+
+ Each data row contains a name, and value. The row also contains a
+ type or mimetype. Type corresponds to a .NET class that support
+ text/value conversion through the TypeConverter architecture.
+ Classes that don't support this are serialized and stored with the
+ mimetype set.
+
+ The mimetype is used for serialized objects, and tells the
+ ResXResourceReader how to depersist the object. This is currently not
+ extensible. For a given mimetype the value must be set accordingly:
+
+ Note - application/x-microsoft.net.object.binary.base64 is the format
+ that the ResXResourceWriter will generate, however the reader can
+ read any of the formats listed below.
+
+ mimetype: application/x-microsoft.net.object.binary.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.soap.base64
+ value : The object must be serialized with
+ : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+ : and then encoded with base64 encoding.
+
+ mimetype: application/x-microsoft.net.object.bytearray.base64
+ value : The object must be serialized into a byte array
+ : using a System.ComponentModel.TypeConverter
+ : and then encoded with base64 encoding.
+ -->
+ <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+ <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+ <xsd:element name="root" msdata:IsDataSet="true">
+ <xsd:complexType>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="metadata">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" />
+ </xsd:sequence>
+ <xsd:attribute name="name" use="required" type="xsd:string" />
+ <xsd:attribute name="type" type="xsd:string" />
+ <xsd:attribute name="mimetype" type="xsd:string" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="assembly">
+ <xsd:complexType>
+ <xsd:attribute name="alias" type="xsd:string" />
+ <xsd:attribute name="name" type="xsd:string" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+ <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+ <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+ <xsd:attribute ref="xml:space" />
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resheader">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required" />
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:schema>
+ <resheader name="resmimetype">
+ <value>text/microsoft-resx</value>
+ </resheader>
+ <resheader name="version">
+ <value>2.0</value>
+ </resheader>
+ <resheader name="reader">
+ <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <resheader name="writer">
+ <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+ </resheader>
+ <data name="Policy_InvalidType" xml:space="preserve">
+ <value>The type '{0}' is not a valid output policy.</value>
+ </data>
+</root> \ No newline at end of file
diff --git a/src/Middleware/OutputCaching/src/Serialization/FormatterEntry.cs b/src/Middleware/OutputCaching/src/Serialization/FormatterEntry.cs
new file mode 100644
index 0000000000..f3aadfbadd
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/Serialization/FormatterEntry.cs
@@ -0,0 +1,12 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.OutputCaching.Serialization;
+internal sealed class FormatterEntry
+{
+ public DateTimeOffset Created { get; set; }
+ public int StatusCode { get; set; }
+ public Dictionary<string, string?[]> Headers { get; set; } = default!;
+ public List<byte[]> Body { get; set; } = default!;
+ public string[] Tags { get; set; } = default!;
+}
diff --git a/src/Middleware/OutputCaching/src/Serialization/FormatterEntrySerializerContext.cs b/src/Middleware/OutputCaching/src/Serialization/FormatterEntrySerializerContext.cs
new file mode 100644
index 0000000000..6f49367409
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/Serialization/FormatterEntrySerializerContext.cs
@@ -0,0 +1,12 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Text.Json.Serialization;
+
+namespace Microsoft.AspNetCore.OutputCaching.Serialization;
+
+[JsonSourceGenerationOptions(WriteIndented = false)]
+[JsonSerializable(typeof(FormatterEntry))]
+internal partial class FormatterEntrySerializerContext : JsonSerializerContext
+{
+}
diff --git a/src/Middleware/OutputCaching/src/Streams/OutputCacheStream.cs b/src/Middleware/OutputCaching/src/Streams/OutputCacheStream.cs
new file mode 100644
index 0000000000..d868586d69
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/Streams/OutputCacheStream.cs
@@ -0,0 +1,187 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+internal sealed class OutputCacheStream : Stream
+{
+ private readonly Stream _innerStream;
+ private readonly long _maxBufferSize;
+ private readonly int _segmentSize;
+ private readonly SegmentWriteStream _segmentWriteStream;
+ private readonly Action _startResponseCallback;
+
+ internal OutputCacheStream(Stream innerStream, long maxBufferSize, int segmentSize, Action startResponseCallback)
+ {
+ _innerStream = innerStream;
+ _maxBufferSize = maxBufferSize;
+ _segmentSize = segmentSize;
+ _startResponseCallback = startResponseCallback;
+ _segmentWriteStream = new SegmentWriteStream(_segmentSize);
+ }
+
+ internal bool BufferingEnabled { get; private set; } = true;
+
+ public override bool CanRead => _innerStream.CanRead;
+
+ public override bool CanSeek => _innerStream.CanSeek;
+
+ public override bool CanWrite => _innerStream.CanWrite;
+
+ public override long Length => _innerStream.Length;
+
+ public override long Position
+ {
+ get { return _innerStream.Position; }
+ set
+ {
+ DisableBuffering();
+ _innerStream.Position = value;
+ }
+ }
+
+ internal CachedResponseBody GetCachedResponseBody()
+ {
+ if (!BufferingEnabled)
+ {
+ throw new InvalidOperationException("Buffer stream cannot be retrieved since buffering is disabled.");
+ }
+ return new CachedResponseBody(_segmentWriteStream.GetSegments(), _segmentWriteStream.Length);
+ }
+
+ internal void DisableBuffering()
+ {
+ BufferingEnabled = false;
+ _segmentWriteStream.Dispose();
+ }
+
+ public override void SetLength(long value)
+ {
+ DisableBuffering();
+ _innerStream.SetLength(value);
+ }
+
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ DisableBuffering();
+ return _innerStream.Seek(offset, origin);
+ }
+
+ public override void Flush()
+ {
+ try
+ {
+ _startResponseCallback();
+ _innerStream.Flush();
+ }
+ catch
+ {
+ DisableBuffering();
+ throw;
+ }
+ }
+
+ public override async Task FlushAsync(CancellationToken cancellationToken)
+ {
+ try
+ {
+ _startResponseCallback();
+ await _innerStream.FlushAsync(cancellationToken);
+ }
+ catch
+ {
+ DisableBuffering();
+ throw;
+ }
+ }
+
+ // Underlying stream is write-only, no need to override other read related methods
+ public override int Read(byte[] buffer, int offset, int count)
+ => _innerStream.Read(buffer, offset, count);
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ try
+ {
+ _startResponseCallback();
+ _innerStream.Write(buffer, offset, count);
+ }
+ catch
+ {
+ DisableBuffering();
+ throw;
+ }
+
+ if (BufferingEnabled)
+ {
+ if (_segmentWriteStream.Length + count > _maxBufferSize)
+ {
+ DisableBuffering();
+ }
+ else
+ {
+ _segmentWriteStream.Write(buffer, offset, count);
+ }
+ }
+ }
+
+ public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) =>
+ await WriteAsync(buffer.AsMemory(offset, count), cancellationToken);
+
+ public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
+ {
+ try
+ {
+ _startResponseCallback();
+ await _innerStream.WriteAsync(buffer, cancellationToken);
+ }
+ catch
+ {
+ DisableBuffering();
+ throw;
+ }
+
+ if (BufferingEnabled)
+ {
+ if (_segmentWriteStream.Length + buffer.Length > _maxBufferSize)
+ {
+ DisableBuffering();
+ }
+ else
+ {
+ await _segmentWriteStream.WriteAsync(buffer, cancellationToken);
+ }
+ }
+ }
+
+ public override void WriteByte(byte value)
+ {
+ try
+ {
+ _innerStream.WriteByte(value);
+ }
+ catch
+ {
+ DisableBuffering();
+ throw;
+ }
+
+ if (BufferingEnabled)
+ {
+ if (_segmentWriteStream.Length + 1 > _maxBufferSize)
+ {
+ DisableBuffering();
+ }
+ else
+ {
+ _segmentWriteStream.WriteByte(value);
+ }
+ }
+ }
+
+ public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
+ => TaskToApm.Begin(WriteAsync(buffer, offset, count, CancellationToken.None), callback, state);
+
+ public override void EndWrite(IAsyncResult asyncResult)
+ => TaskToApm.End(asyncResult);
+}
diff --git a/src/Middleware/OutputCaching/src/Streams/SegmentWriteStream.cs b/src/Middleware/OutputCaching/src/Streams/SegmentWriteStream.cs
new file mode 100644
index 0000000000..b7491fc174
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/Streams/SegmentWriteStream.cs
@@ -0,0 +1,199 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+internal sealed class SegmentWriteStream : Stream
+{
+ private readonly List<byte[]> _segments = new();
+ private readonly MemoryStream _bufferStream = new();
+ private readonly int _segmentSize;
+ private long _length;
+ private bool _closed;
+ private bool _disposed;
+
+ internal SegmentWriteStream(int segmentSize)
+ {
+ if (segmentSize <= 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(segmentSize), segmentSize, $"{nameof(segmentSize)} must be greater than 0.");
+ }
+
+ _segmentSize = segmentSize;
+ }
+
+ // Extracting the buffered segments closes the stream for writing
+ internal List<byte[]> GetSegments()
+ {
+ if (!_closed)
+ {
+ _closed = true;
+ FinalizeSegments();
+ }
+ return _segments;
+ }
+
+ public override bool CanRead => false;
+
+ public override bool CanSeek => false;
+
+ public override bool CanWrite => !_closed;
+
+ public override long Length => _length;
+
+ public override long Position
+ {
+ get
+ {
+ return _length;
+ }
+ set
+ {
+ throw new NotSupportedException("The stream does not support seeking.");
+ }
+ }
+
+ private void DisposeMemoryStream()
+ {
+ // Clean up the memory stream
+ _bufferStream.SetLength(0);
+ _bufferStream.Capacity = 0;
+ _bufferStream.Dispose();
+ }
+
+ private void FinalizeSegments()
+ {
+ // Append any remaining segments
+ if (_bufferStream.Length > 0)
+ {
+ // Add the last segment
+ _segments.Add(_bufferStream.ToArray());
+ }
+
+ DisposeMemoryStream();
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ try
+ {
+ if (_disposed)
+ {
+ return;
+ }
+
+ if (disposing)
+ {
+ _segments.Clear();
+ DisposeMemoryStream();
+ }
+
+ _disposed = true;
+ _closed = true;
+ }
+ finally
+ {
+ base.Dispose(disposing);
+ }
+ }
+
+ public override void Flush()
+ {
+ if (!CanWrite)
+ {
+ throw new ObjectDisposedException("The stream has been closed for writing.");
+ }
+ }
+
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ throw new NotSupportedException("The stream does not support reading.");
+ }
+
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ throw new NotSupportedException("The stream does not support seeking.");
+ }
+
+ public override void SetLength(long value)
+ {
+ throw new NotSupportedException("The stream does not support seeking.");
+ }
+
+ public override void Write(byte[] buffer, int offset, int count)
+ {
+ ArgumentNullException.ThrowIfNull(buffer);
+
+ if (offset < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(offset), offset, "Non-negative number required.");
+ }
+ if (count < 0)
+ {
+ throw new ArgumentOutOfRangeException(nameof(count), count, "Non-negative number required.");
+ }
+ if (count > buffer.Length - offset)
+ {
+ throw new ArgumentException("Offset and length were out of bounds for the array or count is greater than the number of elements from index to the end of the source collection.");
+ }
+ if (!CanWrite)
+ {
+ throw new ObjectDisposedException("The stream has been closed for writing.");
+ }
+
+ Write(buffer.AsSpan(offset, count));
+ }
+
+ public override void Write(ReadOnlySpan<byte> buffer)
+ {
+ while (!buffer.IsEmpty)
+ {
+ if ((int)_bufferStream.Length == _segmentSize)
+ {
+ _segments.Add(_bufferStream.ToArray());
+ _bufferStream.SetLength(0);
+ }
+
+ var bytesWritten = Math.Min(buffer.Length, _segmentSize - (int)_bufferStream.Length);
+
+ _bufferStream.Write(buffer[..bytesWritten]);
+ buffer = buffer[bytesWritten..];
+ _length += bytesWritten;
+ }
+ }
+
+ public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ {
+ Write(buffer, offset, count);
+ return Task.CompletedTask;
+ }
+
+ public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
+ {
+ Write(buffer.Span);
+ return default;
+ }
+
+ public override void WriteByte(byte value)
+ {
+ if (!CanWrite)
+ {
+ throw new ObjectDisposedException("The stream has been closed for writing.");
+ }
+
+ if ((int)_bufferStream.Length == _segmentSize)
+ {
+ _segments.Add(_bufferStream.ToArray());
+ _bufferStream.SetLength(0);
+ }
+
+ _bufferStream.WriteByte(value);
+ _length++;
+ }
+
+ public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
+ => TaskToApm.Begin(WriteAsync(buffer, offset, count, CancellationToken.None), callback, state);
+
+ public override void EndWrite(IAsyncResult asyncResult)
+ => TaskToApm.End(asyncResult);
+}
diff --git a/src/Middleware/OutputCaching/src/Streams/StreamUtilities.cs b/src/Middleware/OutputCaching/src/Streams/StreamUtilities.cs
new file mode 100644
index 0000000000..2b9fb359c7
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/Streams/StreamUtilities.cs
@@ -0,0 +1,13 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+internal static class StreamUtilities
+{
+ /// <summary>
+ /// The segment size for buffering the response body in bytes. The default is set to 80 KB (81920 Bytes) to avoid allocations on the LOH.
+ /// </summary>
+ // Internal for testing
+ internal static int BodySegmentSize { get; set; } = 81920;
+}
diff --git a/src/Middleware/OutputCaching/src/StringBuilderExtensions.cs b/src/Middleware/OutputCaching/src/StringBuilderExtensions.cs
new file mode 100644
index 0000000000..6835e9af6c
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/StringBuilderExtensions.cs
@@ -0,0 +1,23 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Text;
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+internal static class StringBuilderExtensions
+{
+ internal static StringBuilder AppendUpperInvariant(this StringBuilder builder, string? value)
+ {
+ if (!string.IsNullOrEmpty(value))
+ {
+ builder.EnsureCapacity(builder.Length + value.Length);
+ for (var i = 0; i < value.Length; i++)
+ {
+ builder.Append(char.ToUpperInvariant(value[i]));
+ }
+ }
+
+ return builder;
+ }
+}
diff --git a/src/Middleware/OutputCaching/src/SystemClock.cs b/src/Middleware/OutputCaching/src/SystemClock.cs
new file mode 100644
index 0000000000..6cb33a828b
--- /dev/null
+++ b/src/Middleware/OutputCaching/src/SystemClock.cs
@@ -0,0 +1,15 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.OutputCaching;
+
+/// <summary>
+/// Provides access to the normal system clock.
+/// </summary>
+internal sealed class SystemClock : ISystemClock
+{
+ /// <summary>
+ /// Retrieves the current system time in UTC.
+ /// </summary>
+ public DateTimeOffset UtcNow => DateTimeOffset.UtcNow;
+}
diff --git a/src/Middleware/OutputCaching/startvs.cmd b/src/Middleware/OutputCaching/startvs.cmd
new file mode 100644
index 0000000000..5c25af9f64
--- /dev/null
+++ b/src/Middleware/OutputCaching/startvs.cmd
@@ -0,0 +1,3 @@
+@ECHO OFF
+
+%~dp0..\..\..\startvs.cmd %~dp0OutputCaching.slnf
diff --git a/src/Middleware/OutputCaching/test/CachedResponseBodyTests.cs b/src/Middleware/OutputCaching/test/CachedResponseBodyTests.cs
new file mode 100644
index 0000000000..6867fc2fb7
--- /dev/null
+++ b/src/Middleware/OutputCaching/test/CachedResponseBodyTests.cs
@@ -0,0 +1,122 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Buffers;
+using System.Diagnostics;
+using System.IO.Pipelines;
+
+namespace Microsoft.AspNetCore.OutputCaching.Tests;
+
+public class CachedResponseBodyTests
+{
+ private readonly int _timeout = Debugger.IsAttached ? -1 : 5000;
+
+ [Fact]
+ public void GetSegments()
+ {
+ var segments = new List<byte[]>();
+ var body = new CachedResponseBody(segments, 0);
+
+ Assert.Same(segments, body.Segments);
+ }
+
+ [Fact]
+ public void GetLength()
+ {
+ var segments = new List<byte[]>();
+ var body = new CachedResponseBody(segments, 42);
+
+ Assert.Equal(42, body.Length);
+ }
+
+ [Fact]
+ public async Task Copy_DoNothingWhenNoSegments()
+ {
+ var segments = new List<byte[]>();
+ var receivedSegments = new List<byte[]>();
+ var body = new CachedResponseBody(segments, 0);
+
+ var pipe = new Pipe();
+ using var cts = new CancellationTokenSource(_timeout);
+
+ var receiverTask = ReceiveDataAsync(pipe.Reader, receivedSegments, cts.Token);
+ var copyTask = body.CopyToAsync(pipe.Writer, cts.Token).ContinueWith(_ => pipe.Writer.CompleteAsync());
+
+ await Task.WhenAll(receiverTask, copyTask);
+
+ Assert.Empty(receivedSegments);
+ }
+
+ [Fact]
+ public async Task Copy_SingleSegment()
+ {
+ var segments = new List<byte[]>
+ {
+ new byte[] { 1 }
+ };
+ var receivedSegments = new List<byte[]>();
+ var body = new CachedResponseBody(segments, 0);
+
+ var pipe = new Pipe();
+
+ using var cts = new CancellationTokenSource(_timeout);
+
+ var receiverTask = ReceiveDataAsync(pipe.Reader, receivedSegments, cts.Token);
+ var copyTask = CopyDataAsync(body, pipe.Writer, cts.Token);
+
+ await Task.WhenAll(receiverTask, copyTask);
+
+ Assert.Equal(segments, receivedSegments);
+ }
+
+ [Fact]
+ public async Task Copy_MultipleSegments()
+ {
+ var segments = new List<byte[]>
+ {
+ new byte[] { 1 },
+ new byte[] { 2, 3 }
+ };
+ var receivedSegments = new List<byte[]>();
+ var body = new CachedResponseBody(segments, 0);
+
+ var pipe = new Pipe();
+
+ using var cts = new CancellationTokenSource(_timeout);
+
+ var receiverTask = ReceiveDataAsync(pipe.Reader, receivedSegments, cts.Token);
+ var copyTask = CopyDataAsync(body, pipe.Writer, cts.Token);
+
+ await Task.WhenAll(receiverTask, copyTask);
+
+ Assert.Equal(new byte[] { 1, 2, 3 }, receivedSegments.SelectMany(x => x).ToArray());
+ }
+
+ static async Task CopyDataAsync(CachedResponseBody body, PipeWriter writer, CancellationToken cancellationToken)
+ {
+ await body.CopyToAsync(writer, cancellationToken);
+ await writer.CompleteAsync();
+ }
+
+ static async Task ReceiveDataAsync(PipeReader reader, List<byte[]> receivedSegments, CancellationToken cancellationToken)
+ {
+ while (true)
+ {
+ var result = await reader.ReadAsync(cancellationToken);
+ var buffer = result.Buffer;
+
+ foreach (var memory in buffer)
+ {
+ receivedSegments.Add(memory.ToArray());
+ }
+
+ reader.AdvanceTo(buffer.End, buffer.End);
+
+ if (result.IsCompleted)
+ {
+ break;
+ }
+ }
+ await reader.CompleteAsync();
+ }
+}
diff --git a/src/Middleware/OutputCaching/test/MemoryOutputCacheStoreTests.cs b/src/Middleware/OutputCaching/test/MemoryOutputCacheStoreTests.cs
new file mode 100644
index 0000000000..c3a8d48388
--- /dev/null
+++ b/src/Middleware/OutputCaching/test/MemoryOutputCacheStoreTests.cs
@@ -0,0 +1,155 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Net.Http;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.OutputCaching.Memory;
+using Microsoft.AspNetCore.TestHost;
+using Microsoft.Extensions.Caching.Memory;
+using Microsoft.Net.Http.Headers;
+
+namespace Microsoft.AspNetCore.OutputCaching.Tests;
+
+public class MemoryOutputCacheStoreTests
+{
+ [Fact]
+ public async Task StoreAndGetValue_Succeeds()
+ {
+ var store = new MemoryOutputCacheStore(new MemoryCache(new MemoryCacheOptions()));
+ var value = "abc"u8.ToArray();
+ var key = "abc";
+
+ await store.SetAsync(key, value, null, TimeSpan.FromMinutes(1), default);
+
+ var result = await store.GetAsync(key, default);
+
+ Assert.Equal(value, result);
+ }
+
+ [Fact]
+ public async Task StoreAndGetValue_TimesOut()
+ {
+ var testClock = new TestMemoryOptionsClock { UtcNow = DateTimeOffset.UtcNow };
+ var store = new MemoryOutputCacheStore(new MemoryCache(new MemoryCacheOptions { Clock = testClock }));
+ var value = "abc"u8.ToArray();
+ var key = "abc";
+
+ await store.SetAsync(key, value, null, TimeSpan.FromMilliseconds(5), default);
+ testClock.Advance(TimeSpan.FromMilliseconds(10));
+
+ var result = await store.GetAsync(key, default);
+
+ Assert.Null(result);
+ }
+
+ [Fact]
+ public async Task StoreNullKey_ThrowsException()
+ {
+ var store = new MemoryOutputCacheStore(new MemoryCache(new MemoryCacheOptions()));
+ var value = "abc"u8.ToArray();
+ string key = null;
+
+ _ = await Assert.ThrowsAsync<ArgumentNullException>("key", () => store.SetAsync(key, value, null, TimeSpan.FromMilliseconds(5), default).AsTask());
+ }
+
+ [Fact]
+ public async Task StoreNullValue_ThrowsException()
+ {
+ var store = new MemoryOutputCacheStore(new MemoryCache(new MemoryCacheOptions()));
+ var value = default(byte[]);
+ string key = "abc";
+
+ _ = await Assert.ThrowsAsync<ArgumentNullException>("value", () => store.SetAsync(key, value, null, TimeSpan.FromMilliseconds(5), default).AsTask());
+ }
+
+ [Fact]
+ public async Task EvictByTag_SingleTag_SingleEntry()
+ {
+ var testClock = new TestMemoryOptionsClock { UtcNow = DateTimeOffset.UtcNow };
+ var store = new MemoryOutputCacheStore(new MemoryCache(new MemoryCacheOptions { Clock = testClock }));
+ var value = "abc"u8.ToArray();
+ var key = "abc";
+ var tags = new string[] { "tag1" };
+
+ await store.SetAsync(key, value, tags, TimeSpan.FromDays(1), default);
+ await store.EvictByTagAsync("tag1", default);
+ var result = await store.GetAsync(key, default);
+
+ Assert.Null(result);
+ }
+
+ [Fact]
+ public async Task EvictByTag_SingleTag_MultipleEntries()
+ {
+ var testClock = new TestMemoryOptionsClock { UtcNow = DateTimeOffset.UtcNow };
+ var store = new MemoryOutputCacheStore(new MemoryCache(new MemoryCacheOptions { Clock = testClock }));
+ var value = "abc"u8.ToArray();
+ var key1 = "abc";
+ var key2 = "def";
+ var tags = new string[] { "tag1" };
+
+ await store.SetAsync(key1, value, tags, TimeSpan.FromDays(1), default);
+ await store.SetAsync(key2, value, tags, TimeSpan.FromDays(1), default);
+ await store.EvictByTagAsync("tag1", default);
+ var result1 = await store.GetAsync(key1, default);
+ var result2 = await store.GetAsync(key2, default);
+
+ Assert.Null(result1);
+ Assert.Null(result2);
+ }
+
+ [Fact]
+ public async Task EvictByTag_MultipleTags_SingleEntry()
+ {
+ var testClock = new TestMemoryOptionsClock { UtcNow = DateTimeOffset.UtcNow };
+ var store = new MemoryOutputCacheStore(new MemoryCache(new MemoryCacheOptions { Clock = testClock }));
+ var value = "abc"u8.ToArray();
+ var key = "abc";
+ var tags = new string[] { "tag1", "tag2" };
+
+ await store.SetAsync(key, value, tags, TimeSpan.FromDays(1), default);
+ await store.EvictByTagAsync("tag1", default);
+ var result1 = await store.GetAsync(key, default);
+
+ Assert.Null(result1);
+ }
+
+ [Fact]
+ public async Task EvictByTag_MultipleTags_MultipleEntries()
+ {
+ var testClock = new TestMemoryOptionsClock { UtcNow = DateTimeOffset.UtcNow };
+ var store = new MemoryOutputCacheStore(new MemoryCache(new MemoryCacheOptions { Clock = testClock }));
+ var value = "abc"u8.ToArray();
+ var key1 = "abc";
+ var key2 = "def";
+ var tags1 = new string[] { "tag1", "tag2" };
+ var tags2 = new string[] { "tag2", "tag3" };
+
+ await store.SetAsync(key1, value, tags1, TimeSpan.FromDays(1), default);
+ await store.SetAsync(key2, value, tags2, TimeSpan.FromDays(1), default);
+ await store.EvictByTagAsync("tag1", default);
+
+ var result1 = await store.GetAsync(key1, default);
+ var result2 = await store.GetAsync(key2, default);
+
+ Assert.Null(result1);
+ Assert.NotNull(result2);
+
+ await store.EvictByTagAsync("tag3", default);
+
+ result1 = await store.GetAsync(key1, default);
+ result2 = await store.GetAsync(key2, default);
+
+ Assert.Null(result1);
+ Assert.Null(result2);
+ }
+
+ private class TestMemoryOptionsClock : Extensions.Internal.ISystemClock
+ {
+ public DateTimeOffset UtcNow { get; set; }
+ public void Advance(TimeSpan duration)
+ {
+ UtcNow += duration;
+ }
+ }
+}
diff --git a/src/Middleware/OutputCaching/test/Microsoft.AspNetCore.OutputCaching.Tests.csproj b/src/Middleware/OutputCaching/test/Microsoft.AspNetCore.OutputCaching.Tests.csproj
new file mode 100644
index 0000000000..f3a12d8a3c
--- /dev/null
+++ b/src/Middleware/OutputCaching/test/Microsoft.AspNetCore.OutputCaching.Tests.csproj
@@ -0,0 +1,18 @@
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Content Include="TestDocument.txt">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
+ </Content>
+ </ItemGroup>
+
+ <ItemGroup>
+ <Reference Include="Microsoft.AspNetCore.OutputCaching" />
+ <Reference Include="Microsoft.AspNetCore.TestHost" />
+ </ItemGroup>
+
+</Project>
diff --git a/src/Middleware/OutputCaching/test/OutputCacheEntryFormatterTests.cs b/src/Middleware/OutputCaching/test/OutputCacheEntryFormatterTests.cs
new file mode 100644
index 0000000000..a0de533fc0
--- /dev/null
+++ b/src/Middleware/OutputCaching/test/OutputCacheEntryFormatterTests.cs
@@ -0,0 +1,66 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Net.Http;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.OutputCaching.Memory;
+using Microsoft.AspNetCore.TestHost;
+using Microsoft.Extensions.Caching.Memory;
+using Microsoft.Net.Http.Headers;
+
+namespace Microsoft.AspNetCore.OutputCaching.Tests;
+
+public class OutputCacheEntryFormatterTests
+{
+ [Fact]
+ public async Task StoreAndGet_StoresEmptyValues()
+ {
+ var store = new TestOutputCache();
+ var key = "abc";
+ var entry = new OutputCacheEntry()
+ {
+ Body = new CachedResponseBody(new List<byte[]>(), 0),
+ Headers = new HeaderDictionary(),
+ Tags = Array.Empty<string>()
+ };
+
+ await OutputCacheEntryFormatter.StoreAsync(key, entry, TimeSpan.Zero, store, default);
+
+ var result = await OutputCacheEntryFormatter.GetAsync(key, store, default);
+
+ AssertEntriesAreSame(entry, result);
+ }
+
+ [Fact]
+ public async Task StoreAndGet_StoresAllValues()
+ {
+ var store = new TestOutputCache();
+ var key = "abc";
+ var entry = new OutputCacheEntry()
+ {
+ Body = new CachedResponseBody(new List<byte[]>() { "lorem"u8.ToArray(), "ipsum"u8.ToArray() }, 10),
+ Created = DateTimeOffset.UtcNow,
+ Headers = new HeaderDictionary { [HeaderNames.Accept] = "text/plain", [HeaderNames.AcceptCharset] = "utf8" },
+ StatusCode = StatusCodes.Status201Created,
+ Tags = new[] { "tag1", "tag2" }
+ };
+
+ await OutputCacheEntryFormatter.StoreAsync(key, entry, TimeSpan.Zero, store, default);
+
+ var result = await OutputCacheEntryFormatter.GetAsync(key, store, default);
+
+ AssertEntriesAreSame(entry, result);
+ }
+
+ private static void AssertEntriesAreSame(OutputCacheEntry expected, OutputCacheEntry actual)
+ {
+ Assert.NotNull(expected);
+ Assert.NotNull(actual);
+ Assert.Equal(expected.Tags, actual.Tags);
+ Assert.Equal(expected.Created, actual.Created);
+ Assert.Equal(expected.StatusCode, actual.StatusCode);
+ Assert.Equal(expected.Headers, actual.Headers);
+ Assert.Equal(expected.Body.Length, actual.Body.Length);
+ Assert.Equal(expected.Body.Segments, actual.Body.Segments);
+ }
+}
diff --git a/src/Middleware/OutputCaching/test/OutputCacheKeyProviderTests.cs b/src/Middleware/OutputCaching/test/OutputCacheKeyProviderTests.cs
new file mode 100644
index 0000000000..ddeb485d73
--- /dev/null
+++ b/src/Middleware/OutputCaching/test/OutputCacheKeyProviderTests.cs
@@ -0,0 +1,214 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.AspNetCore.Http;
+
+namespace Microsoft.AspNetCore.OutputCaching.Tests;
+
+public class OutputCacheKeyProviderTests
+{
+ private const char KeyDelimiter = '\x1e';
+ private const char KeySubDelimiter = '\x1f';
+
+ [Fact]
+ public void OutputCachingKeyProvider_CreateStorageKey_IncludesOnlyNormalizedMethodSchemeHostPortAndPath()
+ {
+ var cacheKeyProvider = TestUtils.CreateTestKeyProvider();
+ var context = TestUtils.CreateTestContext();
+ context.HttpContext.Request.Method = "head";
+ context.HttpContext.Request.Path = "/path/subpath";
+ context.HttpContext.Request.Scheme = "https";
+ context.HttpContext.Request.Host = new HostString("example.com", 80);
+ context.HttpContext.Request.PathBase = "/pathBase";
+ context.HttpContext.Request.QueryString = new QueryString("?query.Key=a&query.Value=b");
+
+ Assert.Equal($"HEAD{KeyDelimiter}HTTPS{KeyDelimiter}EXAMPLE.COM:80/PATHBASE/PATH/SUBPATH", cacheKeyProvider.CreateStorageKey(context));
+ }
+
+ [Fact]
+ public void OutputCachingKeyProvider_CreateStorageKey_CaseInsensitivePath_NormalizesPath()
+ {
+ var cacheKeyProvider = TestUtils.CreateTestKeyProvider(new OutputCacheOptions()
+ {
+ UseCaseSensitivePaths = false
+ });
+ var context = TestUtils.CreateTestContext();
+ context.HttpContext.Request.Method = HttpMethods.Get;
+ context.HttpContext.Request.Path = "/Path";
+
+ Assert.Equal($"{HttpMethods.Get}{KeyDelimiter}{KeyDelimiter}/PATH", cacheKeyProvider.CreateStorageKey(context));
+ }
+
+ [Fact]
+ public void OutputCachingKeyProvider_CreateStorageKey_CaseSensitivePath_PreservesPathCase()
+ {
+ var cacheKeyProvider = TestUtils.CreateTestKeyProvider(new OutputCacheOptions()
+ {
+ UseCaseSensitivePaths = true
+ });
+ var context = TestUtils.CreateTestContext();
+ context.HttpContext.Request.Method = HttpMethods.Get;
+ context.HttpContext.Request.Path = "/Path";
+
+ Assert.Equal($"{HttpMethods.Get}{KeyDelimiter}{KeyDelimiter}/Path", cacheKeyProvider.CreateStorageKey(context));
+ }
+
+ [Fact]
+ public void OutputCachingKeyProvider_CreateStorageKey_VaryByRulesIsotNull()
+ {
+ var cacheKeyProvider = TestUtils.CreateTestKeyProvider();
+ var context = TestUtils.CreateTestContext();
+
+ Assert.NotNull(context.CacheVaryByRules);
+ }
+
+ [Fact]
+ public void OutputCachingKeyProvider_CreateStorageKey_ReturnsCachedVaryByGuid_IfVaryByRulesIsEmpty()
+ {
+ var cacheKeyProvider = TestUtils.CreateTestKeyProvider();
+ var context = TestUtils.CreateTestContext();
+ context.CacheVaryByRules = new CacheVaryByRules()
+ {
+ VaryByPrefix = Guid.NewGuid().ToString("n"),
+ };
+
+ Assert.Equal($"{KeyDelimiter}{KeyDelimiter}{KeyDelimiter}C{KeyDelimiter}{context.CacheVaryByRules.VaryByPrefix}", cacheKeyProvider.CreateStorageKey(context));
+ }
+
+ [Fact]
+ public void OutputCachingKeyProvider_CreateStorageVaryKey_IncludesListedHeadersOnly()
+ {
+ var cacheKeyProvider = TestUtils.CreateTestKeyProvider();
+ var context = TestUtils.CreateTestContext();
+ context.HttpContext.Request.Headers["HeaderA"] = "ValueA";
+ context.HttpContext.Request.Headers["HeaderB"] = "ValueB";
+ context.CacheVaryByRules = new CacheVaryByRules()
+ {
+ Headers = new string[] { "HeaderA", "HeaderC" }
+ };
+
+ Assert.Equal($"{KeyDelimiter}{KeyDelimiter}{KeyDelimiter}H{KeyDelimiter}HeaderA=ValueA{KeyDelimiter}HeaderC=",
+ cacheKeyProvider.CreateStorageKey(context));
+ }
+
+ [Fact]
+ public void OutputCachingKeyProvider_CreateStorageVaryKey_HeaderValuesAreSorted()
+ {
+ var cacheKeyProvider = TestUtils.CreateTestKeyProvider();
+ var context = TestUtils.CreateTestContext();
+ context.HttpContext.Request.Headers["HeaderA"] = "ValueB";
+ context.HttpContext.Request.Headers.Append("HeaderA", "ValueA");
+ context.CacheVaryByRules = new CacheVaryByRules()
+ {
+ Headers = new string[] { "HeaderA", "HeaderC" }
+ };
+
+ Assert.Equal($"{KeyDelimiter}{KeyDelimiter}{KeyDelimiter}H{KeyDelimiter}HeaderA=ValueAValueB{KeyDelimiter}HeaderC=",
+ cacheKeyProvider.CreateStorageKey(context));
+ }
+
+ [Fact]
+ public void OutputCachingKeyProvider_CreateStorageVaryKey_IncludesListedQueryKeysOnly()
+ {
+ var cacheKeyProvider = TestUtils.CreateTestKeyProvider();
+ var context = TestUtils.CreateTestContext();
+ context.HttpContext.Request.QueryString = new QueryString("?QueryA=ValueA&QueryB=ValueB");
+ context.CacheVaryByRules = new CacheVaryByRules()
+ {
+ VaryByPrefix = Guid.NewGuid().ToString("n"),
+ QueryKeys = new string[] { "QueryA", "QueryC" }
+ };
+
+ Assert.Equal($"{KeyDelimiter}{KeyDelimiter}{KeyDelimiter}C{KeyDelimiter}{context.CacheVaryByRules.VaryByPrefix}{KeyDelimiter}Q{KeyDelimiter}QueryA=ValueA{KeyDelimiter}QueryC=",
+ cacheKeyProvider.CreateStorageKey(context));
+ }
+
+ [Fact]
+ public void OutputCachingKeyProvider_CreateStorageVaryKey_IncludesQueryKeys_QueryKeyCaseInsensitive_UseQueryKeyCasing()
+ {
+ var cacheKeyProvider = TestUtils.CreateTestKeyProvider();
+ var context = TestUtils.CreateTestContext();
+ context.HttpContext.Request.QueryString = new QueryString("?queryA=ValueA&queryB=ValueB");
+ context.CacheVaryByRules = new CacheVaryByRules()
+ {
+ VaryByPrefix = Guid.NewGuid().ToString("n"),
+ QueryKeys = new string[] { "QueryA", "QueryC" }
+ };
+
+ Assert.Equal($"{KeyDelimiter}{KeyDelimiter}{KeyDelimiter}C{KeyDelimiter}{context.CacheVaryByRules.VaryByPrefix}{KeyDelimiter}Q{KeyDelimiter}QueryA=ValueA{KeyDelimiter}QueryC=",
+ cacheKeyProvider.CreateStorageKey(context));
+ }
+
+ [Fact]
+ public void OutputCachingKeyProvider_CreateStorageVaryKey_IncludesAllQueryKeysGivenAsterisk()
+ {
+ var cacheKeyProvider = TestUtils.CreateTestKeyProvider();
+ var context = TestUtils.CreateTestContext();
+ context.HttpContext.Request.QueryString = new QueryString("?QueryA=ValueA&QueryB=ValueB");
+ context.CacheVaryByRules = new CacheVaryByRules()
+ {
+ VaryByPrefix = Guid.NewGuid().ToString("n"),
+ QueryKeys = new string[] { "*" }
+ };
+
+ // To support case insensitivity, all query keys are converted to upper case.
+ // Explicit query keys uses the casing specified in the setting.
+ Assert.Equal($"{KeyDelimiter}{KeyDelimiter}{KeyDelimiter}C{KeyDelimiter}{context.CacheVaryByRules.VaryByPrefix}{KeyDelimiter}Q{KeyDelimiter}QUERYA=ValueA{KeyDelimiter}QUERYB=ValueB",
+ cacheKeyProvider.CreateStorageKey(context));
+ }
+
+ [Fact]
+ public void OutputCachingKeyProvider_CreateStorageVaryKey_QueryKeysValuesNotConsolidated()
+ {
+ var cacheKeyProvider = TestUtils.CreateTestKeyProvider();
+ var context = TestUtils.CreateTestContext();
+ context.HttpContext.Request.QueryString = new QueryString("?QueryA=ValueA&QueryA=ValueB");
+ context.CacheVaryByRules = new CacheVaryByRules()
+ {
+ VaryByPrefix = Guid.NewGuid().ToString("n"),
+ QueryKeys = new string[] { "*" }
+ };
+
+ // To support case insensitivity, all query keys are converted to upper case.
+ // Explicit query keys uses the casing specified in the setting.
+ Assert.Equal($"{KeyDelimiter}{KeyDelimiter}{KeyDelimiter}C{KeyDelimiter}{context.CacheVaryByRules.VaryByPrefix}{KeyDelimiter}Q{KeyDelimiter}QUERYA=ValueA{KeySubDelimiter}ValueB",
+ cacheKeyProvider.CreateStorageKey(context));
+ }
+
+ [Fact]
+ public void OutputCachingKeyProvider_CreateStorageVaryKey_QueryKeysValuesAreSorted()
+ {
+ var cacheKeyProvider = TestUtils.CreateTestKeyProvider();
+ var context = TestUtils.CreateTestContext();
+ context.HttpContext.Request.QueryString = new QueryString("?QueryA=ValueB&QueryA=ValueA");
+ context.CacheVaryByRules = new CacheVaryByRules()
+ {
+ VaryByPrefix = Guid.NewGuid().ToString("n"),
+ QueryKeys = new string[] { "*" }
+ };
+
+ // To support case insensitivity, all query keys are converted to upper case.
+ // Explicit query keys uses the casing specified in the setting.
+ Assert.Equal($"{KeyDelimiter}{KeyDelimiter}{KeyDelimiter}C{KeyDelimiter}{context.CacheVaryByRules.VaryByPrefix}{KeyDelimiter}Q{KeyDelimiter}QUERYA=ValueA{KeySubDelimiter}ValueB",
+ cacheKeyProvider.CreateStorageKey(context));
+ }
+
+ [Fact]
+ public void OutputCachingKeyProvider_CreateStorageVaryKey_IncludesListedHeadersAndQueryKeys()
+ {
+ var cacheKeyProvider = TestUtils.CreateTestKeyProvider();
+ var context = TestUtils.CreateTestContext();
+ context.HttpContext.Request.Headers["HeaderA"] = "ValueA";
+ context.HttpContext.Request.Headers["HeaderB"] = "ValueB";
+ context.HttpContext.Request.QueryString = new QueryString("?QueryA=ValueA&QueryB=ValueB");
+ context.CacheVaryByRules = new CacheVaryByRules()
+ {
+ VaryByPrefix = Guid.NewGuid().ToString("n"),
+ Headers = new string[] { "HeaderA", "HeaderC" },
+ QueryKeys = new string[] { "QueryA", "QueryC" }
+ };
+
+ Assert.Equal($"{KeyDelimiter}{KeyDelimiter}{KeyDelimiter}C{KeyDelimiter}{context.CacheVaryByRules.VaryByPrefix}{KeyDelimiter}H{KeyDelimiter}HeaderA=ValueA{KeyDelimiter}HeaderC={KeyDelimiter}Q{KeyDelimiter}QueryA=ValueA{KeyDelimiter}QueryC=",
+ cacheKeyProvider.CreateStorageKey(context));
+ }
+}
diff --git a/src/Middleware/OutputCaching/test/OutputCacheMiddlewareTests.cs b/src/Middleware/OutputCaching/test/OutputCacheMiddlewareTests.cs
new file mode 100644
index 0000000000..690031b2a7
--- /dev/null
+++ b/src/Middleware/OutputCaching/test/OutputCacheMiddlewareTests.cs
@@ -0,0 +1,802 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http.Features;
+using Microsoft.AspNetCore.OutputCaching.Memory;
+using Microsoft.Extensions.Caching.Memory;
+using Microsoft.Extensions.Logging.Testing;
+using Microsoft.Extensions.Primitives;
+using Microsoft.Net.Http.Headers;
+
+namespace Microsoft.AspNetCore.OutputCaching.Tests;
+
+public class OutputCacheMiddlewareTests
+{
+ [Fact]
+ public async Task TryServeFromCacheAsync_OnlyIfCached_Serves504()
+ {
+ var cache = new TestOutputCache();
+ var sink = new TestSink();
+ var middleware = TestUtils.CreateTestMiddleware(testSink: sink, cache: cache, keyProvider: new TestResponseCachingKeyProvider("BaseKey"));
+ var context = TestUtils.CreateTestContext(cache);
+ context.HttpContext.Request.Headers.CacheControl = new CacheControlHeaderValue()
+ {
+ OnlyIfCached = true
+ }.ToString();
+ middleware.TryGetRequestPolicies(context.HttpContext, out var policies);
+
+ Assert.True(await middleware.TryServeFromCacheAsync(context, policies));
+ Assert.Equal(StatusCodes.Status504GatewayTimeout, context.HttpContext.Response.StatusCode);
+ TestUtils.AssertLoggedMessages(
+ sink.Writes,
+ LoggedMessage.GatewayTimeoutServed);
+ }
+
+ [Fact]
+ public async Task TryServeFromCacheAsync_CachedResponseNotFound_Fails()
+ {
+ var cache = new TestOutputCache();
+ var sink = new TestSink();
+ var middleware = TestUtils.CreateTestMiddleware(testSink: sink, cache: cache, keyProvider: new TestResponseCachingKeyProvider("BaseKey"));
+ var context = TestUtils.CreateTestContext(cache);
+ middleware.TryGetRequestPolicies(context.HttpContext, out var policies);
+
+ Assert.False(await middleware.TryServeFromCacheAsync(context, policies));
+ Assert.Equal(1, cache.GetCount);
+ TestUtils.AssertLoggedMessages(
+ sink.Writes,
+ LoggedMessage.NoResponseServed);
+ }
+
+ [Fact]
+ public async Task TryServeFromCacheAsync_CachedResponseFound_Succeeds()
+ {
+ var cache = new TestOutputCache();
+ var sink = new TestSink();
+ var middleware = TestUtils.CreateTestMiddleware(testSink: sink, cache: cache, keyProvider: new TestResponseCachingKeyProvider("BaseKey"));
+ var context = TestUtils.CreateTestContext(cache);
+ middleware.TryGetRequestPolicies(context.HttpContext, out var policies);
+
+ await OutputCacheEntryFormatter.StoreAsync(
+ "BaseKey",
+ new OutputCacheEntry()
+ {
+ Headers = new HeaderDictionary(),
+ Body = new CachedResponseBody(new List<byte[]>(0), 0)
+ },
+ TimeSpan.Zero,
+ cache,
+ default);
+
+ Assert.True(await middleware.TryServeFromCacheAsync(context, policies));
+ Assert.Equal(1, cache.GetCount);
+ TestUtils.AssertLoggedMessages(
+ sink.Writes,
+ LoggedMessage.CachedResponseServed);
+ }
+
+ [Fact]
+ public async Task TryServeFromCacheAsync_CachedResponseFound_OverwritesExistingHeaders()
+ {
+ var cache = new TestOutputCache();
+ var sink = new TestSink();
+ var middleware = TestUtils.CreateTestMiddleware(testSink: sink, cache: cache, keyProvider: new TestResponseCachingKeyProvider("BaseKey"));
+ var context = TestUtils.CreateTestContext(cache);
+ middleware.TryGetRequestPolicies(context.HttpContext, out var policies);
+ context.CacheKey = "BaseKey";
+
+ context.HttpContext.Response.Headers["MyHeader"] = "OldValue";
+ await OutputCacheEntryFormatter.StoreAsync(context.CacheKey,
+ new OutputCacheEntry()
+ {
+ Headers = new HeaderDictionary()
+ {
+ { "MyHeader", "NewValue" }
+ },
+ Body = new CachedResponseBody(new List<byte[]>(0), 0)
+ },
+ TimeSpan.Zero,
+ cache,
+ default);
+
+ Assert.True(await middleware.TryServeFromCacheAsync(context, policies));
+ Assert.Equal("NewValue", context.HttpContext.Response.Headers["MyHeader"]);
+ Assert.Equal(1, cache.GetCount);
+ TestUtils.AssertLoggedMessages(
+ sink.Writes,
+ LoggedMessage.CachedResponseServed);
+ }
+
+ [Fact]
+ public async Task TryServeFromCacheAsync_CachedResponseFound_Serves304IfPossible()
+ {
+ var cache = new TestOutputCache();
+ var sink = new TestSink();
+ var middleware = TestUtils.CreateTestMiddleware(testSink: sink, cache: cache, keyProvider: new TestResponseCachingKeyProvider("BaseKey"));
+ var context = TestUtils.CreateTestContext(cache);
+ context.HttpContext.Request.Headers.IfNoneMatch = "*";
+ middleware.TryGetRequestPolicies(context.HttpContext, out var policies);
+
+ await OutputCacheEntryFormatter.StoreAsync("BaseKey",
+ new OutputCacheEntry()
+ {
+ Body = new CachedResponseBody(new List<byte[]>(0), 0),
+ Headers = new()
+ },
+ TimeSpan.Zero,
+ cache,
+ default);
+
+ Assert.True(await middleware.TryServeFromCacheAsync(context, policies));
+ Assert.Equal(1, cache.GetCount);
+ TestUtils.AssertLoggedMessages(
+ sink.Writes,
+ LoggedMessage.NotModifiedServed);
+ }
+
+ [Fact]
+ public void ContentIsNotModified_NotConditionalRequest_False()
+ {
+ var sink = new TestSink();
+ var context = TestUtils.CreateTestContext(sink);
+ context.CachedResponse = new OutputCacheEntry { Headers = new HeaderDictionary() };
+
+ Assert.False(OutputCacheMiddleware.ContentIsNotModified(context));
+ Assert.Empty(sink.Writes);
+ }
+
+ [Fact]
+ public void ContentIsNotModified_IfModifiedSince_FallsbackToDateHeader()
+ {
+ var utcNow = DateTimeOffset.UtcNow;
+ var sink = new TestSink();
+ var context = TestUtils.CreateTestContext(sink);
+ context.CachedResponse = new OutputCacheEntry { Headers = new HeaderDictionary() };
+
+ context.HttpContext.Request.Headers.IfModifiedSince = HeaderUtilities.FormatDate(utcNow);
+
+ // Verify modifications in the past succeeds
+ context.CachedResponse.Headers[HeaderNames.Date] = HeaderUtilities.FormatDate(utcNow - TimeSpan.FromSeconds(10));
+ Assert.True(OutputCacheMiddleware.ContentIsNotModified(context));
+ Assert.Single(sink.Writes);
+
+ // Verify modifications at present succeeds
+ context.CachedResponse.Headers[HeaderNames.Date] = HeaderUtilities.FormatDate(utcNow);
+ Assert.True(OutputCacheMiddleware.ContentIsNotModified(context));
+ Assert.Equal(2, sink.Writes.Count);
+
+ // Verify modifications in the future fails
+ context.CachedResponse.Headers[HeaderNames.Date] = HeaderUtilities.FormatDate(utcNow + TimeSpan.FromSeconds(10));
+ Assert.False(OutputCacheMiddleware.ContentIsNotModified(context));
+
+ // Verify logging
+ TestUtils.AssertLoggedMessages(
+ sink.Writes,
+ LoggedMessage.NotModifiedIfModifiedSinceSatisfied,
+ LoggedMessage.NotModifiedIfModifiedSinceSatisfied);
+ }
+
+ [Fact]
+ public void ContentIsNotModified_IfModifiedSince_LastModifiedOverridesDateHeader()
+ {
+ var utcNow = DateTimeOffset.UtcNow;
+ var sink = new TestSink();
+ var context = TestUtils.CreateTestContext(sink);
+ context.CachedResponse = new OutputCacheEntry { Headers = new HeaderDictionary() };
+
+ context.HttpContext.Request.Headers.IfModifiedSince = HeaderUtilities.FormatDate(utcNow);
+
+ // Verify modifications in the past succeeds
+ context.CachedResponse.Headers[HeaderNames.Date] = HeaderUtilities.FormatDate(utcNow + TimeSpan.FromSeconds(10));
+ context.CachedResponse.Headers[HeaderNames.LastModified] = HeaderUtilities.FormatDate(utcNow - TimeSpan.FromSeconds(10));
+ Assert.True(OutputCacheMiddleware.ContentIsNotModified(context));
+ Assert.Single(sink.Writes);
+
+ // Verify modifications at present
+ context.CachedResponse.Headers[HeaderNames.Date] = HeaderUtilities.FormatDate(utcNow + TimeSpan.FromSeconds(10));
+ context.CachedResponse.Headers[HeaderNames.LastModified] = HeaderUtilities.FormatDate(utcNow);
+ Assert.True(OutputCacheMiddleware.ContentIsNotModified(context));
+ Assert.Equal(2, sink.Writes.Count);
+
+ // Verify modifications in the future fails
+ context.CachedResponse.Headers[HeaderNames.Date] = HeaderUtilities.FormatDate(utcNow - TimeSpan.FromSeconds(10));
+ context.CachedResponse.Headers[HeaderNames.LastModified] = HeaderUtilities.FormatDate(utcNow + TimeSpan.FromSeconds(10));
+ Assert.False(OutputCacheMiddleware.ContentIsNotModified(context));
+
+ // Verify logging
+ TestUtils.AssertLoggedMessages(
+ sink.Writes,
+ LoggedMessage.NotModifiedIfModifiedSinceSatisfied,
+ LoggedMessage.NotModifiedIfModifiedSinceSatisfied);
+ }
+
+ [Fact]
+ public void ContentIsNotModified_IfNoneMatch_Overrides_IfModifiedSince_ToTrue()
+ {
+ var utcNow = DateTimeOffset.UtcNow;
+ var sink = new TestSink();
+ var context = TestUtils.CreateTestContext(sink);
+ context.CachedResponse = new OutputCacheEntry { Headers = new HeaderDictionary() };
+
+ // This would fail the IfModifiedSince checks
+ context.HttpContext.Request.Headers.IfModifiedSince = HeaderUtilities.FormatDate(utcNow);
+ context.CachedResponse.Headers[HeaderNames.LastModified] = HeaderUtilities.FormatDate(utcNow + TimeSpan.FromSeconds(10));
+
+ context.HttpContext.Request.Headers.IfNoneMatch = EntityTagHeaderValue.Any.ToString();
+ Assert.True(OutputCacheMiddleware.ContentIsNotModified(context));
+ TestUtils.AssertLoggedMessages(
+ sink.Writes,
+ LoggedMessage.NotModifiedIfNoneMatchStar);
+ }
+
+ [Fact]
+ public void ContentIsNotModified_IfNoneMatch_Overrides_IfModifiedSince_ToFalse()
+ {
+ var utcNow = DateTimeOffset.UtcNow;
+ var sink = new TestSink();
+ var context = TestUtils.CreateTestContext(sink);
+ context.CachedResponse = new OutputCacheEntry { Headers = new HeaderDictionary() };
+
+ // This would pass the IfModifiedSince checks
+ context.HttpContext.Request.Headers.IfModifiedSince = HeaderUtilities.FormatDate(utcNow);
+ context.CachedResponse.Headers[HeaderNames.LastModified] = HeaderUtilities.FormatDate(utcNow - TimeSpan.FromSeconds(10));
+
+ context.HttpContext.Request.Headers.IfNoneMatch = "\"E1\"";
+ Assert.False(OutputCacheMiddleware.ContentIsNotModified(context));
+ Assert.Empty(sink.Writes);
+ }
+
+ [Fact]
+ public void ContentIsNotModified_IfNoneMatch_AnyWithoutETagInResponse_False()
+ {
+ var sink = new TestSink();
+ var context = TestUtils.CreateTestContext(sink);
+ context.CachedResponse = new OutputCacheEntry { Headers = new HeaderDictionary() };
+ context.HttpContext.Request.Headers.IfNoneMatch = "\"E1\"";
+
+ Assert.False(OutputCacheMiddleware.ContentIsNotModified(context));
+ Assert.Empty(sink.Writes);
+ }
+
+ public static TheoryData<EntityTagHeaderValue, EntityTagHeaderValue> EquivalentWeakETags
+ {
+ get
+ {
+ return new TheoryData<EntityTagHeaderValue, EntityTagHeaderValue>
+ {
+ { new EntityTagHeaderValue("\"tag\""), new EntityTagHeaderValue("\"tag\"") },
+ { new EntityTagHeaderValue("\"tag\"", true), new EntityTagHeaderValue("\"tag\"") },
+ { new EntityTagHeaderValue("\"tag\""), new EntityTagHeaderValue("\"tag\"", true) },
+ { new EntityTagHeaderValue("\"tag\"", true), new EntityTagHeaderValue("\"tag\"", true) }
+ };
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(EquivalentWeakETags))]
+ public void ContentIsNotModified_IfNoneMatch_ExplicitWithMatch_True(EntityTagHeaderValue responseETag, EntityTagHeaderValue requestETag)
+ {
+ var sink = new TestSink();
+ var context = TestUtils.CreateTestContext(sink);
+ context.CachedResponse = new OutputCacheEntry { Headers = new HeaderDictionary() };
+ context.CachedResponse.Headers[HeaderNames.ETag] = responseETag.ToString();
+ context.HttpContext.Request.Headers.IfNoneMatch = requestETag.ToString();
+
+ Assert.True(OutputCacheMiddleware.ContentIsNotModified(context));
+ TestUtils.AssertLoggedMessages(
+ sink.Writes,
+ LoggedMessage.NotModifiedIfNoneMatchMatched);
+ }
+
+ [Fact]
+ public void ContentIsNotModified_IfNoneMatch_ExplicitWithoutMatch_False()
+ {
+ var sink = new TestSink();
+ var context = TestUtils.CreateTestContext(sink);
+ context.CachedResponse = new OutputCacheEntry { Headers = new HeaderDictionary() };
+ context.CachedResponse.Headers[HeaderNames.ETag] = "\"E2\"";
+ context.HttpContext.Request.Headers.IfNoneMatch = "\"E1\"";
+
+ Assert.False(OutputCacheMiddleware.ContentIsNotModified(context));
+ Assert.Empty(sink.Writes);
+ }
+
+ [Fact]
+ public void ContentIsNotModified_IfNoneMatch_MatchesAtLeastOneValue_True()
+ {
+ var sink = new TestSink();
+ var context = TestUtils.CreateTestContext(sink);
+ context.CachedResponse = new OutputCacheEntry { Headers = new HeaderDictionary() };
+ context.CachedResponse.Headers[HeaderNames.ETag] = "\"E2\"";
+ context.HttpContext.Request.Headers.IfNoneMatch = new string[] { "\"E0\", \"E1\"", "\"E1\", \"E2\"" };
+
+ Assert.True(OutputCacheMiddleware.ContentIsNotModified(context));
+ TestUtils.AssertLoggedMessages(
+ sink.Writes,
+ LoggedMessage.NotModifiedIfNoneMatchMatched);
+ }
+
+ [Fact]
+ public void StartResponsegAsync_IfAllowResponseCaptureIsTrue_SetsResponseTime()
+ {
+ var clock = new TestClock
+ {
+ UtcNow = DateTimeOffset.UtcNow
+ };
+ var middleware = TestUtils.CreateTestMiddleware(options: new OutputCacheOptions
+ {
+ SystemClock = clock
+ });
+ var context = TestUtils.CreateTestContext();
+ context.ResponseTime = null;
+
+ middleware.StartResponse(context);
+
+ Assert.Equal(clock.UtcNow, context.ResponseTime);
+ }
+
+ [Fact]
+ public void StartResponseAsync_IfAllowResponseCaptureIsTrue_SetsResponseTimeOnlyOnce()
+ {
+ var clock = new TestClock
+ {
+ UtcNow = DateTimeOffset.UtcNow
+ };
+ var middleware = TestUtils.CreateTestMiddleware(options: new OutputCacheOptions
+ {
+ SystemClock = clock
+ });
+ var context = TestUtils.CreateTestContext();
+ var initialTime = clock.UtcNow;
+ context.ResponseTime = null;
+
+ middleware.StartResponse(context);
+ Assert.Equal(initialTime, context.ResponseTime);
+
+ clock.UtcNow += TimeSpan.FromSeconds(10);
+
+ middleware.StartResponse(context);
+ Assert.NotEqual(clock.UtcNow, context.ResponseTime);
+ Assert.Equal(initialTime, context.ResponseTime);
+ }
+
+ [Fact]
+ public void FinalizeCacheHeadersAsync_DefaultResponseValidity_Is60Seconds()
+ {
+ var sink = new TestSink();
+ var middleware = TestUtils.CreateTestMiddleware(testSink: sink);
+ var context = TestUtils.CreateTestContext();
+
+ middleware.FinalizeCacheHeaders(context);
+
+ Assert.Equal(TimeSpan.FromSeconds(60), context.CachedResponseValidFor);
+ Assert.Empty(sink.Writes);
+ }
+
+ [Fact]
+ public void FinalizeCacheHeadersAsync_ResponseValidity_IgnoresExpiryIfAvailable()
+ {
+ // The Expires header should not be used when set in the response
+
+ var clock = new TestClock
+ {
+ UtcNow = DateTimeOffset.MinValue
+ };
+ var options = new OutputCacheOptions
+ {
+ SystemClock = clock
+ };
+ var sink = new TestSink();
+ var middleware = TestUtils.CreateTestMiddleware(testSink: sink, options: options);
+ var context = TestUtils.CreateTestContext();
+
+ context.ResponseTime = clock.UtcNow;
+ context.HttpContext.Response.Headers.Expires = HeaderUtilities.FormatDate(clock.UtcNow + TimeSpan.FromSeconds(11));
+
+ middleware.FinalizeCacheHeaders(context);
+
+ Assert.Equal(options.DefaultExpirationTimeSpan, context.CachedResponseValidFor);
+ Assert.Empty(sink.Writes);
+ }
+
+ [Fact]
+ public void FinalizeCacheHeadersAsync_ResponseValidity_UseMaxAgeIfAvailable()
+ {
+ // The MaxAge header should not be used if set in the response
+
+ var clock = new TestClock
+ {
+ UtcNow = DateTimeOffset.UtcNow
+ };
+ var sink = new TestSink();
+ var options = new OutputCacheOptions
+ {
+ SystemClock = clock
+ };
+ var middleware = TestUtils.CreateTestMiddleware(testSink: sink, options: options);
+ var context = TestUtils.CreateTestContext();
+
+ context.ResponseTime = clock.UtcNow;
+ context.HttpContext.Response.Headers.CacheControl = new CacheControlHeaderValue()
+ {
+ MaxAge = TimeSpan.FromSeconds(12)
+ }.ToString();
+
+ context.HttpContext.Response.Headers.Expires = HeaderUtilities.FormatDate(clock.UtcNow + TimeSpan.FromSeconds(11));
+
+ middleware.FinalizeCacheHeaders(context);
+
+ Assert.Equal(options.DefaultExpirationTimeSpan, context.CachedResponseValidFor);
+ Assert.Empty(sink.Writes);
+ }
+
+ [Fact]
+ public void FinalizeCacheHeadersAsync_ResponseValidity_UseSharedMaxAgeIfAvailable()
+ {
+ var clock = new TestClock
+ {
+ UtcNow = DateTimeOffset.UtcNow
+ };
+ var sink = new TestSink();
+ var options = new OutputCacheOptions
+ {
+ SystemClock = clock
+ };
+ var middleware = TestUtils.CreateTestMiddleware(testSink: sink, options: options);
+ var context = TestUtils.CreateTestContext();
+
+ context.ResponseTime = clock.UtcNow;
+ context.HttpContext.Response.Headers.CacheControl = new CacheControlHeaderValue()
+ {
+ MaxAge = TimeSpan.FromSeconds(12),
+ SharedMaxAge = TimeSpan.FromSeconds(13)
+ }.ToString();
+ context.HttpContext.Response.Headers.Expires = HeaderUtilities.FormatDate(clock.UtcNow + TimeSpan.FromSeconds(11));
+
+ middleware.FinalizeCacheHeaders(context);
+
+ Assert.Equal(options.DefaultExpirationTimeSpan, context.CachedResponseValidFor);
+ Assert.Empty(sink.Writes);
+ }
+
+ public static TheoryData<StringValues> NullOrEmptyVaryRules
+ {
+ get
+ {
+ return new TheoryData<StringValues>
+ {
+ default(StringValues),
+ StringValues.Empty,
+ new StringValues((string)null),
+ new StringValues(string.Empty),
+ new StringValues((string[])null),
+ new StringValues(new string[0]),
+ new StringValues(new string[] { null }),
+ new StringValues(new string[] { string.Empty })
+ };
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(NullOrEmptyVaryRules))]
+ public void FinalizeCacheHeadersAsync_UpdateCachedVaryByRules_NullOrEmptyRules(StringValues vary)
+ {
+ var cache = new TestOutputCache();
+ var sink = new TestSink();
+ var middleware = TestUtils.CreateTestMiddleware(testSink: sink, cache: cache);
+ var context = TestUtils.CreateTestContext(cache);
+
+ context.HttpContext.Response.Headers.Vary = vary;
+ context.HttpContext.Features.Set<IOutputCacheFeature>(new OutputCacheFeature(context));
+ context.CacheVaryByRules.QueryKeys = vary;
+
+ middleware.FinalizeCacheHeaders(context);
+
+ // Vary rules should not be updated
+ Assert.Equal(0, cache.SetCount);
+ Assert.Empty(sink.Writes);
+ }
+
+ [Fact]
+ public void FinalizeCacheHeadersAsync_AddsDate_IfNoneSpecified()
+ {
+ var utcNow = DateTimeOffset.UtcNow;
+ var sink = new TestSink();
+ var middleware = TestUtils.CreateTestMiddleware(testSink: sink);
+ var context = TestUtils.CreateTestContext();
+ // ResponseTime is the actual value that's used to set the Date header in FinalizeCacheHeadersAsync
+ context.ResponseTime = utcNow;
+
+ Assert.True(StringValues.IsNullOrEmpty(context.HttpContext.Response.Headers.Date));
+
+ middleware.FinalizeCacheHeaders(context);
+
+ Assert.Equal(HeaderUtilities.FormatDate(utcNow), context.HttpContext.Response.Headers.Date);
+ Assert.Empty(sink.Writes);
+ }
+
+ [Fact]
+ public void FinalizeCacheHeadersAsync_IgnoresDate_IfSpecified()
+ {
+ // The Date header should not be used when set in the response
+
+ var utcNow = DateTimeOffset.UtcNow;
+ var responseTime = utcNow + TimeSpan.FromSeconds(10);
+ var sink = new TestSink();
+ var middleware = TestUtils.CreateTestMiddleware(testSink: sink);
+ var context = TestUtils.CreateTestContext();
+
+ context.HttpContext.Response.Headers.Date = HeaderUtilities.FormatDate(utcNow);
+ context.ResponseTime = responseTime;
+
+ Assert.Equal(HeaderUtilities.FormatDate(utcNow), context.HttpContext.Response.Headers.Date);
+
+ middleware.FinalizeCacheHeaders(context);
+
+ Assert.Equal(HeaderUtilities.FormatDate(responseTime), context.HttpContext.Response.Headers.Date);
+ Assert.Empty(sink.Writes);
+ }
+
+ [Fact]
+ public void FinalizeCacheHeadersAsync_StoresCachedResponse_InState()
+ {
+ var sink = new TestSink();
+ var middleware = TestUtils.CreateTestMiddleware(testSink: sink);
+ var context = TestUtils.CreateTestContext();
+
+ Assert.Null(context.CachedResponse);
+
+ middleware.FinalizeCacheHeaders(context);
+
+ Assert.NotNull(context.CachedResponse);
+ Assert.Empty(sink.Writes);
+ }
+
+ [Fact]
+ public void FinalizeCacheHeadersAsync_StoresHeaders()
+ {
+ var sink = new TestSink();
+ var middleware = TestUtils.CreateTestMiddleware(testSink: sink);
+ var context = TestUtils.CreateTestContext();
+
+ context.HttpContext.Response.Headers.Vary = "HeaderB, heaDera";
+
+ middleware.FinalizeCacheHeaders(context);
+
+ Assert.Equal(new StringValues(new[] { "HeaderB, heaDera" }), context.CachedResponse.Headers[HeaderNames.Vary]);
+ }
+
+ [Fact]
+ public async Task FinalizeCacheBody_Cache_IfContentLengthMatches()
+ {
+ var cache = new TestOutputCache();
+ var sink = new TestSink();
+ var middleware = TestUtils.CreateTestMiddleware(testSink: sink, cache: cache);
+ var context = TestUtils.CreateTestContext(cache);
+
+ middleware.ShimResponseStream(context);
+ context.HttpContext.Response.ContentLength = 20;
+
+ await context.HttpContext.Response.WriteAsync(new string('0', 20));
+
+ context.CachedResponse = new OutputCacheEntry { Headers = new() };
+ context.CacheKey = "BaseKey";
+ context.CachedResponseValidFor = TimeSpan.FromSeconds(10);
+
+ await middleware.FinalizeCacheBodyAsync(context);
+
+ Assert.Equal(1, cache.SetCount);
+ TestUtils.AssertLoggedMessages(
+ sink.Writes,
+ LoggedMessage.ResponseCached);
+ }
+
+ [Theory]
+ [InlineData("GET")]
+ [InlineData("HEAD")]
+ public async Task FinalizeCacheBody_DoNotCache_IfContentLengthMismatches(string method)
+ {
+ var cache = new TestOutputCache();
+ var sink = new TestSink();
+ var middleware = TestUtils.CreateTestMiddleware(testSink: sink, cache: cache);
+ var context = TestUtils.CreateTestContext(cache);
+
+ middleware.ShimResponseStream(context);
+ context.HttpContext.Response.ContentLength = 9;
+ context.HttpContext.Request.Method = method;
+
+ await context.HttpContext.Response.WriteAsync(new string('0', 10));
+
+ context.CachedResponse = new OutputCacheEntry();
+ context.CacheKey = "BaseKey";
+ context.CachedResponseValidFor = TimeSpan.FromSeconds(10);
+
+ await middleware.FinalizeCacheBodyAsync(context);
+
+ Assert.Equal(0, cache.SetCount);
+ TestUtils.AssertLoggedMessages(
+ sink.Writes,
+ LoggedMessage.ResponseContentLengthMismatchNotCached);
+ }
+
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public async Task FinalizeCacheBody_RequestHead_Cache_IfContentLengthPresent_AndBodyAbsentOrOfSameLength(bool includeBody)
+ {
+ var cache = new TestOutputCache();
+ var sink = new TestSink();
+ var middleware = TestUtils.CreateTestMiddleware(testSink: sink, cache: cache);
+ var context = TestUtils.CreateTestContext(cache);
+
+ middleware.ShimResponseStream(context);
+ context.HttpContext.Response.ContentLength = 10;
+ context.HttpContext.Request.Method = "HEAD";
+
+ if (includeBody)
+ {
+ // A response to HEAD should not include a body, but it may be present
+ await context.HttpContext.Response.WriteAsync(new string('0', 10));
+ }
+
+ context.CachedResponse = new OutputCacheEntry { Headers = new() };
+ context.CacheKey = "BaseKey";
+ context.CachedResponseValidFor = TimeSpan.FromSeconds(10);
+
+ await middleware.FinalizeCacheBodyAsync(context);
+
+ Assert.Equal(1, cache.SetCount);
+ TestUtils.AssertLoggedMessages(
+ sink.Writes,
+ LoggedMessage.ResponseCached);
+ }
+
+ [Fact]
+ public async Task FinalizeCacheBody_Cache_IfContentLengthAbsent()
+ {
+ var cache = new TestOutputCache();
+ var sink = new TestSink();
+ var middleware = TestUtils.CreateTestMiddleware(testSink: sink, cache: cache);
+ var context = TestUtils.CreateTestContext(cache);
+
+ middleware.ShimResponseStream(context);
+
+ await context.HttpContext.Response.WriteAsync(new string('0', 10));
+
+ context.CachedResponse = new OutputCacheEntry { Headers = new HeaderDictionary() };
+ context.CacheKey = "BaseKey";
+ context.CachedResponseValidFor = TimeSpan.FromSeconds(10);
+
+ await middleware.FinalizeCacheBodyAsync(context);
+
+ Assert.Equal(1, cache.SetCount);
+ TestUtils.AssertLoggedMessages(
+ sink.Writes,
+ LoggedMessage.ResponseCached);
+ }
+
+ [Fact]
+ public async Task FinalizeCacheBody_DoNotCache_IfIsResponseCacheableFalse()
+ {
+ var cache = new TestOutputCache();
+ var sink = new TestSink();
+ var middleware = TestUtils.CreateTestMiddleware(testSink: sink, cache: cache);
+ var context = TestUtils.CreateTestContext(cache);
+
+ middleware.ShimResponseStream(context);
+ await context.HttpContext.Response.WriteAsync(new string('0', 10));
+ context.AllowCacheStorage = false;
+ context.CacheKey = "BaseKey";
+
+ await middleware.FinalizeCacheBodyAsync(context);
+
+ Assert.Equal(0, cache.SetCount);
+ TestUtils.AssertLoggedMessages(
+ sink.Writes,
+ LoggedMessage.ResponseNotCached);
+ }
+
+ [Fact]
+ public async Task FinalizeCacheBody_DoNotCache_IfBufferingDisabled()
+ {
+ var cache = new TestOutputCache();
+ var sink = new TestSink();
+ var middleware = TestUtils.CreateTestMiddleware(testSink: sink, cache: cache);
+ var context = TestUtils.CreateTestContext(cache);
+
+ middleware.ShimResponseStream(context);
+ await context.HttpContext.Response.WriteAsync(new string('0', 10));
+
+ context.OutputCacheStream.DisableBuffering();
+
+ await middleware.FinalizeCacheBodyAsync(context);
+
+ Assert.Equal(0, cache.SetCount);
+ TestUtils.AssertLoggedMessages(
+ sink.Writes,
+ LoggedMessage.ResponseNotCached);
+ }
+
+ [Fact]
+ public async Task FinalizeCacheBody_DoNotCache_IfSizeTooBig()
+ {
+ var sink = new TestSink();
+ var middleware = TestUtils.CreateTestMiddleware(
+ testSink: sink,
+ keyProvider: new TestResponseCachingKeyProvider("BaseKey"),
+ cache: new MemoryOutputCacheStore(new MemoryCache(new MemoryCacheOptions
+ {
+ SizeLimit = 100
+ })));
+ var context = TestUtils.CreateTestContext();
+ middleware.TryGetRequestPolicies(context.HttpContext, out var policies);
+ middleware.ShimResponseStream(context);
+
+ await context.HttpContext.Response.WriteAsync(new string('0', 101));
+
+ context.CachedResponse = new OutputCacheEntry() { Headers = new HeaderDictionary() };
+ context.CacheKey = "BaseKey";
+ context.CachedResponseValidFor = TimeSpan.FromSeconds(10);
+
+ await middleware.FinalizeCacheBodyAsync(context);
+
+ // The response cached message will be logged but the adding of the entry will no-op
+ TestUtils.AssertLoggedMessages(
+ sink.Writes,
+ LoggedMessage.ResponseCached);
+
+ // The entry cannot be retrieved
+ Assert.False(await middleware.TryServeFromCacheAsync(context, policies));
+ }
+
+ [Fact]
+ public void AddOutputCachingFeature_SecondInvocation_Throws()
+ {
+ var httpContext = new DefaultHttpContext();
+ var context = TestUtils.CreateTestContext(httpContext);
+
+ // Should not throw
+ OutputCacheMiddleware.AddOutputCacheFeature(context);
+
+ // Should throw
+ Assert.ThrowsAny<InvalidOperationException>(() => OutputCacheMiddleware.AddOutputCacheFeature(context));
+ }
+
+ private class FakeResponseFeature : HttpResponseFeature
+ {
+ public override void OnStarting(Func<object, Task> callback, object state) { }
+ }
+
+ [Fact]
+ public void GetOrderCasingNormalizedStringValues_NormalizesCasingToUpper()
+ {
+ var uppercaseStrings = new StringValues(new[] { "STRINGA", "STRINGB" });
+ var lowercaseStrings = new StringValues(new[] { "stringA", "stringB" });
+
+ var normalizedStrings = OutputCacheMiddleware.GetOrderCasingNormalizedStringValues(lowercaseStrings);
+
+ Assert.Equal(uppercaseStrings, normalizedStrings);
+ }
+
+ [Fact]
+ public void GetOrderCasingNormalizedStringValues_NormalizesOrder()
+ {
+ var orderedStrings = new StringValues(new[] { "STRINGA", "STRINGB" });
+ var reverseOrderStrings = new StringValues(new[] { "STRINGB", "STRINGA" });
+
+ var normalizedStrings = OutputCacheMiddleware.GetOrderCasingNormalizedStringValues(reverseOrderStrings);
+
+ Assert.Equal(orderedStrings, normalizedStrings);
+ }
+
+ [Fact]
+ public void GetOrderCasingNormalizedStringValues_PreservesCommas()
+ {
+ var originalStrings = new StringValues(new[] { "STRINGA, STRINGB" });
+
+ var normalizedStrings = OutputCacheMiddleware.GetOrderCasingNormalizedStringValues(originalStrings);
+
+ Assert.Equal(originalStrings, normalizedStrings);
+ }
+}
diff --git a/src/Middleware/OutputCaching/test/OutputCachePoliciesTests.cs b/src/Middleware/OutputCaching/test/OutputCachePoliciesTests.cs
new file mode 100644
index 0000000000..53acf44c2a
--- /dev/null
+++ b/src/Middleware/OutputCaching/test/OutputCachePoliciesTests.cs
@@ -0,0 +1,288 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.AspNetCore.OutputCaching.Policies;
+
+namespace Microsoft.AspNetCore.OutputCaching.Tests;
+
+public class OutputCachePoliciesTests
+{
+ [Fact]
+ public async Task DefaultCachePolicy_EnablesCache()
+ {
+ IOutputCachePolicy policy = DefaultPolicy.Instance;
+ var context = TestUtils.CreateUninitializedContext();
+
+ await policy.CacheRequestAsync(context, default);
+
+ Assert.True(context.EnableOutputCaching);
+ }
+
+ [Fact]
+ public async Task DefaultCachePolicy_AllowsLocking()
+ {
+ IOutputCachePolicy policy = DefaultPolicy.Instance;
+ var context = TestUtils.CreateUninitializedContext();
+
+ await policy.CacheRequestAsync(context, default);
+
+ Assert.True(context.AllowLocking);
+ }
+
+ [Fact]
+ public async Task DefaultCachePolicy_VariesByStar()
+ {
+ IOutputCachePolicy policy = DefaultPolicy.Instance;
+ var context = TestUtils.CreateUninitializedContext();
+
+ await policy.CacheRequestAsync(context, default);
+
+ Assert.Equal("*", context.CacheVaryByRules.QueryKeys);
+ }
+
+ [Fact]
+ public async Task EnableCachePolicy_DisablesCache()
+ {
+ IOutputCachePolicy policy = EnableCachePolicy.Disabled;
+ var context = TestUtils.CreateUninitializedContext();
+ context.EnableOutputCaching = true;
+
+ await policy.CacheRequestAsync(context, default);
+
+ Assert.False(context.EnableOutputCaching);
+ }
+
+ [Fact]
+ public async Task ExpirationPolicy_SetsResponseExpirationTimeSpan()
+ {
+ var duration = TimeSpan.FromDays(1);
+ IOutputCachePolicy policy = new ExpirationPolicy(duration);
+ var context = TestUtils.CreateUninitializedContext();
+
+ await policy.CacheRequestAsync(context, default);
+
+ Assert.Equal(duration, context.ResponseExpirationTimeSpan);
+ }
+
+ [Fact]
+ public async Task LockingPolicy_EnablesLocking()
+ {
+ IOutputCachePolicy policy = LockingPolicy.Enabled;
+ var context = TestUtils.CreateUninitializedContext();
+
+ await policy.CacheRequestAsync(context, default);
+
+ Assert.True(context.AllowLocking);
+ }
+
+ [Fact]
+ public async Task LockingPolicy_DisablesLocking()
+ {
+ IOutputCachePolicy policy = LockingPolicy.Disabled;
+ var context = TestUtils.CreateUninitializedContext();
+
+ await policy.CacheRequestAsync(context, default);
+
+ Assert.False(context.AllowLocking);
+ }
+
+ [Fact]
+ public async Task NoLookupPolicy_DisablesLookup()
+ {
+ IOutputCachePolicy policy = NoLookupPolicy.Instance;
+ var context = TestUtils.CreateUninitializedContext();
+
+ await policy.CacheRequestAsync(context, default);
+
+ Assert.False(context.AllowCacheLookup);
+ }
+
+ [Fact]
+ public async Task NoStorePolicy_DisablesStore()
+ {
+ IOutputCachePolicy policy = NoStorePolicy.Instance;
+ var context = TestUtils.CreateUninitializedContext();
+
+ await policy.CacheRequestAsync(context, default);
+
+ Assert.False(context.AllowCacheStorage);
+ }
+
+ [Theory]
+ [InlineData(true, true, true)]
+ [InlineData(true, false, false)]
+ [InlineData(false, true, false)]
+ [InlineData(false, false, false)]
+ public async Task PredicatePolicy_Filters(bool filter, bool enabled, bool expected)
+ {
+ IOutputCachePolicy predicate = new PredicatePolicy(c => ValueTask.FromResult(filter), enabled ? EnableCachePolicy.Enabled : EnableCachePolicy.Disabled);
+ var context = TestUtils.CreateUninitializedContext();
+
+ await predicate.CacheRequestAsync(context, default);
+
+ Assert.Equal(expected, context.EnableOutputCaching);
+ }
+
+ [Fact]
+ public async Task ProfilePolicy_UsesNamedProfile()
+ {
+ var context = TestUtils.CreateUninitializedContext();
+ context.Options.AddPolicy("enabled", EnableCachePolicy.Enabled);
+ context.Options.AddPolicy("disabled", EnableCachePolicy.Disabled);
+
+ IOutputCachePolicy policy = new NamedPolicy("enabled");
+
+ await policy.CacheRequestAsync(context, default);
+
+ Assert.True(context.EnableOutputCaching);
+
+ policy = new NamedPolicy("disabled");
+
+ await policy.CacheRequestAsync(context, default);
+
+ Assert.False(context.EnableOutputCaching);
+ }
+
+ [Fact]
+ public async Task TagsPolicy_Tags()
+ {
+ var context = TestUtils.CreateUninitializedContext();
+
+ IOutputCachePolicy policy = new TagsPolicy("tag1", "tag2");
+
+ await policy.CacheRequestAsync(context, default);
+
+ Assert.Contains("tag1", context.Tags);
+ Assert.Contains("tag2", context.Tags);
+ }
+
+ [Fact]
+ public async Task VaryByHeadersPolicy_IsEmpty()
+ {
+ var context = TestUtils.CreateUninitializedContext();
+
+ IOutputCachePolicy policy = new VaryByHeaderPolicy();
+
+ await policy.CacheRequestAsync(context, default);
+
+ Assert.Empty(context.CacheVaryByRules.Headers);
+ }
+
+ [Fact]
+ public async Task VaryByHeadersPolicy_AddsSingleHeader()
+ {
+ var context = TestUtils.CreateUninitializedContext();
+ var header = "header";
+
+ IOutputCachePolicy policy = new VaryByHeaderPolicy(header);
+
+ await policy.CacheRequestAsync(context, default);
+
+ Assert.Equal(header, context.CacheVaryByRules.Headers);
+ }
+
+ [Fact]
+ public async Task VaryByHeadersPolicy_AddsMultipleHeaders()
+ {
+ var context = TestUtils.CreateUninitializedContext();
+ var headers = new[] { "header1", "header2" };
+
+ IOutputCachePolicy policy = new VaryByHeaderPolicy(headers);
+
+ await policy.CacheRequestAsync(context, default);
+
+ Assert.Equal(headers, context.CacheVaryByRules.Headers);
+ }
+
+ [Fact]
+ public async Task VaryByQueryPolicy_IsEmpty()
+ {
+ var context = TestUtils.CreateUninitializedContext();
+
+ IOutputCachePolicy policy = new VaryByQueryPolicy();
+
+ await policy.CacheRequestAsync(context, default);
+
+ Assert.Empty(context.CacheVaryByRules.QueryKeys);
+ }
+
+ [Fact]
+ public async Task VaryByQueryPolicy_AddsSingleHeader()
+ {
+ var context = TestUtils.CreateUninitializedContext();
+ var query = "query";
+
+ IOutputCachePolicy policy = new VaryByQueryPolicy(query);
+
+ await policy.CacheRequestAsync(context, default);
+
+ Assert.Equal(query, context.CacheVaryByRules.QueryKeys);
+ }
+
+ [Fact]
+ public async Task VaryByQueryPolicy_AddsMultipleHeaders()
+ {
+ var context = TestUtils.CreateUninitializedContext();
+ var queries = new[] { "query1", "query2" };
+
+ IOutputCachePolicy policy = new VaryByQueryPolicy(queries);
+
+ await policy.CacheRequestAsync(context, default);
+
+ Assert.Equal(queries, context.CacheVaryByRules.QueryKeys);
+ }
+
+ [Fact]
+ public async Task VaryByValuePolicy_SingleValue()
+ {
+ var context = TestUtils.CreateUninitializedContext();
+ var value = "value";
+
+ IOutputCachePolicy policy = new VaryByValuePolicy(context => value);
+
+ await policy.CacheRequestAsync(context, default);
+
+ Assert.Equal(value, context.CacheVaryByRules.VaryByPrefix);
+ }
+
+ [Fact]
+ public async Task VaryByValuePolicy_SingleValueAsync()
+ {
+ var context = TestUtils.CreateUninitializedContext();
+ var value = "value";
+
+ IOutputCachePolicy policy = new VaryByValuePolicy((context, token) => ValueTask.FromResult(value));
+
+ await policy.CacheRequestAsync(context, default);
+
+ Assert.Equal(value, context.CacheVaryByRules.VaryByPrefix);
+ }
+
+ [Fact]
+ public async Task VaryByValuePolicy_KeyValuePair()
+ {
+ var context = TestUtils.CreateUninitializedContext();
+ var key = "key";
+ var value = "value";
+
+ IOutputCachePolicy policy = new VaryByValuePolicy(context => new KeyValuePair<string, string>(key, value));
+
+ await policy.CacheRequestAsync(context, default);
+
+ Assert.Contains(new KeyValuePair<string, string>(key, value), context.CacheVaryByRules.VaryByCustom);
+ }
+
+ [Fact]
+ public async Task VaryByValuePolicy_KeyValuePairAsync()
+ {
+ var context = TestUtils.CreateUninitializedContext();
+ var key = "key";
+ var value = "value";
+
+ IOutputCachePolicy policy = new VaryByValuePolicy((context, token) => ValueTask.FromResult(new KeyValuePair<string, string>(key, value)));
+
+ await policy.CacheRequestAsync(context, default);
+
+ Assert.Contains(new KeyValuePair<string, string>(key, value), context.CacheVaryByRules.VaryByCustom);
+ }
+}
diff --git a/src/Middleware/OutputCaching/test/OutputCachePolicyProviderTests.cs b/src/Middleware/OutputCaching/test/OutputCachePolicyProviderTests.cs
new file mode 100644
index 0000000000..6238b09791
--- /dev/null
+++ b/src/Middleware/OutputCaching/test/OutputCachePolicyProviderTests.cs
@@ -0,0 +1,410 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.OutputCaching.Policies;
+using Microsoft.Extensions.Logging.Testing;
+using Microsoft.Extensions.Options;
+using Microsoft.Net.Http.Headers;
+
+namespace Microsoft.AspNetCore.OutputCaching.Tests;
+
+public class OutputCachePolicyProviderTests
+{
+ public static TheoryData<string> CacheableMethods
+ {
+ get
+ {
+ return new TheoryData<string>
+ {
+ HttpMethods.Get,
+ HttpMethods.Head
+ };
+ }
+ }
+
+ public static TheoryData<string> NonCacheableMethods
+ {
+ get
+ {
+ return new TheoryData<string>
+ {
+ HttpMethods.Post,
+ HttpMethods.Put,
+ HttpMethods.Delete,
+ HttpMethods.Trace,
+ HttpMethods.Connect,
+ HttpMethods.Options,
+ "",
+ null
+ };
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(CacheableMethods))]
+ public async Task AttemptOutputCaching_CacheableMethods_IsAllowed(string method)
+ {
+ var sink = new TestSink();
+ var context = TestUtils.CreateTestContext(sink);
+ var policies = new[] { new OutputCachePolicyBuilder().Build() };
+ context.HttpContext.Request.Method = method;
+
+ foreach (var policy in policies)
+ {
+ await policy.CacheRequestAsync(context, default);
+ }
+
+ Assert.True(context.AllowCacheStorage);
+ Assert.True(context.AllowCacheLookup);
+ Assert.Empty(sink.Writes);
+ }
+
+ [Theory]
+ [MemberData(nameof(NonCacheableMethods))]
+ public async Task AttemptOutputCaching_UncacheableMethods_NotAllowed(string method)
+ {
+ var sink = new TestSink();
+ var context = TestUtils.CreateTestContext(sink);
+ var policy = new OutputCachePolicyBuilder().Build();
+ context.HttpContext.Request.Method = method;
+
+ await policy.CacheRequestAsync(context, default);
+
+ Assert.False(context.AllowCacheLookup);
+ Assert.False(context.AllowCacheStorage);
+ TestUtils.AssertLoggedMessages(
+ sink.Writes,
+ LoggedMessage.RequestMethodNotCacheable);
+ }
+
+ [Fact]
+ public async Task AttemptResponseCaching_AuthorizationHeaders_NotAllowed()
+ {
+ var sink = new TestSink();
+ var context = TestUtils.CreateTestContext(sink);
+ context.HttpContext.Request.Method = HttpMethods.Get;
+ context.HttpContext.Request.Headers.Authorization = "Placeholder";
+
+ var policy = new OutputCachePolicyBuilder().Build();
+
+ await policy.CacheRequestAsync(context, default);
+
+ Assert.False(context.AllowCacheStorage);
+ Assert.False(context.AllowCacheLookup);
+
+ TestUtils.AssertLoggedMessages(
+ sink.Writes,
+ LoggedMessage.RequestWithAuthorizationNotCacheable);
+ }
+
+ [Fact]
+ public async Task AllowCacheStorage_NoStore_IsAllowed()
+ {
+ var sink = new TestSink();
+ var context = TestUtils.CreateTestContext(sink);
+ context.HttpContext.Request.Method = HttpMethods.Get;
+ context.HttpContext.Request.Headers.CacheControl = new CacheControlHeaderValue()
+ {
+ NoStore = true
+ }.ToString();
+
+ var policy = new OutputCachePolicyBuilder().Build();
+ await policy.CacheRequestAsync(context, default);
+
+ Assert.True(context.AllowCacheStorage);
+ Assert.Empty(sink.Writes);
+ }
+
+ [Fact]
+ public async Task AllowCacheLookup_LegacyDirectives_OverridenByCacheControl()
+ {
+ var sink = new TestSink();
+ var context = TestUtils.CreateTestContext(sink);
+ context.HttpContext.Request.Method = HttpMethods.Get;
+ context.HttpContext.Request.Headers.Pragma = "no-cache";
+ context.HttpContext.Request.Headers.CacheControl = "max-age=10";
+
+ var policy = new OutputCachePolicyBuilder().Build();
+ await policy.CacheRequestAsync(context, default);
+
+ Assert.True(context.AllowCacheLookup);
+ Assert.Empty(sink.Writes);
+ }
+
+ [Fact]
+ public async Task IsResponseCacheable_NoPublic_IsAllowed()
+ {
+ var sink = new TestSink();
+ var context = TestUtils.CreateTestContext(sink);
+
+ var policy = new OutputCachePolicyBuilder().Build();
+ await policy.ServeResponseAsync(context, default);
+
+ Assert.True(context.AllowCacheStorage);
+ Assert.True(context.AllowCacheLookup);
+ Assert.Empty(sink.Writes);
+ }
+
+ [Fact]
+ public async Task IsResponseCacheable_Public_IsAllowed()
+ {
+ var sink = new TestSink();
+ var context = TestUtils.CreateTestContext(sink);
+ context.HttpContext.Response.Headers.CacheControl = new CacheControlHeaderValue()
+ {
+ Public = true
+ }.ToString();
+
+ var policy = new OutputCachePolicyBuilder().Build();
+ await policy.ServeResponseAsync(context, default);
+
+ Assert.True(context.AllowCacheStorage);
+ Assert.True(context.AllowCacheLookup);
+ Assert.Empty(sink.Writes);
+ }
+
+ [Fact]
+ public async Task IsResponseCacheable_NoCache_IsAllowed()
+ {
+ var sink = new TestSink();
+ var context = TestUtils.CreateTestContext(sink);
+ context.HttpContext.Response.Headers.CacheControl = new CacheControlHeaderValue()
+ {
+ NoCache = true
+ }.ToString();
+
+ var policy = new OutputCachePolicyBuilder().Build();
+ await policy.ServeResponseAsync(context, default);
+
+ Assert.True(context.AllowCacheStorage);
+ Assert.True(context.AllowCacheLookup);
+ Assert.Empty(sink.Writes);
+ }
+
+ [Fact]
+ public async Task IsResponseCacheable_ResponseNoStore_IsAllowed()
+ {
+ var sink = new TestSink();
+ var context = TestUtils.CreateTestContext(sink);
+ context.HttpContext.Response.Headers.CacheControl = new CacheControlHeaderValue()
+ {
+ NoStore = true
+ }.ToString();
+
+ var policy = new OutputCachePolicyBuilder().Build();
+ await policy.ServeResponseAsync(context, default);
+
+ Assert.True(context.AllowCacheStorage);
+ Assert.True(context.AllowCacheLookup);
+ Assert.Empty(sink.Writes);
+ }
+
+ [Fact]
+ public async Task IsResponseCacheable_SetCookieHeader_NotAllowed()
+ {
+ var sink = new TestSink();
+ var context = TestUtils.CreateTestContext(sink);
+ context.HttpContext.Response.Headers.SetCookie = "cookieName=cookieValue";
+
+ var policy = new OutputCachePolicyBuilder().Build();
+ await policy.ServeResponseAsync(context, default);
+
+ Assert.False(context.AllowCacheStorage);
+ Assert.True(context.AllowCacheLookup);
+ TestUtils.AssertLoggedMessages(
+ sink.Writes,
+ LoggedMessage.ResponseWithSetCookieNotCacheable);
+ }
+
+ [Fact]
+ public async Task IsResponseCacheable_VaryHeaderByStar_IsAllowed()
+ {
+ var sink = new TestSink();
+ var context = TestUtils.CreateTestContext(sink);
+ context.HttpContext.Response.Headers.Vary = "*";
+ var policy = new OutputCachePolicyBuilder().Build();
+ await policy.ServeResponseAsync(context, default);
+
+ Assert.True(context.AllowCacheStorage);
+ Assert.True(context.AllowCacheLookup);
+ Assert.Empty(sink.Writes);
+ }
+
+ [Fact]
+ public async Task IsResponseCacheable_Private_IsAllowed()
+ {
+ var sink = new TestSink();
+ var context = TestUtils.CreateTestContext(sink);
+ context.HttpContext.Response.Headers.CacheControl = new CacheControlHeaderValue()
+ {
+ Private = true
+ }.ToString();
+
+ var policy = new OutputCachePolicyBuilder().Build();
+ await policy.ServeResponseAsync(context, default);
+
+ Assert.True(context.AllowCacheStorage);
+ Assert.True(context.AllowCacheLookup);
+ Assert.Empty(sink.Writes);
+ }
+
+ [Theory]
+ [InlineData(StatusCodes.Status200OK)]
+ public async Task IsResponseCacheable_SuccessStatusCodes_IsAllowed(int statusCode)
+ {
+ var sink = new TestSink();
+ var context = TestUtils.CreateTestContext(sink);
+ context.HttpContext.Response.StatusCode = statusCode;
+
+ var policy = new OutputCachePolicyBuilder().Build();
+ await policy.ServeResponseAsync(context, default);
+
+ Assert.True(context.AllowCacheStorage);
+ Assert.True(context.AllowCacheLookup);
+ Assert.Empty(sink.Writes);
+ }
+
+ [Theory]
+ [InlineData(StatusCodes.Status100Continue)]
+ [InlineData(StatusCodes.Status101SwitchingProtocols)]
+ [InlineData(StatusCodes.Status102Processing)]
+ [InlineData(StatusCodes.Status201Created)]
+ [InlineData(StatusCodes.Status202Accepted)]
+ [InlineData(StatusCodes.Status203NonAuthoritative)]
+ [InlineData(StatusCodes.Status204NoContent)]
+ [InlineData(StatusCodes.Status205ResetContent)]
+ [InlineData(StatusCodes.Status206PartialContent)]
+ [InlineData(StatusCodes.Status207MultiStatus)]
+ [InlineData(StatusCodes.Status208AlreadyReported)]
+ [InlineData(StatusCodes.Status226IMUsed)]
+ [InlineData(StatusCodes.Status300MultipleChoices)]
+ [InlineData(StatusCodes.Status301MovedPermanently)]
+ [InlineData(StatusCodes.Status302Found)]
+ [InlineData(StatusCodes.Status303SeeOther)]
+ [InlineData(StatusCodes.Status304NotModified)]
+ [InlineData(StatusCodes.Status305UseProxy)]
+ [InlineData(StatusCodes.Status306SwitchProxy)]
+ [InlineData(StatusCodes.Status307TemporaryRedirect)]
+ [InlineData(StatusCodes.Status308PermanentRedirect)]
+ [InlineData(StatusCodes.Status400BadRequest)]
+ [InlineData(StatusCodes.Status401Unauthorized)]
+ [InlineData(StatusCodes.Status402PaymentRequired)]
+ [InlineData(StatusCodes.Status403Forbidden)]
+ [InlineData(StatusCodes.Status404NotFound)]
+ [InlineData(StatusCodes.Status405MethodNotAllowed)]
+ [InlineData(StatusCodes.Status406NotAcceptable)]
+ [InlineData(StatusCodes.Status407ProxyAuthenticationRequired)]
+ [InlineData(StatusCodes.Status408RequestTimeout)]
+ [InlineData(StatusCodes.Status409Conflict)]
+ [InlineData(StatusCodes.Status410Gone)]
+ [InlineData(StatusCodes.Status411LengthRequired)]
+ [InlineData(StatusCodes.Status412PreconditionFailed)]
+ [InlineData(StatusCodes.Status413RequestEntityTooLarge)]
+ [InlineData(StatusCodes.Status414RequestUriTooLong)]
+ [InlineData(StatusCodes.Status415UnsupportedMediaType)]
+ [InlineData(StatusCodes.Status416RequestedRangeNotSatisfiable)]
+ [InlineData(StatusCodes.Status417ExpectationFailed)]
+ [InlineData(StatusCodes.Status418ImATeapot)]
+ [InlineData(StatusCodes.Status419AuthenticationTimeout)]
+ [InlineData(StatusCodes.Status421MisdirectedRequest)]
+ [InlineData(StatusCodes.Status422UnprocessableEntity)]
+ [InlineData(StatusCodes.Status423Locked)]
+ [InlineData(StatusCodes.Status424FailedDependency)]
+ [InlineData(StatusCodes.Status426UpgradeRequired)]
+ [InlineData(StatusCodes.Status428PreconditionRequired)]
+ [InlineData(StatusCodes.Status429TooManyRequests)]
+ [InlineData(StatusCodes.Status431RequestHeaderFieldsTooLarge)]
+ [InlineData(StatusCodes.Status451UnavailableForLegalReasons)]
+ [InlineData(StatusCodes.Status500InternalServerError)]
+ [InlineData(StatusCodes.Status501NotImplemented)]
+ [InlineData(StatusCodes.Status502BadGateway)]
+ [InlineData(StatusCodes.Status503ServiceUnavailable)]
+ [InlineData(StatusCodes.Status504GatewayTimeout)]
+ [InlineData(StatusCodes.Status505HttpVersionNotsupported)]
+ [InlineData(StatusCodes.Status506VariantAlsoNegotiates)]
+ [InlineData(StatusCodes.Status507InsufficientStorage)]
+ [InlineData(StatusCodes.Status508LoopDetected)]
+ [InlineData(StatusCodes.Status510NotExtended)]
+ [InlineData(StatusCodes.Status511NetworkAuthenticationRequired)]
+ public async Task IsResponseCacheable_NonSuccessStatusCodes_NotAllowed(int statusCode)
+ {
+ var sink = new TestSink();
+ var context = TestUtils.CreateTestContext(sink);
+ context.HttpContext.Response.StatusCode = statusCode;
+
+ var policy = new OutputCachePolicyBuilder().Build();
+ await policy.ServeResponseAsync(context, default);
+
+ Assert.True(context.AllowCacheLookup);
+ Assert.False(context.AllowCacheStorage);
+ TestUtils.AssertLoggedMessages(
+ sink.Writes,
+ LoggedMessage.ResponseWithUnsuccessfulStatusCodeNotCacheable);
+ }
+
+ [Fact]
+ public async Task IsResponseCacheable_NoExpiryRequirements_IsAllowed()
+ {
+ var sink = new TestSink();
+ var context = TestUtils.CreateTestContext(sink);
+ context.HttpContext.Response.StatusCode = StatusCodes.Status200OK;
+
+ var utcNow = DateTimeOffset.UtcNow;
+ context.HttpContext.Response.Headers.Date = HeaderUtilities.FormatDate(utcNow);
+ context.ResponseTime = DateTimeOffset.MaxValue;
+
+ var policy = new OutputCachePolicyBuilder().Build();
+ await policy.ServeResponseAsync(context, default);
+
+ Assert.True(context.AllowCacheStorage);
+ Assert.True(context.AllowCacheLookup);
+ Assert.Empty(sink.Writes);
+ }
+
+ [Fact]
+ public async Task IsResponseCacheable_MaxAgeOverridesExpiry_IsAllowed()
+ {
+ var utcNow = DateTimeOffset.UtcNow;
+ var sink = new TestSink();
+ var context = TestUtils.CreateTestContext(sink);
+ context.HttpContext.Response.StatusCode = StatusCodes.Status200OK;
+ context.HttpContext.Response.Headers.CacheControl = new CacheControlHeaderValue()
+ {
+ MaxAge = TimeSpan.FromSeconds(10)
+ }.ToString();
+ context.HttpContext.Response.Headers.Expires = HeaderUtilities.FormatDate(utcNow);
+ context.HttpContext.Response.Headers.Date = HeaderUtilities.FormatDate(utcNow);
+ context.ResponseTime = utcNow + TimeSpan.FromSeconds(9);
+
+ var policy = new OutputCachePolicyBuilder().Build();
+ await policy.ServeResponseAsync(context, default);
+
+ Assert.True(context.AllowCacheStorage);
+ Assert.True(context.AllowCacheLookup);
+ Assert.Empty(sink.Writes);
+ }
+
+ [Fact]
+ public async Task IsResponseCacheable_SharedMaxAgeOverridesMaxAge_IsAllowed()
+ {
+ var utcNow = DateTimeOffset.UtcNow;
+ var sink = new TestSink();
+ var context = TestUtils.CreateTestContext(sink);
+ context.HttpContext.Response.StatusCode = StatusCodes.Status200OK;
+ context.HttpContext.Response.Headers.CacheControl = new CacheControlHeaderValue()
+ {
+ MaxAge = TimeSpan.FromSeconds(10),
+ SharedMaxAge = TimeSpan.FromSeconds(15)
+ }.ToString();
+ context.HttpContext.Response.Headers.Date = HeaderUtilities.FormatDate(utcNow);
+ context.ResponseTime = utcNow + TimeSpan.FromSeconds(11);
+
+ var policy = new OutputCachePolicyBuilder().Build();
+ await policy.ServeResponseAsync(context, default);
+
+ Assert.True(context.AllowCacheStorage);
+ Assert.True(context.AllowCacheLookup);
+ Assert.Empty(sink.Writes);
+ }
+}
diff --git a/src/Middleware/OutputCaching/test/OutputCacheTests.cs b/src/Middleware/OutputCaching/test/OutputCacheTests.cs
new file mode 100644
index 0000000000..83080b67ef
--- /dev/null
+++ b/src/Middleware/OutputCaching/test/OutputCacheTests.cs
@@ -0,0 +1,985 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Net.Http;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.TestHost;
+using Microsoft.Net.Http.Headers;
+
+namespace Microsoft.AspNetCore.OutputCaching.Tests;
+
+public class OutputCacheTests
+{
+ [Theory]
+ [InlineData("GET")]
+ [InlineData("HEAD")]
+ public async Task ServesCachedContent_IfAvailable(string method)
+ {
+ var builders = TestUtils.CreateBuildersWithOutputCaching();
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ var initialResponse = await client.SendAsync(TestUtils.CreateRequest(method, ""));
+ var subsequentResponse = await client.SendAsync(TestUtils.CreateRequest(method, ""));
+
+ await AssertCachedResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Theory]
+ [InlineData("GET")]
+ [InlineData("HEAD")]
+ public async Task ServesFreshContent_IfNotAvailable(string method)
+ {
+ var builders = TestUtils.CreateBuildersWithOutputCaching();
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ var initialResponse = await client.SendAsync(TestUtils.CreateRequest(method, ""));
+ var subsequentResponse = await client.SendAsync(TestUtils.CreateRequest(method, "different"));
+
+ await AssertFreshResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Fact]
+ public async Task ServesFreshContent_Post()
+ {
+ var builders = TestUtils.CreateBuildersWithOutputCaching();
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ var initialResponse = await client.PostAsync("", new StringContent(string.Empty));
+ var subsequentResponse = await client.PostAsync("", new StringContent(string.Empty));
+
+ await AssertFreshResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Fact]
+ public async Task ServesFreshContent_Head_Get()
+ {
+ var builders = TestUtils.CreateBuildersWithOutputCaching();
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ var subsequentResponse = await client.SendAsync(new HttpRequestMessage(HttpMethod.Head, ""));
+ var initialResponse = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, ""));
+
+ await AssertFreshResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Fact]
+ public async Task ServesFreshContent_Get_Head()
+ {
+ var builders = TestUtils.CreateBuildersWithOutputCaching();
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ var initialResponse = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, ""));
+ var subsequentResponse = await client.SendAsync(new HttpRequestMessage(HttpMethod.Head, ""));
+
+ await AssertFreshResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Theory]
+ [InlineData("GET")]
+ [InlineData("HEAD")]
+ public async Task ServesCachedContent_If_CacheControlNoCache(string method)
+ {
+ var builders = TestUtils.CreateBuildersWithOutputCaching();
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+
+ var initialResponse = await client.SendAsync(TestUtils.CreateRequest(method, ""));
+
+ // verify the response is cached
+ var cachedResponse = await client.SendAsync(TestUtils.CreateRequest(method, ""));
+ await AssertCachedResponseAsync(initialResponse, cachedResponse);
+
+ // assert cached response still served
+ client.DefaultRequestHeaders.CacheControl =
+ new System.Net.Http.Headers.CacheControlHeaderValue { NoCache = true };
+ var subsequentResponse = await client.SendAsync(TestUtils.CreateRequest(method, ""));
+
+ await AssertCachedResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Theory]
+ [InlineData("GET")]
+ [InlineData("HEAD")]
+ public async Task ServesCachedContent_If_PragmaNoCache(string method)
+ {
+ var builders = TestUtils.CreateBuildersWithOutputCaching();
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+
+ var initialResponse = await client.SendAsync(TestUtils.CreateRequest(method, ""));
+
+ // verify the response is cached
+ var cachedResponse = await client.SendAsync(TestUtils.CreateRequest(method, ""));
+ await AssertCachedResponseAsync(initialResponse, cachedResponse);
+
+ // assert cached response still served
+ client.DefaultRequestHeaders.Pragma.Clear();
+ client.DefaultRequestHeaders.Pragma.Add(new System.Net.Http.Headers.NameValueHeaderValue("no-cache"));
+ var subsequentResponse = await client.SendAsync(TestUtils.CreateRequest(method, ""));
+
+ await AssertCachedResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Theory]
+ [InlineData("GET")]
+ [InlineData("HEAD")]
+ public async Task ServesCachedContent_If_PathCasingDiffers(string method)
+ {
+ var builders = TestUtils.CreateBuildersWithOutputCaching();
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ var initialResponse = await client.SendAsync(TestUtils.CreateRequest(method, "path"));
+ var subsequentResponse = await client.SendAsync(TestUtils.CreateRequest(method, "PATH"));
+
+ await AssertCachedResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Theory]
+ [InlineData("GET")]
+ [InlineData("HEAD")]
+ public async Task ServesFreshContent_If_PathCasingDiffers(string method)
+ {
+ var options = new OutputCacheOptions { UseCaseSensitivePaths = true };
+ var builders = TestUtils.CreateBuildersWithOutputCaching(options: options);
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ var initialResponse = await client.SendAsync(TestUtils.CreateRequest(method, "path"));
+ var subsequentResponse = await client.SendAsync(TestUtils.CreateRequest(method, "PATH"));
+
+ await AssertFreshResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Theory]
+ [InlineData("GET")]
+ [InlineData("HEAD")]
+ public async Task ServesFreshContent_If_ResponseExpired(string method)
+ {
+ var options = new OutputCacheOptions
+ {
+ DefaultExpirationTimeSpan = TimeSpan.FromMicroseconds(100)
+ };
+
+ var builders = TestUtils.CreateBuildersWithOutputCaching(options: options);
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ var initialResponse = await client.SendAsync(TestUtils.CreateRequest(method, ""));
+ await Task.Delay(1);
+ var subsequentResponse = await client.SendAsync(TestUtils.CreateRequest(method, ""));
+
+ await AssertFreshResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Theory]
+ [InlineData("GET")]
+ [InlineData("HEAD")]
+ public async Task ServesFreshContent_If_Authorization_HeaderExists(string method)
+ {
+ var builders = TestUtils.CreateBuildersWithOutputCaching();
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("abc");
+ var initialResponse = await client.SendAsync(TestUtils.CreateRequest(method, ""));
+ var subsequentResponse = await client.SendAsync(TestUtils.CreateRequest(method, ""));
+
+ await AssertFreshResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Theory]
+ [InlineData("GET")]
+ [InlineData("HEAD")]
+ public async Task ServesCachedContent_If_Authorization_HeaderExists(string method)
+ {
+ var options = new OutputCacheOptions();
+
+ var builders = TestUtils.CreateBuildersWithOutputCaching(options: options);
+
+ // This is added after the DefaultPolicy which disables caching for authenticated requests
+ options.AddBasePolicy(b => b.AddPolicy<AllowTestPolicy>());
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("abc");
+ var initialResponse = await client.SendAsync(TestUtils.CreateRequest(method, ""));
+ var subsequentResponse = await client.SendAsync(TestUtils.CreateRequest(method, ""));
+
+ await AssertCachedResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Fact]
+ public async Task ServesCachedContent_IfVaryHeader_Matches()
+ {
+ var builders = TestUtils.CreateBuildersWithOutputCaching(contextAction: context => context.Response.Headers.Vary = HeaderNames.From);
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ client.DefaultRequestHeaders.From = "user@example.com";
+ var initialResponse = await client.GetAsync("");
+ var subsequentResponse = await client.GetAsync("");
+
+ await AssertCachedResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Fact]
+ public async Task ServesFreshContent_IfVaryHeader_Mismatches()
+ {
+ var options = new OutputCacheOptions();
+ options.AddBasePolicy(b => b.VaryByHeader(HeaderNames.From).Build());
+
+ var builders = TestUtils.CreateBuildersWithOutputCaching(options: options);
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ client.DefaultRequestHeaders.From = "user@example.com";
+ var initialResponse = await client.GetAsync("");
+ client.DefaultRequestHeaders.From = "user2@example.com";
+ var subsequentResponse = await client.GetAsync("");
+
+ await AssertFreshResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Fact]
+ public async Task ServesCachedContent_IfVaryQueryKeys_Matches()
+ {
+ var options = new OutputCacheOptions();
+ options.AddBasePolicy(b => b.VaryByQuery("query"));
+
+ var builders = TestUtils.CreateBuildersWithOutputCaching(options: options);
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ var initialResponse = await client.GetAsync("?query=value");
+ var subsequentResponse = await client.GetAsync("?query=value");
+
+ await AssertCachedResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Fact]
+ public async Task ServesCachedContent_IfVaryQueryKeysExplicit_Matches_QueryKeyCaseInsensitive()
+ {
+ var options = new OutputCacheOptions();
+ options.AddBasePolicy(b => b.VaryByQuery("QueryA", "queryb"));
+
+ var builders = TestUtils.CreateBuildersWithOutputCaching(options: options);
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ var initialResponse = await client.GetAsync("?querya=valuea&queryb=valueb");
+ var subsequentResponse = await client.GetAsync("?QueryA=valuea&QueryB=valueb");
+
+ await AssertCachedResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Fact]
+ public async Task ServesCachedContent_IfVaryQueryKeyStar_Matches_QueryKeyCaseInsensitive()
+ {
+ var options = new OutputCacheOptions();
+ options.AddBasePolicy(b => b.VaryByQuery("*"));
+
+ var builders = TestUtils.CreateBuildersWithOutputCaching(options: options);
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ var initialResponse = await client.GetAsync("?querya=valuea&queryb=valueb");
+ var subsequentResponse = await client.GetAsync("?QueryA=valuea&QueryB=valueb");
+
+ await AssertCachedResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Fact]
+ public async Task ServesCachedContent_IfVaryQueryKeyExplicit_Matches_OrderInsensitive()
+ {
+ var options = new OutputCacheOptions();
+ options.AddBasePolicy(b => b.VaryByQuery("QueryB", "QueryA"));
+
+ var builders = TestUtils.CreateBuildersWithOutputCaching(options: options);
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ var initialResponse = await client.GetAsync("?QueryA=ValueA&QueryB=ValueB");
+ var subsequentResponse = await client.GetAsync("?QueryB=ValueB&QueryA=ValueA");
+
+ await AssertCachedResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Fact]
+ public async Task ServesCachedContent_IfVaryQueryKeyStar_Matches_OrderInsensitive()
+ {
+ var options = new OutputCacheOptions();
+ options.AddBasePolicy(b => b.VaryByQuery("*"));
+
+ var builders = TestUtils.CreateBuildersWithOutputCaching(options: options);
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ var initialResponse = await client.GetAsync("?QueryA=ValueA&QueryB=ValueB");
+ var subsequentResponse = await client.GetAsync("?QueryB=ValueB&QueryA=ValueA");
+
+ await AssertCachedResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Fact]
+ public async Task ServesFreshContent_IfVaryQueryKey_Mismatches()
+ {
+ var options = new OutputCacheOptions();
+ options.AddBasePolicy(b => b.VaryByQuery("query").Build());
+ var builders = TestUtils.CreateBuildersWithOutputCaching(options: options);
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ var initialResponse = await client.GetAsync("?query=value");
+ var subsequentResponse = await client.GetAsync("?query=value2");
+
+ await AssertFreshResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Fact]
+ public async Task ServesCachedContent_IfOtherVaryQueryKey_Mismatches()
+ {
+ var options = new OutputCacheOptions();
+ options.AddBasePolicy(b => b.VaryByQuery("query").Build());
+
+ var builders = TestUtils.CreateBuildersWithOutputCaching(options: options);
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ var initialResponse = await client.GetAsync("?other=value1");
+ var subsequentResponse = await client.GetAsync("?other=value2");
+
+ await AssertCachedResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Fact]
+ public async Task ServesFreshContent_IfVaryQueryKeyExplicit_Mismatch_QueryKeyCaseSensitive()
+ {
+ var options = new OutputCacheOptions();
+ options.AddBasePolicy(new VaryByQueryPolicy("QueryA", "QueryB"));
+ var builders = TestUtils.CreateBuildersWithOutputCaching(options: options);
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ var initialResponse = await client.GetAsync("?querya=valuea&queryb=valueb");
+ var subsequentResponse = await client.GetAsync("?querya=ValueA&queryb=ValueB");
+
+ await AssertFreshResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Fact]
+ public async Task ServesFreshContent_IfVaryQueryKeyStar_Mismatch_QueryKeyValueCaseSensitive()
+ {
+ var options = new OutputCacheOptions();
+ options.AddBasePolicy(new VaryByQueryPolicy("*"));
+ var builders = TestUtils.CreateBuildersWithOutputCaching(options: options);
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ var initialResponse = await client.GetAsync("?querya=valuea&queryb=valueb");
+ var subsequentResponse = await client.GetAsync("?querya=ValueA&queryb=ValueB");
+
+ await AssertFreshResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Fact]
+ public async Task ServesCachedContent_IfRequestRequirements_NotMet()
+ {
+ var builders = TestUtils.CreateBuildersWithOutputCaching();
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ var initialResponse = await client.GetAsync("");
+ client.DefaultRequestHeaders.CacheControl = new System.Net.Http.Headers.CacheControlHeaderValue()
+ {
+ MaxAge = TimeSpan.FromSeconds(0)
+ };
+ var subsequentResponse = await client.GetAsync("");
+
+ await AssertCachedResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Fact]
+ public async Task Serves504_IfOnlyIfCachedHeader_IsSpecified()
+ {
+ var builders = TestUtils.CreateBuildersWithOutputCaching();
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ var initialResponse = await client.GetAsync("");
+ client.DefaultRequestHeaders.CacheControl = new System.Net.Http.Headers.CacheControlHeaderValue()
+ {
+ OnlyIfCached = true
+ };
+ var subsequentResponse = await client.GetAsync("/different");
+
+ initialResponse.EnsureSuccessStatusCode();
+ Assert.Equal(System.Net.HttpStatusCode.GatewayTimeout, subsequentResponse.StatusCode);
+ }
+ }
+
+ [Fact]
+ public async Task ServesFreshContent_IfSetCookie_IsSpecified()
+ {
+ var builders = TestUtils.CreateBuildersWithOutputCaching(contextAction: context => context.Response.Headers.SetCookie = "cookieName=cookieValue");
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ var initialResponse = await client.GetAsync("");
+ var subsequentResponse = await client.GetAsync("");
+
+ await AssertFreshResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Fact]
+ public async Task ServesCachedContent_IfSubsequentRequestContainsNoStore()
+ {
+ var builders = TestUtils.CreateBuildersWithOutputCaching();
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ var initialResponse = await client.GetAsync("");
+ client.DefaultRequestHeaders.CacheControl = new System.Net.Http.Headers.CacheControlHeaderValue()
+ {
+ NoStore = true
+ };
+ var subsequentResponse = await client.GetAsync("");
+
+ await AssertCachedResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Fact]
+ public async Task ServesCachedContent_IfInitialRequestContainsNoStore()
+ {
+ var builders = TestUtils.CreateBuildersWithOutputCaching();
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ client.DefaultRequestHeaders.CacheControl = new System.Net.Http.Headers.CacheControlHeaderValue()
+ {
+ NoStore = true
+ };
+ var initialResponse = await client.GetAsync("");
+ var subsequentResponse = await client.GetAsync("");
+
+ await AssertCachedResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Fact]
+ public async Task ServesCachedContent_IfInitialResponseContainsNoStore()
+ {
+ var builders = TestUtils.CreateBuildersWithOutputCaching(contextAction: context => context.Response.Headers.CacheControl = CacheControlHeaderValue.NoStoreString);
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ var initialResponse = await client.GetAsync("");
+ var subsequentResponse = await client.GetAsync("");
+
+ await AssertCachedResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Fact]
+ public async Task Serves304_IfIfModifiedSince_Satisfied()
+ {
+ var builders = TestUtils.CreateBuildersWithOutputCaching(contextAction: context =>
+ {
+ // Ensure these headers are also returned on the subsequent response
+ context.Response.GetTypedHeaders().ETag = new EntityTagHeaderValue("\"E1\"");
+ context.Response.Headers.ContentLocation = "/";
+ context.Response.Headers.Vary = HeaderNames.From;
+ });
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ var initialResponse = await client.GetAsync("");
+ client.DefaultRequestHeaders.IfModifiedSince = DateTimeOffset.MaxValue;
+ var subsequentResponse = await client.GetAsync("");
+
+ initialResponse.EnsureSuccessStatusCode();
+ Assert.Equal(System.Net.HttpStatusCode.NotModified, subsequentResponse.StatusCode);
+ Assert304Headers(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Fact]
+ public async Task ServesCachedContent_IfIfModifiedSince_NotSatisfied()
+ {
+ var builders = TestUtils.CreateBuildersWithOutputCaching();
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ var initialResponse = await client.GetAsync("");
+ client.DefaultRequestHeaders.IfModifiedSince = DateTimeOffset.MinValue;
+ var subsequentResponse = await client.GetAsync("");
+
+ await AssertCachedResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Fact]
+ public async Task Serves304_IfIfNoneMatch_Satisfied()
+ {
+ var builders = TestUtils.CreateBuildersWithOutputCaching(contextAction: context =>
+ {
+ context.Response.GetTypedHeaders().ETag = new EntityTagHeaderValue("\"E1\"");
+ context.Response.Headers.ContentLocation = "/";
+ context.Response.Headers.Vary = HeaderNames.From;
+ });
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ var initialResponse = await client.GetAsync("");
+ client.DefaultRequestHeaders.IfNoneMatch.Add(new System.Net.Http.Headers.EntityTagHeaderValue("\"E1\""));
+ var subsequentResponse = await client.GetAsync("");
+
+ initialResponse.EnsureSuccessStatusCode();
+ Assert.Equal(System.Net.HttpStatusCode.NotModified, subsequentResponse.StatusCode);
+ Assert304Headers(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Fact]
+ public async Task ServesCachedContent_IfIfNoneMatch_NotSatisfied()
+ {
+ var builders = TestUtils.CreateBuildersWithOutputCaching(contextAction: context => context.Response.GetTypedHeaders().ETag = new EntityTagHeaderValue("\"E1\""));
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ var initialResponse = await client.GetAsync("");
+ client.DefaultRequestHeaders.IfNoneMatch.Add(new System.Net.Http.Headers.EntityTagHeaderValue("\"E2\""));
+ var subsequentResponse = await client.GetAsync("");
+
+ await AssertCachedResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Fact]
+ public async Task ServesCachedContent_IfBodySize_IsCacheable()
+ {
+ var options = new OutputCacheOptions
+ {
+ MaximumBodySize = 1000
+ };
+ options.AddBasePolicy(b => b.Build());
+
+ var builders = TestUtils.CreateBuildersWithOutputCaching(options: options);
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ var initialResponse = await client.GetAsync("");
+ var subsequentResponse = await client.GetAsync("");
+
+ await AssertCachedResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Fact]
+ public async Task ServesFreshContent_IfBodySize_IsNotCacheable()
+ {
+ var builders = TestUtils.CreateBuildersWithOutputCaching(options: new OutputCacheOptions()
+ {
+ MaximumBodySize = 1
+ });
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ var initialResponse = await client.GetAsync("");
+ var subsequentResponse = await client.GetAsync("/different");
+
+ await AssertFreshResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Fact]
+ public async Task ServesFreshContent_CaseSensitivePaths_IsNotCacheable()
+ {
+ var builders = TestUtils.CreateBuildersWithOutputCaching(options: new OutputCacheOptions()
+ {
+ UseCaseSensitivePaths = true
+ });
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ var initialResponse = await client.GetAsync("/path");
+ var subsequentResponse = await client.GetAsync("/Path");
+
+ await AssertFreshResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Fact]
+ public async Task ServesCachedContent_WithoutReplacingCachedVaryBy_OnCacheMiss()
+ {
+ var builders = TestUtils.CreateBuildersWithOutputCaching(contextAction: context => context.Response.Headers.Vary = HeaderNames.From);
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ client.DefaultRequestHeaders.From = "user@example.com";
+ var initialResponse = await client.GetAsync("");
+ client.DefaultRequestHeaders.From = "user2@example.com";
+ var otherResponse = await client.GetAsync("");
+ client.DefaultRequestHeaders.From = "user@example.com";
+ var subsequentResponse = await client.GetAsync("");
+
+ await AssertCachedResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Fact]
+ public async Task ServesCachedContent_IfCachedVaryByNotUpdated_OnCacheMiss()
+ {
+ var builders = TestUtils.CreateBuildersWithOutputCaching(contextAction: context => context.Response.Headers.Vary = context.Request.Headers.Pragma);
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ client.DefaultRequestHeaders.From = "user@example.com";
+ client.DefaultRequestHeaders.Pragma.Clear();
+ client.DefaultRequestHeaders.Pragma.Add(new System.Net.Http.Headers.NameValueHeaderValue("From"));
+ client.DefaultRequestHeaders.MaxForwards = 1;
+ var initialResponse = await client.GetAsync("");
+ client.DefaultRequestHeaders.From = "user2@example.com";
+ client.DefaultRequestHeaders.Pragma.Clear();
+ client.DefaultRequestHeaders.Pragma.Add(new System.Net.Http.Headers.NameValueHeaderValue("From"));
+ client.DefaultRequestHeaders.MaxForwards = 2;
+ var otherResponse = await client.GetAsync("");
+ client.DefaultRequestHeaders.From = "user@example.com";
+ client.DefaultRequestHeaders.Pragma.Clear();
+ client.DefaultRequestHeaders.Pragma.Add(new System.Net.Http.Headers.NameValueHeaderValue("From"));
+ client.DefaultRequestHeaders.MaxForwards = 1;
+ var subsequentResponse = await client.GetAsync("");
+
+ await AssertCachedResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ [Fact]
+ public async Task ServesCachedContent_IfAvailable_UsingHead_WithContentLength()
+ {
+ var builders = TestUtils.CreateBuildersWithOutputCaching();
+
+ foreach (var builder in builders)
+ {
+ using var host = builder.Build();
+
+ await host.StartAsync();
+
+ using var server = host.GetTestServer();
+ var client = server.CreateClient();
+ var initialResponse = await client.SendAsync(TestUtils.CreateRequest("HEAD", "?contentLength=10"));
+ var subsequentResponse = await client.SendAsync(TestUtils.CreateRequest("HEAD", "?contentLength=10"));
+
+ await AssertCachedResponseAsync(initialResponse, subsequentResponse);
+ }
+ }
+
+ private static void Assert304Headers(HttpResponseMessage initialResponse, HttpResponseMessage subsequentResponse)
+ {
+ // https://tools.ietf.org/html/rfc7232#section-4.1
+ // The server generating a 304 response MUST generate any of the
+ // following header fields that would have been sent in a 200 (OK)
+ // response to the same request: Cache-Control, Content-Location, Date,
+ // ETag, Expires, and Vary.
+
+ Assert.Equal(initialResponse.Headers.CacheControl, subsequentResponse.Headers.CacheControl);
+ Assert.Equal(initialResponse.Content.Headers.ContentLocation, subsequentResponse.Content.Headers.ContentLocation);
+ Assert.Equal(initialResponse.Headers.Date, subsequentResponse.Headers.Date);
+ Assert.Equal(initialResponse.Headers.ETag, subsequentResponse.Headers.ETag);
+ Assert.Equal(initialResponse.Content.Headers.Expires, subsequentResponse.Content.Headers.Expires);
+ Assert.Equal(initialResponse.Headers.Vary, subsequentResponse.Headers.Vary);
+ }
+
+ private static async Task AssertCachedResponseAsync(HttpResponseMessage initialResponse, HttpResponseMessage subsequentResponse)
+ {
+ initialResponse.EnsureSuccessStatusCode();
+ subsequentResponse.EnsureSuccessStatusCode();
+
+ foreach (var header in initialResponse.Headers)
+ {
+ Assert.Equal(initialResponse.Headers.GetValues(header.Key), subsequentResponse.Headers.GetValues(header.Key));
+ }
+ Assert.True(subsequentResponse.Headers.Contains(HeaderNames.Age));
+ Assert.Equal(await initialResponse.Content.ReadAsStringAsync(), await subsequentResponse.Content.ReadAsStringAsync());
+ }
+
+ private static async Task AssertFreshResponseAsync(HttpResponseMessage initialResponse, HttpResponseMessage subsequentResponse)
+ {
+ initialResponse.EnsureSuccessStatusCode();
+ subsequentResponse.EnsureSuccessStatusCode();
+
+ Assert.False(subsequentResponse.Headers.Contains(HeaderNames.Age));
+
+ if (initialResponse.RequestMessage.Method == HttpMethod.Head &&
+ subsequentResponse.RequestMessage.Method == HttpMethod.Head)
+ {
+ Assert.True(initialResponse.Headers.Contains("X-Value"));
+ Assert.NotEqual(initialResponse.Headers.GetValues("X-Value"), subsequentResponse.Headers.GetValues("X-Value"));
+ }
+ else
+ {
+ Assert.NotEqual(await initialResponse.Content.ReadAsStringAsync(), await subsequentResponse.Content.ReadAsStringAsync());
+ }
+ }
+}
diff --git a/src/Middleware/OutputCaching/test/SegmentWriteStreamTests.cs b/src/Middleware/OutputCaching/test/SegmentWriteStreamTests.cs
new file mode 100644
index 0000000000..cb8d4bcfbb
--- /dev/null
+++ b/src/Middleware/OutputCaching/test/SegmentWriteStreamTests.cs
@@ -0,0 +1,105 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.OutputCaching.Tests;
+
+public class SegmentWriteStreamTests
+{
+ private static readonly byte[] WriteData = new byte[]
+ {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
+ };
+
+ [Theory]
+ [InlineData(0)]
+ [InlineData(-1)]
+ public void SegmentWriteStream_InvalidSegmentSize_Throws(int segmentSize)
+ {
+ Assert.Throws<ArgumentOutOfRangeException>(() => new SegmentWriteStream(segmentSize));
+ }
+
+ [Fact]
+ public void ReadAndSeekOperations_Throws()
+ {
+ var stream = new SegmentWriteStream(1);
+
+ Assert.Throws<NotSupportedException>(() => stream.Read(new byte[1], 0, 0));
+ Assert.Throws<NotSupportedException>(() => stream.Position = 0);
+ Assert.Throws<NotSupportedException>(() => stream.Seek(0, SeekOrigin.Begin));
+ }
+
+ [Fact]
+ public void GetSegments_ExtractionDisablesWriting()
+ {
+ var stream = new SegmentWriteStream(1);
+
+ Assert.True(stream.CanWrite);
+ Assert.Empty(stream.GetSegments());
+ Assert.False(stream.CanWrite);
+ }
+
+ [Theory]
+ [InlineData(4)]
+ [InlineData(5)]
+ [InlineData(6)]
+ public void WriteByte_CanWriteAllBytes(int segmentSize)
+ {
+ var stream = new SegmentWriteStream(segmentSize);
+
+ foreach (var datum in WriteData)
+ {
+ stream.WriteByte(datum);
+ }
+ var segments = stream.GetSegments();
+
+ Assert.Equal(WriteData.Length, stream.Length);
+ Assert.Equal((WriteData.Length + segmentSize - 1) / segmentSize, segments.Count);
+
+ for (var i = 0; i < WriteData.Length; i += segmentSize)
+ {
+ var expectedSegmentSize = Math.Min(segmentSize, WriteData.Length - i);
+ var expectedSegment = new byte[expectedSegmentSize];
+ for (int j = 0; j < expectedSegmentSize; j++)
+ {
+ expectedSegment[j] = (byte)(i + j);
+ }
+ var segment = segments[i / segmentSize];
+
+ Assert.Equal(expectedSegmentSize, segment.Length);
+ Assert.True(expectedSegment.SequenceEqual(segment));
+ }
+ }
+
+ [Theory]
+ [InlineData(4)]
+ [InlineData(5)]
+ [InlineData(6)]
+ public void Write_CanWriteAllBytes(int writeSize)
+ {
+ var segmentSize = 5;
+ var stream = new SegmentWriteStream(segmentSize);
+
+ for (var i = 0; i < WriteData.Length; i += writeSize)
+ {
+ stream.Write(WriteData, i, Math.Min(writeSize, WriteData.Length - i));
+ }
+ var segments = stream.GetSegments();
+
+ Assert.Equal(WriteData.Length, stream.Length);
+ Assert.Equal((WriteData.Length + segmentSize - 1) / segmentSize, segments.Count);
+
+ for (var i = 0; i < WriteData.Length; i += segmentSize)
+ {
+ var expectedSegmentSize = Math.Min(segmentSize, WriteData.Length - i);
+ var expectedSegment = new byte[expectedSegmentSize];
+ for (int j = 0; j < expectedSegmentSize; j++)
+ {
+ expectedSegment[j] = (byte)(i + j);
+ }
+ var segment = segments[i / segmentSize];
+
+ Assert.Equal(expectedSegmentSize, segment.Length);
+ Assert.True(expectedSegment.SequenceEqual(segment));
+ }
+ }
+}
diff --git a/src/Middleware/OutputCaching/test/TestDocument.txt b/src/Middleware/OutputCaching/test/TestDocument.txt
new file mode 100644
index 0000000000..fb31ae6de8
--- /dev/null
+++ b/src/Middleware/OutputCaching/test/TestDocument.txt
@@ -0,0 +1 @@
+0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ \ No newline at end of file
diff --git a/src/Middleware/OutputCaching/test/TestUtils.cs b/src/Middleware/OutputCaching/test/TestUtils.cs
new file mode 100644
index 0000000000..33ac6f56c8
--- /dev/null
+++ b/src/Middleware/OutputCaching/test/TestUtils.cs
@@ -0,0 +1,374 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#nullable enable
+using System.Net.Http;
+using System.Text;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http.Features;
+using Microsoft.AspNetCore.TestHost;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
+using Microsoft.Extensions.Logging.Testing;
+using Microsoft.Extensions.ObjectPool;
+using Microsoft.Extensions.Options;
+
+namespace Microsoft.AspNetCore.OutputCaching.Tests;
+
+internal class TestUtils
+{
+ static TestUtils()
+ {
+ // Force sharding in tests
+ StreamUtilities.BodySegmentSize = 10;
+ }
+
+ private static bool TestRequestDelegate(HttpContext context, string guid)
+ {
+ var headers = context.Response.GetTypedHeaders();
+ headers.Date = DateTimeOffset.UtcNow;
+ headers.Headers["X-Value"] = guid;
+
+ if (context.Request.Method != "HEAD")
+ {
+ return true;
+ }
+ return false;
+ }
+
+ internal static async Task TestRequestDelegateWriteAsync(HttpContext context)
+ {
+ var uniqueId = Guid.NewGuid().ToString();
+ if (TestRequestDelegate(context, uniqueId))
+ {
+ await context.Response.WriteAsync(uniqueId);
+ }
+ }
+
+ internal static async Task TestRequestDelegateSendFileAsync(HttpContext context)
+ {
+ var path = Path.Combine(AppContext.BaseDirectory, "TestDocument.txt");
+ var uniqueId = Guid.NewGuid().ToString();
+ if (TestRequestDelegate(context, uniqueId))
+ {
+ await context.Response.SendFileAsync(path, 0, null);
+ await context.Response.WriteAsync(uniqueId);
+ }
+ }
+
+ internal static Task TestRequestDelegateWrite(HttpContext context)
+ {
+ var uniqueId = Guid.NewGuid().ToString();
+ if (TestRequestDelegate(context, uniqueId))
+ {
+ var feature = context.Features.Get<IHttpBodyControlFeature>();
+ if (feature != null)
+ {
+ feature.AllowSynchronousIO = true;
+ }
+ context.Response.Write(uniqueId);
+ }
+ return Task.CompletedTask;
+ }
+
+ internal static IOutputCacheKeyProvider CreateTestKeyProvider()
+ {
+ return CreateTestKeyProvider(new OutputCacheOptions());
+ }
+
+ internal static IOutputCacheKeyProvider CreateTestKeyProvider(OutputCacheOptions options)
+ {
+ return new OutputCacheKeyProvider(new DefaultObjectPoolProvider(), Options.Create(options));
+ }
+
+ internal static IEnumerable<IHostBuilder> CreateBuildersWithOutputCaching(
+ Action<IApplicationBuilder>? configureDelegate = null,
+ OutputCacheOptions? options = null,
+ Action<HttpContext>? contextAction = null)
+ {
+ return CreateBuildersWithOutputCaching(configureDelegate, options, new RequestDelegate[]
+ {
+ context =>
+ {
+ contextAction?.Invoke(context);
+ return TestRequestDelegateWrite(context);
+ },
+ context =>
+ {
+ contextAction?.Invoke(context);
+ return TestRequestDelegateWriteAsync(context);
+ },
+ context =>
+ {
+ contextAction?.Invoke(context);
+ return TestRequestDelegateSendFileAsync(context);
+ },
+ });
+ }
+
+ private static IEnumerable<IHostBuilder> CreateBuildersWithOutputCaching(
+ Action<IApplicationBuilder>? configureDelegate = null,
+ OutputCacheOptions? options = null,
+ IEnumerable<RequestDelegate>? requestDelegates = null)
+ {
+ if (configureDelegate == null)
+ {
+ configureDelegate = app => { };
+ }
+ if (requestDelegates == null)
+ {
+ requestDelegates = new RequestDelegate[]
+ {
+ TestRequestDelegateWriteAsync,
+ TestRequestDelegateWrite
+ };
+ }
+
+ foreach (var requestDelegate in requestDelegates)
+ {
+ // Test with in memory OutputCache
+ yield return new HostBuilder()
+ .ConfigureWebHost(webHostBuilder =>
+ {
+ webHostBuilder
+ .UseTestServer()
+ .ConfigureServices(services =>
+ {
+ services.AddOutputCache(outputCachingOptions =>
+ {
+ if (options != null)
+ {
+ outputCachingOptions.MaximumBodySize = options.MaximumBodySize;
+ outputCachingOptions.UseCaseSensitivePaths = options.UseCaseSensitivePaths;
+ outputCachingOptions.SystemClock = options.SystemClock;
+ outputCachingOptions.BasePolicies = options.BasePolicies;
+ outputCachingOptions.DefaultExpirationTimeSpan = options.DefaultExpirationTimeSpan;
+ outputCachingOptions.SizeLimit = options.SizeLimit;
+ }
+ else
+ {
+ outputCachingOptions.BasePolicies = new();
+ outputCachingOptions.BasePolicies.Add(new OutputCachePolicyBuilder().Build());
+ }
+ });
+ })
+ .Configure(app =>
+ {
+ configureDelegate(app);
+ app.UseOutputCache();
+ app.Run(requestDelegate);
+ });
+ });
+ }
+ }
+
+ internal static OutputCacheMiddleware CreateTestMiddleware(
+ RequestDelegate? next = null,
+ IOutputCacheStore? cache = null,
+ OutputCacheOptions? options = null,
+ TestSink? testSink = null,
+ IOutputCacheKeyProvider? keyProvider = null
+ )
+ {
+ if (next == null)
+ {
+ next = httpContext => Task.CompletedTask;
+ }
+ if (cache == null)
+ {
+ cache = new TestOutputCache();
+ }
+ if (options == null)
+ {
+ options = new OutputCacheOptions();
+ }
+ if (keyProvider == null)
+ {
+ keyProvider = new OutputCacheKeyProvider(new DefaultObjectPoolProvider(), Options.Create(options));
+ }
+
+ return new OutputCacheMiddleware(
+ next,
+ Options.Create(options),
+ testSink == null ? (ILoggerFactory)NullLoggerFactory.Instance : new TestLoggerFactory(testSink, true),
+ cache,
+ keyProvider);
+ }
+
+ internal static OutputCacheContext CreateTestContext(IOutputCacheStore? cache = null, OutputCacheOptions? options = null)
+ {
+ return new OutputCacheContext(new DefaultHttpContext(), cache ?? new TestOutputCache(), options ?? Options.Create(new OutputCacheOptions()).Value, NullLogger.Instance)
+ {
+ EnableOutputCaching = true,
+ AllowCacheStorage = true,
+ AllowCacheLookup = true,
+ ResponseTime = DateTimeOffset.UtcNow
+ };
+ }
+
+ internal static OutputCacheContext CreateTestContext(HttpContext httpContext, IOutputCacheStore? cache = null, OutputCacheOptions? options = null)
+ {
+ return new OutputCacheContext(httpContext, cache ?? new TestOutputCache(), options ?? Options.Create(new OutputCacheOptions()).Value, NullLogger.Instance)
+ {
+ EnableOutputCaching = true,
+ AllowCacheStorage = true,
+ AllowCacheLookup = true,
+ ResponseTime = DateTimeOffset.UtcNow
+ };
+ }
+
+ internal static OutputCacheContext CreateTestContext(ITestSink testSink, IOutputCacheStore? cache = null, OutputCacheOptions? options = null)
+ {
+ return new OutputCacheContext(new DefaultHttpContext(), cache ?? new TestOutputCache(), options ?? Options.Create(new OutputCacheOptions()).Value, new TestLogger("OutputCachingTests", testSink, true))
+ {
+ EnableOutputCaching = true,
+ AllowCacheStorage = true,
+ AllowCacheLookup = true,
+ ResponseTime = DateTimeOffset.UtcNow
+ };
+ }
+
+ internal static OutputCacheContext CreateUninitializedContext(IOutputCacheStore? cache = null, OutputCacheOptions? options = null)
+ {
+ return new OutputCacheContext(new DefaultHttpContext(), cache ?? new TestOutputCache(), options ?? Options.Create(new OutputCacheOptions()).Value, NullLogger.Instance)
+ {
+ };
+ }
+
+ internal static void AssertLoggedMessages(IEnumerable<WriteContext> messages, params LoggedMessage[] expectedMessages)
+ {
+ var messageList = messages.ToList();
+ Assert.Equal(expectedMessages.Length, messageList.Count);
+
+ for (var i = 0; i < messageList.Count; i++)
+ {
+ Assert.Equal(expectedMessages[i].EventId, messageList[i].EventId);
+ Assert.Equal(expectedMessages[i].LogLevel, messageList[i].LogLevel);
+ }
+ }
+
+ public static HttpRequestMessage CreateRequest(string method, string requestUri)
+ {
+ return new HttpRequestMessage(new HttpMethod(method), requestUri);
+ }
+}
+
+internal static class HttpResponseWritingExtensions
+{
+ internal static void Write(this HttpResponse response, string text)
+ {
+ ArgumentNullException.ThrowIfNull(response);
+ ArgumentNullException.ThrowIfNull(text);
+
+ var data = Encoding.UTF8.GetBytes(text);
+ response.Body.Write(data, 0, data.Length);
+ }
+}
+
+internal class LoggedMessage
+{
+ internal static LoggedMessage RequestMethodNotCacheable => new LoggedMessage(1, LogLevel.Debug);
+ internal static LoggedMessage RequestWithAuthorizationNotCacheable => new LoggedMessage(2, LogLevel.Debug);
+ internal static LoggedMessage ResponseWithSetCookieNotCacheable => new LoggedMessage(3, LogLevel.Debug);
+ internal static LoggedMessage ResponseWithUnsuccessfulStatusCodeNotCacheable => new LoggedMessage(4, LogLevel.Debug);
+ internal static LoggedMessage NotModifiedIfNoneMatchStar => new LoggedMessage(5, LogLevel.Debug);
+ internal static LoggedMessage NotModifiedIfNoneMatchMatched => new LoggedMessage(6, LogLevel.Debug);
+ internal static LoggedMessage NotModifiedIfModifiedSinceSatisfied => new LoggedMessage(7, LogLevel.Debug);
+ internal static LoggedMessage NotModifiedServed => new LoggedMessage(8, LogLevel.Information);
+ internal static LoggedMessage CachedResponseServed => new LoggedMessage(9, LogLevel.Information);
+ internal static LoggedMessage GatewayTimeoutServed => new LoggedMessage(10, LogLevel.Information);
+ internal static LoggedMessage NoResponseServed => new LoggedMessage(11, LogLevel.Information);
+ internal static LoggedMessage VaryByRulesUpdated => new LoggedMessage(12, LogLevel.Debug);
+ internal static LoggedMessage ResponseCached => new LoggedMessage(13, LogLevel.Information);
+ internal static LoggedMessage ResponseNotCached => new LoggedMessage(14, LogLevel.Information);
+ internal static LoggedMessage ResponseContentLengthMismatchNotCached => new LoggedMessage(15, LogLevel.Warning);
+ internal static LoggedMessage ExpirationExpiresExceeded => new LoggedMessage(15, LogLevel.Debug);
+
+ private LoggedMessage(int evenId, LogLevel logLevel)
+ {
+ EventId = evenId;
+ LogLevel = logLevel;
+ }
+
+ internal int EventId { get; }
+ internal LogLevel LogLevel { get; }
+}
+
+internal class TestResponseCachingKeyProvider : IOutputCacheKeyProvider
+{
+ private readonly string _key;
+
+ public TestResponseCachingKeyProvider(string key)
+ {
+ _key = key;
+ }
+
+ public string CreateStorageKey(OutputCacheContext? context)
+ {
+ return _key;
+ }
+}
+
+internal class TestOutputCache : IOutputCacheStore
+{
+ private readonly Dictionary<string, byte[]?> _storage = new();
+ public int GetCount { get; private set; }
+ public int SetCount { get; private set; }
+
+ public ValueTask EvictByTagAsync(string tag, CancellationToken token)
+ {
+ throw new NotImplementedException();
+ }
+
+ public ValueTask<byte[]?> GetAsync(string? key, CancellationToken token)
+ {
+ ArgumentNullException.ThrowIfNull(key);
+
+ GetCount++;
+ try
+ {
+ return ValueTask.FromResult(_storage[key]);
+ }
+ catch
+ {
+ return ValueTask.FromResult(default(byte[]));
+ }
+ }
+
+ public ValueTask SetAsync(string key, byte[] entry, string[]? tags, TimeSpan validFor, CancellationToken token)
+ {
+ SetCount++;
+ _storage[key] = entry;
+
+ return ValueTask.CompletedTask;
+ }
+}
+
+internal class TestClock : ISystemClock
+{
+ public DateTimeOffset UtcNow { get; set; }
+}
+
+internal class AllowTestPolicy : IOutputCachePolicy
+{
+ public ValueTask CacheRequestAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ context.AllowCacheLookup = true;
+ context.AllowCacheStorage = true;
+ return ValueTask.CompletedTask;
+ }
+
+ public ValueTask ServeFromCacheAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ return ValueTask.CompletedTask;
+ }
+
+ public ValueTask ServeResponseAsync(OutputCacheContext context, CancellationToken cancellationToken)
+ {
+ return ValueTask.CompletedTask;
+ }
+}
diff --git a/src/Middleware/RequestDecompression/src/Microsoft.AspNetCore.RequestDecompression.csproj b/src/Middleware/RequestDecompression/src/Microsoft.AspNetCore.RequestDecompression.csproj
index 3089a5ca16..be80f8b518 100644
--- a/src/Middleware/RequestDecompression/src/Microsoft.AspNetCore.RequestDecompression.csproj
+++ b/src/Middleware/RequestDecompression/src/Microsoft.AspNetCore.RequestDecompression.csproj
@@ -7,6 +7,7 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>aspnetcore</PackageTags>
<IsPackable>false</IsPackable>
+ <IsTrimmable>true</IsTrimmable>
</PropertyGroup>
<ItemGroup>
diff --git a/src/Middleware/StaticFiles/src/DefaultFilesMiddleware.cs b/src/Middleware/StaticFiles/src/DefaultFilesMiddleware.cs
index 678b7606ba..318aacd52e 100644
--- a/src/Middleware/StaticFiles/src/DefaultFilesMiddleware.cs
+++ b/src/Middleware/StaticFiles/src/DefaultFilesMiddleware.cs
@@ -59,7 +59,7 @@ public class DefaultFilesMiddleware
/// <returns></returns>
public Task Invoke(HttpContext context)
{
- if (context.GetEndpoint() == null
+ if (context.GetEndpoint()?.RequestDelegate is null
&& Helpers.IsGetOrHeadMethod(context.Request.Method)
&& Helpers.TryMatchPath(context, _matchUrl, forDirectory: true, subpath: out var subpath))
{
diff --git a/src/Middleware/StaticFiles/src/DirectoryBrowserMiddleware.cs b/src/Middleware/StaticFiles/src/DirectoryBrowserMiddleware.cs
index b5ea2f5c75..c0929c013b 100644
--- a/src/Middleware/StaticFiles/src/DirectoryBrowserMiddleware.cs
+++ b/src/Middleware/StaticFiles/src/DirectoryBrowserMiddleware.cs
@@ -75,8 +75,8 @@ public class DirectoryBrowserMiddleware
/// <returns></returns>
public Task Invoke(HttpContext context)
{
- // Check if the URL matches any expected paths, skip if an endpoint was selected
- if (context.GetEndpoint() == null
+ // Check if the URL matches any expected paths, skip if an endpoint with a request delegate was selected
+ if (context.GetEndpoint()?.RequestDelegate is null
&& Helpers.IsGetOrHeadMethod(context.Request.Method)
&& Helpers.TryMatchPath(context, _matchUrl, forDirectory: true, subpath: out var subpath)
&& TryGetDirectoryInfo(subpath, out var contents))
diff --git a/src/Middleware/StaticFiles/src/StaticFileMiddleware.cs b/src/Middleware/StaticFiles/src/StaticFileMiddleware.cs
index abccd7e95d..aa3e44193f 100644
--- a/src/Middleware/StaticFiles/src/StaticFileMiddleware.cs
+++ b/src/Middleware/StaticFiles/src/StaticFileMiddleware.cs
@@ -66,7 +66,7 @@ public class StaticFileMiddleware
/// <returns></returns>
public Task Invoke(HttpContext context)
{
- if (!ValidateNoEndpoint(context))
+ if (!ValidateNoEndpointDelegate(context))
{
_logger.EndpointMatched();
}
@@ -91,8 +91,8 @@ public class StaticFileMiddleware
return _next(context);
}
- // Return true because we only want to run if there is no endpoint.
- private static bool ValidateNoEndpoint(HttpContext context) => context.GetEndpoint() == null;
+ // Return true because we only want to run if there is no endpoint delegate.
+ private static bool ValidateNoEndpointDelegate(HttpContext context) => context.GetEndpoint()?.RequestDelegate is null;
private static bool ValidateMethod(HttpContext context)
{
diff --git a/src/Middleware/StaticFiles/test/UnitTests/DefaultFilesMiddlewareTests.cs b/src/Middleware/StaticFiles/test/UnitTests/DefaultFilesMiddlewareTests.cs
index 5f65db02a3..5e1ae00792 100644
--- a/src/Middleware/StaticFiles/test/UnitTests/DefaultFilesMiddlewareTests.cs
+++ b/src/Middleware/StaticFiles/test/UnitTests/DefaultFilesMiddlewareTests.cs
@@ -78,7 +78,7 @@ public class DefaultFilesMiddlewareTests
}
[Fact]
- public async Task Endpoint_PassesThrough()
+ public async Task Endpoint_With_RequestDelegate_PassesThrough()
{
using (var fileProvider = new PhysicalFileProvider(Path.Combine(AppContext.BaseDirectory, ".")))
{
@@ -107,6 +107,9 @@ public class DefaultFilesMiddlewareTests
});
app.UseEndpoints(endpoints => { });
+
+ // Echo back the current request path value
+ app.Run(context => context.Response.WriteAsync(context.Request.Path.Value));
},
services => { services.AddDirectoryBrowser(); services.AddRouting(); });
using var server = host.GetTestServer();
@@ -117,6 +120,48 @@ public class DefaultFilesMiddlewareTests
}
}
+ [Fact]
+ public async Task Endpoint_With_Null_RequestDelegate_Does_Not_PassThrough()
+ {
+ using (var fileProvider = new PhysicalFileProvider(Path.Combine(AppContext.BaseDirectory, ".")))
+ {
+ using var host = await StaticFilesTestServer.Create(
+ app =>
+ {
+ app.UseRouting();
+
+ app.Use(next => context =>
+ {
+ // Assign an endpoint with a null RequestDelegate, the default files should still run
+ context.SetEndpoint(new Endpoint(requestDelegate: null,
+ new EndpointMetadataCollection(),
+ "test"));
+
+ return next(context);
+ });
+
+ app.UseDefaultFiles(new DefaultFilesOptions
+ {
+ RequestPath = new PathString(""),
+ FileProvider = fileProvider
+ });
+
+ app.UseEndpoints(endpoints => { });
+
+ // Echo back the current request path value
+ app.Run(context => context.Response.WriteAsync(context.Request.Path.Value));
+ },
+ services => { services.AddDirectoryBrowser(); services.AddRouting(); });
+ using var server = host.GetTestServer();
+
+ var response = await server.CreateRequest("/SubFolder/").GetAsync();
+ var responseContent = await response.Content.ReadAsStringAsync();
+
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal("/SubFolder/default.html", responseContent); // Should be modified and be valid path to file
+ }
+ }
+
[Theory]
[InlineData("", @".", "/SubFolder/")]
[InlineData("", @"./", "/SubFolder/")]
diff --git a/src/Middleware/StaticFiles/test/UnitTests/DirectoryBrowserMiddlewareTests.cs b/src/Middleware/StaticFiles/test/UnitTests/DirectoryBrowserMiddlewareTests.cs
index d078d26e26..bf3341916b 100644
--- a/src/Middleware/StaticFiles/test/UnitTests/DirectoryBrowserMiddlewareTests.cs
+++ b/src/Middleware/StaticFiles/test/UnitTests/DirectoryBrowserMiddlewareTests.cs
@@ -95,7 +95,7 @@ public class DirectoryBrowserMiddlewareTests
}
[Fact]
- public async Task Endpoint_PassesThrough()
+ public async Task Endpoint_With_RequestDelegate_PassesThrough()
{
using (var fileProvider = new PhysicalFileProvider(Path.Combine(AppContext.BaseDirectory, ".")))
{
@@ -135,6 +135,45 @@ public class DirectoryBrowserMiddlewareTests
}
}
+ [Fact]
+ public async Task Endpoint_With_Null_RequestDelegate_Does_Not_PassThrough()
+ {
+ using (var fileProvider = new PhysicalFileProvider(Path.Combine(AppContext.BaseDirectory, ".")))
+ {
+ using var host = await StaticFilesTestServer.Create(
+ app =>
+ {
+ app.UseRouting();
+
+ app.Use(next => context =>
+ {
+ // Assign an endpoint with a null RequestDelegate, the directory browser should still run
+ context.SetEndpoint(new Endpoint(requestDelegate: null,
+ new EndpointMetadataCollection(),
+ "test"));
+
+ return next(context);
+ });
+
+ app.UseDirectoryBrowser(new DirectoryBrowserOptions
+ {
+ RequestPath = new PathString(""),
+ FileProvider = fileProvider
+ });
+
+ app.UseEndpoints(endpoints => { });
+ },
+ services => { services.AddDirectoryBrowser(); services.AddRouting(); });
+ using var server = host.GetTestServer();
+
+ var response = await server.CreateRequest("/").GetAsync();
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal("text/html; charset=utf-8", response.Content.Headers.ContentType.ToString());
+ Assert.True(response.Content.Headers.ContentLength > 0);
+ Assert.Equal(response.Content.Headers.ContentLength, (await response.Content.ReadAsByteArrayAsync()).Length);
+ }
+ }
+
[Theory]
[InlineData("", @".", "/")]
[InlineData("", @".", "/SubFolder/")]
diff --git a/src/Middleware/StaticFiles/test/UnitTests/StaticFileMiddlewareTests.cs b/src/Middleware/StaticFiles/test/UnitTests/StaticFileMiddlewareTests.cs
index 0dd5fb8a96..bdb9347a86 100644
--- a/src/Middleware/StaticFiles/test/UnitTests/StaticFileMiddlewareTests.cs
+++ b/src/Middleware/StaticFiles/test/UnitTests/StaticFileMiddlewareTests.cs
@@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
+using System.Globalization;
using System.Net;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
@@ -9,6 +10,7 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.TestHost;
using Microsoft.AspNetCore.Testing;
+using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Hosting;
using Moq;
@@ -193,6 +195,86 @@ public class StaticFileMiddlewareTests
}
}
+ [Fact]
+ public async Task File_Served_If_Endpoint_With_Null_RequestDelegate_Is_Active()
+ {
+ using (var fileProvider = new PhysicalFileProvider(Path.Combine(AppContext.BaseDirectory, ".")))
+ {
+ using var host = await StaticFilesTestServer.Create(app =>
+ {
+ app.UseRouting();
+ app.Use((ctx, next) =>
+ {
+ ctx.SetEndpoint(new Endpoint(requestDelegate: null, new EndpointMetadataCollection(), "NullRequestDelegateEndpoint"));
+ return next();
+ });
+ app.UseStaticFiles(new StaticFileOptions
+ {
+ RequestPath = new PathString(),
+ FileProvider = fileProvider
+ });
+ app.UseEndpoints(endpoints => { });
+ }, services => services.AddRouting());
+ using var server = host.GetTestServer();
+ var requestUrl = "/TestDocument.txt";
+ var fileInfo = fileProvider.GetFileInfo(Path.GetFileName(requestUrl));
+ var response = await server.CreateRequest(requestUrl).GetAsync();
+ var responseContent = await response.Content.ReadAsByteArrayAsync();
+
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal("text/plain", response.Content.Headers.ContentType.ToString());
+ Assert.True(response.Content.Headers.ContentLength == fileInfo.Length);
+ Assert.Equal(response.Content.Headers.ContentLength, responseContent.Length);
+ Assert.NotNull(response.Headers.ETag);
+
+ using (var stream = fileInfo.CreateReadStream())
+ {
+ var fileContents = new byte[stream.Length];
+ stream.Read(fileContents, 0, (int)stream.Length);
+ Assert.True(responseContent.SequenceEqual(fileContents));
+ }
+ }
+ }
+
+ [Fact]
+ public async Task File_NotServed_If_Endpoint_With_RequestDelegate_Is_Active()
+ {
+ var responseText = DateTime.UtcNow.Ticks.ToString(CultureInfo.InvariantCulture);
+ RequestDelegate handler = async (ctx) =>
+ {
+ ctx.Response.ContentType = "text/customfortest+plain";
+ await ctx.Response.WriteAsync(responseText);
+ };
+
+ using (var fileProvider = new PhysicalFileProvider(Path.Combine(AppContext.BaseDirectory, ".")))
+ {
+ using var host = await StaticFilesTestServer.Create(app =>
+ {
+ app.UseRouting();
+ app.Use((ctx, next) =>
+ {
+ ctx.SetEndpoint(new Endpoint(handler, new EndpointMetadataCollection(), "RequestDelegateEndpoint"));
+ return next();
+ });
+ app.UseStaticFiles(new StaticFileOptions
+ {
+ RequestPath = new PathString(),
+ FileProvider = fileProvider
+ });
+ app.UseEndpoints(endpoints => { });
+ }, services => services.AddRouting());
+ using var server = host.GetTestServer();
+ var requestUrl = "/TestDocument.txt";
+
+ var response = await server.CreateRequest(requestUrl).GetAsync();
+ var responseContent = await response.Content.ReadAsStringAsync();
+
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal("text/customfortest+plain", response.Content.Headers.ContentType.ToString());
+ Assert.Equal(responseText, responseContent);
+ }
+ }
+
[Theory]
[MemberData(nameof(ExistingFiles))]
public async Task HeadFile_HeadersButNotBodyServed(string baseUrl, string baseDir, string requestUrl)
diff --git a/src/Mvc/Mvc.Core/src/Filters/IOutputCacheFilter.cs b/src/Mvc/Mvc.Core/src/Filters/IOutputCacheFilter.cs
new file mode 100644
index 0000000000..d852233e48
--- /dev/null
+++ b/src/Mvc/Mvc.Core/src/Filters/IOutputCacheFilter.cs
@@ -0,0 +1,11 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.Mvc.Filters;
+
+/// <summary>
+/// A filter which sets the appropriate headers related to Output caching.
+/// </summary>
+internal interface IOutputCacheFilter : IFilterMetadata
+{
+}
diff --git a/src/Mvc/Mvc.Core/src/Filters/OutputCacheFilter.cs b/src/Mvc/Mvc.Core/src/Filters/OutputCacheFilter.cs
new file mode 100644
index 0000000000..08beb1bf29
--- /dev/null
+++ b/src/Mvc/Mvc.Core/src/Filters/OutputCacheFilter.cs
@@ -0,0 +1,56 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Microsoft.AspNetCore.Mvc.Core;
+using Microsoft.AspNetCore.OutputCaching;
+using Microsoft.Extensions.Logging;
+
+namespace Microsoft.AspNetCore.Mvc.Filters;
+
+/// <summary>
+/// An <see cref="IActionFilter"/> which sets the appropriate headers related to output caching.
+/// </summary>
+internal partial class OutputCacheFilter : IActionFilter
+{
+ private readonly ILogger _logger;
+
+ /// <summary>
+ /// Creates a new instance of <see cref="OutputCacheFilter"/>
+ /// </summary>
+ /// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
+ public OutputCacheFilter(ILoggerFactory loggerFactory)
+ {
+ _logger = loggerFactory.CreateLogger(GetType());
+ }
+
+ public void OnActionExecuting(ActionExecutingContext context)
+ {
+ ArgumentNullException.ThrowIfNull(context);
+
+ // If there are more filters which can override the values written by this filter,
+ // then skip execution of this filter.
+ var effectivePolicy = context.FindEffectivePolicy<IOutputCacheFilter>();
+ if (effectivePolicy != null && effectivePolicy != this)
+ {
+ Log.NotMostEffectiveFilter(_logger, GetType(), effectivePolicy.GetType(), typeof(IOutputCacheFilter));
+ return;
+ }
+
+ var outputCachingFeature = context.HttpContext.Features.Get<IOutputCacheFeature>();
+ if (outputCachingFeature == null)
+ {
+ throw new InvalidOperationException(
+ Resources.FormatOutputCacheAttribute_Requires_OutputCachingMiddleware(nameof(OutputCacheAttribute)));
+ }
+ }
+
+ public void OnActionExecuted(ActionExecutedContext context)
+ {
+ }
+
+ private static partial class Log
+ {
+ [LoggerMessage(1, LogLevel.Debug, "Execution of filter {OverriddenFilter} is preempted by filter {OverridingFilter} which is the most effective filter implementing policy {FilterPolicy}.", EventName = "NotMostEffectiveFilter")]
+ public static partial void NotMostEffectiveFilter(ILogger logger, Type overriddenFilter, Type overridingFilter, Type filterPolicy);
+ }
+}
diff --git a/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.csproj b/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.csproj
index 9d9b2ac58e..d437875d6a 100644
--- a/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.csproj
+++ b/src/Mvc/Mvc.Core/src/Microsoft.AspNetCore.Mvc.Core.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>ASP.NET Core MVC core components. Contains common action result types, attribute routing, application model conventions, API explorer, application parts, filters, formatters, model binding, and more.
@@ -48,6 +48,7 @@ Microsoft.AspNetCore.Mvc.RouteAttribute</Description>
<Reference Include="Microsoft.AspNetCore.Http" />
<Reference Include="Microsoft.AspNetCore.Http.Extensions" />
<Reference Include="Microsoft.AspNetCore.ResponseCaching.Abstractions" />
+ <Reference Include="Microsoft.AspNetCore.OutputCaching" />
<Reference Include="Microsoft.AspNetCore.Routing.Abstractions" />
<Reference Include="Microsoft.AspNetCore.Routing" />
<Reference Include="Microsoft.Extensions.DependencyInjection" />
diff --git a/src/Mvc/Mvc.Core/src/Resources.resx b/src/Mvc/Mvc.Core/src/Resources.resx
index 206a2f420b..3427c5ab3f 100644
--- a/src/Mvc/Mvc.Core/src/Resources.resx
+++ b/src/Mvc/Mvc.Core/src/Resources.resx
@@ -510,7 +510,10 @@
<data name="GetContentTypes_WildcardsNotSupported" xml:space="preserve">
<value>Could not parse '{0}'. Content types with wildcards are not supported.</value>
</data>
+ <data name="OutputCacheAttribute_Requires_OutputCachingMiddleware" xml:space="preserve">
+ <value>'{0}' requires the output cache middleware.</value>
+ </data>
<data name="TryParseModelBinder_InvalidType" xml:space="preserve">
<value>The type '{0}' does not contain a TryParse method and the binder '{1}' cannot be used.</value>
</data>
-</root> \ No newline at end of file
+</root>
diff --git a/src/Mvc/Mvc.Core/test/Formatters/SystemTextJsonInputFormatterTest.cs b/src/Mvc/Mvc.Core/test/Formatters/SystemTextJsonInputFormatterTest.cs
index 4d5fa4d01b..45ecc1c5c1 100644
--- a/src/Mvc/Mvc.Core/test/Formatters/SystemTextJsonInputFormatterTest.cs
+++ b/src/Mvc/Mvc.Core/test/Formatters/SystemTextJsonInputFormatterTest.cs
@@ -133,7 +133,6 @@ public class SystemTextJsonInputFormatterTest : JsonInputFormatterTestBase
[InlineData("{\"a\":{\"b\"}}", "$.a", "'}' is invalid after a property name. Expected a ':'. Path: $.a | LineNumber: 0 | BytePositionInLine: 9.")]
[InlineData("{\"age\":\"x\"}", "$.age", "The JSON value could not be converted to System.Decimal. Path: $.age | LineNumber: 0 | BytePositionInLine: 10.")]
[InlineData("{\"login\":1}", "$.login", "The JSON value could not be converted to Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonInputFormatterTest+UserLogin. Path: $.login | LineNumber: 0 | BytePositionInLine: 10.")]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/40661")]
public async Task ReadAsync_WithAllowInputFormatterExceptionMessages_RegistersJsonInputExceptionsAsInputFormatterException(
string content,
string modelStateKey,
diff --git a/src/Mvc/Mvc.slnf b/src/Mvc/Mvc.slnf
index 1dee0bc428..2599ea39e3 100644
--- a/src/Mvc/Mvc.slnf
+++ b/src/Mvc/Mvc.slnf
@@ -46,6 +46,7 @@
"src\\Middleware\\HttpOverrides\\src\\Microsoft.AspNetCore.HttpOverrides.csproj",
"src\\Middleware\\Localization.Routing\\src\\Microsoft.AspNetCore.Localization.Routing.csproj",
"src\\Middleware\\Localization\\src\\Microsoft.AspNetCore.Localization.csproj",
+ "src\\Middleware\\OutputCaching\\src\\Microsoft.AspNetCore.OutputCaching.csproj",
"src\\Middleware\\ResponseCaching.Abstractions\\src\\Microsoft.AspNetCore.ResponseCaching.Abstractions.csproj",
"src\\Middleware\\ResponseCaching\\src\\Microsoft.AspNetCore.ResponseCaching.csproj",
"src\\Middleware\\Session\\src\\Microsoft.AspNetCore.Session.csproj",
diff --git a/src/Mvc/samples/MvcSandbox/MvcSandbox.csproj b/src/Mvc/samples/MvcSandbox/MvcSandbox.csproj
index b677481776..6c3cf21a5a 100644
--- a/src/Mvc/samples/MvcSandbox/MvcSandbox.csproj
+++ b/src/Mvc/samples/MvcSandbox/MvcSandbox.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk.Web">
+<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
<PreserveCompilationContext>true</PreserveCompilationContext>
diff --git a/src/OpenApi/build.sh b/src/OpenApi/build.sh
index 491835c0cb..491835c0cb 100644..100755
--- a/src/OpenApi/build.sh
+++ b/src/OpenApi/build.sh
diff --git a/src/OpenApi/src/OpenApiRouteHandlerBuilderExtensions.cs b/src/OpenApi/src/OpenApiEndpointConventionBuilderExtensions.cs
index ed9faa65c5..12671868f4 100644
--- a/src/OpenApi/src/OpenApiRouteHandlerBuilderExtensions.cs
+++ b/src/OpenApi/src/OpenApiEndpointConventionBuilderExtensions.cs
@@ -3,26 +3,26 @@
using System.Linq;
using System.Reflection;
-using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.OpenApi;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
-namespace Microsoft.AspNetCore.OpenApi;
+namespace Microsoft.AspNetCore.Builder;
/// <summary>
/// Extension methods for annotating OpenAPI descriptions on an <see cref="Endpoint" />.
/// </summary>
-public static class OpenApiRouteHandlerBuilderExtensions
+public static class OpenApiEndpointConventionBuilderExtensions
{
/// <summary>
/// Adds an OpenAPI annotation to <see cref="Endpoint.Metadata" /> associated
/// with the current endpoint.
/// </summary>
- /// <param name="builder">The <see cref="RouteHandlerBuilder"/>.</param>
- /// <returns>A <see cref="RouteHandlerBuilder"/> that can be used to further customize the endpoint.</returns>
+ /// <param name="builder">The <see cref="IEndpointConventionBuilder"/>.</param>
+ /// <returns>A <see cref="IEndpointConventionBuilder"/> that can be used to further customize the endpoint.</returns>
public static TBuilder WithOpenApi<TBuilder>(this TBuilder builder) where TBuilder : IEndpointConventionBuilder
{
builder.Add(builder => AddAndConfigureOperationForEndpoint(builder));
@@ -33,9 +33,9 @@ public static class OpenApiRouteHandlerBuilderExtensions
/// Adds an OpenAPI annotation to <see cref="Endpoint.Metadata" /> associated
/// with the current endpoint and modifies it with the given <paramref name="configureOperation"/>.
/// </summary>
- /// <param name="builder">The <see cref="RouteHandlerBuilder"/>.</param>
+ /// <param name="builder">The <see cref="IEndpointConventionBuilder"/>.</param>
/// <param name="configureOperation">An <see cref="Func{T, TResult}"/> that returns a new OpenAPI annotation given a generated operation.</param>
- /// <returns>A <see cref="RouteHandlerBuilder"/> that can be used to further customize the endpoint.</returns>
+ /// <returns>A <see cref="IEndpointConventionBuilder"/> that can be used to further customize the endpoint.</returns>
public static TBuilder WithOpenApi<TBuilder>(this TBuilder builder, Func<OpenApiOperation, OpenApiOperation> configureOperation)
where TBuilder : IEndpointConventionBuilder
{
diff --git a/src/OpenApi/src/PublicAPI.Unshipped.txt b/src/OpenApi/src/PublicAPI.Unshipped.txt
index a6362d7b20..ca80deee67 100644
--- a/src/OpenApi/src/PublicAPI.Unshipped.txt
+++ b/src/OpenApi/src/PublicAPI.Unshipped.txt
@@ -1,4 +1,4 @@
#nullable enable
-Microsoft.AspNetCore.OpenApi.OpenApiRouteHandlerBuilderExtensions
-static Microsoft.AspNetCore.OpenApi.OpenApiRouteHandlerBuilderExtensions.WithOpenApi<TBuilder>(this TBuilder builder) -> TBuilder
-static Microsoft.AspNetCore.OpenApi.OpenApiRouteHandlerBuilderExtensions.WithOpenApi<TBuilder>(this TBuilder builder, System.Func<Microsoft.OpenApi.Models.OpenApiOperation!, Microsoft.OpenApi.Models.OpenApiOperation!>! configureOperation) -> TBuilder
+Microsoft.AspNetCore.Builder.OpenApiEndpointConventionBuilderExtensions
+static Microsoft.AspNetCore.Builder.OpenApiEndpointConventionBuilderExtensions.WithOpenApi<TBuilder>(this TBuilder builder) -> TBuilder
+static Microsoft.AspNetCore.Builder.OpenApiEndpointConventionBuilderExtensions.WithOpenApi<TBuilder>(this TBuilder builder, System.Func<Microsoft.OpenApi.Models.OpenApiOperation!, Microsoft.OpenApi.Models.OpenApiOperation!>! configureOperation) -> TBuilder
diff --git a/src/ProjectTemplates/BlazorTemplates.Tests/yarn.lock b/src/ProjectTemplates/BlazorTemplates.Tests/yarn.lock
index 25dae07962..8a1d841904 100644
--- a/src/ProjectTemplates/BlazorTemplates.Tests/yarn.lock
+++ b/src/ProjectTemplates/BlazorTemplates.Tests/yarn.lock
@@ -29,17 +29,22 @@
resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812"
integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==
+"@types/json-buffer@~3.0.0":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/@types/json-buffer/-/json-buffer-3.0.0.tgz#85c1ff0f0948fc159810d4b5be35bf8c20875f64"
+ integrity sha512-3YP80IxxFJB4b5tYC2SUPwkg0XQLiu0nWvhRgEatgjf+29IcWO9X1k8xRv5DGssJ/lCrjYTjQPcobJr2yWIVuQ==
+
"@types/keyv@*":
- version "3.1.3"
- resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.3.tgz#1c9aae32872ec1f20dcdaee89a9f3ba88f465e41"
- integrity sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==
+ version "3.1.4"
+ resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6"
+ integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==
dependencies:
"@types/node" "*"
"@types/node@*":
- version "17.0.21"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.21.tgz#864b987c0c68d07b4345845c3e63b75edd143644"
- integrity sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==
+ version "18.0.0"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.0.tgz#67c7b724e1bcdd7a8821ce0d5ee184d3b4dd525a"
+ integrity sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA==
"@types/responselike@*", "@types/responselike@^1.0.0":
version "1.0.0"
@@ -96,7 +101,7 @@ cacheable-request@^7.0.2:
clone-response@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b"
- integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=
+ integrity sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==
dependencies:
mimic-response "^1.0.0"
@@ -105,6 +110,14 @@ commander@^7.2.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
+compress-brotli@^1.3.8:
+ version "1.3.8"
+ resolved "https://registry.yarnpkg.com/compress-brotli/-/compress-brotli-1.3.8.tgz#0c0a60c97a989145314ec381e84e26682e7b38db"
+ integrity sha512-lVcQsjhxhIXsuupfy9fmZUFtAIdBmXA7EGY6GBdgZ++qkM9zG4YFT8iU7FoBxzryNDMOpD1HIFHUSX4D87oqhQ==
+ dependencies:
+ "@types/json-buffer" "~3.0.0"
+ json-buffer "~3.0.1"
+
cross-spawn@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
@@ -169,9 +182,9 @@ get-stream@^5.1.0:
pump "^3.0.0"
got@^11.8.2:
- version "11.8.3"
- resolved "https://registry.yarnpkg.com/got/-/got-11.8.3.tgz#f496c8fdda5d729a90b4905d2b07dbd148170770"
- integrity sha512-7gtQ5KiPh1RtGS9/Jbv1ofDpBFuq42gyfEib+ejaRBJuj/3tQFeR5+gw57e4ipaU8c/rCjvX6fkQz2lyDlGAOg==
+ version "11.8.5"
+ resolved "https://registry.yarnpkg.com/got/-/got-11.8.5.tgz#ce77d045136de56e8f024bebb82ea349bc730046"
+ integrity sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ==
dependencies:
"@sindresorhus/is" "^4.0.0"
"@szmarczak/http-timer" "^4.0.5"
@@ -223,7 +236,7 @@ isexe@^2.0.0:
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
-json-buffer@3.0.1:
+json-buffer@3.0.1, json-buffer@~3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==
@@ -238,10 +251,11 @@ jsonfile@^6.0.1:
graceful-fs "^4.1.6"
keyv@^4.0.0:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.1.1.tgz#02c538bfdbd2a9308cc932d4096f05ae42bfa06a"
- integrity sha512-tGv1yP6snQVDSM4X6yxrv2zzq/EvpW+oYiUz6aueW1u9CtS8RzUQYxxmFwgZlO2jSgCxQbchhxaqXXp2hnKGpQ==
+ version "4.3.2"
+ resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.3.2.tgz#e839df676a0c7ee594c8835e7c1c83742558e5c2"
+ integrity sha512-kn8WmodVBe12lmHpA6W8OY7SNh6wVR+Z+wZESF4iF5FCazaVXGWOtnbnvX0tMQ1bO+/TmOD9LziuYMvrIIs0xw==
dependencies:
+ compress-brotli "^1.3.8"
json-buffer "3.0.1"
lodash.mapvalues@^4.6.0:
@@ -292,7 +306,7 @@ normalize-url@^6.0.1:
once@^1.3.1, once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
- integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
+ integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
dependencies:
wrappy "1"
@@ -425,7 +439,7 @@ which@^2.0.1, which@^2.0.2:
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
- integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
+ integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
yauzl@^2.10.0:
version "2.10.0"
diff --git a/src/ProjectTemplates/ProjectTemplates.slnf b/src/ProjectTemplates/ProjectTemplates.slnf
index be4040672d..e54935c13a 100644
--- a/src/ProjectTemplates/ProjectTemplates.slnf
+++ b/src/ProjectTemplates/ProjectTemplates.slnf
@@ -66,9 +66,10 @@
"src\\ProjectTemplates\\Web.Client.ItemTemplates\\Microsoft.DotNet.Web.Client.ItemTemplates.csproj",
"src\\ProjectTemplates\\Web.ItemTemplates\\Microsoft.DotNet.Web.ItemTemplates.csproj",
"src\\ProjectTemplates\\Web.ProjectTemplates\\Microsoft.DotNet.Web.ProjectTemplates.csproj",
- "src\\ProjectTemplates\\test\\Templates.Tests\\Templates.Tests.csproj",
+ "src\\ProjectTemplates\\test\\Templates.Blazor.Server.Tests\\Templates.Blazor.Server.Tests.csproj",
+ "src\\ProjectTemplates\\test\\Templates.Blazor.WebAssembly.Tests\\Templates.Blazor.WebAssembly.Tests.csproj",
"src\\ProjectTemplates\\test\\Templates.Mvc.Tests\\Templates.Mvc.Tests.csproj",
- "src\\ProjectTemplates\\test\\Templates.Blazor.Tests\\Templates.Blazor.Tests.csproj",
+ "src\\ProjectTemplates\\test\\Templates.Tests\\Templates.Tests.csproj",
"src\\Razor\\Razor.Runtime\\src\\Microsoft.AspNetCore.Razor.Runtime.csproj",
"src\\Razor\\Razor\\src\\Microsoft.AspNetCore.Razor.csproj",
"src\\Security\\Authentication\\Cookies\\src\\Microsoft.AspNetCore.Authentication.Cookies.csproj",
@@ -94,4 +95,4 @@
"src\\submodules\\spa-templates\\src\\Microsoft.DotNet.Web.Spa.ProjectTemplates.csproj"
]
}
-}
+} \ No newline at end of file
diff --git a/src/ProjectTemplates/ProjectTemplatesNoDeps.slnf b/src/ProjectTemplates/ProjectTemplatesNoDeps.slnf
index a19e3cfbb5..e79a6da6be 100644
--- a/src/ProjectTemplates/ProjectTemplatesNoDeps.slnf
+++ b/src/ProjectTemplates/ProjectTemplatesNoDeps.slnf
@@ -9,7 +9,8 @@
"src\\submodules\\spa-templates\\src\\Microsoft.DotNet.Web.Spa.ProjectTemplates.csproj",
"src\\ProjectTemplates\\test\\Templates.Tests\\Templates.Tests.csproj",
"src\\ProjectTemplates\\test\\Templates.Mvc.Tests\\Templates.Mvc.Tests.csproj",
- "src\\ProjectTemplates\\test\\Templates.Blazor.Tests\\Templates.Blazor.Tests.csproj",
+ "src\\ProjectTemplates\\test\\Templates.Blazor.Server.Tests\\Templates.Blazor.Server.Tests.csproj",
+ "src\\ProjectTemplates\\test\\Templates.Blazor.WebAssembly.Tests\\Templates.Blazor.WebAssembly.Tests.csproj",
"src\\Shared\\BrowserTesting\\src\\Microsoft.AspNetCore.BrowserTesting.csproj"
]
}
diff --git a/src/ProjectTemplates/test/Templates.Blazor.Tests/BlazorTemplateTest.cs b/src/ProjectTemplates/Shared/BlazorTemplateTest.cs
index 48c25d41d3..48c25d41d3 100644
--- a/src/ProjectTemplates/test/Templates.Blazor.Tests/BlazorTemplateTest.cs
+++ b/src/ProjectTemplates/Shared/BlazorTemplateTest.cs
diff --git a/src/ProjectTemplates/Shared/Project.cs b/src/ProjectTemplates/Shared/Project.cs
index 1706366af3..0fe3631e56 100644
--- a/src/ProjectTemplates/Shared/Project.cs
+++ b/src/ProjectTemplates/Shared/Project.cs
@@ -466,7 +466,7 @@ public class Project : IDisposable
if (result.ExitCode != 0 && !string.IsNullOrEmpty(ArtifactsLogDir))
{
var sourceFile = Path.Combine(TemplateOutputDir, "msbuild.binlog");
- Assert.True(File.Exists(sourceFile), $"Log for '{ProjectName}' not found in '{sourceFile}'.");
+ Assert.True(File.Exists(sourceFile), $"Log for '{ProjectName}' not found in '{sourceFile}'. Execution output: {result.Output}");
var destination = Path.Combine(ArtifactsLogDir, ProjectName + ".binlog");
File.Move(sourceFile, destination, overwrite: true); // binlog will exist on retries
}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/EmptyBlazorServerWeb-CSharp.csproj.in b/src/ProjectTemplates/Web.ProjectTemplates/EmptyBlazorServerWeb-CSharp.csproj.in
new file mode 100644
index 0000000000..d3a39c6503
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/EmptyBlazorServerWeb-CSharp.csproj.in
@@ -0,0 +1,11 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+
+ <PropertyGroup>
+ <TargetFramework>${DefaultNetCoreTargetFramework}</TargetFramework>
+ <Nullable>enable</Nullable>
+ <ImplicitUsings>enable</ImplicitUsings>
+ <NoDefaultLaunchSettingsFile Condition="'$(ExcludeLaunchSettings)' == 'True'">True</NoDefaultLaunchSettingsFile>
+ <RootNamespace Condition="'$(name)' != '$(name{-VALUE-FORMS-}safe_namespace)'">EmptyBlazorServerWeb_CSharp</RootNamespace>
+ </PropertyGroup>
+
+</Project>
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/EmptyComponentsWebAssembly-CSharp.Client.csproj.in b/src/ProjectTemplates/Web.ProjectTemplates/EmptyComponentsWebAssembly-CSharp.Client.csproj.in
new file mode 100644
index 0000000000..4b41335026
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/EmptyComponentsWebAssembly-CSharp.Client.csproj.in
@@ -0,0 +1,33 @@
+<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
+
+ <PropertyGroup>
+ <TargetFramework>${DefaultNetCoreTargetFramework}</TargetFramework>
+ <Nullable>enable</Nullable>
+ <ImplicitUsings>enable</ImplicitUsings>
+ <!--#if PWA -->
+ <ServiceWorkerAssetsManifest>service-worker-assets.js</ServiceWorkerAssetsManifest>
+ <!--#endif -->
+ <!--#if Hosted -->
+ <AssemblyName Condition="'$(name)' != '$(name{-VALUE-FORMS-}safe_namespace)'">`$(AssemblyName.Replace(' ', '_'))</AssemblyName>
+ <!--#endif -->
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="${MicrosoftAspNetCoreComponentsWebAssemblyVersion}" />
+ <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="${MicrosoftAspNetCoreComponentsWebAssemblyDevServerVersion}" PrivateAssets="all" />
+ <PackageReference Include="Microsoft.Extensions.Http" Version="${MicrosoftExtensionsHttpVersion}" Condition="'$(Hosted)' == 'true'" />
+ </ItemGroup>
+
+ <!--#if Hosted -->
+ <ItemGroup>
+ <ProjectReference Include="..\Shared\EmptyComponentsWebAssembly-CSharp.Shared.csproj" />
+ </ItemGroup>
+
+ <!--#endif -->
+ <!--#if PWA -->
+ <ItemGroup>
+ <ServiceWorker Include="wwwroot\service-worker.js" PublishedContent="wwwroot\service-worker.published.js" />
+ </ItemGroup>
+
+ <!--#endif -->
+</Project>
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/EmptyComponentsWebAssembly-CSharp.Server.csproj.in b/src/ProjectTemplates/Web.ProjectTemplates/EmptyComponentsWebAssembly-CSharp.Server.csproj.in
new file mode 100644
index 0000000000..f54a03b7f4
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/EmptyComponentsWebAssembly-CSharp.Server.csproj.in
@@ -0,0 +1,21 @@
+<Project Sdk="Microsoft.NET.Sdk.Web">
+
+ <PropertyGroup>
+ <TargetFramework>${DefaultNetCoreTargetFramework}</TargetFramework>
+ <Nullable>enable</Nullable>
+ <ImplicitUsings>enable</ImplicitUsings>
+ <NoDefaultLaunchSettingsFile Condition="'$(ExcludeLaunchSettings)' == 'True'">True</NoDefaultLaunchSettingsFile>
+ <RootNamespace Condition="'$(name)' != '$(name{-VALUE-FORMS-}safe_namespace)'">EmptyComponentsWebAssembly-CSharp.Server</RootNamespace>
+ <AssemblyName Condition="'$(name)' != '$(name{-VALUE-FORMS-}safe_namespace)'">`$(AssemblyName.Replace(' ', '_'))</AssemblyName>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="${MicrosoftAspNetCoreComponentsWebAssemblyServerVersion}" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <ProjectReference Include="..\Client\EmptyComponentsWebAssembly-CSharp.Client.csproj" />
+ <ProjectReference Include="..\Shared\EmptyComponentsWebAssembly-CSharp.Shared.csproj" />
+ </ItemGroup>
+
+</Project>
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/EmptyComponentsWebAssembly-CSharp.Shared.csproj.in b/src/ProjectTemplates/Web.ProjectTemplates/EmptyComponentsWebAssembly-CSharp.Shared.csproj.in
new file mode 100644
index 0000000000..ba1c0026e9
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/EmptyComponentsWebAssembly-CSharp.Shared.csproj.in
@@ -0,0 +1,12 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+ <PropertyGroup>
+ <TargetFramework>${DefaultNetCoreTargetFramework}</TargetFramework>
+ <Nullable>enable</Nullable>
+ <ImplicitUsings>enable</ImplicitUsings>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <SupportedPlatform Include="browser" />
+ </ItemGroup>
+</Project>
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj b/src/ProjectTemplates/Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj
index 2dc4136434..1edb1e6ef0 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj
+++ b/src/ProjectTemplates/Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj
@@ -56,6 +56,7 @@
<GeneratedContent Include="EmptyWeb-FSharp.fsproj.in" OutputPath="content/EmptyWeb-FSharp/Company.WebApplication1.fsproj" />
<GeneratedContent Include="GrpcService-CSharp.csproj.in" OutputPath="content/GrpcService-CSharp/GrpcService-CSharp.csproj" />
<GeneratedContent Include="BlazorServerWeb-CSharp.csproj.in" OutputPath="content/BlazorServerWeb-CSharp/BlazorServerWeb-CSharp.csproj" />
+ <GeneratedContent Include="EmptyBlazorServerWeb-CSharp.csproj.in" OutputPath="content/EmptyBlazorServerWeb-CSharp/EmptyBlazorServerWeb-CSharp.csproj" />
<GeneratedContent Include="RazorPagesWeb-CSharp.csproj.in" OutputPath="content/RazorPagesWeb-CSharp/Company.WebApplication1.csproj" />
<GeneratedContent Include="RazorClassLibrary-CSharp.csproj.in" OutputPath="content/RazorClassLibrary-CSharp/Company.RazorClassLibrary1.csproj" />
<GeneratedContent Include="StarterWeb-CSharp.csproj.in" OutputPath="content/StarterWeb-CSharp/Company.WebApplication1.csproj" />
@@ -67,6 +68,9 @@
<GeneratedContent Include="ComponentsWebAssembly-CSharp.Client.csproj.in" OutputPath="content/ComponentsWebAssembly-CSharp/Client/ComponentsWebAssembly-CSharp.Client.csproj" />
<GeneratedContent Include="ComponentsWebAssembly-CSharp.Shared.csproj.in" OutputPath="content/ComponentsWebAssembly-CSharp/Shared/ComponentsWebAssembly-CSharp.Shared.csproj" />
<GeneratedContent Include="ComponentsWebAssembly-CSharp.Server.csproj.in" OutputPath="content/ComponentsWebAssembly-CSharp/Server/ComponentsWebAssembly-CSharp.Server.csproj" />
+ <GeneratedContent Include="EmptyComponentsWebAssembly-CSharp.Client.csproj.in" OutputPath="content/EmptyComponentsWebAssembly-CSharp/Client/EmptyComponentsWebAssembly-CSharp.Client.csproj" />
+ <GeneratedContent Include="EmptyComponentsWebAssembly-CSharp.Shared.csproj.in" OutputPath="content/EmptyComponentsWebAssembly-CSharp/Shared/EmptyComponentsWebAssembly-CSharp.Shared.csproj" />
+ <GeneratedContent Include="EmptyComponentsWebAssembly-CSharp.Server.csproj.in" OutputPath="content/EmptyComponentsWebAssembly-CSharp/Server/EmptyComponentsWebAssembly-CSharp.Server.csproj" />
</ItemGroup>
</Project>
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/.template.config/template.json
index 161abdfe1a..750bf24e80 100644
--- a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/.template.config/template.json
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/.template.config/template.json
@@ -293,6 +293,13 @@
"Client/Shared/NavMenu.NoGraphOrApi.razor",
"Client/Shared/NavMenu.CallsMicrosoftGraph.razor"
]
+ },
+ {
+ "condition": "(ExcludeLaunchSettings)",
+ "exclude": [
+ "Client/Properties/launchSettings.json",
+ "Server/Properties/launchSettings.json"
+ ]
}
]
}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/dotnetcli.host.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/dotnetcli.host.json
new file mode 100644
index 0000000000..d97858472b
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/dotnetcli.host.json
@@ -0,0 +1,35 @@
+{
+ "$schema": "http://json.schemastore.org/dotnetcli.host",
+ "symbolInfo": {
+ "Framework": {
+ "longName": "framework"
+ },
+ "skipRestore": {
+ "longName": "no-restore",
+ "shortName": ""
+ },
+ "kestrelHttpPort": {
+ "isHidden": true
+ },
+ "kestrelHttpsPort": {
+ "isHidden": true
+ },
+ "iisHttpPort": {
+ "isHidden": true
+ },
+ "iisHttpsPort": {
+ "isHidden": true
+ },
+ "ExcludeLaunchSettings": {
+ "longName": "exclude-launch-settings",
+ "shortName": ""
+ },
+ "NoHttps": {
+ "longName": "no-https",
+ "shortName": ""
+ }
+ },
+ "usageExamples": [
+ ""
+ ]
+}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/ide.host.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/ide.host.json
new file mode 100644
index 0000000000..278a9a31ca
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/ide.host.json
@@ -0,0 +1,18 @@
+{
+ "$schema": "http://json.schemastore.org/ide.host",
+ "order": 650,
+ "icon": "ide/icon.png",
+ "disableHttpsSymbol": "NoHttps",
+ "supportsDocker": true,
+ "tags": [
+ {
+ "type": "projectType",
+ "add": [ "Cloud", "Web" ],
+ "remove": [ "*" ]
+ },
+ {
+ "type": "platform",
+ "add": [ "Linux", "macOS", "Windows" ]
+ }
+ ]
+}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/ide/icon.png b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/ide/icon.png
new file mode 100644
index 0000000000..8422b59695
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/ide/icon.png
Binary files differ
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.cs.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.cs.json
new file mode 100644
index 0000000000..1951a6ed34
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.cs.json
@@ -0,0 +1,17 @@
+{
+ "author": "Microsoft",
+ "name": "Blazor Server App Empty",
+ "description": "An empty project template for creating a Blazor server app that runs server-side inside an ASP.NET Core app and handles user interactions over a SignalR connection. This template does not have any content in it.",
+ "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.",
+ "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.",
+ "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.",
+ "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/HasHttpProfile/description": "Always have HTTP profile.",
+ "symbols/NoHttps/description": "Whether to turn off HTTPS.",
+ "symbols/Framework/description": "The target framework for the project.",
+ "symbols/Framework/choices/net7.0/description": "Target net7.0",
+ "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.",
+ "postActions/restore/description": "Restore NuGet packages required by this project.",
+ "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'"
+} \ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.de.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.de.json
new file mode 100644
index 0000000000..1951a6ed34
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.de.json
@@ -0,0 +1,17 @@
+{
+ "author": "Microsoft",
+ "name": "Blazor Server App Empty",
+ "description": "An empty project template for creating a Blazor server app that runs server-side inside an ASP.NET Core app and handles user interactions over a SignalR connection. This template does not have any content in it.",
+ "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.",
+ "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.",
+ "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.",
+ "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/HasHttpProfile/description": "Always have HTTP profile.",
+ "symbols/NoHttps/description": "Whether to turn off HTTPS.",
+ "symbols/Framework/description": "The target framework for the project.",
+ "symbols/Framework/choices/net7.0/description": "Target net7.0",
+ "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.",
+ "postActions/restore/description": "Restore NuGet packages required by this project.",
+ "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'"
+} \ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.en.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.en.json
new file mode 100644
index 0000000000..1951a6ed34
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.en.json
@@ -0,0 +1,17 @@
+{
+ "author": "Microsoft",
+ "name": "Blazor Server App Empty",
+ "description": "An empty project template for creating a Blazor server app that runs server-side inside an ASP.NET Core app and handles user interactions over a SignalR connection. This template does not have any content in it.",
+ "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.",
+ "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.",
+ "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.",
+ "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/HasHttpProfile/description": "Always have HTTP profile.",
+ "symbols/NoHttps/description": "Whether to turn off HTTPS.",
+ "symbols/Framework/description": "The target framework for the project.",
+ "symbols/Framework/choices/net7.0/description": "Target net7.0",
+ "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.",
+ "postActions/restore/description": "Restore NuGet packages required by this project.",
+ "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'"
+} \ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.es.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.es.json
new file mode 100644
index 0000000000..1951a6ed34
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.es.json
@@ -0,0 +1,17 @@
+{
+ "author": "Microsoft",
+ "name": "Blazor Server App Empty",
+ "description": "An empty project template for creating a Blazor server app that runs server-side inside an ASP.NET Core app and handles user interactions over a SignalR connection. This template does not have any content in it.",
+ "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.",
+ "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.",
+ "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.",
+ "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/HasHttpProfile/description": "Always have HTTP profile.",
+ "symbols/NoHttps/description": "Whether to turn off HTTPS.",
+ "symbols/Framework/description": "The target framework for the project.",
+ "symbols/Framework/choices/net7.0/description": "Target net7.0",
+ "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.",
+ "postActions/restore/description": "Restore NuGet packages required by this project.",
+ "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'"
+} \ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.fr.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.fr.json
new file mode 100644
index 0000000000..1951a6ed34
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.fr.json
@@ -0,0 +1,17 @@
+{
+ "author": "Microsoft",
+ "name": "Blazor Server App Empty",
+ "description": "An empty project template for creating a Blazor server app that runs server-side inside an ASP.NET Core app and handles user interactions over a SignalR connection. This template does not have any content in it.",
+ "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.",
+ "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.",
+ "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.",
+ "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/HasHttpProfile/description": "Always have HTTP profile.",
+ "symbols/NoHttps/description": "Whether to turn off HTTPS.",
+ "symbols/Framework/description": "The target framework for the project.",
+ "symbols/Framework/choices/net7.0/description": "Target net7.0",
+ "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.",
+ "postActions/restore/description": "Restore NuGet packages required by this project.",
+ "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'"
+} \ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.it.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.it.json
new file mode 100644
index 0000000000..1951a6ed34
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.it.json
@@ -0,0 +1,17 @@
+{
+ "author": "Microsoft",
+ "name": "Blazor Server App Empty",
+ "description": "An empty project template for creating a Blazor server app that runs server-side inside an ASP.NET Core app and handles user interactions over a SignalR connection. This template does not have any content in it.",
+ "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.",
+ "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.",
+ "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.",
+ "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/HasHttpProfile/description": "Always have HTTP profile.",
+ "symbols/NoHttps/description": "Whether to turn off HTTPS.",
+ "symbols/Framework/description": "The target framework for the project.",
+ "symbols/Framework/choices/net7.0/description": "Target net7.0",
+ "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.",
+ "postActions/restore/description": "Restore NuGet packages required by this project.",
+ "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'"
+} \ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.ja.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.ja.json
new file mode 100644
index 0000000000..1951a6ed34
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.ja.json
@@ -0,0 +1,17 @@
+{
+ "author": "Microsoft",
+ "name": "Blazor Server App Empty",
+ "description": "An empty project template for creating a Blazor server app that runs server-side inside an ASP.NET Core app and handles user interactions over a SignalR connection. This template does not have any content in it.",
+ "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.",
+ "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.",
+ "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.",
+ "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/HasHttpProfile/description": "Always have HTTP profile.",
+ "symbols/NoHttps/description": "Whether to turn off HTTPS.",
+ "symbols/Framework/description": "The target framework for the project.",
+ "symbols/Framework/choices/net7.0/description": "Target net7.0",
+ "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.",
+ "postActions/restore/description": "Restore NuGet packages required by this project.",
+ "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'"
+} \ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.ko.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.ko.json
new file mode 100644
index 0000000000..1951a6ed34
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.ko.json
@@ -0,0 +1,17 @@
+{
+ "author": "Microsoft",
+ "name": "Blazor Server App Empty",
+ "description": "An empty project template for creating a Blazor server app that runs server-side inside an ASP.NET Core app and handles user interactions over a SignalR connection. This template does not have any content in it.",
+ "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.",
+ "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.",
+ "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.",
+ "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/HasHttpProfile/description": "Always have HTTP profile.",
+ "symbols/NoHttps/description": "Whether to turn off HTTPS.",
+ "symbols/Framework/description": "The target framework for the project.",
+ "symbols/Framework/choices/net7.0/description": "Target net7.0",
+ "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.",
+ "postActions/restore/description": "Restore NuGet packages required by this project.",
+ "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'"
+} \ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.pl.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.pl.json
new file mode 100644
index 0000000000..1951a6ed34
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.pl.json
@@ -0,0 +1,17 @@
+{
+ "author": "Microsoft",
+ "name": "Blazor Server App Empty",
+ "description": "An empty project template for creating a Blazor server app that runs server-side inside an ASP.NET Core app and handles user interactions over a SignalR connection. This template does not have any content in it.",
+ "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.",
+ "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.",
+ "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.",
+ "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/HasHttpProfile/description": "Always have HTTP profile.",
+ "symbols/NoHttps/description": "Whether to turn off HTTPS.",
+ "symbols/Framework/description": "The target framework for the project.",
+ "symbols/Framework/choices/net7.0/description": "Target net7.0",
+ "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.",
+ "postActions/restore/description": "Restore NuGet packages required by this project.",
+ "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'"
+} \ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.pt-BR.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.pt-BR.json
new file mode 100644
index 0000000000..1951a6ed34
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.pt-BR.json
@@ -0,0 +1,17 @@
+{
+ "author": "Microsoft",
+ "name": "Blazor Server App Empty",
+ "description": "An empty project template for creating a Blazor server app that runs server-side inside an ASP.NET Core app and handles user interactions over a SignalR connection. This template does not have any content in it.",
+ "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.",
+ "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.",
+ "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.",
+ "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/HasHttpProfile/description": "Always have HTTP profile.",
+ "symbols/NoHttps/description": "Whether to turn off HTTPS.",
+ "symbols/Framework/description": "The target framework for the project.",
+ "symbols/Framework/choices/net7.0/description": "Target net7.0",
+ "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.",
+ "postActions/restore/description": "Restore NuGet packages required by this project.",
+ "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'"
+} \ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.ru.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.ru.json
new file mode 100644
index 0000000000..1951a6ed34
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.ru.json
@@ -0,0 +1,17 @@
+{
+ "author": "Microsoft",
+ "name": "Blazor Server App Empty",
+ "description": "An empty project template for creating a Blazor server app that runs server-side inside an ASP.NET Core app and handles user interactions over a SignalR connection. This template does not have any content in it.",
+ "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.",
+ "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.",
+ "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.",
+ "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/HasHttpProfile/description": "Always have HTTP profile.",
+ "symbols/NoHttps/description": "Whether to turn off HTTPS.",
+ "symbols/Framework/description": "The target framework for the project.",
+ "symbols/Framework/choices/net7.0/description": "Target net7.0",
+ "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.",
+ "postActions/restore/description": "Restore NuGet packages required by this project.",
+ "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'"
+} \ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.tr.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.tr.json
new file mode 100644
index 0000000000..1951a6ed34
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.tr.json
@@ -0,0 +1,17 @@
+{
+ "author": "Microsoft",
+ "name": "Blazor Server App Empty",
+ "description": "An empty project template for creating a Blazor server app that runs server-side inside an ASP.NET Core app and handles user interactions over a SignalR connection. This template does not have any content in it.",
+ "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.",
+ "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.",
+ "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.",
+ "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/HasHttpProfile/description": "Always have HTTP profile.",
+ "symbols/NoHttps/description": "Whether to turn off HTTPS.",
+ "symbols/Framework/description": "The target framework for the project.",
+ "symbols/Framework/choices/net7.0/description": "Target net7.0",
+ "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.",
+ "postActions/restore/description": "Restore NuGet packages required by this project.",
+ "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'"
+} \ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.zh-Hans.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.zh-Hans.json
new file mode 100644
index 0000000000..1951a6ed34
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.zh-Hans.json
@@ -0,0 +1,17 @@
+{
+ "author": "Microsoft",
+ "name": "Blazor Server App Empty",
+ "description": "An empty project template for creating a Blazor server app that runs server-side inside an ASP.NET Core app and handles user interactions over a SignalR connection. This template does not have any content in it.",
+ "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.",
+ "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.",
+ "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.",
+ "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/HasHttpProfile/description": "Always have HTTP profile.",
+ "symbols/NoHttps/description": "Whether to turn off HTTPS.",
+ "symbols/Framework/description": "The target framework for the project.",
+ "symbols/Framework/choices/net7.0/description": "Target net7.0",
+ "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.",
+ "postActions/restore/description": "Restore NuGet packages required by this project.",
+ "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'"
+} \ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.zh-Hant.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.zh-Hant.json
new file mode 100644
index 0000000000..1951a6ed34
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/localize/templatestrings.zh-Hant.json
@@ -0,0 +1,17 @@
+{
+ "author": "Microsoft",
+ "name": "Blazor Server App Empty",
+ "description": "An empty project template for creating a Blazor server app that runs server-side inside an ASP.NET Core app and handles user interactions over a SignalR connection. This template does not have any content in it.",
+ "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.",
+ "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.",
+ "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.",
+ "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/HasHttpProfile/description": "Always have HTTP profile.",
+ "symbols/NoHttps/description": "Whether to turn off HTTPS.",
+ "symbols/Framework/description": "The target framework for the project.",
+ "symbols/Framework/choices/net7.0/description": "Target net7.0",
+ "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.",
+ "postActions/restore/description": "Restore NuGet packages required by this project.",
+ "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'"
+} \ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/template.json
new file mode 100644
index 0000000000..577fe64633
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/.template.config/template.json
@@ -0,0 +1,198 @@
+{
+ "$schema": "http://json.schemastore.org/template",
+ "author": "Microsoft",
+ "classifications": [
+ "Web",
+ "Blazor",
+ "Empty"
+ ],
+ "name": "Blazor Server App Empty",
+ "generatorVersions": "[1.0.0.0-*)",
+ "description": "An empty project template for creating a Blazor server app that runs server-side inside an ASP.NET Core app and handles user interactions over a SignalR connection. This template does not have any content in it.",
+ "groupIdentity": "Microsoft.Web.Blazor.Server.Empty",
+ "precedence": "9000",
+ "identity": "Microsoft.Web.Blazor.Server.Empty.CSharp.7.0",
+ "shortName": "blazorserver-empty",
+ "thirdPartyNotices": "https://aka.ms/aspnetcore/7.0-third-party-notices",
+ "tags": {
+ "language": "C#",
+ "type": "project"
+ },
+ "sourceName": "EmptyBlazorServerWeb-CSharp",
+ "preferNameDirectory": true,
+ "sources": [
+ {
+ "source": "./",
+ "target": "./",
+ "exclude": [
+ ".template.config/**"
+ ],
+ "copyOnly": [
+ "wwwroot/**"
+ ],
+ "modifiers": [
+ {
+ "condition": "(ExcludeLaunchSettings)",
+ "exclude": [
+ "Properties/launchSettings.json"
+ ]
+ }
+ ]
+ }
+ ],
+ "symbols": {
+ "ExcludeLaunchSettings": {
+ "type": "parameter",
+ "datatype": "bool",
+ "defaultValue": "false",
+ "description": "Whether to exclude launchSettings.json from the generated template."
+ },
+ "kestrelHttpPort": {
+ "type": "parameter",
+ "datatype": "integer",
+ "description": "Port number to use for the HTTP endpoint in launchSettings.json."
+ },
+ "kestrelHttpPortGenerated": {
+ "type": "generated",
+ "generator": "port",
+ "parameters": {
+ "low": 5000,
+ "high": 5300
+ }
+ },
+ "kestrelHttpPortReplacer": {
+ "type": "generated",
+ "generator": "coalesce",
+ "parameters": {
+ "sourceVariableName": "kestrelHttpPort",
+ "fallbackVariableName": "kestrelHttpPortGenerated"
+ },
+ "replaces": "5000"
+ },
+ "kestrelHttpsPort": {
+ "type": "parameter",
+ "datatype": "integer",
+ "description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used."
+ },
+ "kestrelHttpsPortGenerated": {
+ "type": "generated",
+ "generator": "port",
+ "parameters": {
+ "low": 7000,
+ "high": 7300
+ }
+ },
+ "kestrelHttpsPortReplacer": {
+ "type": "generated",
+ "generator": "coalesce",
+ "parameters": {
+ "sourceVariableName": "kestrelHttpsPort",
+ "fallbackVariableName": "kestrelHttpsPortGenerated"
+ },
+ "replaces": "5001"
+ },
+ "iisHttpPort": {
+ "type": "parameter",
+ "datatype": "integer",
+ "description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json."
+ },
+ "iisHttpPortGenerated": {
+ "type": "generated",
+ "generator": "port"
+ },
+ "iisHttpPortReplacer": {
+ "type": "generated",
+ "generator": "coalesce",
+ "parameters": {
+ "sourceVariableName": "iisHttpPort",
+ "fallbackVariableName": "iisHttpPortGenerated"
+ },
+ "replaces": "8080"
+ },
+ "iisHttpsPort": {
+ "type": "parameter",
+ "datatype": "integer",
+ "description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used."
+ },
+ "iisHttpsPortGenerated": {
+ "type": "generated",
+ "generator": "port",
+ "parameters": {
+ "low": 44300,
+ "high": 44399
+ }
+ },
+ "iisHttpsPortReplacer": {
+ "type": "generated",
+ "generator": "coalesce",
+ "parameters": {
+ "sourceVariableName": "iisHttpsPort",
+ "fallbackVariableName": "iisHttpsPortGenerated"
+ },
+ "replaces": "44300"
+ },
+ "HasHttpProfile": {
+ "type": "parameter",
+ "datatype": "bool",
+ "defaultValue": "true",
+ "description": "Always have HTTP profile."
+ },
+ "HasHttpsProfile": {
+ "type": "computed",
+ "value": "(!NoHttps)"
+ },
+ "NoHttps": {
+ "type": "parameter",
+ "datatype": "bool",
+ "defaultValue": "false",
+ "description": "Whether to turn off HTTPS."
+ },
+ "Framework": {
+ "type": "parameter",
+ "description": "The target framework for the project.",
+ "datatype": "choice",
+ "choices": [
+ {
+ "choice": "net7.0",
+ "description": "Target net7.0"
+ }
+ ],
+ "replaces": "net7.0",
+ "defaultValue": "net7.0"
+ },
+ "copyrightYear": {
+ "type": "generated",
+ "generator": "now",
+ "replaces": "copyrightYear",
+ "parameters": {
+ "format": "yyyy"
+ }
+ },
+ "skipRestore": {
+ "type": "parameter",
+ "datatype": "bool",
+ "description": "If specified, skips the automatic restore of the project on create.",
+ "defaultValue": "false"
+ }
+ },
+ "primaryOutputs": [
+ {
+ "path": "EmptyBlazorServerWeb-CSharp.csproj"
+ }
+ ],
+ "defaultName": "EmptyBlazorApp",
+ "postActions": [
+ {
+ "id": "restore",
+ "condition": "(!skipRestore)",
+ "description": "Restore NuGet packages required by this project.",
+ "manualInstructions": [
+ {
+ "text": "Run 'dotnet restore'"
+ }
+ ],
+ "actionId": "210D431B-A78B-4D2F-B762-4ED3E3EA9025",
+ "continueOnError": true
+ }
+ ]
+}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/App.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/App.razor
new file mode 100644
index 0000000000..6fd3ed1b5a
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/App.razor
@@ -0,0 +1,12 @@
+<Router AppAssembly="@typeof(App).Assembly">
+ <Found Context="routeData">
+ <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
+ <FocusOnNavigate RouteData="@routeData" Selector="h1" />
+ </Found>
+ <NotFound>
+ <PageTitle>Not found</PageTitle>
+ <LayoutView Layout="@typeof(MainLayout)">
+ <p role="alert">Sorry, there's nothing at this address.</p>
+ </LayoutView>
+ </NotFound>
+</Router>
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/MainLayout.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/MainLayout.razor
new file mode 100644
index 0000000000..a5af3489ae
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/MainLayout.razor
@@ -0,0 +1,3 @@
+@inherits LayoutComponentBase
+
+<main> @Body </main>
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/Pages/Index.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/Pages/Index.razor
new file mode 100644
index 0000000000..cba3252e00
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/Pages/Index.razor
@@ -0,0 +1,3 @@
+@page "/"
+
+<h1>Hello, world!</h1>
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/Pages/_Host.cshtml b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/Pages/_Host.cshtml
new file mode 100644
index 0000000000..5dd2752eea
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/Pages/_Host.cshtml
@@ -0,0 +1,30 @@
+@page "/"
+@using Microsoft.AspNetCore.Components.Web
+@namespace EmptyBlazorServerWeb_CSharp.Pages
+@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
+
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8" />
+ <base href="~/" />
+ <link href="css/site.css" rel="stylesheet" />
+ <component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" />
+</head>
+<body>
+ <component type="typeof(App)" render-mode="ServerPrerendered" />
+
+ <div id="blazor-error-ui">
+ <environment include="Staging,Production">
+ An error has occurred. This application may no longer respond until reloaded.
+ </environment>
+ <environment include="Development">
+ An unhandled exception has occurred. See browser dev tools for details.
+ </environment>
+ <a href="" class="reload">Reload</a>
+ <a class="dismiss">🗙</a>
+ </div>
+
+ <script src="_framework/blazor.server.js"></script>
+</body>
+</html>
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/Program.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/Program.cs
new file mode 100644
index 0000000000..a03e57e09d
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/Program.cs
@@ -0,0 +1,27 @@
+using Microsoft.AspNetCore.Components;
+using Microsoft.AspNetCore.Components.Web;
+
+var builder = WebApplication.CreateBuilder(args);
+builder.Services.AddRazorPages();
+builder.Services.AddServerSideBlazor();
+
+var app = builder.Build();
+
+#if (HasHttpsProfile)
+if (!app.Environment.IsDevelopment())
+{
+ // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
+ app.UseHsts();
+}
+
+app.UseHttpsRedirection();
+#endif
+
+app.UseStaticFiles();
+
+app.UseRouting();
+
+app.MapBlazorHub();
+app.MapFallbackToPage("/_Host");
+
+app.Run();
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/Properties/launchSettings.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/Properties/launchSettings.json
new file mode 100644
index 0000000000..fad0d512b6
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/Properties/launchSettings.json
@@ -0,0 +1,43 @@
+{
+ "iisSettings": {
+ "iisExpress": {
+ "applicationUrl": "http://localhost:8080",
+ //#if (HasHttpsProfile)
+ "sslPort": 44300
+ //#else
+ "sslPort": 0
+ //#endif
+ }
+ },
+ "profiles": {
+ //#if(HasHttpProfile)
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "applicationUrl": "http://localhost:5000",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ //#endif
+ //#if(HasHttpsProfile)
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "applicationUrl": "https://localhost:5001;http://localhost:5000",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ //#endif
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/_Imports.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/_Imports.razor
new file mode 100644
index 0000000000..73a335442d
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/_Imports.razor
@@ -0,0 +1,4 @@
+@using Microsoft.AspNetCore.Components.Routing
+@using Microsoft.AspNetCore.Components.Web
+@using Microsoft.JSInterop
+@using EmptyBlazorServerWeb_CSharp
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/appsettings.Development.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/appsettings.Development.json
new file mode 100644
index 0000000000..770d3e9314
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/appsettings.Development.json
@@ -0,0 +1,9 @@
+{
+ "DetailedErrors": true,
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/appsettings.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/appsettings.json
new file mode 100644
index 0000000000..10f68b8c8b
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/wwwroot/css/site.css b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/wwwroot/css/site.css
new file mode 100644
index 0000000000..08e7f0bec9
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyBlazorServerWeb-CSharp/wwwroot/css/site.css
@@ -0,0 +1,28 @@
+#blazor-error-ui {
+ background: lightyellow;
+ bottom: 0;
+ box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
+ display: none;
+ left: 0;
+ padding: 0.6rem 1.25rem 0.7rem 1.25rem;
+ position: fixed;
+ width: 100%;
+ z-index: 1000;
+}
+
+ #blazor-error-ui .dismiss {
+ cursor: pointer;
+ position: absolute;
+ right: 3.5rem;
+ top: 0.5rem;
+ }
+
+.blazor-error-boundary {
+ background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
+ padding: 1rem 1rem 1rem 3.7rem;
+ color: white;
+}
+
+ .blazor-error-boundary::after {
+ content: "An error has occurred."
+ }
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/dotnetcli.host.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/dotnetcli.host.json
new file mode 100644
index 0000000000..f650ca83fa
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/dotnetcli.host.json
@@ -0,0 +1,42 @@
+{
+ "$schema": "http://json.schemastore.org/dotnetcli.host",
+ "symbolInfo": {
+ "skipRestore": {
+ "longName": "no-restore",
+ "shortName": ""
+ },
+ "Hosted": {
+ "longName": "hosted"
+ },
+ "PWA": {
+ "longName": "pwa"
+ },
+ "Framework": {
+ "longName": "framework"
+ },
+ "kestrelHttpPort": {
+ "isHidden": true
+ },
+ "kestrelHttpsPort": {
+ "isHidden": true
+ },
+ "iisHttpPort": {
+ "isHidden": true
+ },
+ "iisHttpsPort": {
+ "isHidden": true
+ },
+ "ExcludeLaunchSettings": {
+ "longName": "exclude-launch-settings",
+ "shortName": ""
+ },
+ "NoHttps": {
+ "longName": "no-https",
+ "shortName": ""
+ },
+ "CalledApiUrl": {
+ "longName": "called-api-url",
+ "shortName": ""
+ }
+ }
+}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/ide.host.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/ide.host.json
new file mode 100644
index 0000000000..8e687ab7bc
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/ide.host.json
@@ -0,0 +1,29 @@
+{
+ "$schema": "http://json.schemastore.org/ide.host",
+ "order": 660,
+ "icon": "ide/icon.png",
+ "disableHttpsSymbol": "NoHttps",
+ "symbolInfo": [
+ {
+ "id": "Hosted",
+ "isVisible": "true",
+ "persistenceScope": "templateGroup"
+ },
+ {
+ "id": "PWA",
+ "isVisible": "true",
+ "persistenceScope": "templateGroup"
+ }
+ ],
+ "tags": [
+ {
+ "type": "projectType",
+ "add": [ "Cloud", "Web" ],
+ "remove": [ "*" ]
+ },
+ {
+ "type": "platform",
+ "add": [ "Linux", "macOS", "Windows" ]
+ }
+ ]
+}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/ide/icon.png b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/ide/icon.png
new file mode 100644
index 0000000000..8422b59695
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/ide/icon.png
Binary files differ
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.cs.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.cs.json
new file mode 100644
index 0000000000..cd69b8df84
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.cs.json
@@ -0,0 +1,23 @@
+{
+ "author": "Microsoft",
+ "name": "Blazor WebAssembly App Empty",
+ "description": "An empty project template for creating a Blazor app that runs on WebAssembly and is optionally hosted by an ASP.NET Core app. This template does not have any content in it.",
+ "symbols/Framework/description": "The target framework for the project.",
+ "symbols/Framework/choices/net7.0/description": "Target net7.0",
+ "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.",
+ "symbols/Hosted/displayName": "ASP.NET Core _Hosted",
+ "symbols/Hosted/description": "If specified, includes an ASP.NET Core host for the Blazor WebAssembly app.",
+ "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.",
+ "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.",
+ "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.",
+ "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/PWA/displayName": "_Progressive Web Application",
+ "symbols/PWA/description": "If specified, produces a Progressive Web Application (PWA) supporting installation and offline use.",
+ "symbols/HasHttpProfile/description": "Always have HTTP profile.",
+ "symbols/NoHttps/description": "Whether to turn off HTTPS.",
+ "postActions/restore/description": "Restore NuGet packages required by this project.",
+ "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'",
+ "postActions/restoreClient/description": "Restore NuGet packages required by this project.",
+ "postActions/restoreClient/manualInstructions/default/text": "Run 'dotnet restore'"
+} \ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.de.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.de.json
new file mode 100644
index 0000000000..cd69b8df84
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.de.json
@@ -0,0 +1,23 @@
+{
+ "author": "Microsoft",
+ "name": "Blazor WebAssembly App Empty",
+ "description": "An empty project template for creating a Blazor app that runs on WebAssembly and is optionally hosted by an ASP.NET Core app. This template does not have any content in it.",
+ "symbols/Framework/description": "The target framework for the project.",
+ "symbols/Framework/choices/net7.0/description": "Target net7.0",
+ "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.",
+ "symbols/Hosted/displayName": "ASP.NET Core _Hosted",
+ "symbols/Hosted/description": "If specified, includes an ASP.NET Core host for the Blazor WebAssembly app.",
+ "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.",
+ "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.",
+ "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.",
+ "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/PWA/displayName": "_Progressive Web Application",
+ "symbols/PWA/description": "If specified, produces a Progressive Web Application (PWA) supporting installation and offline use.",
+ "symbols/HasHttpProfile/description": "Always have HTTP profile.",
+ "symbols/NoHttps/description": "Whether to turn off HTTPS.",
+ "postActions/restore/description": "Restore NuGet packages required by this project.",
+ "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'",
+ "postActions/restoreClient/description": "Restore NuGet packages required by this project.",
+ "postActions/restoreClient/manualInstructions/default/text": "Run 'dotnet restore'"
+} \ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.en.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.en.json
new file mode 100644
index 0000000000..cd69b8df84
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.en.json
@@ -0,0 +1,23 @@
+{
+ "author": "Microsoft",
+ "name": "Blazor WebAssembly App Empty",
+ "description": "An empty project template for creating a Blazor app that runs on WebAssembly and is optionally hosted by an ASP.NET Core app. This template does not have any content in it.",
+ "symbols/Framework/description": "The target framework for the project.",
+ "symbols/Framework/choices/net7.0/description": "Target net7.0",
+ "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.",
+ "symbols/Hosted/displayName": "ASP.NET Core _Hosted",
+ "symbols/Hosted/description": "If specified, includes an ASP.NET Core host for the Blazor WebAssembly app.",
+ "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.",
+ "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.",
+ "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.",
+ "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/PWA/displayName": "_Progressive Web Application",
+ "symbols/PWA/description": "If specified, produces a Progressive Web Application (PWA) supporting installation and offline use.",
+ "symbols/HasHttpProfile/description": "Always have HTTP profile.",
+ "symbols/NoHttps/description": "Whether to turn off HTTPS.",
+ "postActions/restore/description": "Restore NuGet packages required by this project.",
+ "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'",
+ "postActions/restoreClient/description": "Restore NuGet packages required by this project.",
+ "postActions/restoreClient/manualInstructions/default/text": "Run 'dotnet restore'"
+} \ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.es.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.es.json
new file mode 100644
index 0000000000..cd69b8df84
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.es.json
@@ -0,0 +1,23 @@
+{
+ "author": "Microsoft",
+ "name": "Blazor WebAssembly App Empty",
+ "description": "An empty project template for creating a Blazor app that runs on WebAssembly and is optionally hosted by an ASP.NET Core app. This template does not have any content in it.",
+ "symbols/Framework/description": "The target framework for the project.",
+ "symbols/Framework/choices/net7.0/description": "Target net7.0",
+ "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.",
+ "symbols/Hosted/displayName": "ASP.NET Core _Hosted",
+ "symbols/Hosted/description": "If specified, includes an ASP.NET Core host for the Blazor WebAssembly app.",
+ "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.",
+ "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.",
+ "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.",
+ "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/PWA/displayName": "_Progressive Web Application",
+ "symbols/PWA/description": "If specified, produces a Progressive Web Application (PWA) supporting installation and offline use.",
+ "symbols/HasHttpProfile/description": "Always have HTTP profile.",
+ "symbols/NoHttps/description": "Whether to turn off HTTPS.",
+ "postActions/restore/description": "Restore NuGet packages required by this project.",
+ "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'",
+ "postActions/restoreClient/description": "Restore NuGet packages required by this project.",
+ "postActions/restoreClient/manualInstructions/default/text": "Run 'dotnet restore'"
+} \ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.fr.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.fr.json
new file mode 100644
index 0000000000..cd69b8df84
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.fr.json
@@ -0,0 +1,23 @@
+{
+ "author": "Microsoft",
+ "name": "Blazor WebAssembly App Empty",
+ "description": "An empty project template for creating a Blazor app that runs on WebAssembly and is optionally hosted by an ASP.NET Core app. This template does not have any content in it.",
+ "symbols/Framework/description": "The target framework for the project.",
+ "symbols/Framework/choices/net7.0/description": "Target net7.0",
+ "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.",
+ "symbols/Hosted/displayName": "ASP.NET Core _Hosted",
+ "symbols/Hosted/description": "If specified, includes an ASP.NET Core host for the Blazor WebAssembly app.",
+ "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.",
+ "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.",
+ "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.",
+ "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/PWA/displayName": "_Progressive Web Application",
+ "symbols/PWA/description": "If specified, produces a Progressive Web Application (PWA) supporting installation and offline use.",
+ "symbols/HasHttpProfile/description": "Always have HTTP profile.",
+ "symbols/NoHttps/description": "Whether to turn off HTTPS.",
+ "postActions/restore/description": "Restore NuGet packages required by this project.",
+ "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'",
+ "postActions/restoreClient/description": "Restore NuGet packages required by this project.",
+ "postActions/restoreClient/manualInstructions/default/text": "Run 'dotnet restore'"
+} \ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.it.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.it.json
new file mode 100644
index 0000000000..cd69b8df84
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.it.json
@@ -0,0 +1,23 @@
+{
+ "author": "Microsoft",
+ "name": "Blazor WebAssembly App Empty",
+ "description": "An empty project template for creating a Blazor app that runs on WebAssembly and is optionally hosted by an ASP.NET Core app. This template does not have any content in it.",
+ "symbols/Framework/description": "The target framework for the project.",
+ "symbols/Framework/choices/net7.0/description": "Target net7.0",
+ "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.",
+ "symbols/Hosted/displayName": "ASP.NET Core _Hosted",
+ "symbols/Hosted/description": "If specified, includes an ASP.NET Core host for the Blazor WebAssembly app.",
+ "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.",
+ "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.",
+ "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.",
+ "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/PWA/displayName": "_Progressive Web Application",
+ "symbols/PWA/description": "If specified, produces a Progressive Web Application (PWA) supporting installation and offline use.",
+ "symbols/HasHttpProfile/description": "Always have HTTP profile.",
+ "symbols/NoHttps/description": "Whether to turn off HTTPS.",
+ "postActions/restore/description": "Restore NuGet packages required by this project.",
+ "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'",
+ "postActions/restoreClient/description": "Restore NuGet packages required by this project.",
+ "postActions/restoreClient/manualInstructions/default/text": "Run 'dotnet restore'"
+} \ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.ja.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.ja.json
new file mode 100644
index 0000000000..cd69b8df84
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.ja.json
@@ -0,0 +1,23 @@
+{
+ "author": "Microsoft",
+ "name": "Blazor WebAssembly App Empty",
+ "description": "An empty project template for creating a Blazor app that runs on WebAssembly and is optionally hosted by an ASP.NET Core app. This template does not have any content in it.",
+ "symbols/Framework/description": "The target framework for the project.",
+ "symbols/Framework/choices/net7.0/description": "Target net7.0",
+ "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.",
+ "symbols/Hosted/displayName": "ASP.NET Core _Hosted",
+ "symbols/Hosted/description": "If specified, includes an ASP.NET Core host for the Blazor WebAssembly app.",
+ "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.",
+ "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.",
+ "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.",
+ "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/PWA/displayName": "_Progressive Web Application",
+ "symbols/PWA/description": "If specified, produces a Progressive Web Application (PWA) supporting installation and offline use.",
+ "symbols/HasHttpProfile/description": "Always have HTTP profile.",
+ "symbols/NoHttps/description": "Whether to turn off HTTPS.",
+ "postActions/restore/description": "Restore NuGet packages required by this project.",
+ "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'",
+ "postActions/restoreClient/description": "Restore NuGet packages required by this project.",
+ "postActions/restoreClient/manualInstructions/default/text": "Run 'dotnet restore'"
+} \ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.ko.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.ko.json
new file mode 100644
index 0000000000..cd69b8df84
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.ko.json
@@ -0,0 +1,23 @@
+{
+ "author": "Microsoft",
+ "name": "Blazor WebAssembly App Empty",
+ "description": "An empty project template for creating a Blazor app that runs on WebAssembly and is optionally hosted by an ASP.NET Core app. This template does not have any content in it.",
+ "symbols/Framework/description": "The target framework for the project.",
+ "symbols/Framework/choices/net7.0/description": "Target net7.0",
+ "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.",
+ "symbols/Hosted/displayName": "ASP.NET Core _Hosted",
+ "symbols/Hosted/description": "If specified, includes an ASP.NET Core host for the Blazor WebAssembly app.",
+ "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.",
+ "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.",
+ "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.",
+ "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/PWA/displayName": "_Progressive Web Application",
+ "symbols/PWA/description": "If specified, produces a Progressive Web Application (PWA) supporting installation and offline use.",
+ "symbols/HasHttpProfile/description": "Always have HTTP profile.",
+ "symbols/NoHttps/description": "Whether to turn off HTTPS.",
+ "postActions/restore/description": "Restore NuGet packages required by this project.",
+ "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'",
+ "postActions/restoreClient/description": "Restore NuGet packages required by this project.",
+ "postActions/restoreClient/manualInstructions/default/text": "Run 'dotnet restore'"
+} \ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.pl.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.pl.json
new file mode 100644
index 0000000000..cd69b8df84
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.pl.json
@@ -0,0 +1,23 @@
+{
+ "author": "Microsoft",
+ "name": "Blazor WebAssembly App Empty",
+ "description": "An empty project template for creating a Blazor app that runs on WebAssembly and is optionally hosted by an ASP.NET Core app. This template does not have any content in it.",
+ "symbols/Framework/description": "The target framework for the project.",
+ "symbols/Framework/choices/net7.0/description": "Target net7.0",
+ "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.",
+ "symbols/Hosted/displayName": "ASP.NET Core _Hosted",
+ "symbols/Hosted/description": "If specified, includes an ASP.NET Core host for the Blazor WebAssembly app.",
+ "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.",
+ "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.",
+ "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.",
+ "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/PWA/displayName": "_Progressive Web Application",
+ "symbols/PWA/description": "If specified, produces a Progressive Web Application (PWA) supporting installation and offline use.",
+ "symbols/HasHttpProfile/description": "Always have HTTP profile.",
+ "symbols/NoHttps/description": "Whether to turn off HTTPS.",
+ "postActions/restore/description": "Restore NuGet packages required by this project.",
+ "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'",
+ "postActions/restoreClient/description": "Restore NuGet packages required by this project.",
+ "postActions/restoreClient/manualInstructions/default/text": "Run 'dotnet restore'"
+} \ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.pt-BR.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.pt-BR.json
new file mode 100644
index 0000000000..cd69b8df84
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.pt-BR.json
@@ -0,0 +1,23 @@
+{
+ "author": "Microsoft",
+ "name": "Blazor WebAssembly App Empty",
+ "description": "An empty project template for creating a Blazor app that runs on WebAssembly and is optionally hosted by an ASP.NET Core app. This template does not have any content in it.",
+ "symbols/Framework/description": "The target framework for the project.",
+ "symbols/Framework/choices/net7.0/description": "Target net7.0",
+ "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.",
+ "symbols/Hosted/displayName": "ASP.NET Core _Hosted",
+ "symbols/Hosted/description": "If specified, includes an ASP.NET Core host for the Blazor WebAssembly app.",
+ "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.",
+ "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.",
+ "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.",
+ "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/PWA/displayName": "_Progressive Web Application",
+ "symbols/PWA/description": "If specified, produces a Progressive Web Application (PWA) supporting installation and offline use.",
+ "symbols/HasHttpProfile/description": "Always have HTTP profile.",
+ "symbols/NoHttps/description": "Whether to turn off HTTPS.",
+ "postActions/restore/description": "Restore NuGet packages required by this project.",
+ "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'",
+ "postActions/restoreClient/description": "Restore NuGet packages required by this project.",
+ "postActions/restoreClient/manualInstructions/default/text": "Run 'dotnet restore'"
+} \ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.ru.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.ru.json
new file mode 100644
index 0000000000..cd69b8df84
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.ru.json
@@ -0,0 +1,23 @@
+{
+ "author": "Microsoft",
+ "name": "Blazor WebAssembly App Empty",
+ "description": "An empty project template for creating a Blazor app that runs on WebAssembly and is optionally hosted by an ASP.NET Core app. This template does not have any content in it.",
+ "symbols/Framework/description": "The target framework for the project.",
+ "symbols/Framework/choices/net7.0/description": "Target net7.0",
+ "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.",
+ "symbols/Hosted/displayName": "ASP.NET Core _Hosted",
+ "symbols/Hosted/description": "If specified, includes an ASP.NET Core host for the Blazor WebAssembly app.",
+ "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.",
+ "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.",
+ "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.",
+ "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/PWA/displayName": "_Progressive Web Application",
+ "symbols/PWA/description": "If specified, produces a Progressive Web Application (PWA) supporting installation and offline use.",
+ "symbols/HasHttpProfile/description": "Always have HTTP profile.",
+ "symbols/NoHttps/description": "Whether to turn off HTTPS.",
+ "postActions/restore/description": "Restore NuGet packages required by this project.",
+ "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'",
+ "postActions/restoreClient/description": "Restore NuGet packages required by this project.",
+ "postActions/restoreClient/manualInstructions/default/text": "Run 'dotnet restore'"
+} \ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.tr.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.tr.json
new file mode 100644
index 0000000000..cd69b8df84
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.tr.json
@@ -0,0 +1,23 @@
+{
+ "author": "Microsoft",
+ "name": "Blazor WebAssembly App Empty",
+ "description": "An empty project template for creating a Blazor app that runs on WebAssembly and is optionally hosted by an ASP.NET Core app. This template does not have any content in it.",
+ "symbols/Framework/description": "The target framework for the project.",
+ "symbols/Framework/choices/net7.0/description": "Target net7.0",
+ "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.",
+ "symbols/Hosted/displayName": "ASP.NET Core _Hosted",
+ "symbols/Hosted/description": "If specified, includes an ASP.NET Core host for the Blazor WebAssembly app.",
+ "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.",
+ "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.",
+ "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.",
+ "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/PWA/displayName": "_Progressive Web Application",
+ "symbols/PWA/description": "If specified, produces a Progressive Web Application (PWA) supporting installation and offline use.",
+ "symbols/HasHttpProfile/description": "Always have HTTP profile.",
+ "symbols/NoHttps/description": "Whether to turn off HTTPS.",
+ "postActions/restore/description": "Restore NuGet packages required by this project.",
+ "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'",
+ "postActions/restoreClient/description": "Restore NuGet packages required by this project.",
+ "postActions/restoreClient/manualInstructions/default/text": "Run 'dotnet restore'"
+} \ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.zh-Hans.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.zh-Hans.json
new file mode 100644
index 0000000000..cd69b8df84
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.zh-Hans.json
@@ -0,0 +1,23 @@
+{
+ "author": "Microsoft",
+ "name": "Blazor WebAssembly App Empty",
+ "description": "An empty project template for creating a Blazor app that runs on WebAssembly and is optionally hosted by an ASP.NET Core app. This template does not have any content in it.",
+ "symbols/Framework/description": "The target framework for the project.",
+ "symbols/Framework/choices/net7.0/description": "Target net7.0",
+ "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.",
+ "symbols/Hosted/displayName": "ASP.NET Core _Hosted",
+ "symbols/Hosted/description": "If specified, includes an ASP.NET Core host for the Blazor WebAssembly app.",
+ "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.",
+ "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.",
+ "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.",
+ "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/PWA/displayName": "_Progressive Web Application",
+ "symbols/PWA/description": "If specified, produces a Progressive Web Application (PWA) supporting installation and offline use.",
+ "symbols/HasHttpProfile/description": "Always have HTTP profile.",
+ "symbols/NoHttps/description": "Whether to turn off HTTPS.",
+ "postActions/restore/description": "Restore NuGet packages required by this project.",
+ "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'",
+ "postActions/restoreClient/description": "Restore NuGet packages required by this project.",
+ "postActions/restoreClient/manualInstructions/default/text": "Run 'dotnet restore'"
+} \ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.zh-Hant.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.zh-Hant.json
new file mode 100644
index 0000000000..cd69b8df84
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/localize/templatestrings.zh-Hant.json
@@ -0,0 +1,23 @@
+{
+ "author": "Microsoft",
+ "name": "Blazor WebAssembly App Empty",
+ "description": "An empty project template for creating a Blazor app that runs on WebAssembly and is optionally hosted by an ASP.NET Core app. This template does not have any content in it.",
+ "symbols/Framework/description": "The target framework for the project.",
+ "symbols/Framework/choices/net7.0/description": "Target net7.0",
+ "symbols/skipRestore/description": "If specified, skips the automatic restore of the project on create.",
+ "symbols/Hosted/displayName": "ASP.NET Core _Hosted",
+ "symbols/Hosted/description": "If specified, includes an ASP.NET Core host for the Blazor WebAssembly app.",
+ "symbols/ExcludeLaunchSettings/description": "Whether to exclude launchSettings.json from the generated template.",
+ "symbols/kestrelHttpPort/description": "Port number to use for the HTTP endpoint in launchSettings.json.",
+ "symbols/kestrelHttpsPort/description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/iisHttpPort/description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json.",
+ "symbols/iisHttpsPort/description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used.",
+ "symbols/PWA/displayName": "_Progressive Web Application",
+ "symbols/PWA/description": "If specified, produces a Progressive Web Application (PWA) supporting installation and offline use.",
+ "symbols/HasHttpProfile/description": "Always have HTTP profile.",
+ "symbols/NoHttps/description": "Whether to turn off HTTPS.",
+ "postActions/restore/description": "Restore NuGet packages required by this project.",
+ "postActions/restore/manualInstructions/default/text": "Run 'dotnet restore'",
+ "postActions/restoreClient/description": "Restore NuGet packages required by this project.",
+ "postActions/restoreClient/manualInstructions/default/text": "Run 'dotnet restore'"
+} \ No newline at end of file
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/template.json
new file mode 100644
index 0000000000..91499117ce
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/.template.config/template.json
@@ -0,0 +1,276 @@
+{
+ "$schema": "http://json.schemastore.org/template",
+ "author": "Microsoft",
+ "classifications": [
+ "Web",
+ "Blazor",
+ "WebAssembly",
+ "PWA",
+ "Empty"
+ ],
+ "name": "Blazor WebAssembly App Empty",
+ "defaultName": "BlazorApp-empty",
+ "description": "An empty project template for creating a Blazor app that runs on WebAssembly and is optionally hosted by an ASP.NET Core app. This template does not have any content in it.",
+ "groupIdentity": "Microsoft.Web.Blazor.Wasm.Empty",
+ "precedence": "9000",
+ "identity": "Microsoft.Web.Blazor.Wasm.Empty.CSharp.7.0",
+ "thirdPartyNotices": "https://aka.ms/aspnetcore/7.0-third-party-notices",
+ "preferNameDirectory": true,
+ "primaryOutputs": [
+ {
+ "condition": "(Hosted && (HostIdentifier == \"dotnetcli\" || HostIdentifier == \"dotnetcli-preview\"))",
+ "path": "EmptyComponentsWebAssembly-CSharp.sln"
+ },
+ {
+ "condition": "(Hosted && HostIdentifier != \"dotnetcli\" && HostIdentifier != \"dotnetcli-preview\")",
+ "path": "Server/EmptyComponentsWebAssembly-CSharp.Server.csproj"
+ },
+ {
+ "condition": "(!Hosted)",
+ "path": "EmptyComponentsWebAssembly-CSharp.csproj"
+ },
+ {
+ "condition": "(Hosted && HostIdentifier != \"dotnetcli\" && HostIdentifier != \"dotnetcli-preview\")",
+ "path": "Client/EmptyComponentsWebAssembly-CSharp.Client.csproj"
+ },
+ {
+ "condition": "(Hosted && HostIdentifier != \"dotnetcli\" && HostIdentifier != \"dotnetcli-preview\")",
+ "path": "Shared/EmptyComponentsWebAssembly-CSharp.Shared.csproj"
+ }
+ ],
+ "shortName": "blazorwasm-empty",
+ "sourceName": "EmptyComponentsWebAssembly-CSharp",
+ "sources": [
+ {
+ "source": "./",
+ "target": "./",
+ "exclude": [
+ ".template.config/**"
+ ],
+ "copyOnly": [
+ "**/wwwroot/css/**"
+ ],
+ "modifiers": [
+ {
+ "condition": "(ExcludeLaunchSettings)",
+ "exclude": [
+ "Client/Properties/launchSettings.json",
+ "Server/Properties/launchSettings.json"
+ ]
+ },
+ {
+ "condition": "(!Hosted)",
+ "exclude": [
+ "Server/**",
+ "Shared/**",
+ "*.sln"
+ ],
+ "rename": {
+ ".Client.csproj": ".csproj",
+ "Client": "."
+ }
+ },
+ {
+ "condition": "(Hosted && HostIdentifier != \"dotnetcli\" && HostIdentifier != \"dotnetcli-preview\")",
+ "exclude": [
+ "*.sln"
+ ]
+ },
+ {
+ "condition": "(!PWA)",
+ "exclude": [
+ "Client/wwwroot/service-worker*.js",
+ "Client/wwwroot/manifest.json",
+ "Client/wwwroot/icon-512.png"
+ ]
+ }
+ ]
+ }
+ ],
+ "symbols": {
+ "Framework": {
+ "type": "parameter",
+ "description": "The target framework for the project.",
+ "datatype": "choice",
+ "choices": [
+ {
+ "choice": "net7.0",
+ "description": "Target net7.0"
+ }
+ ],
+ "replaces": "net7.0",
+ "defaultValue": "net7.0"
+ },
+ "HostIdentifier": {
+ "type": "bind",
+ "binding": "HostIdentifier"
+ },
+ "skipRestore": {
+ "type": "parameter",
+ "datatype": "bool",
+ "description": "If specified, skips the automatic restore of the project on create.",
+ "defaultValue": "false"
+ },
+ "Hosted": {
+ "type": "parameter",
+ "datatype": "bool",
+ "defaultValue": "false",
+ "displayName": "ASP.NET Core _Hosted",
+ "description": "If specified, includes an ASP.NET Core host for the Blazor WebAssembly app."
+ },
+ "ExcludeLaunchSettings": {
+ "type": "parameter",
+ "datatype": "bool",
+ "defaultValue": "false",
+ "description": "Whether to exclude launchSettings.json from the generated template."
+ },
+ "kestrelHttpPort": {
+ "type": "parameter",
+ "datatype": "integer",
+ "description": "Port number to use for the HTTP endpoint in launchSettings.json."
+ },
+ "kestrelHttpPortGenerated": {
+ "type": "generated",
+ "generator": "port",
+ "parameters": {
+ "low": 5000,
+ "high": 5300
+ }
+ },
+ "kestrelHttpPortReplacer": {
+ "type": "generated",
+ "generator": "coalesce",
+ "parameters": {
+ "sourceVariableName": "kestrelHttpPort",
+ "fallbackVariableName": "kestrelHttpPortGenerated"
+ },
+ "replaces": "5000"
+ },
+ "kestrelHttpsPort": {
+ "type": "parameter",
+ "datatype": "integer",
+ "description": "Port number to use for the HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used."
+ },
+ "kestrelHttpsPortGenerated": {
+ "type": "generated",
+ "generator": "port",
+ "parameters": {
+ "low": 7000,
+ "high": 7300
+ }
+ },
+ "kestrelHttpsPortReplacer": {
+ "type": "generated",
+ "generator": "coalesce",
+ "parameters": {
+ "sourceVariableName": "kestrelHttpsPort",
+ "fallbackVariableName": "kestrelHttpsPortGenerated"
+ },
+ "replaces": "5001"
+ },
+ "iisHttpPort": {
+ "type": "parameter",
+ "datatype": "integer",
+ "description": "Port number to use for the IIS Express HTTP endpoint in launchSettings.json."
+ },
+ "iisHttpPortGenerated": {
+ "type": "generated",
+ "generator": "port"
+ },
+ "iisHttpPortReplacer": {
+ "type": "generated",
+ "generator": "coalesce",
+ "parameters": {
+ "sourceVariableName": "iisHttpPort",
+ "fallbackVariableName": "iisHttpPortGenerated"
+ },
+ "replaces": "8080"
+ },
+ "iisHttpsPort": {
+ "type": "parameter",
+ "datatype": "integer",
+ "description": "Port number to use for the IIS Express HTTPS endpoint in launchSettings.json. This option is only applicable when the parameter no-https is not used."
+ },
+ "iisHttpsPortGenerated": {
+ "type": "generated",
+ "generator": "port",
+ "parameters": {
+ "low": 44300,
+ "high": 44399
+ }
+ },
+ "iisHttpsPortReplacer": {
+ "type": "generated",
+ "generator": "coalesce",
+ "parameters": {
+ "sourceVariableName": "iisHttpsPort",
+ "fallbackVariableName": "iisHttpsPortGenerated"
+ },
+ "replaces": "44300"
+ },
+ "PWA": {
+ "type": "parameter",
+ "datatype": "bool",
+ "defaultValue": "false",
+ "displayName": "_Progressive Web Application",
+ "description": "If specified, produces a Progressive Web Application (PWA) supporting installation and offline use."
+ },
+ "HasHttpProfile": {
+ "type": "parameter",
+ "datatype": "bool",
+ "defaultValue": "true",
+ "description": "Always have HTTP profile."
+ },
+ "HasHttpsProfile": {
+ "type": "computed",
+ "value": "(!NoHttps)"
+ },
+ "NoHttps": {
+ "type": "parameter",
+ "datatype": "bool",
+ "defaultValue": "false",
+ "description": "Whether to turn off HTTPS."
+ },
+ "copyrightYear": {
+ "type": "generated",
+ "generator": "now",
+ "replaces": "copyrightYear",
+ "parameters": {
+ "format": "yyyy"
+ }
+ }
+ },
+ "tags": {
+ "language": "C#",
+ "type": "project"
+ },
+ "postActions": [
+ {
+ "id": "restore",
+ "condition": "(!skipRestore && Hosted)",
+ "description": "Restore NuGet packages required by this project.",
+ "manualInstructions": [
+ {
+ "text": "Run 'dotnet restore'"
+ }
+ ],
+ "actionId": "210D431B-A78B-4D2F-B762-4ED3E3EA9025",
+ "continueOnError": true
+ },
+ {
+ "id": "restoreClient",
+ "condition": "(!skipRestore && !Hosted)",
+ "description": "Restore NuGet packages required by this project.",
+ "manualInstructions": [
+ {
+ "text": "Run 'dotnet restore'"
+ }
+ ],
+ "args": {
+ "files": [ "EmptyComponentsWebAssembly-CSharp.Client.csproj" ]
+ },
+ "actionId": "210D431B-A78B-4D2F-B762-4ED3E3EA9025",
+ "continueOnError": true
+ }
+ ]
+}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/App.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/App.razor
new file mode 100644
index 0000000000..6fd3ed1b5a
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/App.razor
@@ -0,0 +1,12 @@
+<Router AppAssembly="@typeof(App).Assembly">
+ <Found Context="routeData">
+ <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
+ <FocusOnNavigate RouteData="@routeData" Selector="h1" />
+ </Found>
+ <NotFound>
+ <PageTitle>Not found</PageTitle>
+ <LayoutView Layout="@typeof(MainLayout)">
+ <p role="alert">Sorry, there's nothing at this address.</p>
+ </LayoutView>
+ </NotFound>
+</Router>
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/MainLayout.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/MainLayout.razor
new file mode 100644
index 0000000000..a5af3489ae
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/MainLayout.razor
@@ -0,0 +1,3 @@
+@inherits LayoutComponentBase
+
+<main> @Body </main>
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/Pages/Index.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/Pages/Index.razor
new file mode 100644
index 0000000000..cba3252e00
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/Pages/Index.razor
@@ -0,0 +1,3 @@
+@page "/"
+
+<h1>Hello, world!</h1>
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/Program.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/Program.cs
new file mode 100644
index 0000000000..e5be5e452d
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/Program.cs
@@ -0,0 +1,22 @@
+using Microsoft.AspNetCore.Components.Web;
+using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
+#if (Hosted)
+using EmptyComponentsWebAssembly_CSharp.Client;
+#else
+using EmptyComponentsWebAssembly_CSharp;
+#endif
+
+var builder = WebAssemblyHostBuilder.CreateDefault(args);
+builder.RootComponents.Add<App>("#app");
+builder.RootComponents.Add<HeadOutlet>("head::after");
+
+#if (!Hosted)
+builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
+#else
+builder.Services.AddHttpClient("EmptyComponentsWebAssembly_CSharp.ServerAPI", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));
+
+// Supply HttpClient instances that include access tokens when making requests to the server project
+builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("EmptyComponentsWebAssembly_CSharp.ServerAPI"));
+#endif
+
+await builder.Build().RunAsync();
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/Properties/launchSettings.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/Properties/launchSettings.json
new file mode 100644
index 0000000000..ae527fca2e
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/Properties/launchSettings.json
@@ -0,0 +1,46 @@
+{
+ "iisSettings": {
+ "iisExpress": {
+ "applicationUrl": "http://localhost:8080",
+ //#if(HasHttpsProfile)
+ "sslPort": 44300
+ //#else
+ "sslPort": 0
+ //#endif
+ }
+ },
+ "profiles": {
+ //#if (HasHttpProfile)
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
+ "applicationUrl": "http://localhost:5000",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ //#endif
+ //#if (HasHttpsProfile)
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
+ "applicationUrl": "https://localhost:5001;http://localhost:5000",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ //#endif
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/_Imports.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/_Imports.razor
new file mode 100644
index 0000000000..31cd28c457
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/_Imports.razor
@@ -0,0 +1,11 @@
+@using System.Net.Http
+@using System.Net.Http.Json
+@using Microsoft.AspNetCore.Components.Routing
+@using Microsoft.AspNetCore.Components.Web
+@using Microsoft.AspNetCore.Components.WebAssembly.Http
+@using Microsoft.JSInterop
+@*#if (!Hosted)
+@using EmptyComponentsWebAssembly_CSharp
+#else
+@using EmptyComponentsWebAssembly_CSharp.Client
+#endif*@
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/wwwroot/css/app.css b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/wwwroot/css/app.css
new file mode 100644
index 0000000000..3afadc202e
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/wwwroot/css/app.css
@@ -0,0 +1,28 @@
+#blazor-error-ui {
+ background: lightyellow;
+ bottom: 0;
+ box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
+ display: none;
+ left: 0;
+ padding: 0.6rem 1.25rem 0.7rem 1.25rem;
+ position: fixed;
+ width: 100%;
+ z-index: 1000;
+}
+
+ #blazor-error-ui .dismiss {
+ cursor: pointer;
+ position: absolute;
+ right: 0.75rem;
+ top: 0.5rem;
+ }
+
+.blazor-error-boundary {
+ background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
+ padding: 1rem 1rem 1rem 3.7rem;
+ color: white;
+}
+
+ .blazor-error-boundary::after {
+ content: "An error has occurred."
+ }
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/wwwroot/icon-512.png b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/wwwroot/icon-512.png
new file mode 100644
index 0000000000..c2dd4842dc
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/wwwroot/icon-512.png
Binary files differ
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/wwwroot/index.html b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/wwwroot/index.html
new file mode 100644
index 0000000000..1cb32df1f8
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/wwwroot/index.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+ <meta charset="utf-8" />
+ <base href="/" />
+ <link href="css/app.css" rel="stylesheet" />
+ <!--#if PWA -->
+ <link href="manifest.json" rel="manifest" />
+ <!--#endif -->
+</head>
+
+<body>
+ <div id="app">Loading...</div>
+
+ <div id="blazor-error-ui">
+ An unhandled error has occurred.
+ <a href="" class="reload">Reload</a>
+ <a class="dismiss">🗙</a>
+ </div>
+ <script src="_framework/blazor.webassembly.js"></script>
+ <!--#if PWA -->
+ <script>navigator.serviceWorker.register('service-worker.js');</script>
+ <!--#endif -->
+</body>
+
+</html>
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/wwwroot/manifest.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/wwwroot/manifest.json
new file mode 100644
index 0000000000..9548c2f65e
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/wwwroot/manifest.json
@@ -0,0 +1,16 @@
+{
+ "name": "EmptyComponentsWebAssembly-CSharp",
+ "short_name": "EmptyComponentsWebAssembly-CSharp",
+ "start_url": "./",
+ "display": "standalone",
+ "background_color": "#ffffff",
+ "theme_color": "#03173d",
+ "prefer_related_applications": false,
+ "icons": [
+ {
+ "src": "icon-512.png",
+ "type": "image/png",
+ "sizes": "512x512"
+ }
+ ]
+}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/wwwroot/service-worker.js b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/wwwroot/service-worker.js
new file mode 100644
index 0000000000..fe614daee0
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/wwwroot/service-worker.js
@@ -0,0 +1,4 @@
+// In development, always fetch from the network and do not enable offline support.
+// This is because caching would make development more difficult (changes would not
+// be reflected on the first load after each change).
+self.addEventListener('fetch', () => { });
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/wwwroot/service-worker.published.js b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/wwwroot/service-worker.published.js
new file mode 100644
index 0000000000..e44e13a326
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Client/wwwroot/service-worker.published.js
@@ -0,0 +1,59 @@
+// Caution! Be sure you understand the caveats before publishing an application with
+// offline support. See https://aka.ms/blazor-offline-considerations
+
+self.importScripts('./service-worker-assets.js');
+self.addEventListener('install', event => event.waitUntil(onInstall(event)));
+self.addEventListener('activate', event => event.waitUntil(onActivate(event)));
+self.addEventListener('fetch', event => event.respondWith(onFetch(event)));
+
+const cacheNamePrefix = 'offline-cache-';
+const cacheName = `${cacheNamePrefix}${self.assetsManifest.version}`;
+const offlineAssetsInclude = [ /\.dll$/, /\.pdb$/, /\.wasm/, /\.html/, /\.js$/, /\.json$/, /\.css$/, /\.woff$/, /\.png$/, /\.jpe?g$/, /\.gif$/, /\.ico$/, /\.blat$/, /\.dat$/ ];
+const offlineAssetsExclude = [ /^service-worker\.js$/ ];
+
+async function onInstall(event) {
+ console.info('Service worker: Install');
+
+ // Fetch and cache all matching items from the assets manifest
+ const assetsRequests = self.assetsManifest.assets
+ .filter(asset => offlineAssetsInclude.some(pattern => pattern.test(asset.url)))
+ .filter(asset => !offlineAssetsExclude.some(pattern => pattern.test(asset.url)))
+ .map(asset => new Request(asset.url, { integrity: asset.hash, cache: 'no-cache' }));
+//#if(Hosted)
+
+ // Also cache authentication configuration
+ assetsRequests.push(new Request('_configuration/EmptyComponentsWebAssembly-CSharp.Client'));
+
+//#endif
+ await caches.open(cacheName).then(cache => cache.addAll(assetsRequests));
+}
+
+async function onActivate(event) {
+ console.info('Service worker: Activate');
+
+ // Delete unused caches
+ const cacheKeys = await caches.keys();
+ await Promise.all(cacheKeys
+ .filter(key => key.startsWith(cacheNamePrefix) && key !== cacheName)
+ .map(key => caches.delete(key)));
+}
+
+async function onFetch(event) {
+ let cachedResponse = null;
+ if (event.request.method === 'GET') {
+ // For all navigation requests, try to serve index.html from cache
+ // If you need some URLs to be server-rendered, edit the following check to exclude those URLs
+//#if(Hosted)
+ const shouldServeIndexHtml = event.request.mode === 'navigate'
+ && !event.request.url.includes('/connect/')
+//#else
+ const shouldServeIndexHtml = event.request.mode === 'navigate';
+//#endif
+
+ const request = shouldServeIndexHtml ? 'index.html' : event.request;
+ const cache = await caches.open(cacheName);
+ cachedResponse = await cache.match(request);
+ }
+
+ return cachedResponse || fetch(event.request);
+}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/EmptyComponentsWebAssembly-CSharp.sln b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/EmptyComponentsWebAssembly-CSharp.sln
new file mode 100644
index 0000000000..f30f423de2
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/EmptyComponentsWebAssembly-CSharp.sln
@@ -0,0 +1,64 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.0.0
+MinimumVisualStudioVersion = 16.0.0.0
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EmptyComponentsWebAssembly-CSharp.Server", "Server\EmptyComponentsWebAssembly-CSharp.Server.csproj", "{650B3CE7-2E93-4CC4-9F46-466686815EAA}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EmptyComponentsWebAssembly-CSharp.Client", "Client\EmptyComponentsWebAssembly-CSharp.Client.csproj", "{5990939C-7E7B-4CFA-86FF-44CA5756498A}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EmptyComponentsWebAssembly-CSharp.Shared", "Shared\EmptyComponentsWebAssembly-CSharp.Shared.csproj", "{0AFFA7FD-4E37-4636-AB91-3753E746DB98}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Debug|x64.Build.0 = Debug|Any CPU
+ {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Debug|x86.Build.0 = Debug|Any CPU
+ {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Release|x64.ActiveCfg = Release|Any CPU
+ {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Release|x64.Build.0 = Release|Any CPU
+ {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Release|x86.ActiveCfg = Release|Any CPU
+ {5990939C-7E7B-4CFA-86FF-44CA5756498A}.Release|x86.Build.0 = Release|Any CPU
+ {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Debug|x64.Build.0 = Debug|Any CPU
+ {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Debug|x86.Build.0 = Debug|Any CPU
+ {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Release|x64.ActiveCfg = Release|Any CPU
+ {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Release|x64.Build.0 = Release|Any CPU
+ {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Release|x86.ActiveCfg = Release|Any CPU
+ {650B3CE7-2E93-4CC4-9F46-466686815EAA}.Release|x86.Build.0 = Release|Any CPU
+ {0AFFA7FD-4E37-4636-AB91-3753E746DB98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0AFFA7FD-4E37-4636-AB91-3753E746DB98}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0AFFA7FD-4E37-4636-AB91-3753E746DB98}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {0AFFA7FD-4E37-4636-AB91-3753E746DB98}.Debug|x64.Build.0 = Debug|Any CPU
+ {0AFFA7FD-4E37-4636-AB91-3753E746DB98}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {0AFFA7FD-4E37-4636-AB91-3753E746DB98}.Debug|x86.Build.0 = Debug|Any CPU
+ {0AFFA7FD-4E37-4636-AB91-3753E746DB98}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0AFFA7FD-4E37-4636-AB91-3753E746DB98}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0AFFA7FD-4E37-4636-AB91-3753E746DB98}.Release|x64.ActiveCfg = Release|Any CPU
+ {0AFFA7FD-4E37-4636-AB91-3753E746DB98}.Release|x64.Build.0 = Release|Any CPU
+ {0AFFA7FD-4E37-4636-AB91-3753E746DB98}.Release|x86.ActiveCfg = Release|Any CPU
+ {0AFFA7FD-4E37-4636-AB91-3753E746DB98}.Release|x86.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {4C26868E-5E7C-458D-82E3-040509D0C71F}
+ EndGlobalSection
+EndGlobal
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Server/Program.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Server/Program.cs
new file mode 100644
index 0000000000..a8e63acaf0
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Server/Program.cs
@@ -0,0 +1,33 @@
+using Microsoft.AspNetCore.ResponseCompression;
+
+var builder = WebApplication.CreateBuilder(args);
+
+builder.Services.AddControllersWithViews();
+builder.Services.AddRazorPages();
+
+var app = builder.Build();
+
+// Configure the HTTP request pipeline.
+if (app.Environment.IsDevelopment())
+{
+ app.UseWebAssemblyDebugging();
+}
+#if (HasHttpsProfile)
+else
+{
+ // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
+ app.UseHsts();
+}
+
+app.UseHttpsRedirection();
+#endif
+app.UseBlazorFrameworkFiles();
+app.UseStaticFiles();
+
+app.UseRouting();
+
+app.MapRazorPages();
+app.MapControllers();
+app.MapFallbackToFile("index.html");
+
+app.Run();
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Server/Properties/launchSettings.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Server/Properties/launchSettings.json
new file mode 100644
index 0000000000..084a9762a2
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Server/Properties/launchSettings.json
@@ -0,0 +1,44 @@
+{
+ "iisExpress": {
+ "applicationUrl": "http://localhost:8080",
+ //#if (HasHttpsProfile)
+ "sslPort": 44300
+ //#else
+ "sslPort": 0
+ //#endif
+ },
+ "profiles": {
+ //#if (HasHttpProfile)
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
+ "applicationUrl": "http://localhost:5000",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ //#endif
+ //#if (HasHttpsProfile)
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
+ "applicationUrl": "https://localhost:5001;http://localhost:5000",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ //#endif
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Server/appsettings.Development.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Server/appsettings.Development.json
new file mode 100644
index 0000000000..0c208ae918
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Server/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Server/appsettings.json b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Server/appsettings.json
new file mode 100644
index 0000000000..10f68b8c8b
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Server/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Shared/SharedClass.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Shared/SharedClass.cs
new file mode 100644
index 0000000000..bb016a89ad
--- /dev/null
+++ b/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyComponentsWebAssembly-CSharp/Shared/SharedClass.cs
@@ -0,0 +1 @@
+/* Shared classes can be referenced by both the Client and Server */
diff --git a/src/ProjectTemplates/test/Templates.Blazor.Tests/.gitattributes b/src/ProjectTemplates/test/Templates.Blazor.Server.Tests/.gitattributes
index 300e504f3e..300e504f3e 100644
--- a/src/ProjectTemplates/test/Templates.Blazor.Tests/.gitattributes
+++ b/src/ProjectTemplates/test/Templates.Blazor.Server.Tests/.gitattributes
diff --git a/src/ProjectTemplates/test/Templates.Blazor.Tests/BlazorServerTemplateTest.cs b/src/ProjectTemplates/test/Templates.Blazor.Server.Tests/BlazorServerTemplateTest.cs
index 4d805e4326..4d805e4326 100644
--- a/src/ProjectTemplates/test/Templates.Blazor.Tests/BlazorServerTemplateTest.cs
+++ b/src/ProjectTemplates/test/Templates.Blazor.Server.Tests/BlazorServerTemplateTest.cs
diff --git a/src/ProjectTemplates/test/Templates.Blazor.Server.Tests/EmptyBlazorServerTemplateTest.cs b/src/ProjectTemplates/test/Templates.Blazor.Server.Tests/EmptyBlazorServerTemplateTest.cs
new file mode 100644
index 0000000000..7886d95a03
--- /dev/null
+++ b/src/ProjectTemplates/test/Templates.Blazor.Server.Tests/EmptyBlazorServerTemplateTest.cs
@@ -0,0 +1,31 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Testing;
+using Templates.Test.Helpers;
+using Xunit;
+using Xunit.Abstractions;
+using Xunit.Sdk;
+
+namespace Templates.Blazor.Test;
+
+public class EmptyBlazorServerTemplateTest : BlazorTemplateTest
+{
+ public EmptyBlazorServerTemplateTest(ProjectFactoryFixture projectFactory)
+ : base(projectFactory)
+ {
+ }
+
+ public override string ProjectType { get; } = "blazorserver-empty";
+
+ [Fact]
+ public Task EmptyBlazorServerTemplateWorks() => CreateBuildPublishAsync();
+
+ [Fact]
+ public Task EmptyBlazorServerTemplate_NoHttps_Works() => CreateBuildPublishAsync(args: new[] { ArgConstants.NoHttps });
+}
diff --git a/src/ProjectTemplates/test/Templates.Blazor.Tests/Templates.Blazor.Tests.csproj b/src/ProjectTemplates/test/Templates.Blazor.Server.Tests/Templates.Blazor.Server.Tests.csproj
index 2e5efd76d4..e7ed1ccb38 100644
--- a/src/ProjectTemplates/test/Templates.Blazor.Tests/Templates.Blazor.Tests.csproj
+++ b/src/ProjectTemplates/test/Templates.Blazor.Server.Tests/Templates.Blazor.Server.Tests.csproj
@@ -4,7 +4,7 @@
<PropertyGroup>
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
- <TestGroupName>ProjectTemplates.Blazor.Tests</TestGroupName>
+ <TestGroupName>ProjectTemplates.Blazor.Server.Tests</TestGroupName>
<DefineConstants>$(DefineConstants);XPLAT</DefineConstants>
<RunTemplateTests Condition="'$(RunTemplateTests)' == ''">true</RunTemplateTests>
diff --git a/src/ProjectTemplates/test/Templates.Blazor.Tests/BlazorWasmTemplateTest.cs b/src/ProjectTemplates/test/Templates.Blazor.WebAssembly.Tests/BlazorWasmTemplateTest.cs
index 6e2f983891..6e2f983891 100644
--- a/src/ProjectTemplates/test/Templates.Blazor.Tests/BlazorWasmTemplateTest.cs
+++ b/src/ProjectTemplates/test/Templates.Blazor.WebAssembly.Tests/BlazorWasmTemplateTest.cs
diff --git a/src/ProjectTemplates/test/Templates.Blazor.WebAssembly.Tests/EmptyBlazorWasmTemplateTest.cs b/src/ProjectTemplates/test/Templates.Blazor.WebAssembly.Tests/EmptyBlazorWasmTemplateTest.cs
new file mode 100644
index 0000000000..9d9fa41057
--- /dev/null
+++ b/src/ProjectTemplates/test/Templates.Blazor.WebAssembly.Tests/EmptyBlazorWasmTemplateTest.cs
@@ -0,0 +1,114 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text.Json;
+using System.Text.RegularExpressions;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Testing;
+using Newtonsoft.Json.Linq;
+using Templates.Test.Helpers;
+using Xunit;
+using Xunit.Abstractions;
+using Xunit.Sdk;
+
+namespace Templates.Blazor.Test;
+
+public class EmptyBlazorWasmTemplateTest : BlazorTemplateTest
+{
+ public EmptyBlazorWasmTemplateTest(ProjectFactoryFixture projectFactory)
+ : base(projectFactory) { }
+
+ public override string ProjectType { get; } = "blazorwasm-empty";
+
+ [Fact]
+ public async Task EmptyBlazorWasmStandaloneTemplateCanCreateBuildPublish()
+ {
+ var project = await CreateBuildPublishAsync();
+
+ // The service worker assets manifest isn't generated for non-PWA projects
+ var publishDir = Path.Combine(project.TemplatePublishDir, "wwwroot");
+ Assert.False(File.Exists(Path.Combine(publishDir, "service-worker-assets.js")), "Non-PWA templates should not produce service-worker-assets.js");
+ }
+
+ [Fact]
+ public async Task EmptyBlazorWasmStandaloneTemplateNoHttpsCanCreateBuildPublish()
+ {
+ var project = await CreateBuildPublishAsync(args: new[] { ArgConstants.NoHttps });
+
+ // The service worker assets manifest isn't generated for non-PWA projects
+ var publishDir = Path.Combine(project.TemplatePublishDir, "wwwroot");
+ Assert.False(File.Exists(Path.Combine(publishDir, "service-worker-assets.js")), "Non-PWA templates should not produce service-worker-assets.js");
+ }
+
+ [Fact]
+ public Task EmptyBlazorWasmHostedTemplateCanCreateBuildPublish()
+ => CreateBuildPublishAsync(args: new[] { ArgConstants.Hosted }, serverProject: true);
+
+ [Fact]
+ public Task EmptyBlazorWasmHostedTemplateNoHttpsCanCreateBuildPublish()
+ => CreateBuildPublishAsync(args: new[] { ArgConstants.Hosted, ArgConstants.NoHttps }, serverProject: true);
+
+ [Fact]
+ public Task EmptyBlazorWasmStandalonePwaTemplateCanCreateBuildPublish()
+ => CreateBuildPublishAsync(args: new[] { ArgConstants.Pwa });
+
+ [Fact]
+ public Task EmptyBlazorWasmStandalonePwaTemplateNoHttpsCanCreateBuildPublish()
+ => CreateBuildPublishAsync(args: new[] { ArgConstants.Pwa, ArgConstants.NoHttps });
+
+ [Fact]
+ public async Task EmptyBlazorWasmHostedPwaTemplateCanCreateBuildPublish()
+ {
+ var project = await CreateBuildPublishAsync(args: new[] { ArgConstants.Hosted, ArgConstants.Pwa }, serverProject: true);
+
+ var serverProject = GetSubProject(project, "Server", $"{project.ProjectName}.Server");
+
+ ValidatePublishedServiceWorker(serverProject);
+ }
+
+ [Fact]
+ public async Task EmptyBlazorWasmHostedPwaTemplateNoHttpsCanCreateBuildPublish()
+ {
+ var project = await CreateBuildPublishAsync(args: new[] { ArgConstants.Hosted, ArgConstants.Pwa, ArgConstants.NoHttps }, serverProject: true);
+
+ var serverProject = GetSubProject(project, "Server", $"{project.ProjectName}.Server");
+
+ ValidatePublishedServiceWorker(serverProject);
+ }
+
+ private void ValidatePublishedServiceWorker(Project project)
+ {
+ var publishDir = Path.Combine(project.TemplatePublishDir, "wwwroot");
+
+ // When publishing the PWA template, we generate an assets manifest
+ // and move service-worker.published.js to overwrite service-worker.js
+ Assert.False(File.Exists(Path.Combine(publishDir, "service-worker.published.js")), "service-worker.published.js should not be published");
+ Assert.True(File.Exists(Path.Combine(publishDir, "service-worker.js")), "service-worker.js should be published");
+ Assert.True(File.Exists(Path.Combine(publishDir, "service-worker-assets.js")), "service-worker-assets.js should be published");
+
+ // We automatically append the SWAM version as a comment in the published service worker file
+ var serviceWorkerAssetsManifestContents = ReadFile(publishDir, "service-worker-assets.js");
+ var serviceWorkerContents = ReadFile(publishDir, "service-worker.js");
+
+ // Parse the "version": "..." value from the SWAM, and check it's in the service worker
+ var serviceWorkerAssetsManifestVersionMatch = new Regex(@"^\s*\""version\"":\s*(\""[^\""]+\"")", RegexOptions.Multiline)
+ .Match(serviceWorkerAssetsManifestContents);
+ Assert.True(serviceWorkerAssetsManifestVersionMatch.Success);
+ var serviceWorkerAssetsManifestVersionJson = serviceWorkerAssetsManifestVersionMatch.Groups[1].Captures[0].Value;
+ var serviceWorkerAssetsManifestVersion = JsonSerializer.Deserialize<string>(serviceWorkerAssetsManifestVersionJson);
+ Assert.True(serviceWorkerContents.Contains($"/* Manifest version: {serviceWorkerAssetsManifestVersion} */", StringComparison.Ordinal));
+ }
+
+ private string ReadFile(string basePath, string path)
+ {
+ var fullPath = Path.Combine(basePath, path);
+ var doesExist = File.Exists(fullPath);
+
+ Assert.True(doesExist, $"Expected file to exist, but it doesn't: {path}");
+ return File.ReadAllText(Path.Combine(basePath, path));
+ }
+}
diff --git a/src/ProjectTemplates/test/Templates.Blazor.WebAssembly.Tests/Templates.Blazor.WebAssembly.Tests.csproj b/src/ProjectTemplates/test/Templates.Blazor.WebAssembly.Tests/Templates.Blazor.WebAssembly.Tests.csproj
new file mode 100644
index 0000000000..a41d284fc5
--- /dev/null
+++ b/src/ProjectTemplates/test/Templates.Blazor.WebAssembly.Tests/Templates.Blazor.WebAssembly.Tests.csproj
@@ -0,0 +1,68 @@
+<Project Sdk="Microsoft.NET.Sdk">
+ <!-- Shared testing infrastructure for running E2E tests using selenium -->
+ <Import Condition="'$(SkipTestBuild)' != 'true'" Project="$(SharedSourceRoot)E2ETesting\E2ETesting.props" />
+
+ <PropertyGroup>
+ <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
+ <TestGroupName>ProjectTemplates.Blazor.WebAssembly.Tests</TestGroupName>
+ <DefineConstants>$(DefineConstants);XPLAT</DefineConstants>
+
+ <RunTemplateTests Condition="'$(RunTemplateTests)' == ''">true</RunTemplateTests>
+ <SkipTests Condition="'$(RunTemplateTests)' != 'true'">true</SkipTests>
+
+ <BaseOutputPath />
+ <OutputPath />
+
+ <!-- Properties that affect test runs -->
+ <!-- TestTemplateCreationFolder is the folder where the templates will be created. Will point out to $(OutputDir)$(TestTemplateCreationFolder) -->
+ <TestTemplateCreationFolder>TestTemplates\</TestTemplateCreationFolder>
+ <TestPackageRestorePath>$([MSBuild]::EnsureTrailingSlash('$(RepoRoot)'))obj\template-restore\</TestPackageRestorePath>
+ <TestDependsOnAspNetPackages>true</TestDependsOnAspNetPackages>
+ <SkipHelixQueues>
+ $(HelixQueueArmDebian11);
+ </SkipHelixQueues>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <Compile Include="$(SharedSourceRoot)CommandLineUtils\**\*.cs" />
+ <Compile Include="$(SharedSourceRoot)CertificateGeneration\**\*.cs" LinkBase="shared\CertificateGeneration" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <Compile Include="$(SharedSourceRoot)Process\*.cs" LinkBase="shared\Process" />
+ <Compile Include="..\..\Shared\**" LinkBase="Helpers" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <!-- We need the actual templates in the output directory for tests to verify file encodings. -->
+ <Content Include="..\..\Web.ProjectTemplates\**" LinkBase="Assets\Web.ProjectTemplates" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <HelixContent Include="$(OutputPath)$(TargetFramework)Assets\**" />
+ </ItemGroup>
+
+ <ItemGroup>
+ <Reference Include="AngleSharp" />
+ <Reference Include="System.Net.Http" />
+ <ProjectReference Include="$(RepoRoot)src\Framework\App.Runtime\src\Microsoft.AspNetCore.App.Runtime.csproj"
+ Private="false"
+ ReferenceOutputAssembly="false"
+ SkipGetTargetFrameworkProperties="true" />
+
+ <ProjectReference Include="$(RepoRoot)src\Hosting\Server.IntegrationTesting\src\Microsoft.AspNetCore.Server.IntegrationTesting.csproj" />
+ <ProjectReference Include="$(RepoRoot)src\Shared\BrowserTesting\src\Microsoft.AspNetCore.BrowserTesting.csproj" />
+ <ProjectReference Include="../../Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj"
+ Private="false"
+ ReferenceOutputAssembly="false"
+ SkipGetTargetFrameworkProperties="true" />
+ </ItemGroup>
+
+ <PropertyGroup>
+ <PreserveExistingLogsInOutput Condition="'$(PreserveExistingLogsInOutput)' == '' AND '$(ContinuousIntegrationBuild)' == 'true'">true</PreserveExistingLogsInOutput>
+ <PreserveExistingLogsInOutput Condition="'$(PreserveExistingLogsInOutput)' == ''">false</PreserveExistingLogsInOutput>
+ </PropertyGroup>
+
+ <!-- Shared testing infrastructure for running E2E tests -->
+ <Import Condition="'$(SkipTestBuild)' != 'true'" Project="..\..\TestInfrastructure\PrepareForTest.targets" />
+</Project>
diff --git a/src/ProjectTemplates/test/Templates.Tests/WorkerTemplateTest.cs b/src/ProjectTemplates/test/Templates.Tests/WorkerTemplateTest.cs
index e8e06bebbe..53a4847912 100644
--- a/src/ProjectTemplates/test/Templates.Tests/WorkerTemplateTest.cs
+++ b/src/ProjectTemplates/test/Templates.Tests/WorkerTemplateTest.cs
@@ -31,11 +31,9 @@ public class WorkerTemplateTest : LoggedTest
}
[ConditionalTheory]
- [OSSkipCondition(OperatingSystems.Linux, SkipReason = "https://github.com/dotnet/sdk/issues/12831")]
[InlineData("C#", null)]
[InlineData("C#", new [] { ArgConstants.UseProgramMain })]
[InlineData("F#", null)]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/25404")]
public async Task WorkerTemplateAsync(string language, string[] args)
{
var project = await ProjectFactory.CreateProject(Output);
diff --git a/src/ProjectTemplates/test/Templates.Tests/template-baselines.json b/src/ProjectTemplates/test/Templates.Tests/template-baselines.json
index f2a584d6fd..e751f7e959 100644
--- a/src/ProjectTemplates/test/Templates.Tests/template-baselines.json
+++ b/src/ProjectTemplates/test/Templates.Tests/template-baselines.json
@@ -1500,6 +1500,37 @@
"wwwroot/css/open-iconic/font/fonts/open-iconic.woff"
]
},
+ "Empty": {
+ "Template": "blazorserver-empty",
+ "Arguments": "new blazorserver-empty",
+ "Files": [
+ "App.razor",
+ "appsettings.Development.json",
+ "appsettings.json",
+ "MainLayout.razor",
+ "Program.cs",
+ "_Imports.razor",
+ "Pages/Index.razor",
+ "Pages/_Host.cshtml",
+ "Properties/launchSettings.json",
+ "wwwroot/css/site.css"
+ ]
+ },
+ "EmptyExcludeLaunchSettings": {
+ "Template": "blazorserver-empty",
+ "Arguments": "new blazorserver-empty --exclude-launch-settings",
+ "Files": [
+ "App.razor",
+ "appsettings.Development.json",
+ "appsettings.json",
+ "MainLayout.razor",
+ "Program.cs",
+ "_Imports.razor",
+ "Pages/Index.razor",
+ "Pages/_Host.cshtml",
+ "wwwroot/css/site.css"
+ ]
+ },
"SingleOrg": {
"Template": "blazorserver",
"Arguments": "new blazorserver -au SingleOrg",
@@ -1689,5 +1720,433 @@
"Program.cs"
]
}
+ },
+ "blazorwasm": {
+ "None": {
+ "Template": "blazorwasm",
+ "Arguments": "new blazorwasm",
+ "Files": [
+ "App.razor",
+ "_Imports.razor",
+ "Program.cs",
+ "Pages/Counter.razor",
+ "Pages/FetchData.razor",
+ "Pages/Index.razor",
+ "Properties/launchSettings.json",
+ "Shared/MainLayout.razor",
+ "Shared/MainLayout.razor.css",
+ "Shared/NavMenu.razor",
+ "Shared/NavMenu.razor.css",
+ "Shared/SurveyPrompt.razor",
+ "wwwroot/favicon.png",
+ "wwwroot/icon-192.png",
+ "wwwroot/index.html",
+ "wwwroot/css/app.css",
+ "wwwroot/css/bootstrap/bootstrap.min.css",
+ "wwwroot/css/bootstrap/bootstrap.min.css.map",
+ "wwwroot/css/open-iconic/FONT-LICENSE",
+ "wwwroot/css/open-iconic/ICON-LICENSE",
+ "wwwroot/css/open-iconic/README.md",
+ "wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css",
+ "wwwroot/css/open-iconic/font/fonts/open-iconic.eot",
+ "wwwroot/css/open-iconic/font/fonts/open-iconic.otf",
+ "wwwroot/css/open-iconic/font/fonts/open-iconic.svg",
+ "wwwroot/css/open-iconic/font/fonts/open-iconic.ttf",
+ "wwwroot/css/open-iconic/font/fonts/open-iconic.woff",
+ "wwwroot/sample-data/weather.json"
+ ]
+ },
+ "Individual": {
+ "Template": "blazorwasm",
+ "Arguments": "new blazorwasm -au Individual",
+ "Files": [
+ "App.razor",
+ "_Imports.razor",
+ "Program.cs",
+ "Pages/Authentication.razor",
+ "Pages/Counter.razor",
+ "Pages/FetchData.razor",
+ "Pages/Index.razor",
+ "Properties/launchSettings.json",
+ "Shared/LoginDisplay.razor",
+ "Shared/MainLayout.razor",
+ "Shared/MainLayout.razor.css",
+ "Shared/NavMenu.razor",
+ "Shared/NavMenu.razor.css",
+ "Shared/RedirectToLogin.razor",
+ "Shared/SurveyPrompt.razor",
+ "wwwroot/appsettings.Development.json",
+ "wwwroot/appsettings.json",
+ "wwwroot/favicon.png",
+ "wwwroot/icon-192.png",
+ "wwwroot/index.html",
+ "wwwroot/css/app.css",
+ "wwwroot/css/bootstrap/bootstrap.min.css",
+ "wwwroot/css/bootstrap/bootstrap.min.css.map",
+ "wwwroot/css/open-iconic/FONT-LICENSE",
+ "wwwroot/css/open-iconic/ICON-LICENSE",
+ "wwwroot/css/open-iconic/README.md",
+ "wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css",
+ "wwwroot/css/open-iconic/font/fonts/open-iconic.eot",
+ "wwwroot/css/open-iconic/font/fonts/open-iconic.otf",
+ "wwwroot/css/open-iconic/font/fonts/open-iconic.svg",
+ "wwwroot/css/open-iconic/font/fonts/open-iconic.ttf",
+ "wwwroot/css/open-iconic/font/fonts/open-iconic.woff",
+ "wwwroot/sample-data/weather.json"
+ ]
+ },
+ "IndividualB2C": {
+ "Template": "blazorwasm",
+ "Arguments": "new blazorwasm -au IndividualB2C",
+ "Files": [
+ "App.razor",
+ "_Imports.razor",
+ "Program.cs",
+ "Pages/Authentication.razor",
+ "Pages/Counter.razor",
+ "Pages/FetchData.razor",
+ "Pages/Index.razor",
+ "Properties/launchSettings.json",
+ "Shared/LoginDisplay.razor",
+ "Shared/MainLayout.razor",
+ "Shared/MainLayout.razor.css",
+ "Shared/NavMenu.razor",
+ "Shared/NavMenu.razor.css",
+ "Shared/RedirectToLogin.razor",
+ "Shared/SurveyPrompt.razor",
+ "wwwroot/appsettings.json",
+ "wwwroot/favicon.png",
+ "wwwroot/icon-192.png",
+ "wwwroot/index.html",
+ "wwwroot/css/app.css",
+ "wwwroot/css/bootstrap/bootstrap.min.css",
+ "wwwroot/css/bootstrap/bootstrap.min.css.map",
+ "wwwroot/css/open-iconic/FONT-LICENSE",
+ "wwwroot/css/open-iconic/ICON-LICENSE",
+ "wwwroot/css/open-iconic/README.md",
+ "wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css",
+ "wwwroot/css/open-iconic/font/fonts/open-iconic.eot",
+ "wwwroot/css/open-iconic/font/fonts/open-iconic.otf",
+ "wwwroot/css/open-iconic/font/fonts/open-iconic.svg",
+ "wwwroot/css/open-iconic/font/fonts/open-iconic.ttf",
+ "wwwroot/css/open-iconic/font/fonts/open-iconic.woff",
+ "wwwroot/sample-data/weather.json"
+ ]
+ },
+ "SingleOrg": {
+ "Template": "blazorwasm",
+ "Arguments": "new blazorwasm -au SingleOrg",
+ "Files": [
+ "App.razor",
+ "_Imports.razor",
+ "Program.cs",
+ "Pages/Authentication.razor",
+ "Pages/Counter.razor",
+ "Pages/FetchData.razor",
+ "Pages/Index.razor",
+ "Properties/launchSettings.json",
+ "Shared/LoginDisplay.razor",
+ "Shared/MainLayout.razor",
+ "Shared/MainLayout.razor.css",
+ "Shared/NavMenu.razor",
+ "Shared/NavMenu.razor.css",
+ "Shared/RedirectToLogin.razor",
+ "Shared/SurveyPrompt.razor",
+ "wwwroot/appsettings.json",
+ "wwwroot/favicon.png",
+ "wwwroot/icon-192.png",
+ "wwwroot/index.html",
+ "wwwroot/css/app.css",
+ "wwwroot/css/bootstrap/bootstrap.min.css",
+ "wwwroot/css/bootstrap/bootstrap.min.css.map",
+ "wwwroot/css/open-iconic/FONT-LICENSE",
+ "wwwroot/css/open-iconic/ICON-LICENSE",
+ "wwwroot/css/open-iconic/README.md",
+ "wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css",
+ "wwwroot/css/open-iconic/font/fonts/open-iconic.eot",
+ "wwwroot/css/open-iconic/font/fonts/open-iconic.otf",
+ "wwwroot/css/open-iconic/font/fonts/open-iconic.svg",
+ "wwwroot/css/open-iconic/font/fonts/open-iconic.ttf",
+ "wwwroot/css/open-iconic/font/fonts/open-iconic.woff",
+ "wwwroot/sample-data/weather.json"
+ ]
+ },
+ "Hosted": {
+ "Template": "blazorwasm",
+ "Arguments": "new blazorwasm --hosted",
+ "Files": [
+ "Client/_Imports.razor",
+ "Client/App.razor",
+ "Client/Program.cs",
+ "Client/Pages/Counter.razor",
+ "Client/Pages/FetchData.razor",
+ "Client/Pages/Index.razor",
+ "Client/Properties/launchSettings.json",
+ "Client/Shared/MainLayout.razor",
+ "Client/Shared/MainLayout.razor.css",
+ "Client/Shared/NavMenu.razor",
+ "Client/Shared/NavMenu.razor.css",
+ "Client/Shared/SurveyPrompt.razor",
+ "Client/wwwroot/css/app.css",
+ "Client/wwwroot/favicon.png",
+ "Client/wwwroot/icon-192.png",
+ "Client/wwwroot/index.html",
+ "Client/wwwroot/css/bootstrap/bootstrap.min.css",
+ "Client/wwwroot/css/bootstrap/bootstrap.min.css.map",
+ "Client/wwwroot/css/open-iconic/FONT-LICENSE",
+ "Client/wwwroot/css/open-iconic/ICON-LICENSE",
+ "Client/wwwroot/css/open-iconic/README.md",
+ "Client/wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css",
+ "Client/wwwroot/css/open-iconic/font/fonts/open-iconic.eot",
+ "Client/wwwroot/css/open-iconic/font/fonts/open-iconic.otf",
+ "Client/wwwroot/css/open-iconic/font/fonts/open-iconic.svg",
+ "Client/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf",
+ "Client/wwwroot/css/open-iconic/font/fonts/open-iconic.woff",
+ "Server/Controllers/WeatherForecastController.cs",
+ "Server/Pages/Error.cshtml",
+ "Server/Pages/Error.cshtml.cs",
+ "Server/Properties/launchSettings.json",
+ "Server/appsettings.Development.json",
+ "Server/appsettings.json",
+ "Server/Program.cs",
+ "Shared/WeatherForecast.cs"
+ ]
+ },
+ "PWA": {
+ "Template": "blazorwasm",
+ "Arguments": "new blazorwasm --pwa",
+ "Files": [
+ "App.razor",
+ "_Imports.razor",
+ "Program.cs",
+ "Pages/Counter.razor",
+ "Pages/FetchData.razor",
+ "Pages/Index.razor",
+ "Properties/launchSettings.json",
+ "Shared/MainLayout.razor",
+ "Shared/MainLayout.razor.css",
+ "Shared/NavMenu.razor",
+ "Shared/NavMenu.razor.css",
+ "Shared/SurveyPrompt.razor",
+ "wwwroot/favicon.png",
+ "wwwroot/icon-192.png",
+ "wwwroot/icon-512.png",
+ "wwwroot/index.html",
+ "wwwroot/manifest.json",
+ "wwwroot/service-worker.js",
+ "wwwroot/service-worker.published.js",
+ "wwwroot/css/app.css",
+ "wwwroot/css/bootstrap/bootstrap.min.css",
+ "wwwroot/css/bootstrap/bootstrap.min.css.map",
+ "wwwroot/css/open-iconic/FONT-LICENSE",
+ "wwwroot/css/open-iconic/ICON-LICENSE",
+ "wwwroot/css/open-iconic/README.md",
+ "wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css",
+ "wwwroot/css/open-iconic/font/fonts/open-iconic.eot",
+ "wwwroot/css/open-iconic/font/fonts/open-iconic.otf",
+ "wwwroot/css/open-iconic/font/fonts/open-iconic.svg",
+ "wwwroot/css/open-iconic/font/fonts/open-iconic.ttf",
+ "wwwroot/css/open-iconic/font/fonts/open-iconic.woff",
+ "wwwroot/sample-data/weather.json"
+ ]
+ },
+ "PWAHosted": {
+ "Template": "blazorwasm",
+ "Arguments": "new blazorwasm --pwa --hosted",
+ "Files": [
+ "Client/_Imports.razor",
+ "Client/App.razor",
+ "Client/Program.cs",
+ "Client/Pages/Counter.razor",
+ "Client/Pages/FetchData.razor",
+ "Client/Pages/Index.razor",
+ "Client/Properties/launchSettings.json",
+ "Client/Shared/MainLayout.razor",
+ "Client/Shared/MainLayout.razor.css",
+ "Client/Shared/NavMenu.razor",
+ "Client/Shared/NavMenu.razor.css",
+ "Client/Shared/SurveyPrompt.razor",
+ "Client/wwwroot/css/app.css",
+ "Client/wwwroot/favicon.png",
+ "Client/wwwroot/icon-192.png",
+ "Client/wwwroot/icon-512.png",
+ "Client/wwwroot/index.html",
+ "Client/wwwroot/manifest.json",
+ "Client/wwwroot/service-worker.js",
+ "Client/wwwroot/service-worker.published.js",
+ "Client/wwwroot/css/bootstrap/bootstrap.min.css",
+ "Client/wwwroot/css/bootstrap/bootstrap.min.css.map",
+ "Client/wwwroot/css/open-iconic/FONT-LICENSE",
+ "Client/wwwroot/css/open-iconic/ICON-LICENSE",
+ "Client/wwwroot/css/open-iconic/README.md",
+ "Client/wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css",
+ "Client/wwwroot/css/open-iconic/font/fonts/open-iconic.eot",
+ "Client/wwwroot/css/open-iconic/font/fonts/open-iconic.otf",
+ "Client/wwwroot/css/open-iconic/font/fonts/open-iconic.svg",
+ "Client/wwwroot/css/open-iconic/font/fonts/open-iconic.ttf",
+ "Client/wwwroot/css/open-iconic/font/fonts/open-iconic.woff",
+ "Server/Controllers/WeatherForecastController.cs",
+ "Server/Pages/Error.cshtml",
+ "Server/Pages/Error.cshtml.cs",
+ "Server/Properties/launchSettings.json",
+ "Server/appsettings.Development.json",
+ "Server/appsettings.json",
+ "Server/Program.cs",
+ "Shared/WeatherForecast.cs"
+ ]
+ },
+ "ExcludeLaunchSettings": {
+ "Template": "blazorwasm",
+ "Arguments": "new blazorwasm --exclude-launch-settings",
+ "Files": [
+ "App.razor",
+ "_Imports.razor",
+ "Program.cs",
+ "Pages/Counter.razor",
+ "Pages/FetchData.razor",
+ "Pages/Index.razor",
+ "Shared/MainLayout.razor",
+ "Shared/MainLayout.razor.css",
+ "Shared/NavMenu.razor",
+ "Shared/NavMenu.razor.css",
+ "Shared/SurveyPrompt.razor",
+ "wwwroot/favicon.png",
+ "wwwroot/icon-192.png",
+ "wwwroot/index.html",
+ "wwwroot/css/app.css",
+ "wwwroot/css/bootstrap/bootstrap.min.css",
+ "wwwroot/css/bootstrap/bootstrap.min.css.map",
+ "wwwroot/css/open-iconic/FONT-LICENSE",
+ "wwwroot/css/open-iconic/ICON-LICENSE",
+ "wwwroot/css/open-iconic/README.md",
+ "wwwroot/css/open-iconic/font/css/open-iconic-bootstrap.min.css",
+ "wwwroot/css/open-iconic/font/fonts/open-iconic.eot",
+ "wwwroot/css/open-iconic/font/fonts/open-iconic.otf",
+ "wwwroot/css/open-iconic/font/fonts/open-iconic.svg",
+ "wwwroot/css/open-iconic/font/fonts/open-iconic.ttf",
+ "wwwroot/css/open-iconic/font/fonts/open-iconic.woff",
+ "wwwroot/sample-data/weather.json"
+ ]
+ }
+ },
+ "blazorwasm-empty": {
+ "None": {
+ "Template": "blazorwasm-empty",
+ "Arguments": "new blazorwasm-empty",
+ "Files": [
+ "Pages/Index.razor",
+ "Properties/launchSettings.json",
+ "wwwroot/css/app.css",
+ "wwwroot/index.html",
+ "_Imports.razor",
+ "App.razor",
+ "MainLayout.razor",
+ "Program.cs"
+ ]
+ },
+ "Hosted": {
+ "Template": "blazorwasm-empty",
+ "Arguments": "new blazorwasm-empty --hosted",
+ "Files": [
+ "Client/_Imports.razor",
+ "Client/App.razor",
+ "Client/MainLayout.razor",
+ "Client/Program.cs",
+ "Client/Pages/Index.razor",
+ "Client/Properties/launchSettings.json",
+ "Client/wwwroot/index.html",
+ "Client/wwwroot/css/app.css",
+ "Server/Properties/launchSettings.json",
+ "Server/appsettings.Development.json",
+ "Server/appsettings.json",
+ "Server/Program.cs",
+ "Shared/SharedClass.cs"
+ ]
+ },
+ "PWA": {
+ "Template": "blazorwasm-empty",
+ "Arguments": "new blazorwasm-empty --pwa",
+ "Files": [
+ "Pages/Index.razor",
+ "Properties/launchSettings.json",
+ "wwwroot/css/app.css",
+ "wwwroot/icon-512.png",
+ "wwwroot/index.html",
+ "wwwroot/manifest.json",
+ "wwwroot/service-worker.js",
+ "wwwroot/service-worker.published.js",
+ "_Imports.razor",
+ "App.razor",
+ "MainLayout.razor",
+ "Program.cs"
+ ]
+ },
+ "NoHttps": {
+ "Template": "blazorwasm-empty",
+ "Arguments": "new blazorwasm-empty --no-https",
+ "Files": [
+ "Pages/Index.razor",
+ "Properties/launchSettings.json",
+ "wwwroot/css/app.css",
+ "wwwroot/index.html",
+ "_Imports.razor",
+ "App.razor",
+ "MainLayout.razor",
+ "Program.cs"
+ ]
+ },
+ "PWAHosted": {
+ "Template": "blazorwasm-empty",
+ "Arguments": "new blazorwasm-empty --pwa --hosted",
+ "Files": [
+ "Client/_Imports.razor",
+ "Client/App.razor",
+ "Client/MainLayout.razor",
+ "Client/Program.cs",
+ "Client/Pages/Index.razor",
+ "Client/Properties/launchSettings.json",
+ "Client/wwwroot/icon-512.png",
+ "Client/wwwroot/index.html",
+ "Client/wwwroot/manifest.json",
+ "Client/wwwroot/service-worker.js",
+ "Client/wwwroot/service-worker.published.js",
+ "Client/wwwroot/css/app.css",
+ "Server/Properties/launchSettings.json",
+ "Server/appsettings.Development.json",
+ "Server/appsettings.json",
+ "Server/Program.cs",
+ "Shared/SharedClass.cs"
+ ]
+ },
+ "ExcludeLaunchSettings": {
+ "Template": "blazorwasm-empty",
+ "Arguments": "new blazorwasm-empty --exclude-launch-settings",
+ "Files": [
+ "Pages/Index.razor",
+ "wwwroot/css/app.css",
+ "wwwroot/index.html",
+ "_Imports.razor",
+ "App.razor",
+ "MainLayout.razor",
+ "Program.cs"
+ ]
+ },
+ "ExcludeLaunchSettingsHosted": {
+ "Template": "blazorwasm-empty",
+ "Arguments": "new blazorwasm-empty --hosted --exclude-launch-settings",
+ "Files": [
+ "Client/_Imports.razor",
+ "Client/App.razor",
+ "Client/MainLayout.razor",
+ "Client/Program.cs",
+ "Client/Pages/Index.razor",
+ "Client/wwwroot/index.html",
+ "Client/wwwroot/css/app.css",
+ "Server/appsettings.Development.json",
+ "Server/appsettings.json",
+ "Server/Program.cs",
+ "Shared/SharedClass.cs"
+ ]
+ }
}
}
diff --git a/src/ProjectTemplates/test/Templates.Tests/yarn.lock b/src/ProjectTemplates/test/Templates.Tests/yarn.lock
index 25dae07962..8a1d841904 100644
--- a/src/ProjectTemplates/test/Templates.Tests/yarn.lock
+++ b/src/ProjectTemplates/test/Templates.Tests/yarn.lock
@@ -29,17 +29,22 @@
resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812"
integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==
+"@types/json-buffer@~3.0.0":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/@types/json-buffer/-/json-buffer-3.0.0.tgz#85c1ff0f0948fc159810d4b5be35bf8c20875f64"
+ integrity sha512-3YP80IxxFJB4b5tYC2SUPwkg0XQLiu0nWvhRgEatgjf+29IcWO9X1k8xRv5DGssJ/lCrjYTjQPcobJr2yWIVuQ==
+
"@types/keyv@*":
- version "3.1.3"
- resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.3.tgz#1c9aae32872ec1f20dcdaee89a9f3ba88f465e41"
- integrity sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg==
+ version "3.1.4"
+ resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6"
+ integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==
dependencies:
"@types/node" "*"
"@types/node@*":
- version "17.0.21"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.21.tgz#864b987c0c68d07b4345845c3e63b75edd143644"
- integrity sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ==
+ version "18.0.0"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.0.tgz#67c7b724e1bcdd7a8821ce0d5ee184d3b4dd525a"
+ integrity sha512-cHlGmko4gWLVI27cGJntjs/Sj8th9aYwplmZFwmmgYQQvL5NUsgVJG7OddLvNfLqYS31KFN0s3qlaD9qCaxACA==
"@types/responselike@*", "@types/responselike@^1.0.0":
version "1.0.0"
@@ -96,7 +101,7 @@ cacheable-request@^7.0.2:
clone-response@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b"
- integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=
+ integrity sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==
dependencies:
mimic-response "^1.0.0"
@@ -105,6 +110,14 @@ commander@^7.2.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
+compress-brotli@^1.3.8:
+ version "1.3.8"
+ resolved "https://registry.yarnpkg.com/compress-brotli/-/compress-brotli-1.3.8.tgz#0c0a60c97a989145314ec381e84e26682e7b38db"
+ integrity sha512-lVcQsjhxhIXsuupfy9fmZUFtAIdBmXA7EGY6GBdgZ++qkM9zG4YFT8iU7FoBxzryNDMOpD1HIFHUSX4D87oqhQ==
+ dependencies:
+ "@types/json-buffer" "~3.0.0"
+ json-buffer "~3.0.1"
+
cross-spawn@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
@@ -169,9 +182,9 @@ get-stream@^5.1.0:
pump "^3.0.0"
got@^11.8.2:
- version "11.8.3"
- resolved "https://registry.yarnpkg.com/got/-/got-11.8.3.tgz#f496c8fdda5d729a90b4905d2b07dbd148170770"
- integrity sha512-7gtQ5KiPh1RtGS9/Jbv1ofDpBFuq42gyfEib+ejaRBJuj/3tQFeR5+gw57e4ipaU8c/rCjvX6fkQz2lyDlGAOg==
+ version "11.8.5"
+ resolved "https://registry.yarnpkg.com/got/-/got-11.8.5.tgz#ce77d045136de56e8f024bebb82ea349bc730046"
+ integrity sha512-o0Je4NvQObAuZPHLFoRSkdG2lTgtcynqymzg2Vupdx6PorhaT5MCbIyXG6d4D94kk8ZG57QeosgdiqfJWhEhlQ==
dependencies:
"@sindresorhus/is" "^4.0.0"
"@szmarczak/http-timer" "^4.0.5"
@@ -223,7 +236,7 @@ isexe@^2.0.0:
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
-json-buffer@3.0.1:
+json-buffer@3.0.1, json-buffer@~3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==
@@ -238,10 +251,11 @@ jsonfile@^6.0.1:
graceful-fs "^4.1.6"
keyv@^4.0.0:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.1.1.tgz#02c538bfdbd2a9308cc932d4096f05ae42bfa06a"
- integrity sha512-tGv1yP6snQVDSM4X6yxrv2zzq/EvpW+oYiUz6aueW1u9CtS8RzUQYxxmFwgZlO2jSgCxQbchhxaqXXp2hnKGpQ==
+ version "4.3.2"
+ resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.3.2.tgz#e839df676a0c7ee594c8835e7c1c83742558e5c2"
+ integrity sha512-kn8WmodVBe12lmHpA6W8OY7SNh6wVR+Z+wZESF4iF5FCazaVXGWOtnbnvX0tMQ1bO+/TmOD9LziuYMvrIIs0xw==
dependencies:
+ compress-brotli "^1.3.8"
json-buffer "3.0.1"
lodash.mapvalues@^4.6.0:
@@ -292,7 +306,7 @@ normalize-url@^6.0.1:
once@^1.3.1, once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
- integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
+ integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
dependencies:
wrappy "1"
@@ -425,7 +439,7 @@ which@^2.0.1, which@^2.0.2:
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
- integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
+ integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
yauzl@^2.10.0:
version "2.10.0"
diff --git a/src/Security/Authentication/Core/src/AuthenticationConfigurationProviderExtensions.cs b/src/Security/Authentication/Core/src/AuthenticationConfigurationProviderExtensions.cs
index ccca635be8..2002ff02f3 100644
--- a/src/Security/Authentication/Core/src/AuthenticationConfigurationProviderExtensions.cs
+++ b/src/Security/Authentication/Core/src/AuthenticationConfigurationProviderExtensions.cs
@@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.Authentication;
/// </summary>
public static class AuthenticationConfigurationProviderExtensions
{
- private const string AuthenticationSchemesKey = "Authentication:Schemes";
+ private const string AuthenticationSchemesKey = "Schemes";
/// <summary>
/// Returns the specified <see cref="IConfiguration"/> object.
diff --git a/src/Security/Authentication/Negotiate/src/Internal/INegotiateStateFactory.cs b/src/Security/Authentication/Negotiate/src/Internal/INegotiateStateFactory.cs
index 997ca0b06e..1907e9098c 100644
--- a/src/Security/Authentication/Negotiate/src/Internal/INegotiateStateFactory.cs
+++ b/src/Security/Authentication/Negotiate/src/Internal/INegotiateStateFactory.cs
@@ -1,13 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System.Diagnostics.CodeAnalysis;
-
namespace Microsoft.AspNetCore.Authentication.Negotiate;
// For testing
internal interface INegotiateStateFactory
{
- [RequiresUnreferencedCode("Negotiate authentication uses types that cannot be statically analyzed.")]
INegotiateState CreateInstance();
}
diff --git a/src/Security/Authentication/Negotiate/src/Internal/NegotiateState.cs b/src/Security/Authentication/Negotiate/src/Internal/NegotiateState.cs
new file mode 100644
index 0000000000..4d3e74e160
--- /dev/null
+++ b/src/Security/Authentication/Negotiate/src/Internal/NegotiateState.cs
@@ -0,0 +1,88 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Net.Security;
+using System.Security.Authentication;
+using System.Security.Claims;
+using System.Security.Principal;
+
+namespace Microsoft.AspNetCore.Authentication.Negotiate;
+
+internal sealed class NegotiateState : INegotiateState
+{
+ private static readonly NegotiateAuthenticationServerOptions _serverOptions = new();
+ private readonly NegotiateAuthentication _instance;
+
+ public NegotiateState()
+ {
+ _instance = new NegotiateAuthentication(_serverOptions);
+ }
+
+ public string? GetOutgoingBlob(string incomingBlob, out BlobErrorType status, out Exception? error)
+ {
+ var outgoingBlob = _instance.GetOutgoingBlob(incomingBlob, out var authStatus);
+
+ if (authStatus == NegotiateAuthenticationStatusCode.Completed ||
+ authStatus == NegotiateAuthenticationStatusCode.ContinueNeeded)
+ {
+ status = BlobErrorType.None;
+ error = null;
+ }
+ else
+ {
+ error = new AuthenticationException(authStatus.ToString());
+ if (IsCredentialError(authStatus))
+ {
+ status = BlobErrorType.CredentialError;
+ }
+ else if (IsClientError(authStatus))
+ {
+ status = BlobErrorType.ClientError;
+ }
+ else
+ {
+ status = BlobErrorType.Other;
+ }
+ }
+
+ return outgoingBlob;
+ }
+
+ public bool IsCompleted
+ {
+ get => _instance.IsAuthenticated;
+ }
+
+ public string Protocol
+ {
+ get => _instance.Package;
+ }
+
+ public IIdentity GetIdentity()
+ {
+ var remoteIdentity = _instance.RemoteIdentity;
+ return remoteIdentity is ClaimsIdentity claimsIdentity ? claimsIdentity.Clone() : remoteIdentity;
+ }
+
+ public void Dispose()
+ {
+ _instance.Dispose();
+ }
+
+ private static bool IsCredentialError(NegotiateAuthenticationStatusCode error)
+ {
+ return error == NegotiateAuthenticationStatusCode.UnknownCredentials ||
+ error == NegotiateAuthenticationStatusCode.CredentialsExpired ||
+ error == NegotiateAuthenticationStatusCode.BadBinding;
+ }
+
+ private static bool IsClientError(NegotiateAuthenticationStatusCode error)
+ {
+ return error == NegotiateAuthenticationStatusCode.InvalidToken ||
+ error == NegotiateAuthenticationStatusCode.QopNotSupported ||
+ error == NegotiateAuthenticationStatusCode.UnknownCredentials ||
+ error == NegotiateAuthenticationStatusCode.MessageAltered ||
+ error == NegotiateAuthenticationStatusCode.OutOfSequence ||
+ error == NegotiateAuthenticationStatusCode.InvalidCredentials;
+ }
+}
diff --git a/src/Security/Authentication/Negotiate/src/Internal/NegotiateStateFactory.cs b/src/Security/Authentication/Negotiate/src/Internal/NegotiateStateFactory.cs
new file mode 100644
index 0000000000..07bb5ff796
--- /dev/null
+++ b/src/Security/Authentication/Negotiate/src/Internal/NegotiateStateFactory.cs
@@ -0,0 +1,12 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.AspNetCore.Authentication.Negotiate;
+
+internal sealed class NegotiateStateFactory : INegotiateStateFactory
+{
+ public INegotiateState CreateInstance()
+ {
+ return new NegotiateState();
+ }
+}
diff --git a/src/Security/Authentication/Negotiate/src/Internal/ReflectedNegotiateState.cs b/src/Security/Authentication/Negotiate/src/Internal/ReflectedNegotiateState.cs
deleted file mode 100644
index 2ed3065850..0000000000
--- a/src/Security/Authentication/Negotiate/src/Internal/ReflectedNegotiateState.cs
+++ /dev/null
@@ -1,210 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-#pragma warning disable CA1810 // Initialize all static fields inline.
-
-using System.Diagnostics.CodeAnalysis;
-using System.Linq;
-using System.Net;
-using System.Reflection;
-using System.Runtime.ExceptionServices;
-using System.Security.Authentication;
-using System.Security.Principal;
-
-namespace Microsoft.AspNetCore.Authentication.Negotiate;
-
-[RequiresUnreferencedCode("Negotiate authentication uses types that cannot be statically analyzed.")]
-internal sealed class ReflectedNegotiateState : INegotiateState
-{
- // https://www.gnu.org/software/gss/reference/gss.pdf
- private const uint GSS_S_NO_CRED = 7 << 16;
-
- private static readonly ConstructorInfo _constructor;
- private static readonly MethodInfo _getOutgoingBlob;
- private static readonly MethodInfo _isCompleted;
- private static readonly MethodInfo _protocol;
- private static readonly MethodInfo _getIdentity;
- private static readonly MethodInfo _closeContext;
- private static readonly FieldInfo _statusCode;
- private static readonly FieldInfo _statusException;
- private static readonly MethodInfo _getException;
- private static readonly FieldInfo? _gssMinorStatus;
- private static readonly Type? _gssExceptionType;
-
- private readonly object _instance;
-
- static ReflectedNegotiateState()
- {
- var secAssembly = typeof(AuthenticationException).Assembly;
- var ntAuthType = secAssembly.GetType("System.Net.NTAuthentication", throwOnError: true)!;
- _constructor = ntAuthType.GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance).First();
- _getOutgoingBlob = ntAuthType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).Where(info =>
- info.Name.Equals("GetOutgoingBlob") && info.GetParameters().Length == 3).Single();
- _isCompleted = ntAuthType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).Where(info =>
- info.Name.Equals("get_IsCompleted")).Single();
- _protocol = ntAuthType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).Where(info =>
- info.Name.Equals("get_ProtocolName")).Single();
- _closeContext = ntAuthType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).Where(info =>
- info.Name.Equals("CloseContext")).Single();
-
- var securityStatusType = secAssembly.GetType("System.Net.SecurityStatusPal", throwOnError: true)!;
- _statusCode = securityStatusType.GetField("ErrorCode")!;
- _statusException = securityStatusType.GetField("Exception")!;
-
- if (!OperatingSystem.IsWindows())
- {
- var interopType = secAssembly.GetType("Interop", throwOnError: true)!;
- var netNativeType = interopType.GetNestedType("NetSecurityNative", BindingFlags.NonPublic | BindingFlags.Static)!;
- _gssExceptionType = netNativeType.GetNestedType("GssApiException", BindingFlags.NonPublic)!;
- _gssMinorStatus = _gssExceptionType.GetField("_minorStatus", BindingFlags.Instance | BindingFlags.NonPublic)!;
- }
-
- var negoStreamPalType = secAssembly.GetType("System.Net.Security.NegotiateStreamPal", throwOnError: true)!;
- _getIdentity = negoStreamPalType.GetMethods(BindingFlags.NonPublic | BindingFlags.Static).Where(info =>
- info.Name.Equals("GetIdentity")).Single();
- _getException = negoStreamPalType.GetMethods(BindingFlags.NonPublic | BindingFlags.Static).Where(info =>
- info.Name.Equals("CreateExceptionFromError")).Single();
- }
-
- public ReflectedNegotiateState()
- {
- // internal NTAuthentication(bool isServer, string package, NetworkCredential credential, string spn, ContextFlagsPal requestedContextFlags, ChannelBinding channelBinding)
- var credential = CredentialCache.DefaultCredentials;
- _instance = _constructor.Invoke(new object?[] { true, "Negotiate", credential, null, 0, null });
- }
-
- // Copied rather than reflected to remove the IsCompleted -> CloseContext check.
- // The client doesn't need the context once auth is complete, but the server does.
- // I'm not sure why it auto-closes for the client given that the client closes it just a few lines later.
- // https://github.com/dotnet/corefx/blob/a3ab91e10045bb298f48c1d1f9bd5b0782a8ac46/src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.NtAuth.cs#L134
- public string? GetOutgoingBlob(string incomingBlob, out BlobErrorType status, out Exception? error)
- {
- byte[]? decodedIncomingBlob = null;
- if (incomingBlob != null && incomingBlob.Length > 0)
- {
- decodedIncomingBlob = Convert.FromBase64String(incomingBlob);
- }
-
- byte[] decodedOutgoingBlob = GetOutgoingBlob(decodedIncomingBlob, out status, out error);
-
- string? outgoingBlob = null;
- if (decodedOutgoingBlob != null && decodedOutgoingBlob.Length > 0)
- {
- outgoingBlob = Convert.ToBase64String(decodedOutgoingBlob);
- }
-
- return outgoingBlob;
- }
-
- private byte[] GetOutgoingBlob(byte[]? incomingBlob, out BlobErrorType status, out Exception? error)
- {
- try
- {
- // byte[] GetOutgoingBlob(byte[] incomingBlob, bool throwOnError, out SecurityStatusPal statusCode)
- var parameters = new object?[] { incomingBlob, false, null };
- var blob = (byte[])_getOutgoingBlob.Invoke(_instance, parameters)!;
-
- var securityStatus = parameters[2];
- // TODO: Update after corefx changes
- error = (Exception?)(_statusException.GetValue(securityStatus)
- ?? _getException.Invoke(null, new[] { securityStatus }));
- var errorCode = (SecurityStatusPalErrorCode)_statusCode.GetValue(securityStatus)!;
-
- // TODO: Remove after corefx changes
- // The linux implementation always uses InternalError;
- if (errorCode == SecurityStatusPalErrorCode.InternalError
- && !OperatingSystem.IsWindows()
- && _gssExceptionType!.IsInstanceOfType(error))
- {
- var majorStatus = (uint)error.HResult;
- var minorStatus = (uint)_gssMinorStatus!.GetValue(error)!;
-
- // Remap specific errors
- if (majorStatus == GSS_S_NO_CRED && minorStatus == 0)
- {
- errorCode = SecurityStatusPalErrorCode.UnknownCredentials;
- }
-
- error = new Exception($"An authentication exception occurred (0x{majorStatus:X}/0x{minorStatus:X}).", error);
- }
-
- if (errorCode == SecurityStatusPalErrorCode.OK
- || errorCode == SecurityStatusPalErrorCode.ContinueNeeded
- || errorCode == SecurityStatusPalErrorCode.CompleteNeeded)
- {
- status = BlobErrorType.None;
- }
- else if (IsCredentialError(errorCode))
- {
- status = BlobErrorType.CredentialError;
- }
- else if (IsClientError(errorCode))
- {
- status = BlobErrorType.ClientError;
- }
- else
- {
- status = BlobErrorType.Other;
- }
-
- return blob;
- }
- catch (TargetInvocationException tex)
- {
- // Unwrap
- ExceptionDispatchInfo.Capture(tex.InnerException!).Throw();
- throw;
- }
- }
-
- public bool IsCompleted
- {
- get => (bool)_isCompleted.Invoke(_instance, Array.Empty<object>())!;
- }
-
- public string Protocol
- {
- get => (string)_protocol.Invoke(_instance, Array.Empty<object>())!;
- }
-
- public IIdentity GetIdentity()
- {
- return (IIdentity)_getIdentity.Invoke(obj: null, parameters: new object[] { _instance })!;
- }
-
- public void Dispose()
- {
- _closeContext.Invoke(_instance, Array.Empty<object>());
- }
-
- private static bool IsCredentialError(SecurityStatusPalErrorCode error)
- {
- return error == SecurityStatusPalErrorCode.LogonDenied ||
- error == SecurityStatusPalErrorCode.UnknownCredentials ||
- error == SecurityStatusPalErrorCode.NoImpersonation ||
- error == SecurityStatusPalErrorCode.NoAuthenticatingAuthority ||
- error == SecurityStatusPalErrorCode.UntrustedRoot ||
- error == SecurityStatusPalErrorCode.CertExpired ||
- error == SecurityStatusPalErrorCode.SmartcardLogonRequired ||
- error == SecurityStatusPalErrorCode.BadBinding;
- }
-
- private static bool IsClientError(SecurityStatusPalErrorCode error)
- {
- return error == SecurityStatusPalErrorCode.InvalidToken ||
- error == SecurityStatusPalErrorCode.CannotPack ||
- error == SecurityStatusPalErrorCode.QopNotSupported ||
- error == SecurityStatusPalErrorCode.NoCredentials ||
- error == SecurityStatusPalErrorCode.MessageAltered ||
- error == SecurityStatusPalErrorCode.OutOfSequence ||
- error == SecurityStatusPalErrorCode.IncompleteMessage ||
- error == SecurityStatusPalErrorCode.IncompleteCredentials ||
- error == SecurityStatusPalErrorCode.WrongPrincipal ||
- error == SecurityStatusPalErrorCode.TimeSkew ||
- error == SecurityStatusPalErrorCode.IllegalMessage ||
- error == SecurityStatusPalErrorCode.CertUnknown ||
- error == SecurityStatusPalErrorCode.AlgorithmMismatch ||
- error == SecurityStatusPalErrorCode.SecurityQosFailed ||
- error == SecurityStatusPalErrorCode.UnsupportedPreauth;
- }
-}
diff --git a/src/Security/Authentication/Negotiate/src/Internal/ReflectedNegotiateStateFactory.cs b/src/Security/Authentication/Negotiate/src/Internal/ReflectedNegotiateStateFactory.cs
deleted file mode 100644
index 4a418b07f9..0000000000
--- a/src/Security/Authentication/Negotiate/src/Internal/ReflectedNegotiateStateFactory.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-using System.Diagnostics.CodeAnalysis;
-
-namespace Microsoft.AspNetCore.Authentication.Negotiate;
-
-internal sealed class ReflectedNegotiateStateFactory : INegotiateStateFactory
-{
- [RequiresUnreferencedCode("Negotiate authentication uses types that cannot be statically analyzed.")]
- public INegotiateState CreateInstance()
- {
- return new ReflectedNegotiateState();
- }
-}
diff --git a/src/Security/Authentication/Negotiate/src/Internal/SecurityStatusPalErrorCode.cs b/src/Security/Authentication/Negotiate/src/Internal/SecurityStatusPalErrorCode.cs
deleted file mode 100644
index 4abac7f4e1..0000000000
--- a/src/Security/Authentication/Negotiate/src/Internal/SecurityStatusPalErrorCode.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-namespace Microsoft.AspNetCore.Authentication.Negotiate;
-
-internal enum SecurityStatusPalErrorCode
-{
- NotSet = 0,
- OK,
- ContinueNeeded,
- CompleteNeeded,
- CompAndContinue,
- ContextExpired,
- CredentialsNeeded,
- Renegotiate,
-
- // Errors
- OutOfMemory,
- InvalidHandle,
- Unsupported,
- TargetUnknown,
- InternalError,
- PackageNotFound,
- NotOwner,
- CannotInstall,
- InvalidToken,
- CannotPack,
- QopNotSupported,
- NoImpersonation,
- LogonDenied,
- UnknownCredentials,
- NoCredentials,
- MessageAltered,
- OutOfSequence,
- NoAuthenticatingAuthority,
- IncompleteMessage,
- IncompleteCredentials,
- BufferNotEnough,
- WrongPrincipal,
- TimeSkew,
- UntrustedRoot,
- IllegalMessage,
- CertUnknown,
- CertExpired,
- DecryptFailure,
- AlgorithmMismatch,
- SecurityQosFailed,
- SmartcardLogonRequired,
- UnsupportedPreauth,
- BadBinding,
- DowngradeDetected,
- ApplicationProtocolMismatch
-}
diff --git a/src/Security/Authentication/Negotiate/src/NegotiateExtensions.cs b/src/Security/Authentication/Negotiate/src/NegotiateExtensions.cs
index a2d03767ec..c71824f54f 100644
--- a/src/Security/Authentication/Negotiate/src/NegotiateExtensions.cs
+++ b/src/Security/Authentication/Negotiate/src/NegotiateExtensions.cs
@@ -1,7 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Negotiate;
using Microsoft.AspNetCore.Authentication.Negotiate.Internal;
@@ -25,7 +24,6 @@ public static class NegotiateExtensions
/// </summary>
/// <param name="builder">The <see cref="AuthenticationBuilder"/>.</param>
/// <returns>The original builder.</returns>
- [RequiresUnreferencedCode("Negotiate authentication uses types that cannot be statically analyzed.")]
public static AuthenticationBuilder AddNegotiate(this AuthenticationBuilder builder)
=> builder.AddNegotiate(NegotiateDefaults.AuthenticationScheme, _ => { });
@@ -39,7 +37,6 @@ public static class NegotiateExtensions
/// <param name="builder">The <see cref="AuthenticationBuilder"/>.</param>
/// <param name="configureOptions">Allows for configuring the authentication handler.</param>
/// <returns>The original builder.</returns>
- [RequiresUnreferencedCode("Negotiate authentication uses types that cannot be statically analyzed.")]
public static AuthenticationBuilder AddNegotiate(this AuthenticationBuilder builder, Action<NegotiateOptions> configureOptions)
=> builder.AddNegotiate(NegotiateDefaults.AuthenticationScheme, configureOptions);
@@ -54,7 +51,6 @@ public static class NegotiateExtensions
/// <param name="authenticationScheme">The scheme name used to identify the authentication handler internally.</param>
/// <param name="configureOptions">Allows for configuring the authentication handler.</param>
/// <returns>The original builder.</returns>
- [RequiresUnreferencedCode("Negotiate authentication uses types that cannot be statically analyzed.")]
public static AuthenticationBuilder AddNegotiate(this AuthenticationBuilder builder, string authenticationScheme, Action<NegotiateOptions> configureOptions)
=> builder.AddNegotiate(authenticationScheme, displayName: null, configureOptions: configureOptions);
@@ -70,7 +66,6 @@ public static class NegotiateExtensions
/// <param name="displayName">The name displayed to users when selecting an authentication handler. The default is null to prevent this from displaying.</param>
/// <param name="configureOptions">Allows for configuring the authentication handler.</param>
/// <returns>The original builder.</returns>
- [RequiresUnreferencedCode("Negotiate authentication uses types that cannot be statically analyzed.")]
public static AuthenticationBuilder AddNegotiate(this AuthenticationBuilder builder, string authenticationScheme, string? displayName, Action<NegotiateOptions> configureOptions)
{
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<NegotiateOptions>, PostConfigureNegotiateOptions>());
diff --git a/src/Security/Authentication/Negotiate/src/NegotiateHandler.cs b/src/Security/Authentication/Negotiate/src/NegotiateHandler.cs
index f12b1ae7b1..12c7395f66 100644
--- a/src/Security/Authentication/Negotiate/src/NegotiateHandler.cs
+++ b/src/Security/Authentication/Negotiate/src/NegotiateHandler.cs
@@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
@@ -19,7 +18,6 @@ namespace Microsoft.AspNetCore.Authentication.Negotiate;
/// <summary>
/// Authenticates requests using Negotiate, Kerberos, or NTLM.
/// </summary>
-[RequiresUnreferencedCode("Negotiate authentication uses types that cannot be statically analyzed.")]
public class NegotiateHandler : AuthenticationHandler<NegotiateOptions>, IAuthenticationRequestHandler
{
private const string AuthPersistenceKey = nameof(AuthPersistence);
diff --git a/src/Security/Authentication/Negotiate/src/NegotiateOptions.cs b/src/Security/Authentication/Negotiate/src/NegotiateOptions.cs
index bcf86af754..4a92b6b417 100644
--- a/src/Security/Authentication/Negotiate/src/NegotiateOptions.cs
+++ b/src/Security/Authentication/Negotiate/src/NegotiateOptions.cs
@@ -76,5 +76,5 @@ public class NegotiateOptions : AuthenticationSchemeOptions
internal bool DeferToServer { get; set; }
// For testing
- internal INegotiateStateFactory StateFactory { get; set; } = new ReflectedNegotiateStateFactory();
+ internal INegotiateStateFactory StateFactory { get; set; } = new NegotiateStateFactory();
}
diff --git a/src/Security/Authentication/test/AuthenticationMiddlewareTests.cs b/src/Security/Authentication/test/AuthenticationMiddlewareTests.cs
index 422a4e633f..9c11dc1e35 100644
--- a/src/Security/Authentication/test/AuthenticationMiddlewareTests.cs
+++ b/src/Security/Authentication/test/AuthenticationMiddlewareTests.cs
@@ -11,6 +11,7 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
using Moq;
namespace Microsoft.AspNetCore.Authentication;
@@ -156,6 +157,11 @@ public class AuthenticationMiddlewareTests
public async Task WebApplicationBuilder_RegistersAuthenticationMiddlewares()
{
var builder = WebApplication.CreateBuilder();
+ builder.Configuration.AddInMemoryCollection(new[]
+ {
+ new KeyValuePair<string, string>("Authentication:Schemes:Bearer:ClaimsIssuer", "SomeIssuer"),
+ new KeyValuePair<string, string>("Authentication:Schemes:Bearer:Audiences:0", "https://localhost:5001")
+ });
builder.Authentication.AddJwtBearer();
await using var app = builder.Build();
@@ -169,6 +175,10 @@ public class AuthenticationMiddlewareTests
await app.StartAsync();
Assert.True(app.Properties.ContainsKey("__AuthenticationMiddlewareSet"));
+
+ var options = app.Services.GetService<IOptionsMonitor<JwtBearerOptions>>().Get(JwtBearerDefaults.AuthenticationScheme);
+ Assert.Equal(new[] { "SomeIssuer" }, options.TokenValidationParameters.ValidIssuers);
+ Assert.Equal(new[] { "https://localhost:5001" }, options.TokenValidationParameters.ValidAudiences);
}
private HttpContext GetHttpContext(
diff --git a/src/Servers/HttpSys/test/FunctionalTests/Http2Tests.cs b/src/Servers/HttpSys/test/FunctionalTests/Http2Tests.cs
index 101224c7e4..a742f8c3d6 100644
--- a/src/Servers/HttpSys/test/FunctionalTests/Http2Tests.cs
+++ b/src/Servers/HttpSys/test/FunctionalTests/Http2Tests.cs
@@ -837,7 +837,6 @@ public class Http2Tests : LoggedTest
}
[ConditionalFact]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/29126")]
[MinimumOSVersion(OperatingSystems.Windows, VersionForReset)]
public async Task Reset_BeforeRequestBody_Resets()
{
@@ -884,7 +883,6 @@ public class Http2Tests : LoggedTest
}
[ConditionalFact]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/29126")]
[MinimumOSVersion(OperatingSystems.Windows, VersionForReset)]
public async Task Reset_DurringRequestBody_Resets()
{
diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLibTests/CommonLibTests.vcxproj b/src/Servers/IIS/AspNetCoreModuleV2/CommonLibTests/CommonLibTests.vcxproj
index b5fdfb8064..7d397c7662 100644
--- a/src/Servers/IIS/AspNetCoreModuleV2/CommonLibTests/CommonLibTests.vcxproj
+++ b/src/Servers/IIS/AspNetCoreModuleV2/CommonLibTests/CommonLibTests.vcxproj
@@ -40,8 +40,7 @@
<PropertyGroup Label="Globals">
<ProjectGuid>{1eac8125-1765-4e2d-8cbe-56dc98a1c8c1}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
- <WindowsTargetPlatformVersion Condition=" '$(WindowsTargetPlatformVersion)' == '' ">10.0.18362.0</WindowsTargetPlatformVersion>
- <PlatformToolsetVersion Condition=" '$(PlatformToolsetVersion)' == '' ">v142</PlatformToolsetVersion>
+ <PlatformToolsetVersion Condition=" '$(PlatformToolsetVersion)' == '' ">v143</PlatformToolsetVersion>
<ConfigurationType>Application</ConfigurationType>
<PlatformToolset>$(PlatformToolsetVersion)</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
@@ -49,9 +48,6 @@
<DisableArcadeTestFramework>true</DisableArcadeTestFramework>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
- <PropertyGroup Label="Configuration">
- <VCToolsVersion>14.29.30133</VCToolsVersion>
- </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings" />
<ImportGroup Label="Shared" />
@@ -140,4 +136,4 @@
</ItemDefinitionGroup>
-</Project> \ No newline at end of file
+</Project>
diff --git a/src/Servers/IIS/build/Build.Common.Settings b/src/Servers/IIS/build/Build.Common.Settings
index b25ff225e7..d0d5db2bf7 100644
--- a/src/Servers/IIS/build/Build.Common.Settings
+++ b/src/Servers/IIS/build/Build.Common.Settings
@@ -15,12 +15,8 @@
<SolutionDir Condition="'$(SolutionDir)' == '' Or '$(SolutionDir)' == '*Undefined*'">$(MSBuildThisFileDirectory)..\</SolutionDir>
<Configuration Condition="'$(Configuration)'==''">Debug</Configuration>
<VCProjectVersion>15.0</VCProjectVersion>
- <VCToolsVersion>14.29.30133</VCToolsVersion>
<Keyword>Win32Proj</Keyword>
<Platform Condition="'$(Platform)' == ''">x64</Platform>
- <PlatformToolsetVersion>v142</PlatformToolsetVersion>
- <PlatformToolset>$(PlatformToolsetVersion)</PlatformToolset>
- <WindowsTargetPlatformVersion Condition=" '$(WindowsTargetPlatformVersion)' == '' ">10.0.18362.0</WindowsTargetPlatformVersion>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
diff --git a/src/Servers/Kestrel/Transport.Quic/test/QuicConnectionListenerTests.cs b/src/Servers/Kestrel/Transport.Quic/test/QuicConnectionListenerTests.cs
index b51b5ba68b..0080b1d8d3 100644
--- a/src/Servers/Kestrel/Transport.Quic/test/QuicConnectionListenerTests.cs
+++ b/src/Servers/Kestrel/Transport.Quic/test/QuicConnectionListenerTests.cs
@@ -101,18 +101,16 @@ public class QuicConnectionListenerTests : TestApplicationErrorLoggerLoggedTest
[ConditionalFact]
[MsQuicSupported]
- // https://github.com/dotnet/runtime/issues/57308, RemoteCertificateValidationCallback should allow us to accept a null cert,
- // but it doesn't right now.
[OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/41894")]
- public async Task ClientCertificate_Required_NotSent_ConnectionAborted()
+ [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/42389")]
+ public async Task ClientCertificate_Required_NotSent_AcceptedViaCallback()
{
await using var connectionListener = await QuicTestHelpers.CreateConnectionListenerFactory(LoggerFactory, clientCertificateRequired: true);
var options = QuicTestHelpers.CreateClientConnectionOptions(connectionListener.EndPoint);
using var clientConnection = await QuicConnection.ConnectAsync(options);
- var qex = await Assert.ThrowsAnyAsync<QuicException>(async () => await clientConnection.ConnectAsync().DefaultTimeout());
- Assert.StartsWith("Connection has been shutdown by transport:", qex.Message);
+ await clientConnection.ConnectAsync().DefaultTimeout();
+ Assert.True(clientConnection.Connected);
}
}
diff --git a/src/Servers/Kestrel/test/Interop.FunctionalTests/Http3/Http3RequestTests.cs b/src/Servers/Kestrel/test/Interop.FunctionalTests/Http3/Http3RequestTests.cs
index b4b6a03f77..09bb48d766 100644
--- a/src/Servers/Kestrel/test/Interop.FunctionalTests/Http3/Http3RequestTests.cs
+++ b/src/Servers/Kestrel/test/Interop.FunctionalTests/Http3/Http3RequestTests.cs
@@ -393,7 +393,6 @@ public class Http3RequestTests : LoggedTest
[MsQuicSupported]
[InlineData(HttpProtocols.Http3)]
[InlineData(HttpProtocols.Http2)]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/35070")]
public async Task POST_ServerAbort_ClientReceivesAbort(HttpProtocols protocol)
{
// Arrange
diff --git a/src/Servers/Kestrel/test/Interop.FunctionalTests/Http3/Http3TlsTests.cs b/src/Servers/Kestrel/test/Interop.FunctionalTests/Http3/Http3TlsTests.cs
index e06b938282..55a0d7fb7f 100644
--- a/src/Servers/Kestrel/test/Interop.FunctionalTests/Http3/Http3TlsTests.cs
+++ b/src/Servers/Kestrel/test/Interop.FunctionalTests/Http3/Http3TlsTests.cs
@@ -196,17 +196,16 @@ public class Http3TlsTests : LoggedTest
if (!serverAllowInvalid)
{
- // In .NET 6 there is a race condition between throwing HttpRequestException and QuicException.
- // Unable to test the exact error.
- var ex = await Assert.ThrowsAnyAsync<Exception>(() => sendTask).DefaultTimeout();
+ var ex = await Assert.ThrowsAnyAsync<HttpRequestException>(() => sendTask).DefaultTimeout();
Logger.LogInformation(ex, "SendAsync successfully threw error.");
}
else
{
- // Because we can't verify the exact error reason, check that the cert is the cause be successfully
+ // Because we can't verify the exact error reason, check that the cert is the cause by successfully
// making a call when invalid certs are allowed.
- var response = await sendTask.DefaultTimeout();
+ using var response = await sendTask.DefaultTimeout();
response.EnsureSuccessStatusCode();
+ Assert.Equal("True", await response.Content.ReadAsStringAsync().DefaultTimeout());
}
await host.StopAsync().DefaultTimeout();
@@ -215,7 +214,7 @@ public class Http3TlsTests : LoggedTest
[ConditionalFact]
[MsQuicSupported]
[OSSkipCondition(OperatingSystems.MacOSX | OperatingSystems.Linux, SkipReason = "https://github.com/dotnet/aspnetcore/issues/35800")]
- [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/41894")]
+ [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/42388")]
public async Task ClientCertificate_Allow_NotAvailable_Optional()
{
var builder = CreateHostBuilder(async context =>
@@ -245,9 +244,9 @@ public class Http3TlsTests : LoggedTest
request.Version = HttpVersion.Version30;
request.VersionPolicy = HttpVersionPolicy.RequestVersionExact;
- // https://github.com/dotnet/runtime/issues/57308, optional client certs aren't supported.
- var ex = await Assert.ThrowsAsync<HttpRequestException>(() => client.SendAsync(request, CancellationToken.None).DefaultTimeout());
- Assert.StartsWith("Connection has been shutdown by transport:", ex.Message);
+ var response = await client.SendAsync(request, CancellationToken.None).DefaultTimeout();
+ Assert.True(response.IsSuccessStatusCode);
+ Assert.Equal("False", await response.Content.ReadAsStringAsync());
await host.StopAsync().DefaultTimeout();
}
diff --git a/src/Servers/test/FunctionalTests/HelloWorldTest.cs b/src/Servers/test/FunctionalTests/HelloWorldTest.cs
index 04a0384941..f65126a04d 100644
--- a/src/Servers/test/FunctionalTests/HelloWorldTest.cs
+++ b/src/Servers/test/FunctionalTests/HelloWorldTest.cs
@@ -119,6 +119,7 @@ public class HelloWorldTests : LoggedTest
[ConditionalTheory]
[MemberData(nameof(SelfHostTestVariants))]
+ [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/42380")]
public async Task ApplicationException(TestVariant variant)
{
var testName = $"ApplicationException_{variant.Server}_{variant.Tfm}_{variant.Architecture}_{variant.ApplicationType}";
diff --git a/src/Shared/E2ETesting/selenium-config.json b/src/Shared/E2ETesting/selenium-config.json
index 91a5051b6c..71bb468d49 100644
--- a/src/Shared/E2ETesting/selenium-config.json
+++ b/src/Shared/E2ETesting/selenium-config.json
@@ -1,7 +1,7 @@
{
"drivers": {
"chrome": {
- "version" : "100.0.4896.60"
+ "version" : "103.0.5060.53"
}
},
"ignoreExtraDrivers": true
diff --git a/src/Shared/TaskToApm.cs b/src/Shared/TaskToApm.cs
index 0647dc4a00..cec6fa53df 100644
--- a/src/Shared/TaskToApm.cs
+++ b/src/Shared/TaskToApm.cs
@@ -42,7 +42,7 @@ internal static class TaskToApm
return;
}
- throw new ArgumentNullException(nameof(asyncResult));
+ ArgumentNullException.ThrowIfNull(asyncResult, nameof(asyncResult));
}
/// <summary>Processes an IAsyncResult returned by Begin.</summary>
diff --git a/src/SignalR/clients/ts/FunctionalTests/yarn.lock b/src/SignalR/clients/ts/FunctionalTests/yarn.lock
index 09630f49dd..73c8f7e0a6 100644
--- a/src/SignalR/clients/ts/FunctionalTests/yarn.lock
+++ b/src/SignalR/clients/ts/FunctionalTests/yarn.lock
@@ -2441,12 +2441,7 @@ no-case@^3.0.4:
lower-case "^2.0.2"
tslib "^2.0.3"
-node-fetch@^2.6.1:
- version "2.6.1"
- resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
- integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
-
-node-fetch@^2.6.7:
+node-fetch@^2.6.1, node-fetch@^2.6.7:
version "2.6.7"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
@@ -3307,7 +3302,7 @@ tough-cookie@^4.0.0:
tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
- integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=
+ integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
trim-repeated@^1.0.0:
version "1.0.0"
@@ -3510,7 +3505,7 @@ webdriverio@^6.7.0:
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
- integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=
+ integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
webpack-sources@^3.2.3:
version "3.2.3"
@@ -3550,7 +3545,7 @@ webpack@^5.72.1:
whatwg-url@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
- integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0=
+ integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
dependencies:
tr46 "~0.0.3"
webidl-conversions "^3.0.0"
diff --git a/src/Tools/Tools.slnf b/src/Tools/Tools.slnf
index e277c5a315..4b5a33b510 100644
--- a/src/Tools/Tools.slnf
+++ b/src/Tools/Tools.slnf
@@ -53,6 +53,7 @@
"src\\Middleware\\Localization\\src\\Microsoft.AspNetCore.Localization.csproj",
"src\\Middleware\\MiddlewareAnalysis\\src\\Microsoft.AspNetCore.MiddlewareAnalysis.csproj",
"src\\Middleware\\RateLimiting\\src\\Microsoft.AspNetCore.RateLimiting.csproj",
+ "src\\Middleware\\RequestDecompression\\src\\Microsoft.AspNetCore.RequestDecompression.csproj",
"src\\Middleware\\ResponseCaching.Abstractions\\src\\Microsoft.AspNetCore.ResponseCaching.Abstractions.csproj",
"src\\Middleware\\ResponseCaching\\src\\Microsoft.AspNetCore.ResponseCaching.csproj",
"src\\Middleware\\ResponseCompression\\src\\Microsoft.AspNetCore.ResponseCompression.csproj",
diff --git a/src/Tools/dotnet-user-jwts/src/Commands/ClearCommand.cs b/src/Tools/dotnet-user-jwts/src/Commands/ClearCommand.cs
index abc01f770e..2b977e3f63 100644
--- a/src/Tools/dotnet-user-jwts/src/Commands/ClearCommand.cs
+++ b/src/Tools/dotnet-user-jwts/src/Commands/ClearCommand.cs
@@ -45,7 +45,7 @@ internal sealed class ClearCommand
if (!force)
{
- reporter.Output(Resources.ClearCommand_Permission);
+ reporter.Output(Resources.FormatClearCommand_Permission(count, project));
reporter.Output("[Y]es / [N]o");
if (Console.ReadLine().Trim().ToUpperInvariant() != "Y")
{
diff --git a/src/Tools/dotnet-user-jwts/src/Helpers/DevJwtCliHelpers.cs b/src/Tools/dotnet-user-jwts/src/Helpers/DevJwtCliHelpers.cs
index 999d5e7514..7a7dc7541c 100644
--- a/src/Tools/dotnet-user-jwts/src/Helpers/DevJwtCliHelpers.cs
+++ b/src/Tools/dotnet-user-jwts/src/Helpers/DevJwtCliHelpers.cs
@@ -114,13 +114,19 @@ internal static class DevJwtCliHelpers
ArgumentException.ThrowIfNullOrEmpty(nameof(project));
var launchSettingsFilePath = Path.Combine(Path.GetDirectoryName(project)!, "Properties", "launchSettings.json");
- var applicationUrls = new List<string>();
+ var applicationUrls = new HashSet<string>();
if (File.Exists(launchSettingsFilePath))
{
using var launchSettingsFileStream = new FileStream(launchSettingsFilePath, FileMode.Open, FileAccess.Read);
if (launchSettingsFileStream.Length > 0)
{
var launchSettingsJson = JsonDocument.Parse(launchSettingsFileStream);
+
+ if (ExtractIISExpressUrlFromProfile(launchSettingsJson.RootElement) is { } iisUrls)
+ {
+ applicationUrls.UnionWith(iisUrls);
+ }
+
if (launchSettingsJson.RootElement.TryGetProperty("profiles", out var profiles))
{
var profilesEnumerator = profiles.EnumerateObject();
@@ -128,25 +134,20 @@ internal static class DevJwtCliHelpers
{
if (ExtractKestrelUrlsFromProfile(profile) is { } kestrelUrls)
{
- applicationUrls.AddRange(kestrelUrls);
- }
-
- if (ExtractIISExpressUrlFromProfile(profile) is { } iisUrls)
- {
- applicationUrls.AddRange(iisUrls);
+ applicationUrls.UnionWith(kestrelUrls);
}
}
}
}
}
- return applicationUrls;
+ return applicationUrls.ToList();
- static List<string> ExtractIISExpressUrlFromProfile(JsonProperty profile)
+ static List<string> ExtractIISExpressUrlFromProfile(JsonElement rootElement)
{
- if (profile.NameEquals("iisSettings"))
+ if (rootElement.TryGetProperty("iisSettings", out var iisSettings))
{
- if (profile.Value.TryGetProperty("iisExpress", out var iisExpress))
+ if (iisSettings.TryGetProperty("iisExpress", out var iisExpress))
{
List<string> iisUrls = new();
if (iisExpress.TryGetProperty("applicationUrl", out var iisUrl))
diff --git a/src/Tools/dotnet-user-jwts/src/Helpers/JwtAuthenticationSchemeSettings.cs b/src/Tools/dotnet-user-jwts/src/Helpers/JwtAuthenticationSchemeSettings.cs
index b8108f5294..77f95e6df1 100644
--- a/src/Tools/dotnet-user-jwts/src/Helpers/JwtAuthenticationSchemeSettings.cs
+++ b/src/Tools/dotnet-user-jwts/src/Helpers/JwtAuthenticationSchemeSettings.cs
@@ -10,6 +10,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer.Tools;
internal sealed record JwtAuthenticationSchemeSettings(string SchemeName, List<string> Audiences, string ClaimsIssuer)
{
private const string AuthenticationKey = "Authentication";
+ private const string DefaultSchemeKey = "DefaultScheme";
private const string SchemesKey = "Schemes";
private static readonly JsonSerializerOptions _jsonSerializerOptions = new JsonSerializerOptions
@@ -35,7 +36,7 @@ internal sealed record JwtAuthenticationSchemeSettings(string SchemeName, List<s
{
// If a scheme with the same name has already been registered, we
// override with the latest token's options
- schemes[SchemeName] = settingsObject;
+ schemes[SchemeName] = settingsObject;
}
else
{
@@ -56,6 +57,15 @@ internal sealed record JwtAuthenticationSchemeSettings(string SchemeName, List<s
};
}
+ // Set the DefaultScheme if it has not already been set
+ // and only a single scheme has been configured thus far
+ if (config[AuthenticationKey][DefaultSchemeKey] is null
+ && config[AuthenticationKey][SchemesKey] is JsonObject setSchemes
+ && setSchemes.Count == 1)
+ {
+ config[AuthenticationKey][DefaultSchemeKey] = SchemeName;
+ }
+
using var writer = new FileStream(filePath, FileMode.Open, FileAccess.Write);
JsonSerializer.Serialize(writer, config, _jsonSerializerOptions);
}
@@ -70,6 +80,11 @@ internal sealed record JwtAuthenticationSchemeSettings(string SchemeName, List<s
authentication[SchemesKey] is JsonObject schemes)
{
schemes.Remove(name);
+ if (authentication[DefaultSchemeKey] is JsonValue defaultScheme
+ && defaultScheme.GetValue<string>() == name)
+ {
+ authentication.Remove(DefaultSchemeKey);
+ }
}
using var writer = new FileStream(filePath, FileMode.Create, FileAccess.Write);
diff --git a/src/Tools/dotnet-user-jwts/test/UserJwtsTestFixture.cs b/src/Tools/dotnet-user-jwts/test/UserJwtsTestFixture.cs
index d3af1030c3..1e14b7d5d6 100644
--- a/src/Tools/dotnet-user-jwts/test/UserJwtsTestFixture.cs
+++ b/src/Tools/dotnet-user-jwts/test/UserJwtsTestFixture.cs
@@ -25,16 +25,16 @@ public class UserJwtsTestFixture : IDisposable
private const string LaunchSettingsTemplate = @"
{
+ ""iisSettings"": {
+ ""windowsAuthentication"": false,
+ ""anonymousAuthentication"": true,
+ ""iisExpress"": {
+ ""applicationUrl"": ""http://localhost:23528"",
+ ""sslPort"": 44395
+ }
+ },
""profiles"": {
- ""iisSettings"": {
- ""windowsAuthentication"": false,
- ""anonymousAuthentication"": true,
- ""iisExpress"": {
- ""applicationUrl"": ""http://localhost:23528"",
- ""sslPort"": 44395
- }
- },
- ""HttpApiSampleApp"": {
+ ""HttpWebApp"": {
""commandName"": ""Project"",
""dotnetRunMessages"": true,
""launchBrowser"": true,
@@ -42,11 +42,27 @@ public class UserJwtsTestFixture : IDisposable
""environmentVariables"": {
""ASPNETCORE_ENVIRONMENT"": ""Development""
}
+ },
+ ""HttpsOnly"": {
+ ""commandName"": ""Project"",
+ ""dotnetRunMessages"": true,
+ ""launchBrowser"": true,
+ ""applicationUrl"": ""https://localhost:5001"",
+ ""environmentVariables"": {
+ ""ASPNETCORE_ENVIRONMENT"": ""Development""
+ }
+ },
+ ""IIS Express"": {
+ ""commandName"": ""IISExpress"",
+ ""launchBrowser"": true,
+ ""environmentVariables"": {
+ ""ASPNETCORE_ENVIRONMENT"": ""Development""
+ }
}
}
}";
- public string CreateProject(bool hasSecret = true)
+ public string CreateProject(bool hasSecret = true, string appSettingsContent = "{}")
{
var projectPath = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), "userjwtstest", Guid.NewGuid().ToString()));
Directory.CreateDirectory(Path.Combine(projectPath.FullName, "Properties"));
@@ -65,7 +81,7 @@ public class UserJwtsTestFixture : IDisposable
File.WriteAllText(
Path.Combine(projectPath.FullName, "appsettings.Development.json"),
- "{}");
+ appSettingsContent);
if (hasSecret)
{
diff --git a/src/Tools/dotnet-user-jwts/test/UserJwtsTests.cs b/src/Tools/dotnet-user-jwts/test/UserJwtsTests.cs
index 8947aa2234..3c35b0085e 100644
--- a/src/Tools/dotnet-user-jwts/test/UserJwtsTests.cs
+++ b/src/Tools/dotnet-user-jwts/test/UserJwtsTests.cs
@@ -67,15 +67,61 @@ public class UserJwtsTests : IClassFixture<UserJwtsTestFixture>
}
[Fact]
- public void Create_WritesGeneratedTokenToDisk()
+ public async Task Create_SetsDefaultSchemeIfNoOtherSchemesSet()
{
var project = Path.Combine(_fixture.CreateProject(), "TestProject.csproj");
- var appsettings = Path.Combine(Path.GetDirectoryName(project), "appsettings.Development.json");
+ var appSettingsPath = Path.Combine(Path.GetDirectoryName(project), "appsettings.Development.json");
var app = new Program(_console);
app.Run(new[] { "create", "--project", project });
Assert.Contains("New JWT saved", _console.GetOutput());
- Assert.Contains("dotnet-user-jwts", File.ReadAllText(appsettings));
+
+ using FileStream openStream = File.OpenRead(appSettingsPath);
+ var appSettingsFile = await JsonSerializer.DeserializeAsync<JsonObject>(openStream);
+
+ Assert.True(appSettingsFile.TryGetPropertyValue("Authentication", out var authentication));
+ Assert.Equal("Bearer", authentication["DefaultScheme"].GetValue<string>());
+ Assert.Equal("dotnet-user-jwts", authentication["Schemes"]["Bearer"]["ClaimsIssuer"].GetValue<string>());
+ }
+
+ [Fact]
+ public async Task Create_DoesNotOverrideDefaultSchemeIfAlreadySet()
+ {
+ var project = Path.Combine(_fixture.CreateProject(
+ hasSecret: true,
+ appSettingsContent: @"{ ""Authentication"": { ""DefaultScheme"": ""foobar"" } }"), "TestProject.csproj");
+ var appSettingsPath = Path.Combine(Path.GetDirectoryName(project), "appsettings.Development.json");
+ var app = new Program(_console);
+
+ app.Run(new[] { "create", "--project", project });
+ Assert.Contains("New JWT saved", _console.GetOutput());
+
+ using FileStream openStream = File.OpenRead(appSettingsPath);
+ var appSettingsFile = await JsonSerializer.DeserializeAsync<JsonObject>(openStream);
+
+ Assert.True(appSettingsFile.TryGetPropertyValue("Authentication", out var authentication));
+ Assert.Equal("foobar", authentication["DefaultScheme"].GetValue<string>()); //foobar not Bearer
+ Assert.Equal("dotnet-user-jwts", authentication["Schemes"]["Bearer"]["ClaimsIssuer"].GetValue<string>());
+ }
+
+ [Fact]
+ public async Task Create_DoesNotSetDefaultSchemeIfMultipleSchemesConfigured()
+ {
+ var project = Path.Combine(_fixture.CreateProject(
+ hasSecret: true,
+ appSettingsContent: @"{ ""Authentication"": { ""Schemes"": { ""foobar"" : { } } } }"), "TestProject.csproj");
+ var appSettingsPath = Path.Combine(Path.GetDirectoryName(project), "appsettings.Development.json");
+ var app = new Program(_console);
+
+ app.Run(new[] { "create", "--project", project });
+ Assert.Contains("New JWT saved", _console.GetOutput());
+
+ using FileStream openStream = File.OpenRead(appSettingsPath);
+ var appSettingsFile = await JsonSerializer.DeserializeAsync<JsonObject>(openStream);
+
+ Assert.True(appSettingsFile.TryGetPropertyValue("Authentication", out var authentication));
+ Assert.Null(authentication["DefaultScheme"]); // Should not be set beause 2 schemes configured
+ Assert.Equal("dotnet-user-jwts", authentication["Schemes"]["Bearer"]["ClaimsIssuer"].GetValue<string>());
}
[Fact]
@@ -92,7 +138,6 @@ public class UserJwtsTests : IClassFixture<UserJwtsTestFixture>
public void List_ReturnsIdForGeneratedToken()
{
var project = Path.Combine(_fixture.CreateProject(), "TestProject.csproj");
- var appsettings = Path.Combine(Path.GetDirectoryName(project), "appsettings.Development.json");
var app = new Program(_console);
app.Run(new[] { "create", "--project", project, "--scheme", "MyCustomScheme" });
@@ -103,10 +148,10 @@ public class UserJwtsTests : IClassFixture<UserJwtsTestFixture>
}
[Fact]
- public void Remove_RemovesGeneratedToken()
+ public async Task Remove_RemovesGeneratedToken()
{
var project = Path.Combine(_fixture.CreateProject(), "TestProject.csproj");
- var appsettings = Path.Combine(Path.GetDirectoryName(project), "appsettings.Development.json");
+ var appSettingsPath = Path.Combine(Path.GetDirectoryName(project), "appsettings.Development.json");
var app = new Program(_console);
app.Run(new[] { "create", "--project", project });
@@ -115,16 +160,45 @@ public class UserJwtsTests : IClassFixture<UserJwtsTestFixture>
app.Run(new[] { "create", "--project", project, "--scheme", "Scheme2" });
app.Run(new[] { "remove", id, "--project", project });
- var appsettingsContent = File.ReadAllText(appsettings);
- Assert.DoesNotContain("Bearer", appsettingsContent);
- Assert.Contains("Scheme2", appsettingsContent);
+
+ using FileStream openStream = File.OpenRead(appSettingsPath);
+ var appSettingsFile = await JsonSerializer.DeserializeAsync<JsonObject>(openStream);
+
+ Assert.True(appSettingsFile.TryGetPropertyValue("Authentication", out var authentication));
+ Assert.Null(authentication["Schemes"]["Bearer"]);
+ Assert.NotNull(authentication["Schemes"]["Scheme2"]);
+ Assert.Null(authentication["DefaultScheme"]);
+ }
+
+ [Fact]
+ public async Task Remove_DoesNotUnsetDefaultSchemeIfNoMatch()
+ {
+ var project = Path.Combine(_fixture.CreateProject(), "TestProject.csproj");
+ var appSettingsPath = Path.Combine(Path.GetDirectoryName(project), "appsettings.Development.json");
+ var app = new Program(_console);
+
+ app.Run(new[] { "create", "--project", project });
+ _console.ClearOutput();
+ app.Run(new[] { "create", "--project", project, "--scheme", "Scheme2" });
+ var matches = Regex.Matches(_console.GetOutput(), "New JWT saved with ID '(.*?)'");
+ var id = matches.SingleOrDefault().Groups[1].Value;
+
+ app.Run(new[] { "remove", id, "--project", project });
+
+ using FileStream openStream = File.OpenRead(appSettingsPath);
+ var appSettingsFile = await JsonSerializer.DeserializeAsync<JsonObject>(openStream);
+
+ Assert.True(appSettingsFile.TryGetPropertyValue("Authentication", out var authentication));
+ Assert.NotNull(authentication["Schemes"]["Bearer"]);
+ Assert.Null(authentication["Schemes"]["Scheme2"]);
+ Assert.NotNull(authentication["DefaultScheme"]); // We haven't removed the Bearer scheme so it's still the default
}
[Fact]
- public void Clear_RemovesGeneratedTokens()
+ public async Task Clear_RemovesGeneratedTokens()
{
var project = Path.Combine(_fixture.CreateProject(), "TestProject.csproj");
- var appsettings = Path.Combine(Path.GetDirectoryName(project), "appsettings.Development.json");
+ var appSettingsPath = Path.Combine(Path.GetDirectoryName(project), "appsettings.Development.json");
var app = new Program(_console);
app.Run(new[] { "create", "--project", project });
@@ -133,9 +207,14 @@ public class UserJwtsTests : IClassFixture<UserJwtsTestFixture>
Assert.Contains("New JWT saved", _console.GetOutput());
app.Run(new[] { "clear", "--project", project, "--force" });
- var appsettingsContent = File.ReadAllText(appsettings);
- Assert.DoesNotContain("Bearer", appsettingsContent);
- Assert.DoesNotContain("Scheme2", appsettingsContent);
+
+ using FileStream openStream = File.OpenRead(appSettingsPath);
+ var appSettingsFile = await JsonSerializer.DeserializeAsync<JsonObject>(openStream);
+
+ Assert.True(appSettingsFile.TryGetPropertyValue("Authentication", out var authentication));
+ Assert.Null(authentication["Schemes"]["Bearer"]);
+ Assert.Null(authentication["Schemes"]["Scheme2"]);
+ Assert.Null(authentication["DefaultScheme"]);
}
[Fact]
diff --git a/src/submodules/googletest b/src/submodules/googletest
-Subproject 86add13493e5c881d7e4ba77fb91c1f57752b3a
+Subproject 5376968f6948923e2411081fd9372e71a59d8e7