Browse Source

Merge pull request #25603 from dotnet-maestro-bot/merge/release/5.0-rc2-to-master

Stephen Halter 5 years ago
parent
commit
a190fd3485
100 changed files with 3672 additions and 579 deletions
  1. 1 0
      .azure/pipelines/ci.yml
  2. 1 1
      .azure/pipelines/quarantined-pr.yml
  3. 97 0
      AspNetCore.sln
  4. 1 0
      dockerbuild.sh
  5. 0 0
      src/Components/Web.JS/dist/Release/blazor.server.js
  6. 0 0
      src/Components/Web.JS/dist/Release/blazor.webassembly.js
  7. 10 1
      src/Components/Web.JS/src/Boot.WebAssembly.ts
  8. 11 3
      src/Components/Web.JS/src/Virtualize.ts
  9. 11 2
      src/Components/Web/src/Virtualization/Virtualize.cs
  10. 5 1
      src/Components/WebAssembly/JSInterop/src/Microsoft.JSInterop.WebAssembly.csproj
  11. 45 0
      src/Components/WebAssembly/JSInterop/src/WebAssemblyJSObjectReference.cs
  12. 38 19
      src/Components/WebAssembly/JSInterop/src/WebAssemblyJSRuntime.cs
  13. 1 1
      src/Components/WebAssembly/Sdk/integrationtests/ServiceWorkerAssert.cs
  14. 2 2
      src/Components/WebAssembly/Sdk/integrationtests/WasmPublishIntegrationTest.cs
  15. 8 1
      src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.Current.targets
  16. 1 1
      src/Components/WebAssembly/WebAssembly/src/Rendering/WebAssemblyRenderer.cs
  17. 1 1
      src/Components/WebAssembly/WebAssembly/src/Services/WebAssemblyConsoleLogger.cs
  18. 1 1
      src/Components/WebAssembly/WebAssembly/src/Services/WebAssemblyJSRuntimeInvoker.cs
  19. 4 0
      src/Components/test/E2ETest/Tests/ComponentRenderingTest.cs
  20. 1 0
      src/Components/test/E2ETest/Tests/InteropTest.cs
  21. 11 3
      src/Components/test/testassets/BasicTestApp/InteropComponent.razor
  22. 14 0
      src/Components/test/testassets/BasicTestApp/InteropTest/InteropStruct.cs
  23. 4 4
      src/Components/test/testassets/BasicTestApp/InteropTest/JavaScriptInterop.cs
  24. 3 0
      src/Components/test/testassets/BasicTestApp/wwwroot/index.html
  25. 5 0
      src/Components/test/testassets/BasicTestApp/wwwroot/js/jsinteroptests.js
  26. 3 0
      src/Components/test/testassets/TestContentPackage/ComponentFromPackage.razor.css
  27. 1 2
      src/Components/test/testassets/TestContentPackage/wwwroot/styles.css
  28. 1 16
      src/Components/test/testassets/TestServer/Components.TestServer.csproj
  29. 1 2
      src/Components/test/testassets/TestServer/Pages/_ServerHost.cshtml
  30. 1 0
      src/Identity/ApiAuthorization.IdentityServer/src/PublicAPI.Shipped.txt
  31. 129 0
      src/Identity/ApiAuthorization.IdentityServer/src/PublicAPI.Unshipped.txt
  32. 1 0
      src/Identity/Core/src/PublicAPI.Shipped.txt
  33. 139 0
      src/Identity/Core/src/PublicAPI.Unshipped.txt
  34. 3 0
      src/Identity/Core/src/SignInManager.cs
  35. 1 0
      src/Identity/Extensions.Core/src/PublicAPI.Shipped.txt
  36. 540 0
      src/Identity/Extensions.Core/src/PublicAPI.Unshipped.txt
  37. 1 0
      src/Identity/Extensions.Stores/src/PublicAPI.Shipped.txt
  38. 206 0
      src/Identity/Extensions.Stores/src/PublicAPI.Unshipped.txt
  39. 3 16
      src/JSInterop/Microsoft.JSInterop/src/IJSInProcessObjectReference.cs
  40. 41 0
      src/JSInterop/Microsoft.JSInterop/src/IJSObjectReference.cs
  41. 55 0
      src/JSInterop/Microsoft.JSInterop/src/IJSUnmarshalledObjectReference.cs
  42. 45 0
      src/JSInterop/Microsoft.JSInterop/src/Implementation/JSInProcessObjectReference.cs
  43. 72 0
      src/JSInterop/Microsoft.JSInterop/src/Implementation/JSObjectReference.cs
  44. 16 9
      src/JSInterop/Microsoft.JSInterop/src/Infrastructure/JSObjectReferenceJsonConverter.cs
  45. 26 0
      src/JSInterop/Microsoft.JSInterop/src/JSInProcessObjectReferenceExtensions.cs
  46. 5 3
      src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntime.cs
  47. 0 103
      src/JSInterop/Microsoft.JSInterop/src/JSObjectReference.cs
  48. 139 0
      src/JSInterop/Microsoft.JSInterop/src/JSObjectReferenceExtensions.cs
  49. 4 13
      src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs
  50. 4 0
      src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj
  51. 1 0
      src/JSInterop/Microsoft.JSInterop/src/PublicAPI.Shipped.txt
  52. 97 0
      src/JSInterop/Microsoft.JSInterop/src/PublicAPI.Unshipped.txt
  53. 74 8
      src/JSInterop/Microsoft.JSInterop/test/Infrastructure/JSObjectReferenceJsonConverterTest.cs
  54. 1 0
      src/JSInterop/Microsoft.JSInterop/test/JSObjectReferenceTest.cs
  55. 1 0
      src/Localization/Abstractions/src/PublicAPI.Shipped.txt
  56. 28 0
      src/Localization/Abstractions/src/PublicAPI.Unshipped.txt
  57. 1 0
      src/Localization/Localization/src/PublicAPI.Shipped.txt
  58. 37 0
      src/Localization/Localization/src/PublicAPI.Unshipped.txt
  59. 2 0
      src/Logging.AzureAppServices/src/BlobLoggerProvider.cs
  60. 2 0
      src/Logging.AzureAppServices/src/FileLoggerProvider.cs
  61. 1 0
      src/Logging.AzureAppServices/src/PublicAPI.Shipped.txt
  62. 35 0
      src/Logging.AzureAppServices/src/PublicAPI.Unshipped.txt
  63. 18 3
      src/Mvc/Mvc.Core/src/Routing/UrlHelperBase.cs
  64. 38 1
      src/Mvc/Mvc.Core/test/Routing/UrlHelperTestBase.cs
  65. 25 1
      src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/.template.config/template.json
  66. 27 0
      src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Areas/Identity/Pages/Shared/_LoginPartial.Identity.cshtml
  67. 0 0
      src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Areas/Identity/Pages/Shared/_LoginPartial.OrgAuth.cshtml
  68. 1 1
      src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Pages/_Host.cshtml
  69. 0 1
      src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/_Imports.razor
  70. 5 1
      src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/wwwroot/index.html
  71. 75 1
      src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/PackIntegrationTest.cs
  72. 72 4
      src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/ScopedCssIntegrationTests.cs
  73. 35 17
      src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/StaticWebAssetsIntegrationTest.cs
  74. 5 4
      src/Razor/Microsoft.NET.Sdk.Razor/src/ApplyCssScopes.cs
  75. 1 2
      src/Razor/Microsoft.NET.Sdk.Razor/src/ComputeCssScope.cs
  76. 60 6
      src/Razor/Microsoft.NET.Sdk.Razor/src/ConcatenateCssFiles.cs
  77. 4 76
      src/Razor/Microsoft.NET.Sdk.Razor/src/GenerateStaticWebAssetsManifest.cs
  78. 19 18
      src/Razor/Microsoft.NET.Sdk.Razor/src/GenerateStaticWebAsssetsPropsFile.cs
  79. 11 2
      src/Razor/Microsoft.NET.Sdk.Razor/src/ResolveAllScopedCssAssets.cs
  80. 4 4
      src/Razor/Microsoft.NET.Sdk.Razor/src/ValidateStaticWebAssetsUniquePaths.cs
  81. 178 67
      src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.ScopedCss.targets
  82. 34 14
      src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.StaticWebAssets.targets
  83. 3 3
      src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Sdk.Razor.CurrentVersion.targets
  84. 160 0
      src/Razor/Microsoft.NET.Sdk.Razor/test/ApplyCssScopesTest.cs
  85. 139 0
      src/Razor/Microsoft.NET.Sdk.Razor/test/ComputeCssScopesTests.cs
  86. 394 0
      src/Razor/Microsoft.NET.Sdk.Razor/test/ConcatenateFilesTest.cs
  87. 56 0
      src/Razor/Microsoft.NET.Sdk.Razor/test/DiscoverDefaultScopedCssItemsTests.cs
  88. 2 84
      src/Razor/Microsoft.NET.Sdk.Razor/test/GenerateAspNetCoreStaticAssetsManifestTest.cs
  89. 70 46
      src/Razor/Microsoft.NET.Sdk.Razor/test/GenerateStaticWebAssetsPropsFileTest.cs
  90. 10 1
      src/Razor/Microsoft.NET.Sdk.Razor/test/Microsoft.NET.Sdk.Razor.Test.csproj
  91. 124 0
      src/Razor/Microsoft.NET.Sdk.Razor/test/ResolveAllScopedCssAssetsTest.cs
  92. 3 0
      src/Razor/Microsoft.NET.Sdk.Razor/test/TestFiles/Generated/Counter.razor.rz.scp.css
  93. 3 0
      src/Razor/Microsoft.NET.Sdk.Razor/test/TestFiles/Generated/FetchData.razor.rz.scp.css
  94. 3 0
      src/Razor/Microsoft.NET.Sdk.Razor/test/TestFiles/Generated/Index.razor.rz.scp.css
  95. 3 0
      src/Razor/Microsoft.NET.Sdk.Razor/test/TestFiles/Generated/lib.bundle.scp.css
  96. 3 0
      src/Razor/Microsoft.NET.Sdk.Razor/test/TestFiles/Generated/package.bundle.scp.css
  97. 85 9
      src/Razor/Microsoft.NET.Sdk.Razor/test/ValidateStaticWebAssetsUniquePathsTest.cs
  98. 1 0
      src/Servers/IIS/AspNetCoreModuleV2/CommonLib/ConfigurationSection.h
  99. 1 0
      src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.cpp
  100. 7 0
      src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.h

+ 1 - 0
.azure/pipelines/ci.yml

@@ -573,6 +573,7 @@ stages:
         jobName: MacOS_Test
         jobDisplayName: "Test: macOS 10.14"
         agentOs: macOS
+        timeoutInMinutes: 240
         isTestingJob: true
         buildArgs: --all --test "/p:RunTemplateTests=false /p:SkipHelixReadyTests=true" $(_InternalRuntimeDownloadArgs)
         beforeBuild:

+ 1 - 1
.azure/pipelines/quarantined-pr.yml

@@ -86,7 +86,7 @@ jobs:
     jobName: MacOS_Quarantined_Test
     jobDisplayName: "Tests: macOS 10.14"
     agentOs: macOS
-    timeoutInMinutes: 60
+    timeoutInMinutes: 120
     isTestingJob: true
     steps:
     - bash: ./build.sh --all --pack --ci --nobl --no-build-java

+ 97 - 0
AspNetCore.sln

@@ -1492,6 +1492,7 @@ EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.ProtectedBrowserStorage", "src\Components\ProtectedBrowserStorage\src\Microsoft.AspNetCore.Components.ProtectedBrowserStorage.csproj", "{9059AC97-7547-4CC1-A076-680CBCCC1F33}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Components.ProtectedBrowserStorage.Tests", "src\Components\ProtectedBrowserStorage\test\Microsoft.AspNetCore.Components.ProtectedBrowserStorage.Tests.csproj", "{943FD3EC-D330-4277-B3F3-3DFABB57D3B5}"
+EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Configuration.KeyPerFile", "src\Configuration.KeyPerFile\src\Microsoft.Extensions.Configuration.KeyPerFile.csproj", "{498A4F54-F11A-46C5-A58D-09DE56C6A034}"
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Configuration.KeyPerFile", "Configuration.KeyPerFile", "{AEB1933E-9369-4305-B20E-F186F888158F}"
@@ -1506,6 +1507,22 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.FilePr
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.FileProviders.Embedded.Tests", "src\FileProviders\Embedded\test\Microsoft.Extensions.FileProviders.Embedded.Tests.csproj", "{B06ADD57-E855-4D8C-85DC-B323509AE540}"
 EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Localization", "Localization", "{3D34C81F-2CB5-459E-87E9-0CC04757A2A0}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Localization.Abstractions", "src\Localization\Abstractions\src\Microsoft.Extensions.Localization.Abstractions.csproj", "{FEF97646-9BC9-4D1B-A939-784D915C18A4}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Localization", "src\Localization\Localization\src\Microsoft.Extensions.Localization.csproj", "{839CE175-E0D9-43B9-9FA8-F32C47E7F56B}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Localization.RootNamespace.Tests", "src\Localization\Localization\test\Microsoft.Extensions.Localization.RootNamespace.Tests\Microsoft.Extensions.Localization.RootNamespace.Tests.csproj", "{50BF2926-7435-4F4B-88A9-3D0EDEB67FC8}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Localization.Tests", "src\Localization\Localization\test\Microsoft.Extensions.Localization.Tests\Microsoft.Extensions.Localization.Tests.csproj", "{16B899B4-A4F4-4EF7-93BB-355861977A12}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Logging.AzureAppServices", "Logging.AzureAppServices", "{3EAB9890-2C01-444C-ACA0-D77B29CDE08B}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Logging.AzureAppServices", "src\Logging.AzureAppServices\src\Microsoft.Extensions.Logging.AzureAppServices.csproj", "{3E29454A-C4DC-44B7-AF0A-A782AD2E73BC}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Logging.AzureAppServices.Tests", "src\Logging.AzureAppServices\test\Microsoft.Extensions.Logging.AzureAppServices.Tests.csproj", "{43E3B132-2486-44A3-92C6-39E39724FAFD}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -7190,6 +7207,78 @@ Global
 		{B06ADD57-E855-4D8C-85DC-B323509AE540}.Release|x64.Build.0 = Release|Any CPU
 		{B06ADD57-E855-4D8C-85DC-B323509AE540}.Release|x86.ActiveCfg = Release|Any CPU
 		{B06ADD57-E855-4D8C-85DC-B323509AE540}.Release|x86.Build.0 = Release|Any CPU
+		{FEF97646-9BC9-4D1B-A939-784D915C18A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{FEF97646-9BC9-4D1B-A939-784D915C18A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{FEF97646-9BC9-4D1B-A939-784D915C18A4}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{FEF97646-9BC9-4D1B-A939-784D915C18A4}.Debug|x64.Build.0 = Debug|Any CPU
+		{FEF97646-9BC9-4D1B-A939-784D915C18A4}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{FEF97646-9BC9-4D1B-A939-784D915C18A4}.Debug|x86.Build.0 = Debug|Any CPU
+		{FEF97646-9BC9-4D1B-A939-784D915C18A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{FEF97646-9BC9-4D1B-A939-784D915C18A4}.Release|Any CPU.Build.0 = Release|Any CPU
+		{FEF97646-9BC9-4D1B-A939-784D915C18A4}.Release|x64.ActiveCfg = Release|Any CPU
+		{FEF97646-9BC9-4D1B-A939-784D915C18A4}.Release|x64.Build.0 = Release|Any CPU
+		{FEF97646-9BC9-4D1B-A939-784D915C18A4}.Release|x86.ActiveCfg = Release|Any CPU
+		{FEF97646-9BC9-4D1B-A939-784D915C18A4}.Release|x86.Build.0 = Release|Any CPU
+		{839CE175-E0D9-43B9-9FA8-F32C47E7F56B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{839CE175-E0D9-43B9-9FA8-F32C47E7F56B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{839CE175-E0D9-43B9-9FA8-F32C47E7F56B}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{839CE175-E0D9-43B9-9FA8-F32C47E7F56B}.Debug|x64.Build.0 = Debug|Any CPU
+		{839CE175-E0D9-43B9-9FA8-F32C47E7F56B}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{839CE175-E0D9-43B9-9FA8-F32C47E7F56B}.Debug|x86.Build.0 = Debug|Any CPU
+		{839CE175-E0D9-43B9-9FA8-F32C47E7F56B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{839CE175-E0D9-43B9-9FA8-F32C47E7F56B}.Release|Any CPU.Build.0 = Release|Any CPU
+		{839CE175-E0D9-43B9-9FA8-F32C47E7F56B}.Release|x64.ActiveCfg = Release|Any CPU
+		{839CE175-E0D9-43B9-9FA8-F32C47E7F56B}.Release|x64.Build.0 = Release|Any CPU
+		{839CE175-E0D9-43B9-9FA8-F32C47E7F56B}.Release|x86.ActiveCfg = Release|Any CPU
+		{839CE175-E0D9-43B9-9FA8-F32C47E7F56B}.Release|x86.Build.0 = Release|Any CPU
+		{50BF2926-7435-4F4B-88A9-3D0EDEB67FC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{50BF2926-7435-4F4B-88A9-3D0EDEB67FC8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{50BF2926-7435-4F4B-88A9-3D0EDEB67FC8}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{50BF2926-7435-4F4B-88A9-3D0EDEB67FC8}.Debug|x64.Build.0 = Debug|Any CPU
+		{50BF2926-7435-4F4B-88A9-3D0EDEB67FC8}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{50BF2926-7435-4F4B-88A9-3D0EDEB67FC8}.Debug|x86.Build.0 = Debug|Any CPU
+		{50BF2926-7435-4F4B-88A9-3D0EDEB67FC8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{50BF2926-7435-4F4B-88A9-3D0EDEB67FC8}.Release|Any CPU.Build.0 = Release|Any CPU
+		{50BF2926-7435-4F4B-88A9-3D0EDEB67FC8}.Release|x64.ActiveCfg = Release|Any CPU
+		{50BF2926-7435-4F4B-88A9-3D0EDEB67FC8}.Release|x64.Build.0 = Release|Any CPU
+		{50BF2926-7435-4F4B-88A9-3D0EDEB67FC8}.Release|x86.ActiveCfg = Release|Any CPU
+		{50BF2926-7435-4F4B-88A9-3D0EDEB67FC8}.Release|x86.Build.0 = Release|Any CPU
+		{16B899B4-A4F4-4EF7-93BB-355861977A12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{16B899B4-A4F4-4EF7-93BB-355861977A12}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{16B899B4-A4F4-4EF7-93BB-355861977A12}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{16B899B4-A4F4-4EF7-93BB-355861977A12}.Debug|x64.Build.0 = Debug|Any CPU
+		{16B899B4-A4F4-4EF7-93BB-355861977A12}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{16B899B4-A4F4-4EF7-93BB-355861977A12}.Debug|x86.Build.0 = Debug|Any CPU
+		{16B899B4-A4F4-4EF7-93BB-355861977A12}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{16B899B4-A4F4-4EF7-93BB-355861977A12}.Release|Any CPU.Build.0 = Release|Any CPU
+		{16B899B4-A4F4-4EF7-93BB-355861977A12}.Release|x64.ActiveCfg = Release|Any CPU
+		{16B899B4-A4F4-4EF7-93BB-355861977A12}.Release|x64.Build.0 = Release|Any CPU
+		{16B899B4-A4F4-4EF7-93BB-355861977A12}.Release|x86.ActiveCfg = Release|Any CPU
+		{16B899B4-A4F4-4EF7-93BB-355861977A12}.Release|x86.Build.0 = Release|Any CPU
+		{3E29454A-C4DC-44B7-AF0A-A782AD2E73BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{3E29454A-C4DC-44B7-AF0A-A782AD2E73BC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{3E29454A-C4DC-44B7-AF0A-A782AD2E73BC}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{3E29454A-C4DC-44B7-AF0A-A782AD2E73BC}.Debug|x64.Build.0 = Debug|Any CPU
+		{3E29454A-C4DC-44B7-AF0A-A782AD2E73BC}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{3E29454A-C4DC-44B7-AF0A-A782AD2E73BC}.Debug|x86.Build.0 = Debug|Any CPU
+		{3E29454A-C4DC-44B7-AF0A-A782AD2E73BC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{3E29454A-C4DC-44B7-AF0A-A782AD2E73BC}.Release|Any CPU.Build.0 = Release|Any CPU
+		{3E29454A-C4DC-44B7-AF0A-A782AD2E73BC}.Release|x64.ActiveCfg = Release|Any CPU
+		{3E29454A-C4DC-44B7-AF0A-A782AD2E73BC}.Release|x64.Build.0 = Release|Any CPU
+		{3E29454A-C4DC-44B7-AF0A-A782AD2E73BC}.Release|x86.ActiveCfg = Release|Any CPU
+		{3E29454A-C4DC-44B7-AF0A-A782AD2E73BC}.Release|x86.Build.0 = Release|Any CPU
+		{43E3B132-2486-44A3-92C6-39E39724FAFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{43E3B132-2486-44A3-92C6-39E39724FAFD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{43E3B132-2486-44A3-92C6-39E39724FAFD}.Debug|x64.ActiveCfg = Debug|Any CPU
+		{43E3B132-2486-44A3-92C6-39E39724FAFD}.Debug|x64.Build.0 = Debug|Any CPU
+		{43E3B132-2486-44A3-92C6-39E39724FAFD}.Debug|x86.ActiveCfg = Debug|Any CPU
+		{43E3B132-2486-44A3-92C6-39E39724FAFD}.Debug|x86.Build.0 = Debug|Any CPU
+		{43E3B132-2486-44A3-92C6-39E39724FAFD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{43E3B132-2486-44A3-92C6-39E39724FAFD}.Release|Any CPU.Build.0 = Release|Any CPU
+		{43E3B132-2486-44A3-92C6-39E39724FAFD}.Release|x64.ActiveCfg = Release|Any CPU
+		{43E3B132-2486-44A3-92C6-39E39724FAFD}.Release|x64.Build.0 = Release|Any CPU
+		{43E3B132-2486-44A3-92C6-39E39724FAFD}.Release|x86.ActiveCfg = Release|Any CPU
+		{43E3B132-2486-44A3-92C6-39E39724FAFD}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -7945,6 +8034,14 @@ Global
 		{37329855-01B8-4B03-9765-1A941B06E43C} = {8C15FD04-7F90-43FC-B488-023432FE3CE1}
 		{D3246226-BC1A-47F1-8E3E-C3380A8F13FB} = {8C15FD04-7F90-43FC-B488-023432FE3CE1}
 		{B06ADD57-E855-4D8C-85DC-B323509AE540} = {898F7E0B-1671-42CB-9DFB-689AFF212ED3}
+		{3D34C81F-2CB5-459E-87E9-0CC04757A2A0} = {017429CC-C5FB-48B4-9C46-034E29EE2F06}
+		{FEF97646-9BC9-4D1B-A939-784D915C18A4} = {3D34C81F-2CB5-459E-87E9-0CC04757A2A0}
+		{839CE175-E0D9-43B9-9FA8-F32C47E7F56B} = {3D34C81F-2CB5-459E-87E9-0CC04757A2A0}
+		{50BF2926-7435-4F4B-88A9-3D0EDEB67FC8} = {3D34C81F-2CB5-459E-87E9-0CC04757A2A0}
+		{16B899B4-A4F4-4EF7-93BB-355861977A12} = {3D34C81F-2CB5-459E-87E9-0CC04757A2A0}
+		{3EAB9890-2C01-444C-ACA0-D77B29CDE08B} = {017429CC-C5FB-48B4-9C46-034E29EE2F06}
+		{3E29454A-C4DC-44B7-AF0A-A782AD2E73BC} = {3EAB9890-2C01-444C-ACA0-D77B29CDE08B}
+		{43E3B132-2486-44A3-92C6-39E39724FAFD} = {3EAB9890-2C01-444C-ACA0-D77B29CDE08B}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F}

+ 1 - 0
dockerbuild.sh

@@ -138,6 +138,7 @@ docker run \
     -e BUILD_BUILDNUMBER \
     -e BUILD_REPOSITORY_NAME \
     -e BUILD_REPOSITORY_URI \
+    -e BUILD_REPOSITORY_NAME \
     -e BUILD_SOURCEVERSION \
     -e BUILD_SOURCEBRANCH \
     -e SYSTEM_DEFINITIONID \

File diff suppressed because it is too large
+ 0 - 0
src/Components/Web.JS/dist/Release/blazor.server.js


File diff suppressed because it is too large
+ 0 - 0
src/Components/Web.JS/dist/Release/blazor.webassembly.js


+ 10 - 1
src/Components/Web.JS/src/Boot.WebAssembly.ts

@@ -130,7 +130,16 @@ function invokeJSFromDotNet(callInfo: Pointer, arg0: any, arg1: any, arg2: any):
     }
   } else {
     const func = DotNet.jsCallDispatcher.findJSFunction(functionIdentifier, targetInstanceId);
-    return func.call(null, arg0, arg1, arg2);
+    const result = func.call(null, arg0, arg1, arg2);
+
+    switch (resultType) {
+      case DotNet.JSCallResultType.Default:
+        return result;
+      case DotNet.JSCallResultType.JSObjectReference:
+        return DotNet.createJSObjectReference(result).__jsObjectId;
+      default:
+        throw new Error(`Invalid JS call result type '${resultType}'.`);
+    }
   }
 }
 

+ 11 - 3
src/Components/Web.JS/src/Virtualize.ts

@@ -5,7 +5,7 @@ export const Virtualize = {
 
 const observersByDotNetId = {};
 
-function findClosestScrollContainer(element: Element | null): Element | null {
+function findClosestScrollContainer(element: HTMLElement | null): HTMLElement | null {
   if (!element) {
     return null;
   }
@@ -20,8 +20,14 @@ function findClosestScrollContainer(element: Element | null): Element | null {
 }
 
 function init(dotNetHelper: any, spacerBefore: HTMLElement, spacerAfter: HTMLElement, rootMargin = 50): void {
+  // Overflow anchoring can cause an ongoing scroll loop, because when we resize the spacers, the browser
+  // would update the scroll position to compensate. Then the spacer would remain visible and we'd keep on
+  // trying to resize it.
+  const scrollContainer = findClosestScrollContainer(spacerBefore);
+  (scrollContainer || document.documentElement).style.overflowAnchor = 'none';
+
   const intersectionObserver = new IntersectionObserver(intersectionCallback, {
-    root: findClosestScrollContainer(spacerBefore),
+    root: scrollContainer,
     rootMargin: `${rootMargin}px`,
   });
 
@@ -57,7 +63,9 @@ function init(dotNetHelper: any, spacerBefore: HTMLElement, spacerAfter: HTMLEle
         return;
       }
 
-      const spacerSeparation = spacerAfter.offsetTop - (spacerBefore.offsetTop + spacerBefore.offsetHeight);
+      const spacerBeforeRect = spacerBefore.getBoundingClientRect();
+      const spacerAfterRect = spacerAfter.getBoundingClientRect();
+      const spacerSeparation = spacerAfterRect.top - spacerBeforeRect.bottom;
       const containerSize = entry.rootBounds?.height;
 
       if (entry.target === spacerBefore) {

+ 11 - 2
src/Components/Web/src/Virtualization/Virtualize.cs

@@ -88,6 +88,15 @@ namespace Microsoft.AspNetCore.Components.Web.Virtualization
         [Parameter]
         public ICollection<TItem>? Items { get; set; }
 
+        /// <summary>
+        /// Gets or sets a value that determines how many additional items will be rendered
+        /// before and after the visible region. This help to reduce the frequency of rendering
+        /// during scrolling. However, higher values mean that more elements will be present
+        /// in the page.
+        /// </summary>
+        [Parameter]
+        public int OverscanCount { get; set; } = 3;
+
         /// <inheritdoc />
         protected override void OnParametersSet()
         {
@@ -251,8 +260,8 @@ namespace Microsoft.AspNetCore.Components.Web.Virtualization
                 _itemSize = ItemSize;
             }
 
-            itemsInSpacer = Math.Max(0, (int)Math.Floor(spacerSize / _itemSize) - 1);
-            visibleItemCapacity = (int)Math.Ceiling(containerSize / _itemSize) + 2;
+            itemsInSpacer = Math.Max(0, (int)Math.Floor(spacerSize / _itemSize) - OverscanCount);
+            visibleItemCapacity = (int)Math.Ceiling(containerSize / _itemSize) + 2 * OverscanCount;
         }
 
         private void UpdateItemDistribution(int itemsBefore, int visibleItemCapacity)

+ 5 - 1
src/Components/WebAssembly/JSInterop/src/Microsoft.JSInterop.WebAssembly.csproj

@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
     <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
@@ -12,4 +12,8 @@
     <Reference Include="Microsoft.JSInterop" />
   </ItemGroup>
 
+  <ItemGroup>
+    <Compile Include="$(SharedSourceRoot)JSInterop\*.cs" />
+  </ItemGroup>
+
 </Project>

+ 45 - 0
src/Components/WebAssembly/JSInterop/src/WebAssemblyJSObjectReference.cs

@@ -0,0 +1,45 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using Microsoft.JSInterop.Implementation;
+
+namespace Microsoft.JSInterop.WebAssembly
+{
+    internal class WebAssemblyJSObjectReference : JSInProcessObjectReference, IJSUnmarshalledObjectReference
+    {
+        private readonly WebAssemblyJSRuntime _jsRuntime;
+
+        public WebAssemblyJSObjectReference(WebAssemblyJSRuntime jsRuntime, long id) : base(jsRuntime, id)
+        {
+            _jsRuntime = jsRuntime;
+        }
+
+        public TResult InvokeUnmarshalled<TResult>(string identifier)
+        {
+            ThrowIfDisposed();
+
+            return _jsRuntime.InvokeUnmarshalled<object, object, object, TResult>(identifier, null, null, null, Id);
+        }
+
+        public TResult InvokeUnmarshalled<T0, TResult>(string identifier, T0 arg0)
+        {
+            ThrowIfDisposed();
+
+            return _jsRuntime.InvokeUnmarshalled<T0, object, object, TResult>(identifier, arg0, null, null, Id);
+        }
+
+        public TResult InvokeUnmarshalled<T0, T1, TResult>(string identifier, T0 arg0, T1 arg1)
+        {
+            ThrowIfDisposed();
+
+            return _jsRuntime.InvokeUnmarshalled<T0, T1, object, TResult>(identifier, arg0, arg1, null, Id);
+        }
+
+        public TResult InvokeUnmarshalled<T0, T1, T2, TResult>(string identifier, T0 arg0, T1 arg1, T2 arg2)
+        {
+            ThrowIfDisposed();
+
+            return _jsRuntime.InvokeUnmarshalled<T0, T1, T2, TResult>(identifier, arg0, arg1, arg2, Id);
+        }
+    }
+}

+ 38 - 19
src/Components/WebAssembly/JSInterop/src/WebAssemblyJSRuntime.cs

@@ -1,6 +1,7 @@
 // Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
+using System;
 using System.Text.Json;
 using Microsoft.JSInterop.Infrastructure;
 using WebAssembly.JSInterop;
@@ -60,32 +61,50 @@ namespace Microsoft.JSInterop.WebAssembly
             BeginInvokeJS(0, "DotNet.jsCallDispatcher.endInvokeDotNetFromJS", args, JSCallResultType.Default, 0);
         }
 
-        /// <inheritdoc />
-        TResult IJSUnmarshalledRuntime.InvokeUnmarshalled<TResult>(string identifier)
-            => ((IJSUnmarshalledRuntime)this).InvokeUnmarshalled<object, object, object, TResult>(identifier, null, null, null);
-
-        /// <inheritdoc />
-        TResult IJSUnmarshalledRuntime.InvokeUnmarshalled<T0, TResult>(string identifier, T0 arg0)
-            => ((IJSUnmarshalledRuntime)this).InvokeUnmarshalled<T0, object, object, TResult>(identifier, arg0, null, null);
-
-        /// <inheritdoc />
-        TResult IJSUnmarshalledRuntime.InvokeUnmarshalled<T0, T1, TResult>(string identifier, T0 arg0, T1 arg1)
-            => ((IJSUnmarshalledRuntime)this).InvokeUnmarshalled<T0, T1, object, TResult>(identifier, arg0, arg1, null);
-
-        /// <inheritdoc />
-        TResult IJSUnmarshalledRuntime.InvokeUnmarshalled<T0, T1, T2, TResult>(string identifier, T0 arg0, T1 arg1, T2 arg2)
+        internal TResult InvokeUnmarshalled<T0, T1, T2, TResult>(string identifier, T0 arg0, T1 arg1, T2 arg2, long targetInstanceId)
         {
+            var resultType = JSCallResultTypeHelper.FromGeneric<TResult>();
+
             var callInfo = new JSCallInfo
             {
                 FunctionIdentifier = identifier,
-                ResultType = ResultTypeFromGeneric<TResult>()
+                TargetInstanceId = targetInstanceId,
+                ResultType = resultType,
             };
 
-            var result = InternalCalls.InvokeJS<T0, T1, T2, TResult>(out var exception, ref callInfo, arg0, arg1, arg2);
+            string exception;
 
-            return exception != null
-                ? throw new JSException(exception)
-                : result;
+            switch (resultType)
+            {
+                case JSCallResultType.Default:
+                    var result = InternalCalls.InvokeJS<T0, T1, T2, TResult>(out exception, ref callInfo, arg0, arg1, arg2);
+                    return exception != null
+                        ? throw new JSException(exception)
+                        : result;
+                case JSCallResultType.JSObjectReference:
+                    var id = InternalCalls.InvokeJS<T0, T1, T2, int>(out exception, ref callInfo, arg0, arg1, arg2);
+                    return exception != null
+                        ? throw new JSException(exception)
+                        : (TResult)(object)new WebAssemblyJSObjectReference(this, id);
+                default:
+                    throw new InvalidOperationException($"Invalid result type '{resultType}'.");
+            }
         }
+
+        /// <inheritdoc />
+        public TResult InvokeUnmarshalled<TResult>(string identifier)
+            => InvokeUnmarshalled<object, object, object, TResult>(identifier, null, null, null, 0);
+
+        /// <inheritdoc />
+        public TResult InvokeUnmarshalled<T0, TResult>(string identifier, T0 arg0)
+            => InvokeUnmarshalled<T0, object, object, TResult>(identifier, arg0, null, null, 0);
+
+        /// <inheritdoc />
+        public TResult InvokeUnmarshalled<T0, T1, TResult>(string identifier, T0 arg0, T1 arg1)
+            => InvokeUnmarshalled<T0, T1, object, TResult>(identifier, arg0, arg1, null, 0);
+
+        /// <inheritdoc />
+        public TResult InvokeUnmarshalled<T0, T1, T2, TResult>(string identifier, T0 arg0, T1 arg1, T2 arg2)
+            => InvokeUnmarshalled<T0, T1, T2, TResult>(identifier, arg0, arg1, arg2, 0);
     }
 }

+ 1 - 1
src/Components/WebAssembly/Sdk/integrationtests/ServiceWorkerAssert.cs

@@ -1,4 +1,4 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;

+ 2 - 2
src/Components/WebAssembly/Sdk/integrationtests/WasmPublishIntegrationTest.cs

@@ -119,7 +119,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
             Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output.
 
             // Verify scoped css
-            Assert.FileExists(result, blazorPublishDirectory, "_framework", "scoped.styles.css");
+            Assert.FileExists(result, blazorPublishDirectory, "blazorwasm.styles.css");
 
             // Verify referenced static web assets
             Assert.FileExists(result, blazorPublishDirectory, "_content", "RazorClassLibrary", "wwwroot", "exampleJsInterop.js");
@@ -663,7 +663,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
             Assert.FileExists(result, blazorPublishDirectory, "_framework", "System.Text.Json.dll"); // Verify dependencies are part of the output.
 
             // Verify scoped css
-            Assert.FileExists(result, blazorPublishDirectory, "_framework", "scoped.styles.css");
+            Assert.FileExists(result, blazorPublishDirectory, "blazorwasm.styles.css");
 
             // Verify static assets are in the publish directory
             Assert.FileExists(result, blazorPublishDirectory, "index.html");

+ 8 - 1
src/Components/WebAssembly/Sdk/src/targets/Microsoft.NET.Sdk.BlazorWebAssembly.Current.targets

@@ -266,11 +266,18 @@ Copyright (c) .NET Foundation. All rights reserved.
 
     <GetCurrentProjectStaticWebAssetsDependsOn>
       $(GetCurrentProjectStaticWebAssetsDependsOn);
-      AddScopedCssBundle;
+      _BlazorWasmReplaceBundle;
       _BlazorWasmPrepareForRun;
     </GetCurrentProjectStaticWebAssetsDependsOn>
   </PropertyGroup>
 
+  <Target Name="_BlazorWasmReplaceBundle">
+    <ItemGroup>
+      <StaticWebAsset Include="@(_AppBundleStaticWebAsset)" />
+      <Staticwebasset Remove="@(_ProjectBundleStaticWebAsset)" />
+    </ItemGroup>
+  </Target>
+
   <Target Name="_BlazorWasmPrepareForRun" DependsOnTargets="_ProcessBlazorWasmOutputs" BeforeTargets="_RazorPrepareForRun" AfterTargets="GetCurrentProjectStaticWebAssets">
     <PropertyGroup>
       <_BlazorBuildBootJsonPath>$(IntermediateOutputPath)blazor.boot.json</_BlazorBuildBootJsonPath>

+ 1 - 1
src/Components/WebAssembly/WebAssembly/src/Rendering/WebAssemblyRenderer.cs

@@ -98,7 +98,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Rendering
         /// <inheritdoc />
         protected override Task UpdateDisplayAsync(in RenderBatch batch)
         {
-            ((IJSUnmarshalledRuntime)DefaultWebAssemblyJSRuntime.Instance).InvokeUnmarshalled<int, RenderBatch, object>(
+            DefaultWebAssemblyJSRuntime.Instance.InvokeUnmarshalled<int, RenderBatch, object>(
                 "Blazor._internal.renderBatch",
                 _webAssemblyRendererId,
                 batch);

+ 1 - 1
src/Components/WebAssembly/WebAssembly/src/Services/WebAssemblyConsoleLogger.cs

@@ -99,7 +99,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Services
                             _jsRuntime.InvokeVoid("console.error", formattedMessage);
                             break;
                         case LogLevel.Critical:
-                            ((IJSUnmarshalledRuntime)_jsRuntime).InvokeUnmarshalled<string, object>("Blazor._internal.dotNetCriticalError", formattedMessage);
+                            _jsRuntime.InvokeUnmarshalled<string, object>("Blazor._internal.dotNetCriticalError", formattedMessage);
                             break;
                         default: // LogLevel.None or invalid enum values
                             Console.WriteLine(formattedMessage);

+ 1 - 1
src/Components/WebAssembly/WebAssembly/src/Services/WebAssemblyJSRuntimeInvoker.cs

@@ -22,6 +22,6 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Services
         public static WebAssemblyJSRuntimeInvoker Instance = new WebAssemblyJSRuntimeInvoker();
 
         public virtual TResult InvokeUnmarshalled<T0, T1, T2, TResult>(string identifier, T0 arg0, T1 arg1, T2 arg2)
-            => ((IJSUnmarshalledRuntime)DefaultWebAssemblyJSRuntime.Instance).InvokeUnmarshalled<T0, T1, T2, TResult>(identifier, arg0, arg1, arg2);
+            => DefaultWebAssemblyJSRuntime.Instance.InvokeUnmarshalled<T0, T1, T2, TResult>(identifier, arg0, arg1, arg2);
     }
 }

+ 4 - 0
src/Components/test/E2ETest/Tests/ComponentRenderingTest.cs

@@ -344,6 +344,10 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
             var specialStyleDiv = appElement.FindElement(By.ClassName("special-style"));
             Assert.Equal("50px", specialStyleDiv.GetCssValue("padding"));
 
+            // This style is isolated to the component and comes from the bundle that gets generated for BasicTestApp
+            // and that includes the @import for the TestContentPackage.bundle.scp.css file
+            Assert.Equal("20px", specialStyleDiv.GetCssValue("font-size"));
+
             // The external components are fully functional, not just static HTML
             var externalComponentButton = specialStyleDiv.FindElement(By.TagName("button"));
             Assert.Equal("Click me", externalComponentButton.Text);

+ 1 - 0
src/Components/test/E2ETest/Tests/InteropTest.cs

@@ -110,6 +110,7 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
                 ["instanceMethodIncomingByRef"] = "123",
                 ["instanceMethodOutgoingByRef"] = "1234",
                 ["jsInProcessObjectReference.identity"] = "Invoked from JSInProcessObjectReference",
+                ["jsUnmarshalledObjectReference.unmarshalledFunction"] = "True",
                 ["stringValueUpperSync"] = "MY STRING",
                 ["testDtoNonSerializedValueSync"] = "99999",
                 ["testDtoSync"] = "Same",

+ 11 - 3
src/Components/test/testassets/BasicTestApp/InteropComponent.razor

@@ -137,7 +137,7 @@
             ReturnValues["returnArray"] = string.Join(",", ((IJSInProcessRuntime)JSRuntime).Invoke<Segment[]>("returnArray").Select(x => x.Source).ToArray());
         }
 
-        var jsObjectReference = await JSRuntime.InvokeAsync<JSObjectReference>("returnJSObjectReference");
+        var jsObjectReference = await JSRuntime.InvokeAsync<IJSObjectReference>("returnJSObjectReference");
         ReturnValues["jsObjectReference.identity"] = await jsObjectReference.InvokeAsync<string>("identity", "Invoked from JSObjectReference");
         ReturnValues["jsObjectReference.nested.add"] = (await jsObjectReference.InvokeAsync<int>("nested.add", 2, 3)).ToString();
         ReturnValues["addViaJSObjectReference"] = (await JSRuntime.InvokeAsync<int>("addViaJSObjectReference", jsObjectReference, 2, 3)).ToString();
@@ -151,7 +151,7 @@
             JSObjectReferenceInvokeNonFunctionException = e;
         }
 
-        var module = await JSRuntime.InvokeAsync<JSObjectReference>("import", "./js/testmodule.js");
+        var module = await JSRuntime.InvokeAsync<IJSObjectReference>("import", "./js/testmodule.js");
         ReturnValues["jsObjectReferenceModule"] = await module.InvokeAsync<string>("identity", "Returned from module!");
 
         if (shouldSupportSyncInterop)
@@ -192,8 +192,16 @@
     {
         var inProcRuntime = ((IJSInProcessRuntime)JSRuntime);
 
-        var jsInProcObjectReference = inProcRuntime.Invoke<JSInProcessObjectReference>("returnJSObjectReference");
+        var jsInProcObjectReference = inProcRuntime.Invoke<IJSInProcessObjectReference>("returnJSObjectReference");
         ReturnValues["jsInProcessObjectReference.identity"] = jsInProcObjectReference.Invoke<string>("identity", "Invoked from JSInProcessObjectReference");
+
+        var unmarshalledRuntime = (IJSUnmarshalledRuntime)JSRuntime;
+        var jsUnmarshalledReference = unmarshalledRuntime.InvokeUnmarshalled<IJSUnmarshalledObjectReference>("returnJSObjectReference");
+        ReturnValues["jsUnmarshalledObjectReference.unmarshalledFunction"] = jsUnmarshalledReference.InvokeUnmarshalled<InteropStruct, bool>("unmarshalledFunction", new InteropStruct
+        {
+            Message = "Sent from .NET",
+            NumberField = 42,
+        }).ToString();
     }
 
     public class PassDotNetObjectByRefArgs

+ 14 - 0
src/Components/test/testassets/BasicTestApp/InteropTest/InteropStruct.cs

@@ -0,0 +1,14 @@
+using System.Runtime.InteropServices;
+
+namespace BasicTestApp.InteropTest
+{
+    [StructLayout(LayoutKind.Explicit)]
+    public struct InteropStruct
+    {
+        [FieldOffset(0)]
+        public string Message;
+
+        [FieldOffset(8)]
+        public int NumberField;
+    }
+}

+ 4 - 4
src/Components/test/testassets/BasicTestApp/InteropTest/JavaScriptInterop.cs

@@ -415,20 +415,20 @@ namespace BasicTestApp.InteropTest
         }
 
         [JSInvokable]
-        public static JSObjectReference RoundTripJSObjectReference(JSObjectReference jsObjectReference)
+        public static IJSObjectReference RoundTripJSObjectReference(IJSObjectReference jsObjectReference)
         {
             return jsObjectReference;
         }
 
         [JSInvokable]
-        public static async Task<JSObjectReference> RoundTripJSObjectReferenceAsync(JSObjectReference jSObjectReference)
+        public static async Task<IJSObjectReference> RoundTripJSObjectReferenceAsync(IJSObjectReference jSObjectReference)
         {
             await Task.Yield();
             return jSObjectReference;
         }
 
         [JSInvokable]
-        public static string InvokeDisposedJSObjectReferenceException(JSInProcessObjectReference jsObjectReference)
+        public static string InvokeDisposedJSObjectReferenceException(IJSInProcessObjectReference jsObjectReference)
         {
             try
             {
@@ -442,7 +442,7 @@ namespace BasicTestApp.InteropTest
         }
 
         [JSInvokable]
-        public static async Task<string> InvokeDisposedJSObjectReferenceExceptionAsync(JSObjectReference jsObjectReference)
+        public static async Task<string> InvokeDisposedJSObjectReferenceExceptionAsync(IJSObjectReference jsObjectReference)
         {
             try
             {

+ 3 - 0
src/Components/test/testassets/BasicTestApp/wwwroot/index.html

@@ -9,6 +9,9 @@
 
     <!-- Used by ExternalContentPackage -->
     <link href="_content/TestContentPackage/styles.css" rel="stylesheet" />
+
+    <!-- App bundle that contains a reference to the scoped css bundle created by TestContentPackage -->
+    <link href="BasicTestApp.styles.css" rel="stylesheet" />
 </head>
 
 <body>

+ 5 - 0
src/Components/test/testassets/BasicTestApp/wwwroot/js/jsinteroptests.js

@@ -233,6 +233,11 @@ function returnJSObjectReference() {
     dispose: function () {
       DotNet.disposeJSObjectReference(this);
     },
+    unmarshalledFunction: function (fields) {
+      const message = Blazor.platform.readStringField(fields, 0);
+      const numberField = Blazor.platform.readInt32Field(fields, 8);
+      return message === "Sent from .NET" && numberField === 42;
+    }
   };
 }
 

+ 3 - 0
src/Components/test/testassets/TestContentPackage/ComponentFromPackage.razor.css

@@ -0,0 +1,3 @@
+div {
+    font-size: 20px;
+}

+ 1 - 2
src/Components/test/testassets/TestContentPackage/wwwroot/styles.css

@@ -1,10 +1,9 @@
-.special-style {
+.special-style {
     background-image: url('./face.png');
     padding: 50px;
     background-repeat: repeat-x;
     border: 5px dashed red;
     font-family: "Comic Sans MS";
-    font-size: 20px;
     font-weight: bold;
     animation: hideous-rainbow 1s infinite;
 }

+ 1 - 16
src/Components/test/testassets/TestServer/Components.TestServer.csproj

@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk.Web">
+<Project Sdk="Microsoft.NET.Sdk.Web">
 
   <PropertyGroup>
     <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
@@ -36,19 +36,4 @@
     </AssemblyAttribute>
   </ItemGroup>
 
-  <Target Name="CopyClientAssetsForTest" BeforeTargets="Build"
-          Inputs="..\BasicTestApp\wwwroot\js\jsinteroptests.js;
-                  ..\BasicTestApp\wwwroot\js\webComponentPerformingJsInterop.js;
-                  ..\BasicTestApp\wwwroot\NotAComponent.html;
-                  ..\BasicTestApp\wwwroot\style.css"
-          Outputs="wwwroot\js\jsinteroptests.js;
-                   wwwroot\js\webComponentPerformingJsInterop.js;
-                   wwwroot\NotAComponent.html;
-                   wwwroot\style.css">
-
-    <MakeDir Directories="wwwroot" />
-
-    <Copy SourceFiles="..\BasicTestApp\wwwroot\js\jsinteroptests.js;..\BasicTestApp\wwwroot\js\webComponentPerformingJsInterop.js;..\BasicTestApp\wwwroot\NotAComponent.html;..\BasicTestApp\wwwroot\style.css"
-          DestinationFiles="wwwroot\js\jsinteroptests.js;wwwroot\js\webComponentPerformingJsInterop.js;wwwroot\NotAComponent.html;wwwroot\style.css" />
-  </Target>
 </Project>

+ 1 - 2
src/Components/test/testassets/TestServer/Pages/_ServerHost.cshtml

@@ -10,6 +10,7 @@
 
     <!-- Used by ExternalContentPackage -->
     <link href="_content/TestContentPackage/styles.css" rel="stylesheet" />
+    <link href="Components.TestServer.styles.css" rel="stylesheet" />
 </head>
 <body>
     <root><component type="typeof(BasicTestApp.Index)" render-mode="Server" /></root>
@@ -41,8 +42,6 @@
 
     <script src="_content/Microsoft.AspNetCore.Components.Web.Extensions/headManager.js"></script>
 
-    <script src="_content/Microsoft.AspNetCore.Components.Web.Extensions/inputFile.js"></script>
-
     <!-- Used by ExternalContentPackage -->
     <script src="_content/TestContentPackage/prompt.js"></script>
     <script>

+ 1 - 0
src/Identity/ApiAuthorization.IdentityServer/src/PublicAPI.Shipped.txt

@@ -0,0 +1 @@
+#nullable enable

+ 129 - 0
src/Identity/ApiAuthorization.IdentityServer/src/PublicAPI.Unshipped.txt

@@ -0,0 +1,129 @@
+#nullable enable
+Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiAuthorizationOptions
+Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiAuthorizationOptions.ApiAuthorizationOptions() -> void
+Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiResourceBuilder
+Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiResourceBuilder.ApiResourceBuilder() -> void
+Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiResourceCollection
+Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiResourceCollection.ApiResourceCollection() -> void
+Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiScopeCollection
+Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiScopeCollection.ApiScopeCollection() -> void
+Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApplicationProfiles
+Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApplicationProfilesPropertyNames
+Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApplicationProfilesPropertyValues
+Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientBuilder
+Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientBuilder.ClientBuilder() -> void
+Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientCollection
+Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientCollection.ClientCollection() -> void
+Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientParametersTagHelper
+Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IClientRequestParametersProvider
+Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceBuilder
+Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceBuilder.IdentityResourceBuilder() -> void
+Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceCollection
+Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceCollection.AddAddress() -> void
+Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceCollection.AddEmail() -> void
+Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceCollection.AddOpenId() -> void
+Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceCollection.AddPhone() -> void
+Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceCollection.AddProfile() -> void
+Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceCollection.IdentityResourceCollection() -> void
+Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityServerJwtConstants
+Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityServerJwtConstants.IdentityServerJwtConstants() -> void
+Microsoft.AspNetCore.Authentication.AuthenticationBuilderExtensions
+Microsoft.Extensions.DependencyInjection.IdentityServerBuilderConfigurationExtensions
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiAuthorizationDbContext<TUser>
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiAuthorizationDbContext<TUser>.ApiAuthorizationDbContext(Microsoft.EntityFrameworkCore.DbContextOptions options, Microsoft.Extensions.Options.IOptions<IdentityServer4.EntityFramework.Options.OperationalStoreOptions> operationalStoreOptions) -> void
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiAuthorizationDbContext<TUser>.DeviceFlowCodes.get -> Microsoft.EntityFrameworkCore.DbSet<IdentityServer4.EntityFramework.Entities.DeviceFlowCodes>
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiAuthorizationDbContext<TUser>.DeviceFlowCodes.set -> void
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiAuthorizationDbContext<TUser>.PersistedGrants.get -> Microsoft.EntityFrameworkCore.DbSet<IdentityServer4.EntityFramework.Entities.PersistedGrant>
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiAuthorizationDbContext<TUser>.PersistedGrants.set -> void
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiAuthorizationOptions.ApiResources.get -> Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiResourceCollection
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiAuthorizationOptions.ApiResources.set -> void
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiAuthorizationOptions.ApiScopes.get -> Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiScopeCollection
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiAuthorizationOptions.ApiScopes.set -> void
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiAuthorizationOptions.Clients.get -> Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientCollection
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiAuthorizationOptions.Clients.set -> void
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiAuthorizationOptions.IdentityResources.get -> Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceCollection
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiAuthorizationOptions.IdentityResources.set -> void
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiAuthorizationOptions.SigningCredential.get -> Microsoft.IdentityModel.Tokens.SigningCredentials
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiAuthorizationOptions.SigningCredential.set -> void
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiResourceBuilder.AllowAllClients() -> Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiResourceBuilder
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiResourceBuilder.ApiResourceBuilder(IdentityServer4.Models.ApiResource resource) -> void
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiResourceBuilder.Build() -> IdentityServer4.Models.ApiResource
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiResourceBuilder.ReplaceScopes(params string[] resourceScopes) -> Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiResourceBuilder
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiResourceBuilder.WithApplicationProfile(string profile) -> Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiResourceBuilder
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiResourceBuilder.WithScopes(params string[] resourceScopes) -> Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiResourceBuilder
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiResourceCollection.AddApiResource(string name, System.Action<Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiResourceBuilder> configure) -> void
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiResourceCollection.AddIdentityServerJwt(string name, System.Action<Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiResourceBuilder> configure) -> void
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiResourceCollection.AddRange(params IdentityServer4.Models.ApiResource[] resources) -> void
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiResourceCollection.ApiResourceCollection(System.Collections.Generic.IList<IdentityServer4.Models.ApiResource> list) -> void
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiResourceCollection.this[string key].get -> IdentityServer4.Models.ApiResource
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiScopeCollection.AddRange(params IdentityServer4.Models.ApiScope[] scopes) -> void
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiScopeCollection.ApiScopeCollection(System.Collections.Generic.IList<IdentityServer4.Models.ApiScope> list) -> void
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiScopeCollection.ContainsScope(string key) -> bool
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiScopeCollection.this[string key].get -> IdentityServer4.Models.ApiScope
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientBuilder.Build() -> IdentityServer4.Models.Client
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientBuilder.ClientBuilder(IdentityServer4.Models.Client client) -> void
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientBuilder.WithApplicationProfile(string profile) -> Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientBuilder
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientBuilder.WithClientId(string clientId) -> Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientBuilder
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientBuilder.WithLogoutRedirectUri(string logoutUri) -> Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientBuilder
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientBuilder.WithRedirectUri(string redirectUri) -> Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientBuilder
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientBuilder.WithScopes(params string[] scopes) -> Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientBuilder
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientBuilder.WithoutClientSecrets() -> Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientBuilder
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientCollection.AddIdentityServerSPA(string clientId, System.Action<Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientBuilder> configure) -> IdentityServer4.Models.Client
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientCollection.AddNativeApp(string clientId, System.Action<Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientBuilder> configure) -> IdentityServer4.Models.Client
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientCollection.AddRange(params IdentityServer4.Models.Client[] clients) -> void
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientCollection.AddSPA(string clientId, System.Action<Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientBuilder> configure) -> IdentityServer4.Models.Client
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientCollection.ClientCollection(System.Collections.Generic.IList<IdentityServer4.Models.Client> list) -> void
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientCollection.this[string key].get -> IdentityServer4.Models.Client
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientParametersTagHelper.ClientId.get -> string
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientParametersTagHelper.ClientId.set -> void
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientParametersTagHelper.ClientParametersTagHelper(Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IClientRequestParametersProvider clientRequestParametersProvider) -> void
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientParametersTagHelper.ViewContext.get -> Microsoft.AspNetCore.Mvc.Rendering.ViewContext
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientParametersTagHelper.ViewContext.set -> void
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IClientRequestParametersProvider.GetClientParameters(Microsoft.AspNetCore.Http.HttpContext context, string clientId) -> System.Collections.Generic.IDictionary<string, string>
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceBuilder.AllowAllClients() -> Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceBuilder
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceBuilder.Build() -> IdentityServer4.Models.IdentityResource
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceBuilder.IdentityResourceBuilder(IdentityServer4.Models.IdentityResource resource) -> void
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceCollection.AddAddress(System.Action<Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceBuilder> configure) -> void
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceCollection.AddEmail(System.Action<Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceBuilder> configure) -> void
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceCollection.AddOpenId(System.Action<Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceBuilder> configure) -> void
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceCollection.AddPhone(System.Action<Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceBuilder> configure) -> void
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceCollection.AddProfile(System.Action<Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceBuilder> configure) -> void
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceCollection.AddRange(params IdentityServer4.Models.IdentityResource[] identityResources) -> void
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceCollection.IdentityResourceCollection(System.Collections.Generic.IList<IdentityServer4.Models.IdentityResource> list) -> void
+~Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceCollection.this[string key].get -> IdentityServer4.Models.IdentityResource
+~const Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApplicationProfiles.API = "API" -> string
+~const Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApplicationProfiles.IdentityServerJwt = "IdentityServerJwt" -> string
+~const Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApplicationProfiles.IdentityServerSPA = "IdentityServerSPA" -> string
+~const Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApplicationProfiles.NativeApp = "NativeApp" -> string
+~const Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApplicationProfiles.SPA = "SPA" -> string
+~const Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApplicationProfilesPropertyNames.Clients = "Clients" -> string
+~const Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApplicationProfilesPropertyNames.Profile = "Profile" -> string
+~const Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApplicationProfilesPropertyNames.Source = "Source" -> string
+~const Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApplicationProfilesPropertyValues.AllowAllApplications = "*" -> string
+~const Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApplicationProfilesPropertyValues.Configuration = "Configuration" -> string
+~const Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApplicationProfilesPropertyValues.Default = "Default" -> string
+~const Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityServerJwtConstants.IdentityServerJwtBearerScheme = "IdentityServerJwtBearer" -> string
+~const Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityServerJwtConstants.IdentityServerJwtScheme = "IdentityServerJwt" -> string
+~override Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiAuthorizationDbContext<TUser>.OnModelCreating(Microsoft.EntityFrameworkCore.ModelBuilder builder) -> void
+~override Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientParametersTagHelper.Process(Microsoft.AspNetCore.Razor.TagHelpers.TagHelperContext context, Microsoft.AspNetCore.Razor.TagHelpers.TagHelperOutput output) -> void
+~static Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiResourceBuilder.ApiResource(string name) -> Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiResourceBuilder
+~static Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiResourceBuilder.IdentityServerJwt(string name) -> Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiResourceBuilder
+~static Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientBuilder.IdentityServerSPA(string clientId) -> Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientBuilder
+~static Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientBuilder.NativeApp(string clientId) -> Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientBuilder
+~static Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientBuilder.SPA(string clientId) -> Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ClientBuilder
+~static Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceBuilder.Address() -> Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceBuilder
+~static Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceBuilder.Email() -> Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceBuilder
+~static Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceBuilder.OpenId() -> Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceBuilder
+~static Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceBuilder.Phone() -> Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceBuilder
+~static Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceBuilder.Profile() -> Microsoft.AspNetCore.ApiAuthorization.IdentityServer.IdentityResourceBuilder
+~static Microsoft.AspNetCore.Authentication.AuthenticationBuilderExtensions.AddIdentityServerJwt(this Microsoft.AspNetCore.Authentication.AuthenticationBuilder builder) -> Microsoft.AspNetCore.Authentication.AuthenticationBuilder
+~static Microsoft.Extensions.DependencyInjection.IdentityServerBuilderConfigurationExtensions.AddApiAuthorization<TUser, TContext>(this Microsoft.Extensions.DependencyInjection.IIdentityServerBuilder builder) -> Microsoft.Extensions.DependencyInjection.IIdentityServerBuilder
+~static Microsoft.Extensions.DependencyInjection.IdentityServerBuilderConfigurationExtensions.AddApiAuthorization<TUser, TContext>(this Microsoft.Extensions.DependencyInjection.IIdentityServerBuilder builder, System.Action<Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ApiAuthorizationOptions> configure) -> Microsoft.Extensions.DependencyInjection.IIdentityServerBuilder
+~static Microsoft.Extensions.DependencyInjection.IdentityServerBuilderConfigurationExtensions.AddApiResources(this Microsoft.Extensions.DependencyInjection.IIdentityServerBuilder builder) -> Microsoft.Extensions.DependencyInjection.IIdentityServerBuilder
+~static Microsoft.Extensions.DependencyInjection.IdentityServerBuilderConfigurationExtensions.AddApiResources(this Microsoft.Extensions.DependencyInjection.IIdentityServerBuilder builder, Microsoft.Extensions.Configuration.IConfiguration configuration) -> Microsoft.Extensions.DependencyInjection.IIdentityServerBuilder
+~static Microsoft.Extensions.DependencyInjection.IdentityServerBuilderConfigurationExtensions.AddClients(this Microsoft.Extensions.DependencyInjection.IIdentityServerBuilder builder) -> Microsoft.Extensions.DependencyInjection.IIdentityServerBuilder
+~static Microsoft.Extensions.DependencyInjection.IdentityServerBuilderConfigurationExtensions.AddClients(this Microsoft.Extensions.DependencyInjection.IIdentityServerBuilder builder, Microsoft.Extensions.Configuration.IConfiguration configuration) -> Microsoft.Extensions.DependencyInjection.IIdentityServerBuilder
+~static Microsoft.Extensions.DependencyInjection.IdentityServerBuilderConfigurationExtensions.AddIdentityResources(this Microsoft.Extensions.DependencyInjection.IIdentityServerBuilder builder) -> Microsoft.Extensions.DependencyInjection.IIdentityServerBuilder
+~static Microsoft.Extensions.DependencyInjection.IdentityServerBuilderConfigurationExtensions.AddIdentityResources(this Microsoft.Extensions.DependencyInjection.IIdentityServerBuilder builder, Microsoft.Extensions.Configuration.IConfiguration configuration) -> Microsoft.Extensions.DependencyInjection.IIdentityServerBuilder
+~static Microsoft.Extensions.DependencyInjection.IdentityServerBuilderConfigurationExtensions.AddSigningCredentials(this Microsoft.Extensions.DependencyInjection.IIdentityServerBuilder builder) -> Microsoft.Extensions.DependencyInjection.IIdentityServerBuilder
+~static Microsoft.Extensions.DependencyInjection.IdentityServerBuilderConfigurationExtensions.AddSigningCredentials(this Microsoft.Extensions.DependencyInjection.IIdentityServerBuilder builder, Microsoft.Extensions.Configuration.IConfiguration configuration) -> Microsoft.Extensions.DependencyInjection.IIdentityServerBuilder

+ 1 - 0
src/Identity/Core/src/PublicAPI.Shipped.txt

@@ -0,0 +1 @@
+#nullable enable

+ 139 - 0
src/Identity/Core/src/PublicAPI.Unshipped.txt

@@ -0,0 +1,139 @@
+#nullable enable
+Microsoft.AspNetCore.Identity.DataProtectionTokenProviderOptions
+Microsoft.AspNetCore.Identity.DataProtectionTokenProviderOptions.DataProtectionTokenProviderOptions() -> void
+Microsoft.AspNetCore.Identity.DataProtectionTokenProviderOptions.TokenLifespan.get -> System.TimeSpan
+Microsoft.AspNetCore.Identity.DataProtectionTokenProviderOptions.TokenLifespan.set -> void
+Microsoft.AspNetCore.Identity.ExternalLoginInfo
+Microsoft.AspNetCore.Identity.ISecurityStampValidator
+Microsoft.AspNetCore.Identity.ITwoFactorSecurityStampValidator
+Microsoft.AspNetCore.Identity.IdentityBuilderExtensions
+Microsoft.AspNetCore.Identity.IdentityConstants
+Microsoft.AspNetCore.Identity.IdentityConstants.IdentityConstants() -> void
+Microsoft.AspNetCore.Identity.IdentityCookieAuthenticationBuilderExtensions
+Microsoft.AspNetCore.Identity.IdentityCookiesBuilder
+Microsoft.AspNetCore.Identity.IdentityCookiesBuilder.IdentityCookiesBuilder() -> void
+Microsoft.AspNetCore.Identity.SecurityStampRefreshingPrincipalContext
+Microsoft.AspNetCore.Identity.SecurityStampRefreshingPrincipalContext.SecurityStampRefreshingPrincipalContext() -> void
+Microsoft.AspNetCore.Identity.SecurityStampValidator
+Microsoft.AspNetCore.Identity.SecurityStampValidatorOptions
+Microsoft.AspNetCore.Identity.SecurityStampValidatorOptions.SecurityStampValidatorOptions() -> void
+Microsoft.AspNetCore.Identity.SecurityStampValidatorOptions.ValidationInterval.get -> System.TimeSpan
+Microsoft.AspNetCore.Identity.SecurityStampValidatorOptions.ValidationInterval.set -> void
+Microsoft.Extensions.DependencyInjection.IdentityServiceCollectionExtensions
+override Microsoft.AspNetCore.Identity.AspNetRoleManager<TRole>.CancellationToken.get -> System.Threading.CancellationToken
+override Microsoft.AspNetCore.Identity.AspNetUserManager<TUser>.CancellationToken.get -> System.Threading.CancellationToken
+~Microsoft.AspNetCore.Identity.AspNetRoleManager<TRole>
+~Microsoft.AspNetCore.Identity.AspNetRoleManager<TRole>.AspNetRoleManager(Microsoft.AspNetCore.Identity.IRoleStore<TRole> store, System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Identity.IRoleValidator<TRole>> roleValidators, Microsoft.AspNetCore.Identity.ILookupNormalizer keyNormalizer, Microsoft.AspNetCore.Identity.IdentityErrorDescriber errors, Microsoft.Extensions.Logging.ILogger<Microsoft.AspNetCore.Identity.RoleManager<TRole>> logger, Microsoft.AspNetCore.Http.IHttpContextAccessor contextAccessor) -> void
+~Microsoft.AspNetCore.Identity.AspNetUserManager<TUser>
+~Microsoft.AspNetCore.Identity.AspNetUserManager<TUser>.AspNetUserManager(Microsoft.AspNetCore.Identity.IUserStore<TUser> store, Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Identity.IdentityOptions> optionsAccessor, Microsoft.AspNetCore.Identity.IPasswordHasher<TUser> passwordHasher, System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Identity.IUserValidator<TUser>> userValidators, System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Identity.IPasswordValidator<TUser>> passwordValidators, Microsoft.AspNetCore.Identity.ILookupNormalizer keyNormalizer, Microsoft.AspNetCore.Identity.IdentityErrorDescriber errors, System.IServiceProvider services, Microsoft.Extensions.Logging.ILogger<Microsoft.AspNetCore.Identity.UserManager<TUser>> logger) -> void
+~Microsoft.AspNetCore.Identity.DataProtectionTokenProviderOptions.Name.get -> string
+~Microsoft.AspNetCore.Identity.DataProtectionTokenProviderOptions.Name.set -> void
+~Microsoft.AspNetCore.Identity.DataProtectorTokenProvider<TUser>
+~Microsoft.AspNetCore.Identity.DataProtectorTokenProvider<TUser>.DataProtectorTokenProvider(Microsoft.AspNetCore.DataProtection.IDataProtectionProvider dataProtectionProvider, Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Identity.DataProtectionTokenProviderOptions> options, Microsoft.Extensions.Logging.ILogger<Microsoft.AspNetCore.Identity.DataProtectorTokenProvider<TUser>> logger) -> void
+~Microsoft.AspNetCore.Identity.DataProtectorTokenProvider<TUser>.Logger.get -> Microsoft.Extensions.Logging.ILogger<Microsoft.AspNetCore.Identity.DataProtectorTokenProvider<TUser>>
+~Microsoft.AspNetCore.Identity.DataProtectorTokenProvider<TUser>.Name.get -> string
+~Microsoft.AspNetCore.Identity.DataProtectorTokenProvider<TUser>.Options.get -> Microsoft.AspNetCore.Identity.DataProtectionTokenProviderOptions
+~Microsoft.AspNetCore.Identity.DataProtectorTokenProvider<TUser>.Protector.get -> Microsoft.AspNetCore.DataProtection.IDataProtector
+~Microsoft.AspNetCore.Identity.ExternalLoginInfo.AuthenticationProperties.get -> Microsoft.AspNetCore.Authentication.AuthenticationProperties
+~Microsoft.AspNetCore.Identity.ExternalLoginInfo.AuthenticationProperties.set -> void
+~Microsoft.AspNetCore.Identity.ExternalLoginInfo.AuthenticationTokens.get -> System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Authentication.AuthenticationToken>
+~Microsoft.AspNetCore.Identity.ExternalLoginInfo.AuthenticationTokens.set -> void
+~Microsoft.AspNetCore.Identity.ExternalLoginInfo.ExternalLoginInfo(System.Security.Claims.ClaimsPrincipal principal, string loginProvider, string providerKey, string displayName) -> void
+~Microsoft.AspNetCore.Identity.ExternalLoginInfo.Principal.get -> System.Security.Claims.ClaimsPrincipal
+~Microsoft.AspNetCore.Identity.ExternalLoginInfo.Principal.set -> void
+~Microsoft.AspNetCore.Identity.ISecurityStampValidator.ValidateAsync(Microsoft.AspNetCore.Authentication.Cookies.CookieValidatePrincipalContext context) -> System.Threading.Tasks.Task
+~Microsoft.AspNetCore.Identity.IdentityCookiesBuilder.ApplicationCookie.get -> Microsoft.Extensions.Options.OptionsBuilder<Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions>
+~Microsoft.AspNetCore.Identity.IdentityCookiesBuilder.ApplicationCookie.set -> void
+~Microsoft.AspNetCore.Identity.IdentityCookiesBuilder.ExternalCookie.get -> Microsoft.Extensions.Options.OptionsBuilder<Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions>
+~Microsoft.AspNetCore.Identity.IdentityCookiesBuilder.ExternalCookie.set -> void
+~Microsoft.AspNetCore.Identity.IdentityCookiesBuilder.TwoFactorRememberMeCookie.get -> Microsoft.Extensions.Options.OptionsBuilder<Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions>
+~Microsoft.AspNetCore.Identity.IdentityCookiesBuilder.TwoFactorRememberMeCookie.set -> void
+~Microsoft.AspNetCore.Identity.IdentityCookiesBuilder.TwoFactorUserIdCookie.get -> Microsoft.Extensions.Options.OptionsBuilder<Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions>
+~Microsoft.AspNetCore.Identity.IdentityCookiesBuilder.TwoFactorUserIdCookie.set -> void
+~Microsoft.AspNetCore.Identity.SecurityStampRefreshingPrincipalContext.CurrentPrincipal.get -> System.Security.Claims.ClaimsPrincipal
+~Microsoft.AspNetCore.Identity.SecurityStampRefreshingPrincipalContext.CurrentPrincipal.set -> void
+~Microsoft.AspNetCore.Identity.SecurityStampRefreshingPrincipalContext.NewPrincipal.get -> System.Security.Claims.ClaimsPrincipal
+~Microsoft.AspNetCore.Identity.SecurityStampRefreshingPrincipalContext.NewPrincipal.set -> void
+~Microsoft.AspNetCore.Identity.SecurityStampValidator<TUser>
+~Microsoft.AspNetCore.Identity.SecurityStampValidator<TUser>.Clock.get -> Microsoft.AspNetCore.Authentication.ISystemClock
+~Microsoft.AspNetCore.Identity.SecurityStampValidator<TUser>.Logger.get -> Microsoft.Extensions.Logging.ILogger
+~Microsoft.AspNetCore.Identity.SecurityStampValidator<TUser>.Logger.set -> void
+~Microsoft.AspNetCore.Identity.SecurityStampValidator<TUser>.Options.get -> Microsoft.AspNetCore.Identity.SecurityStampValidatorOptions
+~Microsoft.AspNetCore.Identity.SecurityStampValidator<TUser>.SecurityStampValidator(Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Identity.SecurityStampValidatorOptions> options, Microsoft.AspNetCore.Identity.SignInManager<TUser> signInManager, Microsoft.AspNetCore.Authentication.ISystemClock clock, Microsoft.Extensions.Logging.ILoggerFactory logger) -> void
+~Microsoft.AspNetCore.Identity.SecurityStampValidator<TUser>.SignInManager.get -> Microsoft.AspNetCore.Identity.SignInManager<TUser>
+~Microsoft.AspNetCore.Identity.SecurityStampValidatorOptions.OnRefreshingPrincipal.get -> System.Func<Microsoft.AspNetCore.Identity.SecurityStampRefreshingPrincipalContext, System.Threading.Tasks.Task>
+~Microsoft.AspNetCore.Identity.SecurityStampValidatorOptions.OnRefreshingPrincipal.set -> void
+~Microsoft.AspNetCore.Identity.SignInManager<TUser>
+~Microsoft.AspNetCore.Identity.SignInManager<TUser>.ClaimsFactory.get -> Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory<TUser>
+~Microsoft.AspNetCore.Identity.SignInManager<TUser>.ClaimsFactory.set -> void
+~Microsoft.AspNetCore.Identity.SignInManager<TUser>.Context.get -> Microsoft.AspNetCore.Http.HttpContext
+~Microsoft.AspNetCore.Identity.SignInManager<TUser>.Context.set -> void
+~Microsoft.AspNetCore.Identity.SignInManager<TUser>.Options.get -> Microsoft.AspNetCore.Identity.IdentityOptions
+~Microsoft.AspNetCore.Identity.SignInManager<TUser>.Options.set -> void
+~Microsoft.AspNetCore.Identity.SignInManager<TUser>.SignInManager(Microsoft.AspNetCore.Identity.UserManager<TUser> userManager, Microsoft.AspNetCore.Http.IHttpContextAccessor contextAccessor, Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory<TUser> claimsFactory, Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Identity.IdentityOptions> optionsAccessor, Microsoft.Extensions.Logging.ILogger<Microsoft.AspNetCore.Identity.SignInManager<TUser>> logger, Microsoft.AspNetCore.Authentication.IAuthenticationSchemeProvider schemes, Microsoft.AspNetCore.Identity.IUserConfirmation<TUser> confirmation) -> void
+~Microsoft.AspNetCore.Identity.SignInManager<TUser>.UserManager.get -> Microsoft.AspNetCore.Identity.UserManager<TUser>
+~Microsoft.AspNetCore.Identity.SignInManager<TUser>.UserManager.set -> void
+~Microsoft.AspNetCore.Identity.TwoFactorSecurityStampValidator<TUser>
+~Microsoft.AspNetCore.Identity.TwoFactorSecurityStampValidator<TUser>.TwoFactorSecurityStampValidator(Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Identity.SecurityStampValidatorOptions> options, Microsoft.AspNetCore.Identity.SignInManager<TUser> signInManager, Microsoft.AspNetCore.Authentication.ISystemClock clock, Microsoft.Extensions.Logging.ILoggerFactory logger) -> void
+~override Microsoft.AspNetCore.Identity.TwoFactorSecurityStampValidator<TUser>.SecurityStampVerified(TUser user, Microsoft.AspNetCore.Authentication.Cookies.CookieValidatePrincipalContext context) -> System.Threading.Tasks.Task
+~override Microsoft.AspNetCore.Identity.TwoFactorSecurityStampValidator<TUser>.VerifySecurityStamp(System.Security.Claims.ClaimsPrincipal principal) -> System.Threading.Tasks.Task<TUser>
+~static Microsoft.AspNetCore.Identity.IdentityBuilderExtensions.AddDefaultTokenProviders(this Microsoft.AspNetCore.Identity.IdentityBuilder builder) -> Microsoft.AspNetCore.Identity.IdentityBuilder
+~static Microsoft.AspNetCore.Identity.IdentityBuilderExtensions.AddSignInManager(this Microsoft.AspNetCore.Identity.IdentityBuilder builder) -> Microsoft.AspNetCore.Identity.IdentityBuilder
+~static Microsoft.AspNetCore.Identity.IdentityBuilderExtensions.AddSignInManager<TSignInManager>(this Microsoft.AspNetCore.Identity.IdentityBuilder builder) -> Microsoft.AspNetCore.Identity.IdentityBuilder
+~static Microsoft.AspNetCore.Identity.IdentityCookieAuthenticationBuilderExtensions.AddApplicationCookie(this Microsoft.AspNetCore.Authentication.AuthenticationBuilder builder) -> Microsoft.Extensions.Options.OptionsBuilder<Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions>
+~static Microsoft.AspNetCore.Identity.IdentityCookieAuthenticationBuilderExtensions.AddExternalCookie(this Microsoft.AspNetCore.Authentication.AuthenticationBuilder builder) -> Microsoft.Extensions.Options.OptionsBuilder<Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions>
+~static Microsoft.AspNetCore.Identity.IdentityCookieAuthenticationBuilderExtensions.AddIdentityCookies(this Microsoft.AspNetCore.Authentication.AuthenticationBuilder builder) -> Microsoft.AspNetCore.Identity.IdentityCookiesBuilder
+~static Microsoft.AspNetCore.Identity.IdentityCookieAuthenticationBuilderExtensions.AddIdentityCookies(this Microsoft.AspNetCore.Authentication.AuthenticationBuilder builder, System.Action<Microsoft.AspNetCore.Identity.IdentityCookiesBuilder> configureCookies) -> Microsoft.AspNetCore.Identity.IdentityCookiesBuilder
+~static Microsoft.AspNetCore.Identity.IdentityCookieAuthenticationBuilderExtensions.AddTwoFactorRememberMeCookie(this Microsoft.AspNetCore.Authentication.AuthenticationBuilder builder) -> Microsoft.Extensions.Options.OptionsBuilder<Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions>
+~static Microsoft.AspNetCore.Identity.IdentityCookieAuthenticationBuilderExtensions.AddTwoFactorUserIdCookie(this Microsoft.AspNetCore.Authentication.AuthenticationBuilder builder) -> Microsoft.Extensions.Options.OptionsBuilder<Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions>
+~static Microsoft.AspNetCore.Identity.SecurityStampValidator.ValidateAsync<TValidator>(Microsoft.AspNetCore.Authentication.Cookies.CookieValidatePrincipalContext context) -> System.Threading.Tasks.Task
+~static Microsoft.AspNetCore.Identity.SecurityStampValidator.ValidatePrincipalAsync(Microsoft.AspNetCore.Authentication.Cookies.CookieValidatePrincipalContext context) -> System.Threading.Tasks.Task
+~static Microsoft.Extensions.DependencyInjection.IdentityServiceCollectionExtensions.AddIdentity<TUser, TRole>(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) -> Microsoft.AspNetCore.Identity.IdentityBuilder
+~static Microsoft.Extensions.DependencyInjection.IdentityServiceCollectionExtensions.AddIdentity<TUser, TRole>(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action<Microsoft.AspNetCore.Identity.IdentityOptions> setupAction) -> Microsoft.AspNetCore.Identity.IdentityBuilder
+~static Microsoft.Extensions.DependencyInjection.IdentityServiceCollectionExtensions.ConfigureApplicationCookie(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action<Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions> configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection
+~static Microsoft.Extensions.DependencyInjection.IdentityServiceCollectionExtensions.ConfigureExternalCookie(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action<Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions> configure) -> Microsoft.Extensions.DependencyInjection.IServiceCollection
+~static readonly Microsoft.AspNetCore.Identity.IdentityConstants.ApplicationScheme -> string
+~static readonly Microsoft.AspNetCore.Identity.IdentityConstants.ExternalScheme -> string
+~static readonly Microsoft.AspNetCore.Identity.IdentityConstants.TwoFactorRememberMeScheme -> string
+~static readonly Microsoft.AspNetCore.Identity.IdentityConstants.TwoFactorUserIdScheme -> string
+~virtual Microsoft.AspNetCore.Identity.DataProtectorTokenProvider<TUser>.CanGenerateTwoFactorTokenAsync(Microsoft.AspNetCore.Identity.UserManager<TUser> manager, TUser user) -> System.Threading.Tasks.Task<bool>
+~virtual Microsoft.AspNetCore.Identity.DataProtectorTokenProvider<TUser>.GenerateAsync(string purpose, Microsoft.AspNetCore.Identity.UserManager<TUser> manager, TUser user) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.DataProtectorTokenProvider<TUser>.ValidateAsync(string purpose, string token, Microsoft.AspNetCore.Identity.UserManager<TUser> manager, TUser user) -> System.Threading.Tasks.Task<bool>
+~virtual Microsoft.AspNetCore.Identity.SecurityStampValidator<TUser>.SecurityStampVerified(TUser user, Microsoft.AspNetCore.Authentication.Cookies.CookieValidatePrincipalContext context) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.SecurityStampValidator<TUser>.ValidateAsync(Microsoft.AspNetCore.Authentication.Cookies.CookieValidatePrincipalContext context) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.SecurityStampValidator<TUser>.VerifySecurityStamp(System.Security.Claims.ClaimsPrincipal principal) -> System.Threading.Tasks.Task<TUser>
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.CanSignInAsync(TUser user) -> System.Threading.Tasks.Task<bool>
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.CheckPasswordSignInAsync(TUser user, string password, bool lockoutOnFailure) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.SignInResult>
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.ConfigureExternalAuthenticationProperties(string provider, string redirectUrl, string userId = null) -> Microsoft.AspNetCore.Authentication.AuthenticationProperties
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.CreateUserPrincipalAsync(TUser user) -> System.Threading.Tasks.Task<System.Security.Claims.ClaimsPrincipal>
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.ExternalLoginSignInAsync(string loginProvider, string providerKey, bool isPersistent) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.SignInResult>
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.ExternalLoginSignInAsync(string loginProvider, string providerKey, bool isPersistent, bool bypassTwoFactor) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.SignInResult>
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.ForgetTwoFactorClientAsync() -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.GetExternalAuthenticationSchemesAsync() -> System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Authentication.AuthenticationScheme>>
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.GetExternalLoginInfoAsync(string expectedXsrf = null) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.ExternalLoginInfo>
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.GetTwoFactorAuthenticationUserAsync() -> System.Threading.Tasks.Task<TUser>
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.IsLockedOut(TUser user) -> System.Threading.Tasks.Task<bool>
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.IsSignedIn(System.Security.Claims.ClaimsPrincipal principal) -> bool
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.IsTwoFactorClientRememberedAsync(TUser user) -> System.Threading.Tasks.Task<bool>
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.LockedOut(TUser user) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.SignInResult>
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.Logger.get -> Microsoft.Extensions.Logging.ILogger
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.Logger.set -> void
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.PasswordSignInAsync(TUser user, string password, bool isPersistent, bool lockoutOnFailure) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.SignInResult>
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.PasswordSignInAsync(string userName, string password, bool isPersistent, bool lockoutOnFailure) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.SignInResult>
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.PreSignInCheck(TUser user) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.SignInResult>
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.RefreshSignInAsync(TUser user) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.RememberTwoFactorClientAsync(TUser user) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.ResetLockout(TUser user) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.SignInAsync(TUser user, Microsoft.AspNetCore.Authentication.AuthenticationProperties authenticationProperties, string authenticationMethod = null) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.SignInAsync(TUser user, bool isPersistent, string authenticationMethod = null) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.SignInOrTwoFactorAsync(TUser user, bool isPersistent, string loginProvider = null, bool bypassTwoFactor = false) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.SignInResult>
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.SignInWithClaimsAsync(TUser user, Microsoft.AspNetCore.Authentication.AuthenticationProperties authenticationProperties, System.Collections.Generic.IEnumerable<System.Security.Claims.Claim> additionalClaims) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.SignInWithClaimsAsync(TUser user, bool isPersistent, System.Collections.Generic.IEnumerable<System.Security.Claims.Claim> additionalClaims) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.SignOutAsync() -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.TwoFactorAuthenticatorSignInAsync(string code, bool isPersistent, bool rememberClient) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.SignInResult>
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.TwoFactorRecoveryCodeSignInAsync(string recoveryCode) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.SignInResult>
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.TwoFactorSignInAsync(string provider, string code, bool isPersistent, bool rememberClient) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.SignInResult>
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.UpdateExternalAuthenticationTokensAsync(Microsoft.AspNetCore.Identity.ExternalLoginInfo externalLogin) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.ValidateSecurityStampAsync(System.Security.Claims.ClaimsPrincipal principal) -> System.Threading.Tasks.Task<TUser>
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.ValidateSecurityStampAsync(TUser user, string securityStamp) -> System.Threading.Tasks.Task<bool>
+~virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.ValidateTwoFactorSecurityStampAsync(System.Security.Claims.ClaimsPrincipal principal) -> System.Threading.Tasks.Task<TUser>

+ 3 - 0
src/Identity/Core/src/SignInManager.cs

@@ -3,6 +3,7 @@
 
 using System;
 using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 using System.Security.Claims;
 using System.Threading.Tasks;
@@ -197,6 +198,7 @@ namespace Microsoft.AspNetCore.Identity
         /// <param name="isPersistent">Flag indicating whether the sign-in cookie should persist after the browser is closed.</param>
         /// <param name="authenticationMethod">Name of the method used to authenticate the user.</param>
         /// <returns>The task object representing the asynchronous operation.</returns>
+        [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required for backwards compatibility")]
         public virtual Task SignInAsync(TUser user, bool isPersistent, string authenticationMethod = null)
             => SignInAsync(user, new AuthenticationProperties { IsPersistent = isPersistent }, authenticationMethod);
 
@@ -207,6 +209,7 @@ namespace Microsoft.AspNetCore.Identity
         /// <param name="authenticationProperties">Properties applied to the login and authentication cookie.</param>
         /// <param name="authenticationMethod">Name of the method used to authenticate the user.</param>
         /// <returns>The task object representing the asynchronous operation.</returns>
+        [SuppressMessage("ApiDesign", "RS0026:Do not add multiple public overloads with optional parameters", Justification = "Required for backwards compatibility")]
         public virtual Task SignInAsync(TUser user, AuthenticationProperties authenticationProperties, string authenticationMethod = null)
         {
             IList<Claim> additionalClaims = Array.Empty<Claim>();

+ 1 - 0
src/Identity/Extensions.Core/src/PublicAPI.Shipped.txt

@@ -0,0 +1 @@
+#nullable enable

+ 540 - 0
src/Identity/Extensions.Core/src/PublicAPI.Unshipped.txt

@@ -0,0 +1,540 @@
+#nullable enable
+Microsoft.AspNetCore.Identity.AuthenticatorTokenProvider<TUser>.AuthenticatorTokenProvider() -> void
+Microsoft.AspNetCore.Identity.ClaimsIdentityOptions
+Microsoft.AspNetCore.Identity.ClaimsIdentityOptions.ClaimsIdentityOptions() -> void
+Microsoft.AspNetCore.Identity.DefaultPersonalDataProtector
+Microsoft.AspNetCore.Identity.DefaultUserConfirmation<TUser>.DefaultUserConfirmation() -> void
+Microsoft.AspNetCore.Identity.EmailTokenProvider<TUser>.EmailTokenProvider() -> void
+Microsoft.AspNetCore.Identity.ILookupNormalizer
+Microsoft.AspNetCore.Identity.ILookupProtector
+Microsoft.AspNetCore.Identity.ILookupProtectorKeyRing
+Microsoft.AspNetCore.Identity.IPersonalDataProtector
+Microsoft.AspNetCore.Identity.IdentityBuilder
+Microsoft.AspNetCore.Identity.IdentityError
+Microsoft.AspNetCore.Identity.IdentityError.IdentityError() -> void
+Microsoft.AspNetCore.Identity.IdentityErrorDescriber
+Microsoft.AspNetCore.Identity.IdentityErrorDescriber.IdentityErrorDescriber() -> void
+Microsoft.AspNetCore.Identity.IdentityOptions
+Microsoft.AspNetCore.Identity.IdentityOptions.IdentityOptions() -> void
+Microsoft.AspNetCore.Identity.IdentityResult
+Microsoft.AspNetCore.Identity.IdentityResult.IdentityResult() -> void
+Microsoft.AspNetCore.Identity.IdentityResult.Succeeded.get -> bool
+Microsoft.AspNetCore.Identity.IdentityResult.Succeeded.set -> void
+Microsoft.AspNetCore.Identity.LockoutOptions
+Microsoft.AspNetCore.Identity.LockoutOptions.AllowedForNewUsers.get -> bool
+Microsoft.AspNetCore.Identity.LockoutOptions.AllowedForNewUsers.set -> void
+Microsoft.AspNetCore.Identity.LockoutOptions.DefaultLockoutTimeSpan.get -> System.TimeSpan
+Microsoft.AspNetCore.Identity.LockoutOptions.DefaultLockoutTimeSpan.set -> void
+Microsoft.AspNetCore.Identity.LockoutOptions.LockoutOptions() -> void
+Microsoft.AspNetCore.Identity.LockoutOptions.MaxFailedAccessAttempts.get -> int
+Microsoft.AspNetCore.Identity.LockoutOptions.MaxFailedAccessAttempts.set -> void
+Microsoft.AspNetCore.Identity.PasswordHasherCompatibilityMode
+Microsoft.AspNetCore.Identity.PasswordHasherCompatibilityMode.IdentityV2 = 0 -> Microsoft.AspNetCore.Identity.PasswordHasherCompatibilityMode
+Microsoft.AspNetCore.Identity.PasswordHasherCompatibilityMode.IdentityV3 = 1 -> Microsoft.AspNetCore.Identity.PasswordHasherCompatibilityMode
+Microsoft.AspNetCore.Identity.PasswordHasherOptions
+Microsoft.AspNetCore.Identity.PasswordHasherOptions.CompatibilityMode.get -> Microsoft.AspNetCore.Identity.PasswordHasherCompatibilityMode
+Microsoft.AspNetCore.Identity.PasswordHasherOptions.CompatibilityMode.set -> void
+Microsoft.AspNetCore.Identity.PasswordHasherOptions.IterationCount.get -> int
+Microsoft.AspNetCore.Identity.PasswordHasherOptions.IterationCount.set -> void
+Microsoft.AspNetCore.Identity.PasswordHasherOptions.PasswordHasherOptions() -> void
+Microsoft.AspNetCore.Identity.PasswordOptions
+Microsoft.AspNetCore.Identity.PasswordOptions.PasswordOptions() -> void
+Microsoft.AspNetCore.Identity.PasswordOptions.RequireDigit.get -> bool
+Microsoft.AspNetCore.Identity.PasswordOptions.RequireDigit.set -> void
+Microsoft.AspNetCore.Identity.PasswordOptions.RequireLowercase.get -> bool
+Microsoft.AspNetCore.Identity.PasswordOptions.RequireLowercase.set -> void
+Microsoft.AspNetCore.Identity.PasswordOptions.RequireNonAlphanumeric.get -> bool
+Microsoft.AspNetCore.Identity.PasswordOptions.RequireNonAlphanumeric.set -> void
+Microsoft.AspNetCore.Identity.PasswordOptions.RequireUppercase.get -> bool
+Microsoft.AspNetCore.Identity.PasswordOptions.RequireUppercase.set -> void
+Microsoft.AspNetCore.Identity.PasswordOptions.RequiredLength.get -> int
+Microsoft.AspNetCore.Identity.PasswordOptions.RequiredLength.set -> void
+Microsoft.AspNetCore.Identity.PasswordOptions.RequiredUniqueChars.get -> int
+Microsoft.AspNetCore.Identity.PasswordOptions.RequiredUniqueChars.set -> void
+Microsoft.AspNetCore.Identity.PasswordVerificationResult
+Microsoft.AspNetCore.Identity.PasswordVerificationResult.Failed = 0 -> Microsoft.AspNetCore.Identity.PasswordVerificationResult
+Microsoft.AspNetCore.Identity.PasswordVerificationResult.Success = 1 -> Microsoft.AspNetCore.Identity.PasswordVerificationResult
+Microsoft.AspNetCore.Identity.PasswordVerificationResult.SuccessRehashNeeded = 2 -> Microsoft.AspNetCore.Identity.PasswordVerificationResult
+Microsoft.AspNetCore.Identity.PersonalDataAttribute
+Microsoft.AspNetCore.Identity.PersonalDataAttribute.PersonalDataAttribute() -> void
+Microsoft.AspNetCore.Identity.PhoneNumberTokenProvider<TUser>.PhoneNumberTokenProvider() -> void
+Microsoft.AspNetCore.Identity.ProtectedPersonalDataAttribute
+Microsoft.AspNetCore.Identity.ProtectedPersonalDataAttribute.ProtectedPersonalDataAttribute() -> void
+Microsoft.AspNetCore.Identity.RoleManager<TRole>.Dispose() -> void
+Microsoft.AspNetCore.Identity.RoleManager<TRole>.ThrowIfDisposed() -> void
+Microsoft.AspNetCore.Identity.SignInOptions
+Microsoft.AspNetCore.Identity.SignInOptions.RequireConfirmedAccount.get -> bool
+Microsoft.AspNetCore.Identity.SignInOptions.RequireConfirmedAccount.set -> void
+Microsoft.AspNetCore.Identity.SignInOptions.RequireConfirmedEmail.get -> bool
+Microsoft.AspNetCore.Identity.SignInOptions.RequireConfirmedEmail.set -> void
+Microsoft.AspNetCore.Identity.SignInOptions.RequireConfirmedPhoneNumber.get -> bool
+Microsoft.AspNetCore.Identity.SignInOptions.RequireConfirmedPhoneNumber.set -> void
+Microsoft.AspNetCore.Identity.SignInOptions.SignInOptions() -> void
+Microsoft.AspNetCore.Identity.SignInResult
+Microsoft.AspNetCore.Identity.SignInResult.IsLockedOut.get -> bool
+Microsoft.AspNetCore.Identity.SignInResult.IsLockedOut.set -> void
+Microsoft.AspNetCore.Identity.SignInResult.IsNotAllowed.get -> bool
+Microsoft.AspNetCore.Identity.SignInResult.IsNotAllowed.set -> void
+Microsoft.AspNetCore.Identity.SignInResult.RequiresTwoFactor.get -> bool
+Microsoft.AspNetCore.Identity.SignInResult.RequiresTwoFactor.set -> void
+Microsoft.AspNetCore.Identity.SignInResult.SignInResult() -> void
+Microsoft.AspNetCore.Identity.SignInResult.Succeeded.get -> bool
+Microsoft.AspNetCore.Identity.SignInResult.Succeeded.set -> void
+Microsoft.AspNetCore.Identity.StoreOptions
+Microsoft.AspNetCore.Identity.StoreOptions.MaxLengthForKeys.get -> int
+Microsoft.AspNetCore.Identity.StoreOptions.MaxLengthForKeys.set -> void
+Microsoft.AspNetCore.Identity.StoreOptions.ProtectPersonalData.get -> bool
+Microsoft.AspNetCore.Identity.StoreOptions.ProtectPersonalData.set -> void
+Microsoft.AspNetCore.Identity.StoreOptions.StoreOptions() -> void
+Microsoft.AspNetCore.Identity.TokenOptions
+Microsoft.AspNetCore.Identity.TokenOptions.TokenOptions() -> void
+Microsoft.AspNetCore.Identity.TokenProviderDescriptor
+Microsoft.AspNetCore.Identity.TotpSecurityStampBasedTokenProvider<TUser>.TotpSecurityStampBasedTokenProvider() -> void
+Microsoft.AspNetCore.Identity.UpperInvariantLookupNormalizer
+Microsoft.AspNetCore.Identity.UpperInvariantLookupNormalizer.UpperInvariantLookupNormalizer() -> void
+Microsoft.AspNetCore.Identity.UserLoginInfo
+Microsoft.AspNetCore.Identity.UserManager<TUser>.Dispose() -> void
+Microsoft.AspNetCore.Identity.UserManager<TUser>.ThrowIfDisposed() -> void
+Microsoft.AspNetCore.Identity.UserOptions
+Microsoft.AspNetCore.Identity.UserOptions.RequireUniqueEmail.get -> bool
+Microsoft.AspNetCore.Identity.UserOptions.RequireUniqueEmail.set -> void
+Microsoft.AspNetCore.Identity.UserOptions.UserOptions() -> void
+Microsoft.Extensions.DependencyInjection.IdentityServiceCollectionExtensions
+System.Security.Claims.PrincipalExtensions
+virtual Microsoft.AspNetCore.Identity.PasswordValidator<TUser>.IsDigit(char c) -> bool
+virtual Microsoft.AspNetCore.Identity.PasswordValidator<TUser>.IsLetterOrDigit(char c) -> bool
+virtual Microsoft.AspNetCore.Identity.PasswordValidator<TUser>.IsLower(char c) -> bool
+virtual Microsoft.AspNetCore.Identity.PasswordValidator<TUser>.IsUpper(char c) -> bool
+virtual Microsoft.AspNetCore.Identity.RoleManager<TRole>.CancellationToken.get -> System.Threading.CancellationToken
+virtual Microsoft.AspNetCore.Identity.RoleManager<TRole>.Dispose(bool disposing) -> void
+virtual Microsoft.AspNetCore.Identity.RoleManager<TRole>.SupportsQueryableRoles.get -> bool
+virtual Microsoft.AspNetCore.Identity.RoleManager<TRole>.SupportsRoleClaims.get -> bool
+virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.CancellationToken.get -> System.Threading.CancellationToken
+virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.Dispose(bool disposing) -> void
+virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.SupportsQueryableUsers.get -> bool
+virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.SupportsUserAuthenticationTokens.get -> bool
+virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.SupportsUserAuthenticatorKey.get -> bool
+virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.SupportsUserClaim.get -> bool
+virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.SupportsUserEmail.get -> bool
+virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.SupportsUserLockout.get -> bool
+virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.SupportsUserLogin.get -> bool
+virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.SupportsUserPassword.get -> bool
+virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.SupportsUserPhoneNumber.get -> bool
+virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.SupportsUserRole.get -> bool
+virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.SupportsUserSecurityStamp.get -> bool
+virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.SupportsUserTwoFactor.get -> bool
+virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.SupportsUserTwoFactorRecoveryCodes.get -> bool
+~Microsoft.AspNetCore.Identity.AuthenticatorTokenProvider<TUser>
+~Microsoft.AspNetCore.Identity.ClaimsIdentityOptions.EmailClaimType.get -> string
+~Microsoft.AspNetCore.Identity.ClaimsIdentityOptions.EmailClaimType.set -> void
+~Microsoft.AspNetCore.Identity.ClaimsIdentityOptions.RoleClaimType.get -> string
+~Microsoft.AspNetCore.Identity.ClaimsIdentityOptions.RoleClaimType.set -> void
+~Microsoft.AspNetCore.Identity.ClaimsIdentityOptions.SecurityStampClaimType.get -> string
+~Microsoft.AspNetCore.Identity.ClaimsIdentityOptions.SecurityStampClaimType.set -> void
+~Microsoft.AspNetCore.Identity.ClaimsIdentityOptions.UserIdClaimType.get -> string
+~Microsoft.AspNetCore.Identity.ClaimsIdentityOptions.UserIdClaimType.set -> void
+~Microsoft.AspNetCore.Identity.ClaimsIdentityOptions.UserNameClaimType.get -> string
+~Microsoft.AspNetCore.Identity.ClaimsIdentityOptions.UserNameClaimType.set -> void
+~Microsoft.AspNetCore.Identity.DefaultPersonalDataProtector.DefaultPersonalDataProtector(Microsoft.AspNetCore.Identity.ILookupProtectorKeyRing keyRing, Microsoft.AspNetCore.Identity.ILookupProtector protector) -> void
+~Microsoft.AspNetCore.Identity.DefaultUserConfirmation<TUser>
+~Microsoft.AspNetCore.Identity.EmailTokenProvider<TUser>
+~Microsoft.AspNetCore.Identity.ILookupNormalizer.NormalizeEmail(string email) -> string
+~Microsoft.AspNetCore.Identity.ILookupNormalizer.NormalizeName(string name) -> string
+~Microsoft.AspNetCore.Identity.ILookupProtector.Protect(string keyId, string data) -> string
+~Microsoft.AspNetCore.Identity.ILookupProtector.Unprotect(string keyId, string data) -> string
+~Microsoft.AspNetCore.Identity.ILookupProtectorKeyRing.CurrentKeyId.get -> string
+~Microsoft.AspNetCore.Identity.ILookupProtectorKeyRing.GetAllKeyIds() -> System.Collections.Generic.IEnumerable<string>
+~Microsoft.AspNetCore.Identity.ILookupProtectorKeyRing.this[string keyId].get -> string
+~Microsoft.AspNetCore.Identity.IPasswordHasher<TUser>
+~Microsoft.AspNetCore.Identity.IPasswordHasher<TUser>.HashPassword(TUser user, string password) -> string
+~Microsoft.AspNetCore.Identity.IPasswordHasher<TUser>.VerifyHashedPassword(TUser user, string hashedPassword, string providedPassword) -> Microsoft.AspNetCore.Identity.PasswordVerificationResult
+~Microsoft.AspNetCore.Identity.IPasswordValidator<TUser>
+~Microsoft.AspNetCore.Identity.IPasswordValidator<TUser>.ValidateAsync(Microsoft.AspNetCore.Identity.UserManager<TUser> manager, TUser user, string password) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~Microsoft.AspNetCore.Identity.IPersonalDataProtector.Protect(string data) -> string
+~Microsoft.AspNetCore.Identity.IPersonalDataProtector.Unprotect(string data) -> string
+~Microsoft.AspNetCore.Identity.IProtectedUserStore<TUser>
+~Microsoft.AspNetCore.Identity.IQueryableRoleStore<TRole>
+~Microsoft.AspNetCore.Identity.IQueryableRoleStore<TRole>.Roles.get -> System.Linq.IQueryable<TRole>
+~Microsoft.AspNetCore.Identity.IQueryableUserStore<TUser>
+~Microsoft.AspNetCore.Identity.IQueryableUserStore<TUser>.Users.get -> System.Linq.IQueryable<TUser>
+~Microsoft.AspNetCore.Identity.IRoleClaimStore<TRole>
+~Microsoft.AspNetCore.Identity.IRoleClaimStore<TRole>.AddClaimAsync(TRole role, System.Security.Claims.Claim claim, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
+~Microsoft.AspNetCore.Identity.IRoleClaimStore<TRole>.GetClaimsAsync(TRole role, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Generic.IList<System.Security.Claims.Claim>>
+~Microsoft.AspNetCore.Identity.IRoleClaimStore<TRole>.RemoveClaimAsync(TRole role, System.Security.Claims.Claim claim, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
+~Microsoft.AspNetCore.Identity.IRoleStore<TRole>
+~Microsoft.AspNetCore.Identity.IRoleStore<TRole>.CreateAsync(TRole role, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~Microsoft.AspNetCore.Identity.IRoleStore<TRole>.DeleteAsync(TRole role, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~Microsoft.AspNetCore.Identity.IRoleStore<TRole>.FindByIdAsync(string roleId, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<TRole>
+~Microsoft.AspNetCore.Identity.IRoleStore<TRole>.FindByNameAsync(string normalizedRoleName, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<TRole>
+~Microsoft.AspNetCore.Identity.IRoleStore<TRole>.GetNormalizedRoleNameAsync(TRole role, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<string>
+~Microsoft.AspNetCore.Identity.IRoleStore<TRole>.GetRoleIdAsync(TRole role, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<string>
+~Microsoft.AspNetCore.Identity.IRoleStore<TRole>.GetRoleNameAsync(TRole role, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<string>
+~Microsoft.AspNetCore.Identity.IRoleStore<TRole>.SetNormalizedRoleNameAsync(TRole role, string normalizedName, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+~Microsoft.AspNetCore.Identity.IRoleStore<TRole>.SetRoleNameAsync(TRole role, string roleName, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+~Microsoft.AspNetCore.Identity.IRoleStore<TRole>.UpdateAsync(TRole role, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~Microsoft.AspNetCore.Identity.IRoleValidator<TRole>
+~Microsoft.AspNetCore.Identity.IRoleValidator<TRole>.ValidateAsync(Microsoft.AspNetCore.Identity.RoleManager<TRole> manager, TRole role) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~Microsoft.AspNetCore.Identity.IUserAuthenticationTokenStore<TUser>
+~Microsoft.AspNetCore.Identity.IUserAuthenticationTokenStore<TUser>.GetTokenAsync(TUser user, string loginProvider, string name, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<string>
+~Microsoft.AspNetCore.Identity.IUserAuthenticationTokenStore<TUser>.RemoveTokenAsync(TUser user, string loginProvider, string name, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+~Microsoft.AspNetCore.Identity.IUserAuthenticationTokenStore<TUser>.SetTokenAsync(TUser user, string loginProvider, string name, string value, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+~Microsoft.AspNetCore.Identity.IUserAuthenticatorKeyStore<TUser>
+~Microsoft.AspNetCore.Identity.IUserAuthenticatorKeyStore<TUser>.GetAuthenticatorKeyAsync(TUser user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<string>
+~Microsoft.AspNetCore.Identity.IUserAuthenticatorKeyStore<TUser>.SetAuthenticatorKeyAsync(TUser user, string key, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+~Microsoft.AspNetCore.Identity.IUserClaimStore<TUser>
+~Microsoft.AspNetCore.Identity.IUserClaimStore<TUser>.AddClaimsAsync(TUser user, System.Collections.Generic.IEnumerable<System.Security.Claims.Claim> claims, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+~Microsoft.AspNetCore.Identity.IUserClaimStore<TUser>.GetClaimsAsync(TUser user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<System.Collections.Generic.IList<System.Security.Claims.Claim>>
+~Microsoft.AspNetCore.Identity.IUserClaimStore<TUser>.GetUsersForClaimAsync(System.Security.Claims.Claim claim, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<System.Collections.Generic.IList<TUser>>
+~Microsoft.AspNetCore.Identity.IUserClaimStore<TUser>.RemoveClaimsAsync(TUser user, System.Collections.Generic.IEnumerable<System.Security.Claims.Claim> claims, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+~Microsoft.AspNetCore.Identity.IUserClaimStore<TUser>.ReplaceClaimAsync(TUser user, System.Security.Claims.Claim claim, System.Security.Claims.Claim newClaim, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+~Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory<TUser>
+~Microsoft.AspNetCore.Identity.IUserClaimsPrincipalFactory<TUser>.CreateAsync(TUser user) -> System.Threading.Tasks.Task<System.Security.Claims.ClaimsPrincipal>
+~Microsoft.AspNetCore.Identity.IUserConfirmation<TUser>
+~Microsoft.AspNetCore.Identity.IUserConfirmation<TUser>.IsConfirmedAsync(Microsoft.AspNetCore.Identity.UserManager<TUser> manager, TUser user) -> System.Threading.Tasks.Task<bool>
+~Microsoft.AspNetCore.Identity.IUserEmailStore<TUser>
+~Microsoft.AspNetCore.Identity.IUserEmailStore<TUser>.FindByEmailAsync(string normalizedEmail, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<TUser>
+~Microsoft.AspNetCore.Identity.IUserEmailStore<TUser>.GetEmailAsync(TUser user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<string>
+~Microsoft.AspNetCore.Identity.IUserEmailStore<TUser>.GetEmailConfirmedAsync(TUser user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<bool>
+~Microsoft.AspNetCore.Identity.IUserEmailStore<TUser>.GetNormalizedEmailAsync(TUser user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<string>
+~Microsoft.AspNetCore.Identity.IUserEmailStore<TUser>.SetEmailAsync(TUser user, string email, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+~Microsoft.AspNetCore.Identity.IUserEmailStore<TUser>.SetEmailConfirmedAsync(TUser user, bool confirmed, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+~Microsoft.AspNetCore.Identity.IUserEmailStore<TUser>.SetNormalizedEmailAsync(TUser user, string normalizedEmail, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+~Microsoft.AspNetCore.Identity.IUserLockoutStore<TUser>
+~Microsoft.AspNetCore.Identity.IUserLockoutStore<TUser>.GetAccessFailedCountAsync(TUser user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<int>
+~Microsoft.AspNetCore.Identity.IUserLockoutStore<TUser>.GetLockoutEnabledAsync(TUser user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<bool>
+~Microsoft.AspNetCore.Identity.IUserLockoutStore<TUser>.GetLockoutEndDateAsync(TUser user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<System.DateTimeOffset?>
+~Microsoft.AspNetCore.Identity.IUserLockoutStore<TUser>.IncrementAccessFailedCountAsync(TUser user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<int>
+~Microsoft.AspNetCore.Identity.IUserLockoutStore<TUser>.ResetAccessFailedCountAsync(TUser user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+~Microsoft.AspNetCore.Identity.IUserLockoutStore<TUser>.SetLockoutEnabledAsync(TUser user, bool enabled, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+~Microsoft.AspNetCore.Identity.IUserLockoutStore<TUser>.SetLockoutEndDateAsync(TUser user, System.DateTimeOffset? lockoutEnd, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+~Microsoft.AspNetCore.Identity.IUserLoginStore<TUser>
+~Microsoft.AspNetCore.Identity.IUserLoginStore<TUser>.AddLoginAsync(TUser user, Microsoft.AspNetCore.Identity.UserLoginInfo login, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+~Microsoft.AspNetCore.Identity.IUserLoginStore<TUser>.FindByLoginAsync(string loginProvider, string providerKey, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<TUser>
+~Microsoft.AspNetCore.Identity.IUserLoginStore<TUser>.GetLoginsAsync(TUser user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<System.Collections.Generic.IList<Microsoft.AspNetCore.Identity.UserLoginInfo>>
+~Microsoft.AspNetCore.Identity.IUserLoginStore<TUser>.RemoveLoginAsync(TUser user, string loginProvider, string providerKey, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+~Microsoft.AspNetCore.Identity.IUserPasswordStore<TUser>
+~Microsoft.AspNetCore.Identity.IUserPasswordStore<TUser>.GetPasswordHashAsync(TUser user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<string>
+~Microsoft.AspNetCore.Identity.IUserPasswordStore<TUser>.HasPasswordAsync(TUser user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<bool>
+~Microsoft.AspNetCore.Identity.IUserPasswordStore<TUser>.SetPasswordHashAsync(TUser user, string passwordHash, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+~Microsoft.AspNetCore.Identity.IUserPhoneNumberStore<TUser>
+~Microsoft.AspNetCore.Identity.IUserPhoneNumberStore<TUser>.GetPhoneNumberAsync(TUser user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<string>
+~Microsoft.AspNetCore.Identity.IUserPhoneNumberStore<TUser>.GetPhoneNumberConfirmedAsync(TUser user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<bool>
+~Microsoft.AspNetCore.Identity.IUserPhoneNumberStore<TUser>.SetPhoneNumberAsync(TUser user, string phoneNumber, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+~Microsoft.AspNetCore.Identity.IUserPhoneNumberStore<TUser>.SetPhoneNumberConfirmedAsync(TUser user, bool confirmed, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+~Microsoft.AspNetCore.Identity.IUserRoleStore<TUser>
+~Microsoft.AspNetCore.Identity.IUserRoleStore<TUser>.AddToRoleAsync(TUser user, string roleName, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+~Microsoft.AspNetCore.Identity.IUserRoleStore<TUser>.GetRolesAsync(TUser user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<System.Collections.Generic.IList<string>>
+~Microsoft.AspNetCore.Identity.IUserRoleStore<TUser>.GetUsersInRoleAsync(string roleName, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<System.Collections.Generic.IList<TUser>>
+~Microsoft.AspNetCore.Identity.IUserRoleStore<TUser>.IsInRoleAsync(TUser user, string roleName, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<bool>
+~Microsoft.AspNetCore.Identity.IUserRoleStore<TUser>.RemoveFromRoleAsync(TUser user, string roleName, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+~Microsoft.AspNetCore.Identity.IUserSecurityStampStore<TUser>
+~Microsoft.AspNetCore.Identity.IUserSecurityStampStore<TUser>.GetSecurityStampAsync(TUser user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<string>
+~Microsoft.AspNetCore.Identity.IUserSecurityStampStore<TUser>.SetSecurityStampAsync(TUser user, string stamp, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+~Microsoft.AspNetCore.Identity.IUserStore<TUser>
+~Microsoft.AspNetCore.Identity.IUserStore<TUser>.CreateAsync(TUser user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~Microsoft.AspNetCore.Identity.IUserStore<TUser>.DeleteAsync(TUser user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~Microsoft.AspNetCore.Identity.IUserStore<TUser>.FindByIdAsync(string userId, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<TUser>
+~Microsoft.AspNetCore.Identity.IUserStore<TUser>.FindByNameAsync(string normalizedUserName, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<TUser>
+~Microsoft.AspNetCore.Identity.IUserStore<TUser>.GetNormalizedUserNameAsync(TUser user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<string>
+~Microsoft.AspNetCore.Identity.IUserStore<TUser>.GetUserIdAsync(TUser user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<string>
+~Microsoft.AspNetCore.Identity.IUserStore<TUser>.GetUserNameAsync(TUser user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<string>
+~Microsoft.AspNetCore.Identity.IUserStore<TUser>.SetNormalizedUserNameAsync(TUser user, string normalizedName, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+~Microsoft.AspNetCore.Identity.IUserStore<TUser>.SetUserNameAsync(TUser user, string userName, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+~Microsoft.AspNetCore.Identity.IUserStore<TUser>.UpdateAsync(TUser user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~Microsoft.AspNetCore.Identity.IUserTwoFactorRecoveryCodeStore<TUser>
+~Microsoft.AspNetCore.Identity.IUserTwoFactorRecoveryCodeStore<TUser>.CountCodesAsync(TUser user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<int>
+~Microsoft.AspNetCore.Identity.IUserTwoFactorRecoveryCodeStore<TUser>.RedeemCodeAsync(TUser user, string code, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<bool>
+~Microsoft.AspNetCore.Identity.IUserTwoFactorRecoveryCodeStore<TUser>.ReplaceCodesAsync(TUser user, System.Collections.Generic.IEnumerable<string> recoveryCodes, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+~Microsoft.AspNetCore.Identity.IUserTwoFactorStore<TUser>
+~Microsoft.AspNetCore.Identity.IUserTwoFactorStore<TUser>.GetTwoFactorEnabledAsync(TUser user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<bool>
+~Microsoft.AspNetCore.Identity.IUserTwoFactorStore<TUser>.SetTwoFactorEnabledAsync(TUser user, bool enabled, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+~Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider<TUser>
+~Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider<TUser>.CanGenerateTwoFactorTokenAsync(Microsoft.AspNetCore.Identity.UserManager<TUser> manager, TUser user) -> System.Threading.Tasks.Task<bool>
+~Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider<TUser>.GenerateAsync(string purpose, Microsoft.AspNetCore.Identity.UserManager<TUser> manager, TUser user) -> System.Threading.Tasks.Task<string>
+~Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider<TUser>.ValidateAsync(string purpose, string token, Microsoft.AspNetCore.Identity.UserManager<TUser> manager, TUser user) -> System.Threading.Tasks.Task<bool>
+~Microsoft.AspNetCore.Identity.IUserValidator<TUser>
+~Microsoft.AspNetCore.Identity.IUserValidator<TUser>.ValidateAsync(Microsoft.AspNetCore.Identity.UserManager<TUser> manager, TUser user) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~Microsoft.AspNetCore.Identity.IdentityBuilder.IdentityBuilder(System.Type user, Microsoft.Extensions.DependencyInjection.IServiceCollection services) -> void
+~Microsoft.AspNetCore.Identity.IdentityBuilder.IdentityBuilder(System.Type user, System.Type role, Microsoft.Extensions.DependencyInjection.IServiceCollection services) -> void
+~Microsoft.AspNetCore.Identity.IdentityBuilder.RoleType.get -> System.Type
+~Microsoft.AspNetCore.Identity.IdentityBuilder.Services.get -> Microsoft.Extensions.DependencyInjection.IServiceCollection
+~Microsoft.AspNetCore.Identity.IdentityBuilder.UserType.get -> System.Type
+~Microsoft.AspNetCore.Identity.IdentityError.Code.get -> string
+~Microsoft.AspNetCore.Identity.IdentityError.Code.set -> void
+~Microsoft.AspNetCore.Identity.IdentityError.Description.get -> string
+~Microsoft.AspNetCore.Identity.IdentityError.Description.set -> void
+~Microsoft.AspNetCore.Identity.IdentityOptions.ClaimsIdentity.get -> Microsoft.AspNetCore.Identity.ClaimsIdentityOptions
+~Microsoft.AspNetCore.Identity.IdentityOptions.ClaimsIdentity.set -> void
+~Microsoft.AspNetCore.Identity.IdentityOptions.Lockout.get -> Microsoft.AspNetCore.Identity.LockoutOptions
+~Microsoft.AspNetCore.Identity.IdentityOptions.Lockout.set -> void
+~Microsoft.AspNetCore.Identity.IdentityOptions.Password.get -> Microsoft.AspNetCore.Identity.PasswordOptions
+~Microsoft.AspNetCore.Identity.IdentityOptions.Password.set -> void
+~Microsoft.AspNetCore.Identity.IdentityOptions.SignIn.get -> Microsoft.AspNetCore.Identity.SignInOptions
+~Microsoft.AspNetCore.Identity.IdentityOptions.SignIn.set -> void
+~Microsoft.AspNetCore.Identity.IdentityOptions.Stores.get -> Microsoft.AspNetCore.Identity.StoreOptions
+~Microsoft.AspNetCore.Identity.IdentityOptions.Stores.set -> void
+~Microsoft.AspNetCore.Identity.IdentityOptions.Tokens.get -> Microsoft.AspNetCore.Identity.TokenOptions
+~Microsoft.AspNetCore.Identity.IdentityOptions.Tokens.set -> void
+~Microsoft.AspNetCore.Identity.IdentityOptions.User.get -> Microsoft.AspNetCore.Identity.UserOptions
+~Microsoft.AspNetCore.Identity.IdentityOptions.User.set -> void
+~Microsoft.AspNetCore.Identity.IdentityResult.Errors.get -> System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Identity.IdentityError>
+~Microsoft.AspNetCore.Identity.PasswordHasher<TUser>
+~Microsoft.AspNetCore.Identity.PasswordHasher<TUser>.PasswordHasher(Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Identity.PasswordHasherOptions> optionsAccessor = null) -> void
+~Microsoft.AspNetCore.Identity.PasswordValidator<TUser>
+~Microsoft.AspNetCore.Identity.PasswordValidator<TUser>.Describer.get -> Microsoft.AspNetCore.Identity.IdentityErrorDescriber
+~Microsoft.AspNetCore.Identity.PasswordValidator<TUser>.PasswordValidator(Microsoft.AspNetCore.Identity.IdentityErrorDescriber errors = null) -> void
+~Microsoft.AspNetCore.Identity.PhoneNumberTokenProvider<TUser>
+~Microsoft.AspNetCore.Identity.RoleManager<TRole>
+~Microsoft.AspNetCore.Identity.RoleManager<TRole>.ErrorDescriber.get -> Microsoft.AspNetCore.Identity.IdentityErrorDescriber
+~Microsoft.AspNetCore.Identity.RoleManager<TRole>.ErrorDescriber.set -> void
+~Microsoft.AspNetCore.Identity.RoleManager<TRole>.KeyNormalizer.get -> Microsoft.AspNetCore.Identity.ILookupNormalizer
+~Microsoft.AspNetCore.Identity.RoleManager<TRole>.KeyNormalizer.set -> void
+~Microsoft.AspNetCore.Identity.RoleManager<TRole>.RoleManager(Microsoft.AspNetCore.Identity.IRoleStore<TRole> store, System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Identity.IRoleValidator<TRole>> roleValidators, Microsoft.AspNetCore.Identity.ILookupNormalizer keyNormalizer, Microsoft.AspNetCore.Identity.IdentityErrorDescriber errors, Microsoft.Extensions.Logging.ILogger<Microsoft.AspNetCore.Identity.RoleManager<TRole>> logger) -> void
+~Microsoft.AspNetCore.Identity.RoleManager<TRole>.RoleValidators.get -> System.Collections.Generic.IList<Microsoft.AspNetCore.Identity.IRoleValidator<TRole>>
+~Microsoft.AspNetCore.Identity.RoleManager<TRole>.Store.get -> Microsoft.AspNetCore.Identity.IRoleStore<TRole>
+~Microsoft.AspNetCore.Identity.RoleValidator<TRole>
+~Microsoft.AspNetCore.Identity.RoleValidator<TRole>.RoleValidator(Microsoft.AspNetCore.Identity.IdentityErrorDescriber errors = null) -> void
+~Microsoft.AspNetCore.Identity.TokenOptions.AuthenticatorIssuer.get -> string
+~Microsoft.AspNetCore.Identity.TokenOptions.AuthenticatorIssuer.set -> void
+~Microsoft.AspNetCore.Identity.TokenOptions.AuthenticatorTokenProvider.get -> string
+~Microsoft.AspNetCore.Identity.TokenOptions.AuthenticatorTokenProvider.set -> void
+~Microsoft.AspNetCore.Identity.TokenOptions.ChangeEmailTokenProvider.get -> string
+~Microsoft.AspNetCore.Identity.TokenOptions.ChangeEmailTokenProvider.set -> void
+~Microsoft.AspNetCore.Identity.TokenOptions.ChangePhoneNumberTokenProvider.get -> string
+~Microsoft.AspNetCore.Identity.TokenOptions.ChangePhoneNumberTokenProvider.set -> void
+~Microsoft.AspNetCore.Identity.TokenOptions.EmailConfirmationTokenProvider.get -> string
+~Microsoft.AspNetCore.Identity.TokenOptions.EmailConfirmationTokenProvider.set -> void
+~Microsoft.AspNetCore.Identity.TokenOptions.PasswordResetTokenProvider.get -> string
+~Microsoft.AspNetCore.Identity.TokenOptions.PasswordResetTokenProvider.set -> void
+~Microsoft.AspNetCore.Identity.TokenOptions.ProviderMap.get -> System.Collections.Generic.Dictionary<string, Microsoft.AspNetCore.Identity.TokenProviderDescriptor>
+~Microsoft.AspNetCore.Identity.TokenOptions.ProviderMap.set -> void
+~Microsoft.AspNetCore.Identity.TokenProviderDescriptor.ProviderInstance.get -> object
+~Microsoft.AspNetCore.Identity.TokenProviderDescriptor.ProviderInstance.set -> void
+~Microsoft.AspNetCore.Identity.TokenProviderDescriptor.ProviderType.get -> System.Type
+~Microsoft.AspNetCore.Identity.TokenProviderDescriptor.TokenProviderDescriptor(System.Type type) -> void
+~Microsoft.AspNetCore.Identity.TotpSecurityStampBasedTokenProvider<TUser>
+~Microsoft.AspNetCore.Identity.UpperInvariantLookupNormalizer.NormalizeEmail(string email) -> string
+~Microsoft.AspNetCore.Identity.UpperInvariantLookupNormalizer.NormalizeName(string name) -> string
+~Microsoft.AspNetCore.Identity.UserClaimsPrincipalFactory<TUser, TRole>
+~Microsoft.AspNetCore.Identity.UserClaimsPrincipalFactory<TUser, TRole>.RoleManager.get -> Microsoft.AspNetCore.Identity.RoleManager<TRole>
+~Microsoft.AspNetCore.Identity.UserClaimsPrincipalFactory<TUser, TRole>.UserClaimsPrincipalFactory(Microsoft.AspNetCore.Identity.UserManager<TUser> userManager, Microsoft.AspNetCore.Identity.RoleManager<TRole> roleManager, Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Identity.IdentityOptions> options) -> void
+~Microsoft.AspNetCore.Identity.UserClaimsPrincipalFactory<TUser>
+~Microsoft.AspNetCore.Identity.UserClaimsPrincipalFactory<TUser>.Options.get -> Microsoft.AspNetCore.Identity.IdentityOptions
+~Microsoft.AspNetCore.Identity.UserClaimsPrincipalFactory<TUser>.UserClaimsPrincipalFactory(Microsoft.AspNetCore.Identity.UserManager<TUser> userManager, Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Identity.IdentityOptions> optionsAccessor) -> void
+~Microsoft.AspNetCore.Identity.UserClaimsPrincipalFactory<TUser>.UserManager.get -> Microsoft.AspNetCore.Identity.UserManager<TUser>
+~Microsoft.AspNetCore.Identity.UserLoginInfo.LoginProvider.get -> string
+~Microsoft.AspNetCore.Identity.UserLoginInfo.LoginProvider.set -> void
+~Microsoft.AspNetCore.Identity.UserLoginInfo.ProviderDisplayName.get -> string
+~Microsoft.AspNetCore.Identity.UserLoginInfo.ProviderDisplayName.set -> void
+~Microsoft.AspNetCore.Identity.UserLoginInfo.ProviderKey.get -> string
+~Microsoft.AspNetCore.Identity.UserLoginInfo.ProviderKey.set -> void
+~Microsoft.AspNetCore.Identity.UserLoginInfo.UserLoginInfo(string loginProvider, string providerKey, string displayName) -> void
+~Microsoft.AspNetCore.Identity.UserManager<TUser>
+~Microsoft.AspNetCore.Identity.UserManager<TUser>.ErrorDescriber.get -> Microsoft.AspNetCore.Identity.IdentityErrorDescriber
+~Microsoft.AspNetCore.Identity.UserManager<TUser>.ErrorDescriber.set -> void
+~Microsoft.AspNetCore.Identity.UserManager<TUser>.KeyNormalizer.get -> Microsoft.AspNetCore.Identity.ILookupNormalizer
+~Microsoft.AspNetCore.Identity.UserManager<TUser>.KeyNormalizer.set -> void
+~Microsoft.AspNetCore.Identity.UserManager<TUser>.Options.get -> Microsoft.AspNetCore.Identity.IdentityOptions
+~Microsoft.AspNetCore.Identity.UserManager<TUser>.Options.set -> void
+~Microsoft.AspNetCore.Identity.UserManager<TUser>.PasswordHasher.get -> Microsoft.AspNetCore.Identity.IPasswordHasher<TUser>
+~Microsoft.AspNetCore.Identity.UserManager<TUser>.PasswordHasher.set -> void
+~Microsoft.AspNetCore.Identity.UserManager<TUser>.PasswordValidators.get -> System.Collections.Generic.IList<Microsoft.AspNetCore.Identity.IPasswordValidator<TUser>>
+~Microsoft.AspNetCore.Identity.UserManager<TUser>.Store.get -> Microsoft.AspNetCore.Identity.IUserStore<TUser>
+~Microsoft.AspNetCore.Identity.UserManager<TUser>.Store.set -> void
+~Microsoft.AspNetCore.Identity.UserManager<TUser>.UserManager(Microsoft.AspNetCore.Identity.IUserStore<TUser> store, Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Identity.IdentityOptions> optionsAccessor, Microsoft.AspNetCore.Identity.IPasswordHasher<TUser> passwordHasher, System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Identity.IUserValidator<TUser>> userValidators, System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Identity.IPasswordValidator<TUser>> passwordValidators, Microsoft.AspNetCore.Identity.ILookupNormalizer keyNormalizer, Microsoft.AspNetCore.Identity.IdentityErrorDescriber errors, System.IServiceProvider services, Microsoft.Extensions.Logging.ILogger<Microsoft.AspNetCore.Identity.UserManager<TUser>> logger) -> void
+~Microsoft.AspNetCore.Identity.UserManager<TUser>.UserValidators.get -> System.Collections.Generic.IList<Microsoft.AspNetCore.Identity.IUserValidator<TUser>>
+~Microsoft.AspNetCore.Identity.UserManager<TUser>.ValidatePasswordAsync(TUser user, string password) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~Microsoft.AspNetCore.Identity.UserManager<TUser>.ValidateUserAsync(TUser user) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~Microsoft.AspNetCore.Identity.UserOptions.AllowedUserNameCharacters.get -> string
+~Microsoft.AspNetCore.Identity.UserOptions.AllowedUserNameCharacters.set -> void
+~Microsoft.AspNetCore.Identity.UserValidator<TUser>
+~Microsoft.AspNetCore.Identity.UserValidator<TUser>.Describer.get -> Microsoft.AspNetCore.Identity.IdentityErrorDescriber
+~Microsoft.AspNetCore.Identity.UserValidator<TUser>.UserValidator(Microsoft.AspNetCore.Identity.IdentityErrorDescriber errors = null) -> void
+~abstract Microsoft.AspNetCore.Identity.TotpSecurityStampBasedTokenProvider<TUser>.CanGenerateTwoFactorTokenAsync(Microsoft.AspNetCore.Identity.UserManager<TUser> manager, TUser user) -> System.Threading.Tasks.Task<bool>
+~const Microsoft.AspNetCore.Identity.UserManager<TUser>.ChangePhoneNumberTokenPurpose = "ChangePhoneNumber" -> string
+~const Microsoft.AspNetCore.Identity.UserManager<TUser>.ConfirmEmailTokenPurpose = "EmailConfirmation" -> string
+~const Microsoft.AspNetCore.Identity.UserManager<TUser>.ResetPasswordTokenPurpose = "ResetPassword" -> string
+~override Microsoft.AspNetCore.Identity.EmailTokenProvider<TUser>.CanGenerateTwoFactorTokenAsync(Microsoft.AspNetCore.Identity.UserManager<TUser> manager, TUser user) -> System.Threading.Tasks.Task<bool>
+~override Microsoft.AspNetCore.Identity.EmailTokenProvider<TUser>.GetUserModifierAsync(string purpose, Microsoft.AspNetCore.Identity.UserManager<TUser> manager, TUser user) -> System.Threading.Tasks.Task<string>
+~override Microsoft.AspNetCore.Identity.IdentityResult.ToString() -> string
+~override Microsoft.AspNetCore.Identity.PhoneNumberTokenProvider<TUser>.CanGenerateTwoFactorTokenAsync(Microsoft.AspNetCore.Identity.UserManager<TUser> manager, TUser user) -> System.Threading.Tasks.Task<bool>
+~override Microsoft.AspNetCore.Identity.PhoneNumberTokenProvider<TUser>.GetUserModifierAsync(string purpose, Microsoft.AspNetCore.Identity.UserManager<TUser> manager, TUser user) -> System.Threading.Tasks.Task<string>
+~override Microsoft.AspNetCore.Identity.SignInResult.ToString() -> string
+~override Microsoft.AspNetCore.Identity.UserClaimsPrincipalFactory<TUser, TRole>.GenerateClaimsAsync(TUser user) -> System.Threading.Tasks.Task<System.Security.Claims.ClaimsIdentity>
+~static Microsoft.AspNetCore.Identity.IdentityResult.Failed(params Microsoft.AspNetCore.Identity.IdentityError[] errors) -> Microsoft.AspNetCore.Identity.IdentityResult
+~static Microsoft.AspNetCore.Identity.IdentityResult.Success.get -> Microsoft.AspNetCore.Identity.IdentityResult
+~static Microsoft.AspNetCore.Identity.SignInResult.Failed.get -> Microsoft.AspNetCore.Identity.SignInResult
+~static Microsoft.AspNetCore.Identity.SignInResult.LockedOut.get -> Microsoft.AspNetCore.Identity.SignInResult
+~static Microsoft.AspNetCore.Identity.SignInResult.NotAllowed.get -> Microsoft.AspNetCore.Identity.SignInResult
+~static Microsoft.AspNetCore.Identity.SignInResult.Success.get -> Microsoft.AspNetCore.Identity.SignInResult
+~static Microsoft.AspNetCore.Identity.SignInResult.TwoFactorRequired.get -> Microsoft.AspNetCore.Identity.SignInResult
+~static Microsoft.AspNetCore.Identity.UserManager<TUser>.GetChangeEmailTokenPurpose(string newEmail) -> string
+~static Microsoft.Extensions.DependencyInjection.IdentityServiceCollectionExtensions.AddIdentityCore<TUser>(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) -> Microsoft.AspNetCore.Identity.IdentityBuilder
+~static Microsoft.Extensions.DependencyInjection.IdentityServiceCollectionExtensions.AddIdentityCore<TUser>(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action<Microsoft.AspNetCore.Identity.IdentityOptions> setupAction) -> Microsoft.AspNetCore.Identity.IdentityBuilder
+~static System.Security.Claims.PrincipalExtensions.FindFirstValue(this System.Security.Claims.ClaimsPrincipal principal, string claimType) -> string
+~static readonly Microsoft.AspNetCore.Identity.TokenOptions.DefaultAuthenticatorProvider -> string
+~static readonly Microsoft.AspNetCore.Identity.TokenOptions.DefaultEmailProvider -> string
+~static readonly Microsoft.AspNetCore.Identity.TokenOptions.DefaultPhoneProvider -> string
+~static readonly Microsoft.AspNetCore.Identity.TokenOptions.DefaultProvider -> string
+~virtual Microsoft.AspNetCore.Identity.AuthenticatorTokenProvider<TUser>.CanGenerateTwoFactorTokenAsync(Microsoft.AspNetCore.Identity.UserManager<TUser> manager, TUser user) -> System.Threading.Tasks.Task<bool>
+~virtual Microsoft.AspNetCore.Identity.AuthenticatorTokenProvider<TUser>.GenerateAsync(string purpose, Microsoft.AspNetCore.Identity.UserManager<TUser> manager, TUser user) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.AuthenticatorTokenProvider<TUser>.ValidateAsync(string purpose, string token, Microsoft.AspNetCore.Identity.UserManager<TUser> manager, TUser user) -> System.Threading.Tasks.Task<bool>
+~virtual Microsoft.AspNetCore.Identity.DefaultPersonalDataProtector.Protect(string data) -> string
+~virtual Microsoft.AspNetCore.Identity.DefaultPersonalDataProtector.Unprotect(string data) -> string
+~virtual Microsoft.AspNetCore.Identity.DefaultUserConfirmation<TUser>.IsConfirmedAsync(Microsoft.AspNetCore.Identity.UserManager<TUser> manager, TUser user) -> System.Threading.Tasks.Task<bool>
+~virtual Microsoft.AspNetCore.Identity.IdentityBuilder.AddClaimsPrincipalFactory<TFactory>() -> Microsoft.AspNetCore.Identity.IdentityBuilder
+~virtual Microsoft.AspNetCore.Identity.IdentityBuilder.AddErrorDescriber<TDescriber>() -> Microsoft.AspNetCore.Identity.IdentityBuilder
+~virtual Microsoft.AspNetCore.Identity.IdentityBuilder.AddPasswordValidator<TValidator>() -> Microsoft.AspNetCore.Identity.IdentityBuilder
+~virtual Microsoft.AspNetCore.Identity.IdentityBuilder.AddPersonalDataProtection<TProtector, TKeyRing>() -> Microsoft.AspNetCore.Identity.IdentityBuilder
+~virtual Microsoft.AspNetCore.Identity.IdentityBuilder.AddRoleManager<TRoleManager>() -> Microsoft.AspNetCore.Identity.IdentityBuilder
+~virtual Microsoft.AspNetCore.Identity.IdentityBuilder.AddRoleStore<TStore>() -> Microsoft.AspNetCore.Identity.IdentityBuilder
+~virtual Microsoft.AspNetCore.Identity.IdentityBuilder.AddRoleValidator<TRole>() -> Microsoft.AspNetCore.Identity.IdentityBuilder
+~virtual Microsoft.AspNetCore.Identity.IdentityBuilder.AddRoles<TRole>() -> Microsoft.AspNetCore.Identity.IdentityBuilder
+~virtual Microsoft.AspNetCore.Identity.IdentityBuilder.AddTokenProvider(string providerName, System.Type provider) -> Microsoft.AspNetCore.Identity.IdentityBuilder
+~virtual Microsoft.AspNetCore.Identity.IdentityBuilder.AddTokenProvider<TProvider>(string providerName) -> Microsoft.AspNetCore.Identity.IdentityBuilder
+~virtual Microsoft.AspNetCore.Identity.IdentityBuilder.AddUserConfirmation<TUserConfirmation>() -> Microsoft.AspNetCore.Identity.IdentityBuilder
+~virtual Microsoft.AspNetCore.Identity.IdentityBuilder.AddUserManager<TUserManager>() -> Microsoft.AspNetCore.Identity.IdentityBuilder
+~virtual Microsoft.AspNetCore.Identity.IdentityBuilder.AddUserStore<TStore>() -> Microsoft.AspNetCore.Identity.IdentityBuilder
+~virtual Microsoft.AspNetCore.Identity.IdentityBuilder.AddUserValidator<TValidator>() -> Microsoft.AspNetCore.Identity.IdentityBuilder
+~virtual Microsoft.AspNetCore.Identity.IdentityErrorDescriber.ConcurrencyFailure() -> Microsoft.AspNetCore.Identity.IdentityError
+~virtual Microsoft.AspNetCore.Identity.IdentityErrorDescriber.DefaultError() -> Microsoft.AspNetCore.Identity.IdentityError
+~virtual Microsoft.AspNetCore.Identity.IdentityErrorDescriber.DuplicateEmail(string email) -> Microsoft.AspNetCore.Identity.IdentityError
+~virtual Microsoft.AspNetCore.Identity.IdentityErrorDescriber.DuplicateRoleName(string role) -> Microsoft.AspNetCore.Identity.IdentityError
+~virtual Microsoft.AspNetCore.Identity.IdentityErrorDescriber.DuplicateUserName(string userName) -> Microsoft.AspNetCore.Identity.IdentityError
+~virtual Microsoft.AspNetCore.Identity.IdentityErrorDescriber.InvalidEmail(string email) -> Microsoft.AspNetCore.Identity.IdentityError
+~virtual Microsoft.AspNetCore.Identity.IdentityErrorDescriber.InvalidRoleName(string role) -> Microsoft.AspNetCore.Identity.IdentityError
+~virtual Microsoft.AspNetCore.Identity.IdentityErrorDescriber.InvalidToken() -> Microsoft.AspNetCore.Identity.IdentityError
+~virtual Microsoft.AspNetCore.Identity.IdentityErrorDescriber.InvalidUserName(string userName) -> Microsoft.AspNetCore.Identity.IdentityError
+~virtual Microsoft.AspNetCore.Identity.IdentityErrorDescriber.LoginAlreadyAssociated() -> Microsoft.AspNetCore.Identity.IdentityError
+~virtual Microsoft.AspNetCore.Identity.IdentityErrorDescriber.PasswordMismatch() -> Microsoft.AspNetCore.Identity.IdentityError
+~virtual Microsoft.AspNetCore.Identity.IdentityErrorDescriber.PasswordRequiresDigit() -> Microsoft.AspNetCore.Identity.IdentityError
+~virtual Microsoft.AspNetCore.Identity.IdentityErrorDescriber.PasswordRequiresLower() -> Microsoft.AspNetCore.Identity.IdentityError
+~virtual Microsoft.AspNetCore.Identity.IdentityErrorDescriber.PasswordRequiresNonAlphanumeric() -> Microsoft.AspNetCore.Identity.IdentityError
+~virtual Microsoft.AspNetCore.Identity.IdentityErrorDescriber.PasswordRequiresUniqueChars(int uniqueChars) -> Microsoft.AspNetCore.Identity.IdentityError
+~virtual Microsoft.AspNetCore.Identity.IdentityErrorDescriber.PasswordRequiresUpper() -> Microsoft.AspNetCore.Identity.IdentityError
+~virtual Microsoft.AspNetCore.Identity.IdentityErrorDescriber.PasswordTooShort(int length) -> Microsoft.AspNetCore.Identity.IdentityError
+~virtual Microsoft.AspNetCore.Identity.IdentityErrorDescriber.RecoveryCodeRedemptionFailed() -> Microsoft.AspNetCore.Identity.IdentityError
+~virtual Microsoft.AspNetCore.Identity.IdentityErrorDescriber.UserAlreadyHasPassword() -> Microsoft.AspNetCore.Identity.IdentityError
+~virtual Microsoft.AspNetCore.Identity.IdentityErrorDescriber.UserAlreadyInRole(string role) -> Microsoft.AspNetCore.Identity.IdentityError
+~virtual Microsoft.AspNetCore.Identity.IdentityErrorDescriber.UserLockoutNotEnabled() -> Microsoft.AspNetCore.Identity.IdentityError
+~virtual Microsoft.AspNetCore.Identity.IdentityErrorDescriber.UserNotInRole(string role) -> Microsoft.AspNetCore.Identity.IdentityError
+~virtual Microsoft.AspNetCore.Identity.PasswordHasher<TUser>.HashPassword(TUser user, string password) -> string
+~virtual Microsoft.AspNetCore.Identity.PasswordHasher<TUser>.VerifyHashedPassword(TUser user, string hashedPassword, string providedPassword) -> Microsoft.AspNetCore.Identity.PasswordVerificationResult
+~virtual Microsoft.AspNetCore.Identity.PasswordValidator<TUser>.ValidateAsync(Microsoft.AspNetCore.Identity.UserManager<TUser> manager, TUser user, string password) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.RoleManager<TRole>.AddClaimAsync(TRole role, System.Security.Claims.Claim claim) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.RoleManager<TRole>.CreateAsync(TRole role) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.RoleManager<TRole>.DeleteAsync(TRole role) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.RoleManager<TRole>.FindByIdAsync(string roleId) -> System.Threading.Tasks.Task<TRole>
+~virtual Microsoft.AspNetCore.Identity.RoleManager<TRole>.FindByNameAsync(string roleName) -> System.Threading.Tasks.Task<TRole>
+~virtual Microsoft.AspNetCore.Identity.RoleManager<TRole>.GetClaimsAsync(TRole role) -> System.Threading.Tasks.Task<System.Collections.Generic.IList<System.Security.Claims.Claim>>
+~virtual Microsoft.AspNetCore.Identity.RoleManager<TRole>.GetRoleIdAsync(TRole role) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.RoleManager<TRole>.GetRoleNameAsync(TRole role) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.RoleManager<TRole>.Logger.get -> Microsoft.Extensions.Logging.ILogger
+~virtual Microsoft.AspNetCore.Identity.RoleManager<TRole>.Logger.set -> void
+~virtual Microsoft.AspNetCore.Identity.RoleManager<TRole>.NormalizeKey(string key) -> string
+~virtual Microsoft.AspNetCore.Identity.RoleManager<TRole>.RemoveClaimAsync(TRole role, System.Security.Claims.Claim claim) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.RoleManager<TRole>.RoleExistsAsync(string roleName) -> System.Threading.Tasks.Task<bool>
+~virtual Microsoft.AspNetCore.Identity.RoleManager<TRole>.Roles.get -> System.Linq.IQueryable<TRole>
+~virtual Microsoft.AspNetCore.Identity.RoleManager<TRole>.SetRoleNameAsync(TRole role, string name) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.RoleManager<TRole>.UpdateAsync(TRole role) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.RoleManager<TRole>.UpdateNormalizedRoleNameAsync(TRole role) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.RoleManager<TRole>.UpdateRoleAsync(TRole role) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.RoleManager<TRole>.ValidateRoleAsync(TRole role) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.RoleValidator<TRole>.ValidateAsync(Microsoft.AspNetCore.Identity.RoleManager<TRole> manager, TRole role) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.TotpSecurityStampBasedTokenProvider<TUser>.GenerateAsync(string purpose, Microsoft.AspNetCore.Identity.UserManager<TUser> manager, TUser user) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.TotpSecurityStampBasedTokenProvider<TUser>.GetUserModifierAsync(string purpose, Microsoft.AspNetCore.Identity.UserManager<TUser> manager, TUser user) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.TotpSecurityStampBasedTokenProvider<TUser>.ValidateAsync(string purpose, string token, Microsoft.AspNetCore.Identity.UserManager<TUser> manager, TUser user) -> System.Threading.Tasks.Task<bool>
+~virtual Microsoft.AspNetCore.Identity.UserClaimsPrincipalFactory<TUser>.CreateAsync(TUser user) -> System.Threading.Tasks.Task<System.Security.Claims.ClaimsPrincipal>
+~virtual Microsoft.AspNetCore.Identity.UserClaimsPrincipalFactory<TUser>.GenerateClaimsAsync(TUser user) -> System.Threading.Tasks.Task<System.Security.Claims.ClaimsIdentity>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.AccessFailedAsync(TUser user) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.AddClaimAsync(TUser user, System.Security.Claims.Claim claim) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.AddClaimsAsync(TUser user, System.Collections.Generic.IEnumerable<System.Security.Claims.Claim> claims) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.AddLoginAsync(TUser user, Microsoft.AspNetCore.Identity.UserLoginInfo login) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.AddPasswordAsync(TUser user, string password) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.AddToRoleAsync(TUser user, string role) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.AddToRolesAsync(TUser user, System.Collections.Generic.IEnumerable<string> roles) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.ChangeEmailAsync(TUser user, string newEmail, string token) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.ChangePasswordAsync(TUser user, string currentPassword, string newPassword) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.ChangePhoneNumberAsync(TUser user, string phoneNumber, string token) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.CheckPasswordAsync(TUser user, string password) -> System.Threading.Tasks.Task<bool>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.ConfirmEmailAsync(TUser user, string token) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.CountRecoveryCodesAsync(TUser user) -> System.Threading.Tasks.Task<int>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.CreateAsync(TUser user) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.CreateAsync(TUser user, string password) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.CreateSecurityTokenAsync(TUser user) -> System.Threading.Tasks.Task<byte[]>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.CreateTwoFactorRecoveryCode() -> string
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.DeleteAsync(TUser user) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.FindByEmailAsync(string email) -> System.Threading.Tasks.Task<TUser>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.FindByIdAsync(string userId) -> System.Threading.Tasks.Task<TUser>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.FindByLoginAsync(string loginProvider, string providerKey) -> System.Threading.Tasks.Task<TUser>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.FindByNameAsync(string userName) -> System.Threading.Tasks.Task<TUser>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.GenerateChangeEmailTokenAsync(TUser user, string newEmail) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.GenerateChangePhoneNumberTokenAsync(TUser user, string phoneNumber) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.GenerateConcurrencyStampAsync(TUser user) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.GenerateEmailConfirmationTokenAsync(TUser user) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.GenerateNewAuthenticatorKey() -> string
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.GenerateNewTwoFactorRecoveryCodesAsync(TUser user, int number) -> System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<string>>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.GeneratePasswordResetTokenAsync(TUser user) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.GenerateTwoFactorTokenAsync(TUser user, string tokenProvider) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.GenerateUserTokenAsync(TUser user, string tokenProvider, string purpose) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.GetAccessFailedCountAsync(TUser user) -> System.Threading.Tasks.Task<int>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.GetAuthenticationTokenAsync(TUser user, string loginProvider, string tokenName) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.GetAuthenticatorKeyAsync(TUser user) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.GetClaimsAsync(TUser user) -> System.Threading.Tasks.Task<System.Collections.Generic.IList<System.Security.Claims.Claim>>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.GetEmailAsync(TUser user) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.GetLockoutEnabledAsync(TUser user) -> System.Threading.Tasks.Task<bool>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.GetLockoutEndDateAsync(TUser user) -> System.Threading.Tasks.Task<System.DateTimeOffset?>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.GetLoginsAsync(TUser user) -> System.Threading.Tasks.Task<System.Collections.Generic.IList<Microsoft.AspNetCore.Identity.UserLoginInfo>>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.GetPhoneNumberAsync(TUser user) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.GetRolesAsync(TUser user) -> System.Threading.Tasks.Task<System.Collections.Generic.IList<string>>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.GetSecurityStampAsync(TUser user) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.GetTwoFactorEnabledAsync(TUser user) -> System.Threading.Tasks.Task<bool>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.GetUserAsync(System.Security.Claims.ClaimsPrincipal principal) -> System.Threading.Tasks.Task<TUser>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.GetUserId(System.Security.Claims.ClaimsPrincipal principal) -> string
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.GetUserIdAsync(TUser user) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.GetUserName(System.Security.Claims.ClaimsPrincipal principal) -> string
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.GetUserNameAsync(TUser user) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.GetUsersForClaimAsync(System.Security.Claims.Claim claim) -> System.Threading.Tasks.Task<System.Collections.Generic.IList<TUser>>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.GetUsersInRoleAsync(string roleName) -> System.Threading.Tasks.Task<System.Collections.Generic.IList<TUser>>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.GetValidTwoFactorProvidersAsync(TUser user) -> System.Threading.Tasks.Task<System.Collections.Generic.IList<string>>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.HasPasswordAsync(TUser user) -> System.Threading.Tasks.Task<bool>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.IsEmailConfirmedAsync(TUser user) -> System.Threading.Tasks.Task<bool>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.IsInRoleAsync(TUser user, string role) -> System.Threading.Tasks.Task<bool>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.IsLockedOutAsync(TUser user) -> System.Threading.Tasks.Task<bool>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.IsPhoneNumberConfirmedAsync(TUser user) -> System.Threading.Tasks.Task<bool>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.Logger.get -> Microsoft.Extensions.Logging.ILogger
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.Logger.set -> void
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.NormalizeEmail(string email) -> string
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.NormalizeName(string name) -> string
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.RedeemTwoFactorRecoveryCodeAsync(TUser user, string code) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.RegisterTokenProvider(string providerName, Microsoft.AspNetCore.Identity.IUserTwoFactorTokenProvider<TUser> provider) -> void
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.RemoveAuthenticationTokenAsync(TUser user, string loginProvider, string tokenName) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.RemoveClaimAsync(TUser user, System.Security.Claims.Claim claim) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.RemoveClaimsAsync(TUser user, System.Collections.Generic.IEnumerable<System.Security.Claims.Claim> claims) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.RemoveFromRoleAsync(TUser user, string role) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.RemoveFromRolesAsync(TUser user, System.Collections.Generic.IEnumerable<string> roles) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.RemoveLoginAsync(TUser user, string loginProvider, string providerKey) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.RemovePasswordAsync(TUser user) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.ReplaceClaimAsync(TUser user, System.Security.Claims.Claim claim, System.Security.Claims.Claim newClaim) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.ResetAccessFailedCountAsync(TUser user) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.ResetAuthenticatorKeyAsync(TUser user) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.ResetPasswordAsync(TUser user, string token, string newPassword) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.SetAuthenticationTokenAsync(TUser user, string loginProvider, string tokenName, string tokenValue) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.SetEmailAsync(TUser user, string email) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.SetLockoutEnabledAsync(TUser user, bool enabled) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.SetLockoutEndDateAsync(TUser user, System.DateTimeOffset? lockoutEnd) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.SetPhoneNumberAsync(TUser user, string phoneNumber) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.SetTwoFactorEnabledAsync(TUser user, bool enabled) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.SetUserNameAsync(TUser user, string userName) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.UpdateAsync(TUser user) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.UpdateNormalizedEmailAsync(TUser user) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.UpdateNormalizedUserNameAsync(TUser user) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.UpdatePasswordHash(TUser user, string newPassword, bool validatePassword) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.UpdateSecurityStampAsync(TUser user) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.UpdateUserAsync(TUser user) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.Users.get -> System.Linq.IQueryable<TUser>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.VerifyChangePhoneNumberTokenAsync(TUser user, string token, string phoneNumber) -> System.Threading.Tasks.Task<bool>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.VerifyPasswordAsync(Microsoft.AspNetCore.Identity.IUserPasswordStore<TUser> store, TUser user, string password) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.PasswordVerificationResult>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.VerifyTwoFactorTokenAsync(TUser user, string tokenProvider, string token) -> System.Threading.Tasks.Task<bool>
+~virtual Microsoft.AspNetCore.Identity.UserManager<TUser>.VerifyUserTokenAsync(TUser user, string tokenProvider, string purpose, string token) -> System.Threading.Tasks.Task<bool>
+~virtual Microsoft.AspNetCore.Identity.UserValidator<TUser>.ValidateAsync(Microsoft.AspNetCore.Identity.UserManager<TUser> manager, TUser user) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>

+ 1 - 0
src/Identity/Extensions.Stores/src/PublicAPI.Shipped.txt

@@ -0,0 +1 @@
+#nullable enable

+ 206 - 0
src/Identity/Extensions.Stores/src/PublicAPI.Unshipped.txt

@@ -0,0 +1,206 @@
+#nullable enable
+Microsoft.AspNetCore.Identity.IdentityRole
+Microsoft.AspNetCore.Identity.IdentityRole.IdentityRole() -> void
+Microsoft.AspNetCore.Identity.IdentityRole<TKey>.IdentityRole() -> void
+Microsoft.AspNetCore.Identity.IdentityRoleClaim<TKey>.IdentityRoleClaim() -> void
+Microsoft.AspNetCore.Identity.IdentityUser
+Microsoft.AspNetCore.Identity.IdentityUser.IdentityUser() -> void
+Microsoft.AspNetCore.Identity.IdentityUser<TKey>.IdentityUser() -> void
+Microsoft.AspNetCore.Identity.IdentityUserClaim<TKey>.IdentityUserClaim() -> void
+Microsoft.AspNetCore.Identity.IdentityUserLogin<TKey>.IdentityUserLogin() -> void
+Microsoft.AspNetCore.Identity.IdentityUserRole<TKey>.IdentityUserRole() -> void
+Microsoft.AspNetCore.Identity.IdentityUserToken<TKey>.IdentityUserToken() -> void
+Microsoft.AspNetCore.Identity.RoleStoreBase<TRole, TKey, TUserRole, TRoleClaim>.Dispose() -> void
+Microsoft.AspNetCore.Identity.RoleStoreBase<TRole, TKey, TUserRole, TRoleClaim>.ThrowIfDisposed() -> void
+Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.Dispose() -> void
+Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.ThrowIfDisposed() -> void
+virtual Microsoft.AspNetCore.Identity.IdentityRoleClaim<TKey>.Id.get -> int
+virtual Microsoft.AspNetCore.Identity.IdentityRoleClaim<TKey>.Id.set -> void
+virtual Microsoft.AspNetCore.Identity.IdentityUser<TKey>.AccessFailedCount.get -> int
+virtual Microsoft.AspNetCore.Identity.IdentityUser<TKey>.AccessFailedCount.set -> void
+virtual Microsoft.AspNetCore.Identity.IdentityUser<TKey>.EmailConfirmed.get -> bool
+virtual Microsoft.AspNetCore.Identity.IdentityUser<TKey>.EmailConfirmed.set -> void
+virtual Microsoft.AspNetCore.Identity.IdentityUser<TKey>.LockoutEnabled.get -> bool
+virtual Microsoft.AspNetCore.Identity.IdentityUser<TKey>.LockoutEnabled.set -> void
+virtual Microsoft.AspNetCore.Identity.IdentityUser<TKey>.LockoutEnd.get -> System.DateTimeOffset?
+virtual Microsoft.AspNetCore.Identity.IdentityUser<TKey>.LockoutEnd.set -> void
+virtual Microsoft.AspNetCore.Identity.IdentityUser<TKey>.PhoneNumberConfirmed.get -> bool
+virtual Microsoft.AspNetCore.Identity.IdentityUser<TKey>.PhoneNumberConfirmed.set -> void
+virtual Microsoft.AspNetCore.Identity.IdentityUser<TKey>.TwoFactorEnabled.get -> bool
+virtual Microsoft.AspNetCore.Identity.IdentityUser<TKey>.TwoFactorEnabled.set -> void
+virtual Microsoft.AspNetCore.Identity.IdentityUserClaim<TKey>.Id.get -> int
+virtual Microsoft.AspNetCore.Identity.IdentityUserClaim<TKey>.Id.set -> void
+~Microsoft.AspNetCore.Identity.IdentityRole.IdentityRole(string roleName) -> void
+~Microsoft.AspNetCore.Identity.IdentityRole<TKey>
+~Microsoft.AspNetCore.Identity.IdentityRole<TKey>.IdentityRole(string roleName) -> void
+~Microsoft.AspNetCore.Identity.IdentityRoleClaim<TKey>
+~Microsoft.AspNetCore.Identity.IdentityUser.IdentityUser(string userName) -> void
+~Microsoft.AspNetCore.Identity.IdentityUser<TKey>
+~Microsoft.AspNetCore.Identity.IdentityUser<TKey>.IdentityUser(string userName) -> void
+~Microsoft.AspNetCore.Identity.IdentityUserClaim<TKey>
+~Microsoft.AspNetCore.Identity.IdentityUserLogin<TKey>
+~Microsoft.AspNetCore.Identity.IdentityUserRole<TKey>
+~Microsoft.AspNetCore.Identity.IdentityUserToken<TKey>
+~Microsoft.AspNetCore.Identity.RoleStoreBase<TRole, TKey, TUserRole, TRoleClaim>
+~Microsoft.AspNetCore.Identity.RoleStoreBase<TRole, TKey, TUserRole, TRoleClaim>.ErrorDescriber.get -> Microsoft.AspNetCore.Identity.IdentityErrorDescriber
+~Microsoft.AspNetCore.Identity.RoleStoreBase<TRole, TKey, TUserRole, TRoleClaim>.ErrorDescriber.set -> void
+~Microsoft.AspNetCore.Identity.RoleStoreBase<TRole, TKey, TUserRole, TRoleClaim>.RoleStoreBase(Microsoft.AspNetCore.Identity.IdentityErrorDescriber describer) -> void
+~Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>
+~Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.ErrorDescriber.get -> Microsoft.AspNetCore.Identity.IdentityErrorDescriber
+~Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.ErrorDescriber.set -> void
+~Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.UserStoreBase(Microsoft.AspNetCore.Identity.IdentityErrorDescriber describer) -> void
+~Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim>
+~Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim>.UserStoreBase(Microsoft.AspNetCore.Identity.IdentityErrorDescriber describer) -> void
+~abstract Microsoft.AspNetCore.Identity.RoleStoreBase<TRole, TKey, TUserRole, TRoleClaim>.AddClaimAsync(TRole role, System.Security.Claims.Claim claim, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
+~abstract Microsoft.AspNetCore.Identity.RoleStoreBase<TRole, TKey, TUserRole, TRoleClaim>.CreateAsync(TRole role, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~abstract Microsoft.AspNetCore.Identity.RoleStoreBase<TRole, TKey, TUserRole, TRoleClaim>.DeleteAsync(TRole role, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~abstract Microsoft.AspNetCore.Identity.RoleStoreBase<TRole, TKey, TUserRole, TRoleClaim>.FindByIdAsync(string id, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<TRole>
+~abstract Microsoft.AspNetCore.Identity.RoleStoreBase<TRole, TKey, TUserRole, TRoleClaim>.FindByNameAsync(string normalizedName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<TRole>
+~abstract Microsoft.AspNetCore.Identity.RoleStoreBase<TRole, TKey, TUserRole, TRoleClaim>.GetClaimsAsync(TRole role, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Generic.IList<System.Security.Claims.Claim>>
+~abstract Microsoft.AspNetCore.Identity.RoleStoreBase<TRole, TKey, TUserRole, TRoleClaim>.RemoveClaimAsync(TRole role, System.Security.Claims.Claim claim, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
+~abstract Microsoft.AspNetCore.Identity.RoleStoreBase<TRole, TKey, TUserRole, TRoleClaim>.Roles.get -> System.Linq.IQueryable<TRole>
+~abstract Microsoft.AspNetCore.Identity.RoleStoreBase<TRole, TKey, TUserRole, TRoleClaim>.UpdateAsync(TRole role, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~abstract Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.AddClaimsAsync(TUser user, System.Collections.Generic.IEnumerable<System.Security.Claims.Claim> claims, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
+~abstract Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.AddLoginAsync(TUser user, Microsoft.AspNetCore.Identity.UserLoginInfo login, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
+~abstract Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.AddUserTokenAsync(TUserToken token) -> System.Threading.Tasks.Task
+~abstract Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.CreateAsync(TUser user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~abstract Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.DeleteAsync(TUser user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~abstract Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.FindByEmailAsync(string normalizedEmail, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<TUser>
+~abstract Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.FindByIdAsync(string userId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<TUser>
+~abstract Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.FindByNameAsync(string normalizedUserName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<TUser>
+~abstract Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.FindTokenAsync(TUser user, string loginProvider, string name, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<TUserToken>
+~abstract Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.FindUserAsync(TKey userId, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<TUser>
+~abstract Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.FindUserLoginAsync(TKey userId, string loginProvider, string providerKey, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<TUserLogin>
+~abstract Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.FindUserLoginAsync(string loginProvider, string providerKey, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<TUserLogin>
+~abstract Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.GetClaimsAsync(TUser user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Generic.IList<System.Security.Claims.Claim>>
+~abstract Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.GetLoginsAsync(TUser user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Generic.IList<Microsoft.AspNetCore.Identity.UserLoginInfo>>
+~abstract Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.GetUsersForClaimAsync(System.Security.Claims.Claim claim, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Generic.IList<TUser>>
+~abstract Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.RemoveClaimsAsync(TUser user, System.Collections.Generic.IEnumerable<System.Security.Claims.Claim> claims, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
+~abstract Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.RemoveLoginAsync(TUser user, string loginProvider, string providerKey, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
+~abstract Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.RemoveUserTokenAsync(TUserToken token) -> System.Threading.Tasks.Task
+~abstract Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.ReplaceClaimAsync(TUser user, System.Security.Claims.Claim claim, System.Security.Claims.Claim newClaim, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
+~abstract Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.UpdateAsync(TUser user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult>
+~abstract Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.Users.get -> System.Linq.IQueryable<TUser>
+~abstract Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim>.AddToRoleAsync(TUser user, string normalizedRoleName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
+~abstract Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim>.FindRoleAsync(string normalizedRoleName, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<TRole>
+~abstract Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim>.FindUserRoleAsync(TKey userId, TKey roleId, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<TUserRole>
+~abstract Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim>.GetRolesAsync(TUser user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Generic.IList<string>>
+~abstract Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim>.GetUsersInRoleAsync(string normalizedRoleName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.Collections.Generic.IList<TUser>>
+~abstract Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim>.IsInRoleAsync(TUser user, string normalizedRoleName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<bool>
+~abstract Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim>.RemoveFromRoleAsync(TUser user, string normalizedRoleName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
+~override Microsoft.AspNetCore.Identity.IdentityRole<TKey>.ToString() -> string
+~override Microsoft.AspNetCore.Identity.IdentityUser<TKey>.ToString() -> string
+~virtual Microsoft.AspNetCore.Identity.IdentityRole<TKey>.ConcurrencyStamp.get -> string
+~virtual Microsoft.AspNetCore.Identity.IdentityRole<TKey>.ConcurrencyStamp.set -> void
+~virtual Microsoft.AspNetCore.Identity.IdentityRole<TKey>.Id.get -> TKey
+~virtual Microsoft.AspNetCore.Identity.IdentityRole<TKey>.Id.set -> void
+~virtual Microsoft.AspNetCore.Identity.IdentityRole<TKey>.Name.get -> string
+~virtual Microsoft.AspNetCore.Identity.IdentityRole<TKey>.Name.set -> void
+~virtual Microsoft.AspNetCore.Identity.IdentityRole<TKey>.NormalizedName.get -> string
+~virtual Microsoft.AspNetCore.Identity.IdentityRole<TKey>.NormalizedName.set -> void
+~virtual Microsoft.AspNetCore.Identity.IdentityRoleClaim<TKey>.ClaimType.get -> string
+~virtual Microsoft.AspNetCore.Identity.IdentityRoleClaim<TKey>.ClaimType.set -> void
+~virtual Microsoft.AspNetCore.Identity.IdentityRoleClaim<TKey>.ClaimValue.get -> string
+~virtual Microsoft.AspNetCore.Identity.IdentityRoleClaim<TKey>.ClaimValue.set -> void
+~virtual Microsoft.AspNetCore.Identity.IdentityRoleClaim<TKey>.InitializeFromClaim(System.Security.Claims.Claim other) -> void
+~virtual Microsoft.AspNetCore.Identity.IdentityRoleClaim<TKey>.RoleId.get -> TKey
+~virtual Microsoft.AspNetCore.Identity.IdentityRoleClaim<TKey>.RoleId.set -> void
+~virtual Microsoft.AspNetCore.Identity.IdentityRoleClaim<TKey>.ToClaim() -> System.Security.Claims.Claim
+~virtual Microsoft.AspNetCore.Identity.IdentityUser<TKey>.ConcurrencyStamp.get -> string
+~virtual Microsoft.AspNetCore.Identity.IdentityUser<TKey>.ConcurrencyStamp.set -> void
+~virtual Microsoft.AspNetCore.Identity.IdentityUser<TKey>.Email.get -> string
+~virtual Microsoft.AspNetCore.Identity.IdentityUser<TKey>.Email.set -> void
+~virtual Microsoft.AspNetCore.Identity.IdentityUser<TKey>.Id.get -> TKey
+~virtual Microsoft.AspNetCore.Identity.IdentityUser<TKey>.Id.set -> void
+~virtual Microsoft.AspNetCore.Identity.IdentityUser<TKey>.NormalizedEmail.get -> string
+~virtual Microsoft.AspNetCore.Identity.IdentityUser<TKey>.NormalizedEmail.set -> void
+~virtual Microsoft.AspNetCore.Identity.IdentityUser<TKey>.NormalizedUserName.get -> string
+~virtual Microsoft.AspNetCore.Identity.IdentityUser<TKey>.NormalizedUserName.set -> void
+~virtual Microsoft.AspNetCore.Identity.IdentityUser<TKey>.PasswordHash.get -> string
+~virtual Microsoft.AspNetCore.Identity.IdentityUser<TKey>.PasswordHash.set -> void
+~virtual Microsoft.AspNetCore.Identity.IdentityUser<TKey>.PhoneNumber.get -> string
+~virtual Microsoft.AspNetCore.Identity.IdentityUser<TKey>.PhoneNumber.set -> void
+~virtual Microsoft.AspNetCore.Identity.IdentityUser<TKey>.SecurityStamp.get -> string
+~virtual Microsoft.AspNetCore.Identity.IdentityUser<TKey>.SecurityStamp.set -> void
+~virtual Microsoft.AspNetCore.Identity.IdentityUser<TKey>.UserName.get -> string
+~virtual Microsoft.AspNetCore.Identity.IdentityUser<TKey>.UserName.set -> void
+~virtual Microsoft.AspNetCore.Identity.IdentityUserClaim<TKey>.ClaimType.get -> string
+~virtual Microsoft.AspNetCore.Identity.IdentityUserClaim<TKey>.ClaimType.set -> void
+~virtual Microsoft.AspNetCore.Identity.IdentityUserClaim<TKey>.ClaimValue.get -> string
+~virtual Microsoft.AspNetCore.Identity.IdentityUserClaim<TKey>.ClaimValue.set -> void
+~virtual Microsoft.AspNetCore.Identity.IdentityUserClaim<TKey>.InitializeFromClaim(System.Security.Claims.Claim claim) -> void
+~virtual Microsoft.AspNetCore.Identity.IdentityUserClaim<TKey>.ToClaim() -> System.Security.Claims.Claim
+~virtual Microsoft.AspNetCore.Identity.IdentityUserClaim<TKey>.UserId.get -> TKey
+~virtual Microsoft.AspNetCore.Identity.IdentityUserClaim<TKey>.UserId.set -> void
+~virtual Microsoft.AspNetCore.Identity.IdentityUserLogin<TKey>.LoginProvider.get -> string
+~virtual Microsoft.AspNetCore.Identity.IdentityUserLogin<TKey>.LoginProvider.set -> void
+~virtual Microsoft.AspNetCore.Identity.IdentityUserLogin<TKey>.ProviderDisplayName.get -> string
+~virtual Microsoft.AspNetCore.Identity.IdentityUserLogin<TKey>.ProviderDisplayName.set -> void
+~virtual Microsoft.AspNetCore.Identity.IdentityUserLogin<TKey>.ProviderKey.get -> string
+~virtual Microsoft.AspNetCore.Identity.IdentityUserLogin<TKey>.ProviderKey.set -> void
+~virtual Microsoft.AspNetCore.Identity.IdentityUserLogin<TKey>.UserId.get -> TKey
+~virtual Microsoft.AspNetCore.Identity.IdentityUserLogin<TKey>.UserId.set -> void
+~virtual Microsoft.AspNetCore.Identity.IdentityUserRole<TKey>.RoleId.get -> TKey
+~virtual Microsoft.AspNetCore.Identity.IdentityUserRole<TKey>.RoleId.set -> void
+~virtual Microsoft.AspNetCore.Identity.IdentityUserRole<TKey>.UserId.get -> TKey
+~virtual Microsoft.AspNetCore.Identity.IdentityUserRole<TKey>.UserId.set -> void
+~virtual Microsoft.AspNetCore.Identity.IdentityUserToken<TKey>.LoginProvider.get -> string
+~virtual Microsoft.AspNetCore.Identity.IdentityUserToken<TKey>.LoginProvider.set -> void
+~virtual Microsoft.AspNetCore.Identity.IdentityUserToken<TKey>.Name.get -> string
+~virtual Microsoft.AspNetCore.Identity.IdentityUserToken<TKey>.Name.set -> void
+~virtual Microsoft.AspNetCore.Identity.IdentityUserToken<TKey>.UserId.get -> TKey
+~virtual Microsoft.AspNetCore.Identity.IdentityUserToken<TKey>.UserId.set -> void
+~virtual Microsoft.AspNetCore.Identity.IdentityUserToken<TKey>.Value.get -> string
+~virtual Microsoft.AspNetCore.Identity.IdentityUserToken<TKey>.Value.set -> void
+~virtual Microsoft.AspNetCore.Identity.RoleStoreBase<TRole, TKey, TUserRole, TRoleClaim>.ConvertIdFromString(string id) -> TKey
+~virtual Microsoft.AspNetCore.Identity.RoleStoreBase<TRole, TKey, TUserRole, TRoleClaim>.ConvertIdToString(TKey id) -> string
+~virtual Microsoft.AspNetCore.Identity.RoleStoreBase<TRole, TKey, TUserRole, TRoleClaim>.CreateRoleClaim(TRole role, System.Security.Claims.Claim claim) -> TRoleClaim
+~virtual Microsoft.AspNetCore.Identity.RoleStoreBase<TRole, TKey, TUserRole, TRoleClaim>.GetNormalizedRoleNameAsync(TRole role, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.RoleStoreBase<TRole, TKey, TUserRole, TRoleClaim>.GetRoleIdAsync(TRole role, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.RoleStoreBase<TRole, TKey, TUserRole, TRoleClaim>.GetRoleNameAsync(TRole role, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.RoleStoreBase<TRole, TKey, TUserRole, TRoleClaim>.SetNormalizedRoleNameAsync(TRole role, string normalizedName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.RoleStoreBase<TRole, TKey, TUserRole, TRoleClaim>.SetRoleNameAsync(TRole role, string roleName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.ConvertIdFromString(string id) -> TKey
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.ConvertIdToString(TKey id) -> string
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.CountCodesAsync(TUser user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<int>
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.CreateUserClaim(TUser user, System.Security.Claims.Claim claim) -> TUserClaim
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.CreateUserLogin(TUser user, Microsoft.AspNetCore.Identity.UserLoginInfo login) -> TUserLogin
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.CreateUserToken(TUser user, string loginProvider, string name, string value) -> TUserToken
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.FindByLoginAsync(string loginProvider, string providerKey, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<TUser>
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.GetAccessFailedCountAsync(TUser user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<int>
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.GetAuthenticatorKeyAsync(TUser user, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.GetEmailAsync(TUser user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.GetEmailConfirmedAsync(TUser user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<bool>
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.GetLockoutEnabledAsync(TUser user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<bool>
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.GetLockoutEndDateAsync(TUser user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<System.DateTimeOffset?>
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.GetNormalizedEmailAsync(TUser user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.GetNormalizedUserNameAsync(TUser user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.GetPasswordHashAsync(TUser user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.GetPhoneNumberAsync(TUser user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.GetPhoneNumberConfirmedAsync(TUser user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<bool>
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.GetSecurityStampAsync(TUser user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.GetTokenAsync(TUser user, string loginProvider, string name, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.GetTwoFactorEnabledAsync(TUser user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<bool>
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.GetUserIdAsync(TUser user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.GetUserNameAsync(TUser user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<string>
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.HasPasswordAsync(TUser user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<bool>
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.IncrementAccessFailedCountAsync(TUser user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<int>
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.RedeemCodeAsync(TUser user, string code, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<bool>
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.RemoveTokenAsync(TUser user, string loginProvider, string name, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.ReplaceCodesAsync(TUser user, System.Collections.Generic.IEnumerable<string> recoveryCodes, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.ResetAccessFailedCountAsync(TUser user, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.SetAuthenticatorKeyAsync(TUser user, string key, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.SetEmailAsync(TUser user, string email, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.SetEmailConfirmedAsync(TUser user, bool confirmed, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.SetLockoutEnabledAsync(TUser user, bool enabled, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.SetLockoutEndDateAsync(TUser user, System.DateTimeOffset? lockoutEnd, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.SetNormalizedEmailAsync(TUser user, string normalizedEmail, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.SetNormalizedUserNameAsync(TUser user, string normalizedName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.SetPasswordHashAsync(TUser user, string passwordHash, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.SetPhoneNumberAsync(TUser user, string phoneNumber, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.SetPhoneNumberConfirmedAsync(TUser user, bool confirmed, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.SetSecurityStampAsync(TUser user, string stamp, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.SetTokenAsync(TUser user, string loginProvider, string name, string value, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.SetTwoFactorEnabledAsync(TUser user, bool enabled, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TKey, TUserClaim, TUserLogin, TUserToken>.SetUserNameAsync(TUser user, string userName, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task
+~virtual Microsoft.AspNetCore.Identity.UserStoreBase<TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TUserToken, TRoleClaim>.CreateUserRole(TUser user, TRole role) -> TUserRole

+ 3 - 16
src/JSInterop/Microsoft.JSInterop/src/JSInProcessObjectReference.cs → src/JSInterop/Microsoft.JSInterop/src/IJSInProcessObjectReference.cs

@@ -1,22 +1,15 @@
 // Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
-using System.Diagnostics.CodeAnalysis;
+using System;
 
 namespace Microsoft.JSInterop
 {
     /// <summary>
     /// Represents a reference to a JavaScript object whose functions can be invoked synchronously.
     /// </summary>
-    public class JSInProcessObjectReference : JSObjectReference
+    public interface IJSInProcessObjectReference : IJSObjectReference, IDisposable
     {
-        private readonly JSInProcessRuntime _jsRuntime;
-
-        internal JSInProcessObjectReference(JSInProcessRuntime jsRuntime, long id) : base(jsRuntime, id)
-        {
-            _jsRuntime = jsRuntime;
-        }
-
         /// <summary>
         /// Invokes the specified JavaScript function synchronously.
         /// </summary>
@@ -24,12 +17,6 @@ namespace Microsoft.JSInterop
         /// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
         /// <param name="args">JSON-serializable arguments.</param>
         /// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
-        [return: MaybeNull]
-        public TValue Invoke<TValue>(string identifier, params object[] args)
-        {
-            ThrowIfDisposed();
-
-            return _jsRuntime.Invoke<TValue>(identifier, Id, args);
-        }
+        TValue Invoke<TValue>(string identifier, params object?[]? args);
     }
 }

+ 41 - 0
src/JSInterop/Microsoft.JSInterop/src/IJSObjectReference.cs

@@ -0,0 +1,41 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Microsoft.JSInterop
+{
+    /// <summary>
+    /// Represents a reference to a JavaScript object.
+    /// </summary>
+    public interface IJSObjectReference : IAsyncDisposable
+    {
+        /// <summary>
+        /// Invokes the specified JavaScript function asynchronously.
+        /// <para>
+        /// <see cref="JSRuntime"/> will apply timeouts to this operation based on the value configured in <see cref="JSRuntime.DefaultAsyncTimeout"/>. To dispatch a call with a different, or no timeout,
+        /// consider using <see cref="InvokeAsync{TValue}(string, CancellationToken, object[])" />.
+        /// </para>
+        /// </summary>
+        /// <typeparam name="TValue">The JSON-serializable return type.</typeparam>
+        /// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
+        /// <param name="args">JSON-serializable arguments.</param>
+        /// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
+        ValueTask<TValue> InvokeAsync<TValue>(string identifier, object?[]? args);
+
+        /// <summary>
+        /// Invokes the specified JavaScript function asynchronously.
+        /// </summary>
+        /// <typeparam name="TValue">The JSON-serializable return type.</typeparam>
+        /// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
+        /// <param name="cancellationToken">
+        /// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts
+        /// (<see cref="JSRuntime.DefaultAsyncTimeout"/>) from being applied.
+        /// </param>
+        /// <param name="args">JSON-serializable arguments.</param>
+        /// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
+        ValueTask<TValue> InvokeAsync<TValue>(string identifier, CancellationToken cancellationToken, object?[]? args);
+    }
+}

+ 55 - 0
src/JSInterop/Microsoft.JSInterop/src/IJSUnmarshalledObjectReference.cs

@@ -0,0 +1,55 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace Microsoft.JSInterop
+{
+    /// <summary>
+    /// Represents a reference to a JavaScript object whose functions can be invoked synchronously without JSON marshalling.
+    /// </summary>
+    public interface IJSUnmarshalledObjectReference : IJSInProcessObjectReference
+    {
+        /// <summary>
+        /// Invokes the JavaScript function registered with the specified identifier.
+        /// </summary>
+        /// <typeparam name="TResult">The .NET type corresponding to the function's return value type.</typeparam>
+        /// <param name="identifier">The identifier used when registering the target function.</param>
+        /// <returns>The result of the function invocation.</returns>
+        TResult InvokeUnmarshalled<TResult>(string identifier);
+
+        /// <summary>
+        /// Invokes the JavaScript function registered with the specified identifier.
+        /// </summary>
+        /// <typeparam name="T0">The type of the first argument.</typeparam>
+        /// <typeparam name="TResult">The .NET type corresponding to the function's return value type.</typeparam>
+        /// <param name="identifier">The identifier used when registering the target function.</param>
+        /// <param name="arg0">The first argument.</param>
+        /// <returns>The result of the function invocation.</returns>
+        TResult InvokeUnmarshalled<T0, TResult>(string identifier, T0 arg0);
+
+        /// <summary>
+        /// Invokes the JavaScript function registered with the specified identifier.
+        /// </summary>
+        /// <typeparam name="T0">The type of the first argument.</typeparam>
+        /// <typeparam name="T1">The type of the second argument.</typeparam>
+        /// <typeparam name="TResult">The .NET type corresponding to the function's return value type.</typeparam>
+        /// <param name="identifier">The identifier used when registering the target function.</param>
+        /// <param name="arg0">The first argument.</param>
+        /// <param name="arg1">The second argument.</param>
+        /// <returns>The result of the function invocation.</returns>
+        TResult InvokeUnmarshalled<T0, T1, TResult>(string identifier, T0 arg0, T1 arg1);
+
+        /// <summary>
+        /// Invokes the JavaScript function registered with the specified identifier.
+        /// </summary>
+        /// <typeparam name="T0">The type of the first argument.</typeparam>
+        /// <typeparam name="T1">The type of the second argument.</typeparam>
+        /// <typeparam name="T2">The type of the third argument.</typeparam>
+        /// <typeparam name="TResult">The .NET type corresponding to the function's return value type.</typeparam>
+        /// <param name="identifier">The identifier used when registering the target function.</param>
+        /// <param name="arg0">The first argument.</param>
+        /// <param name="arg1">The second argument.</param>
+        /// <param name="arg2">The third argument.</param>
+        /// <returns>The result of the function invocation.</returns>
+        TResult InvokeUnmarshalled<T0, T1, T2, TResult>(string identifier, T0 arg0, T1 arg1, T2 arg2);
+    }
+}

+ 45 - 0
src/JSInterop/Microsoft.JSInterop/src/Implementation/JSInProcessObjectReference.cs

@@ -0,0 +1,45 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Diagnostics.CodeAnalysis;
+
+namespace Microsoft.JSInterop.Implementation
+{
+    /// <summary>
+    /// Implements functionality for <see cref="IJSInProcessObjectReference"/>.
+    /// </summary>
+    public class JSInProcessObjectReference : JSObjectReference, IJSInProcessObjectReference
+    {
+        private readonly JSInProcessRuntime _jsRuntime;
+
+        /// <summary>
+        /// Inititializes a new <see cref="JSInProcessObjectReference"/> instance.
+        /// </summary>
+        /// <param name="jsRuntime">The <see cref="JSInProcessRuntime"/> used for invoking JS interop calls.</param>
+        /// <param name="id">The unique identifier.</param>
+        protected internal JSInProcessObjectReference(JSInProcessRuntime jsRuntime, long id) : base(jsRuntime, id)
+        {
+            _jsRuntime = jsRuntime;
+        }
+
+        /// <inheritdoc />
+        [return: MaybeNull]
+        public TValue Invoke<TValue>(string identifier, params object?[]? args)
+        {
+            ThrowIfDisposed();
+
+            return _jsRuntime.Invoke<TValue>(identifier, Id, args);
+        }
+
+        /// <inheritdoc />
+        public void Dispose()
+        {
+            if (!Disposed)
+            {
+                Disposed = true;
+
+                _jsRuntime.InvokeVoid("DotNet.jsCallDispatcher.disposeJSObjectReferenceById", Id);
+            }
+        }
+    }
+}

+ 72 - 0
src/JSInterop/Microsoft.JSInterop/src/Implementation/JSObjectReference.cs

@@ -0,0 +1,72 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Microsoft.JSInterop.Implementation
+{
+    /// <summary>
+    /// Implements functionality for <see cref="IJSObjectReference"/>.
+    /// </summary>
+    public class JSObjectReference : IJSObjectReference
+    {
+        private readonly JSRuntime _jsRuntime;
+
+        internal bool Disposed { get; set; }
+
+        /// <summary>
+        /// The unique identifier assigned to this instance.
+        /// </summary>
+        protected internal long Id { get; }
+
+        /// <summary>
+        /// Inititializes a new <see cref="JSObjectReference"/> instance.
+        /// </summary>
+        /// <param name="jsRuntime">The <see cref="JSRuntime"/> used for invoking JS interop calls.</param>
+        /// <param name="id">The unique identifier.</param>
+        protected internal JSObjectReference(JSRuntime jsRuntime, long id)
+        {
+            _jsRuntime = jsRuntime;
+
+            Id = id;
+        }
+
+        /// <inheritdoc />
+        public ValueTask<TValue> InvokeAsync<TValue>(string identifier, object?[]? args)
+        {
+            ThrowIfDisposed();
+
+            return _jsRuntime.InvokeAsync<TValue>(Id, identifier, args);
+        }
+
+        /// <inheritdoc />
+        public ValueTask<TValue> InvokeAsync<TValue>(string identifier, CancellationToken cancellationToken, object?[]? args)
+        {
+            ThrowIfDisposed();
+
+            return _jsRuntime.InvokeAsync<TValue>(Id, identifier, cancellationToken, args);
+        }
+
+        /// <inheritdoc />
+        public async ValueTask DisposeAsync()
+        {
+            if (!Disposed)
+            {
+                Disposed = true;
+
+                await _jsRuntime.InvokeVoidAsync("DotNet.jsCallDispatcher.disposeJSObjectReferenceById", Id);
+            }
+        }
+
+        /// <inheritdoc />
+        protected void ThrowIfDisposed()
+        {
+            if (Disposed)
+            {
+                throw new ObjectDisposedException(GetType().Name);
+            }
+        }
+    }
+}

+ 16 - 9
src/JSInterop/Microsoft.JSInterop/src/Infrastructure/JSObjectReferenceJsonConverter.cs

@@ -4,20 +4,27 @@
 using System;
 using System.Text.Json;
 using System.Text.Json.Serialization;
+using Microsoft.JSInterop.Implementation;
 
 namespace Microsoft.JSInterop.Infrastructure
 {
-    internal sealed class JSObjectReferenceJsonConverter<TJSObjectReference>
-        : JsonConverter<TJSObjectReference> where TJSObjectReference : JSObjectReference
+    internal sealed class JSObjectReferenceJsonConverter<TInterface, TImplementation> : JsonConverter<TInterface>
+        where TInterface : class, IJSObjectReference
+        where TImplementation : JSObjectReference, TInterface
     {
-        private readonly Func<long, TJSObjectReference> _jsObjectReferenceFactory;
+        private static readonly JsonEncodedText _idKey = JsonEncodedText.Encode("__jsObjectId");
 
-        public JSObjectReferenceJsonConverter(Func<long, TJSObjectReference> jsObjectReferenceFactory)
+        private readonly Func<long, TImplementation> _jsObjectReferenceFactory;
+
+        public JSObjectReferenceJsonConverter(Func<long, TImplementation> jsObjectReferenceFactory)
         {
             _jsObjectReferenceFactory = jsObjectReferenceFactory;
         }
 
-        public override TJSObjectReference? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+        public override bool CanConvert(Type typeToConvert)
+            => typeToConvert == typeof(TInterface) || typeToConvert == typeof(TImplementation);
+
+        public override TInterface? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
         {
             long id = -1;
 
@@ -25,7 +32,7 @@ namespace Microsoft.JSInterop.Infrastructure
             {
                 if (reader.TokenType == JsonTokenType.PropertyName)
                 {
-                    if (id == -1 && reader.ValueTextEquals(JSObjectReference.IdKey.EncodedUtf8Bytes))
+                    if (id == -1 && reader.ValueTextEquals(_idKey.EncodedUtf8Bytes))
                     {
                         reader.Read();
                         id = reader.GetInt64();
@@ -43,16 +50,16 @@ namespace Microsoft.JSInterop.Infrastructure
 
             if (id == -1)
             {
-                throw new JsonException($"Required property {JSObjectReference.IdKey} not found.");
+                throw new JsonException($"Required property {_idKey} not found.");
             }
 
             return _jsObjectReferenceFactory(id);
         }
 
-        public override void Write(Utf8JsonWriter writer, TJSObjectReference value, JsonSerializerOptions options)
+        public override void Write(Utf8JsonWriter writer, TInterface value, JsonSerializerOptions options)
         {
             writer.WriteStartObject();
-            writer.WriteNumber(JSObjectReference.IdKey, value.Id);
+            writer.WriteNumber(_idKey, ((TImplementation)value).Id);
             writer.WriteEndObject();
         }
     }

+ 26 - 0
src/JSInterop/Microsoft.JSInterop/src/JSInProcessObjectReferenceExtensions.cs

@@ -0,0 +1,26 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+
+namespace Microsoft.JSInterop
+{
+    public static class JSInProcessObjectReferenceExtensions
+    {
+        /// <summary>
+        /// Invokes the specified JavaScript function synchronously.
+        /// </summary>
+        /// <param name="jsObjectReference">The <see cref="IJSInProcessObjectReference"/>.</param>
+        /// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
+        /// <param name="args">JSON-serializable arguments.</param>
+        public static void InvokeVoid(this IJSInProcessObjectReference jsObjectReference, string identifier, params object?[] args)
+        {
+            if (jsObjectReference == null)
+            {
+                throw new ArgumentNullException(nameof(jsObjectReference));
+            }
+
+            jsObjectReference.Invoke<object>(identifier, args);
+        }
+    }
+}

+ 5 - 3
src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntime.cs

@@ -3,6 +3,7 @@
 
 using System.Diagnostics.CodeAnalysis;
 using System.Text.Json;
+using Microsoft.JSInterop.Implementation;
 using Microsoft.JSInterop.Infrastructure;
 
 namespace Microsoft.JSInterop
@@ -17,8 +18,9 @@ namespace Microsoft.JSInterop
         /// </summary>
         protected JSInProcessRuntime()
         {
-            JsonSerializerOptions.Converters.Add(new JSObjectReferenceJsonConverter<JSInProcessObjectReference>(
-                id => new JSInProcessObjectReference(this, id)));
+            JsonSerializerOptions.Converters.Add(
+                new JSObjectReferenceJsonConverter<IJSInProcessObjectReference, JSInProcessObjectReference>(
+                    id => new JSInProcessObjectReference(this, id)));
         }
 
         [return: MaybeNull]
@@ -27,7 +29,7 @@ namespace Microsoft.JSInterop
             var resultJson = InvokeJS(
                 identifier,
                 JsonSerializer.Serialize(args, JsonSerializerOptions),
-                ResultTypeFromGeneric<TValue>(),
+                JSCallResultTypeHelper.FromGeneric<TValue>(),
                 targetInstanceId);
 
             if (resultJson is null)

+ 0 - 103
src/JSInterop/Microsoft.JSInterop/src/JSObjectReference.cs

@@ -1,103 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-using System;
-using System.Text.Json;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace Microsoft.JSInterop
-{
-    /// <summary>
-    /// Represents a reference to a JavaScript object.
-    /// </summary>
-    public class JSObjectReference : IAsyncDisposable
-    {
-        internal static readonly JsonEncodedText IdKey = JsonEncodedText.Encode("__jsObjectId");
-
-        private readonly JSRuntime _jsRuntime;
-
-        private bool _disposed;
-
-        internal long Id { get; }
-
-        internal JSObjectReference(JSRuntime jsRuntime, long id)
-        {
-            _jsRuntime = jsRuntime;
-
-            Id = id;
-        }
-
-        /// <summary>
-        /// Invokes the specified JavaScript function asynchronously.
-        /// </summary>
-        /// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
-        /// <param name="args">JSON-serializable arguments.</param>
-        /// <returns>A <see cref="ValueTask"/> that represents the asynchronous invocation operation.</returns>
-        public async ValueTask InvokeVoidAsync(string identifier, params object[] args)
-        {
-            await InvokeAsync<object>(identifier, args);
-        }
-
-        /// <summary>
-        /// Invokes the specified JavaScript function asynchronously.
-        /// <para>
-        /// <see cref="JSRuntime"/> will apply timeouts to this operation based on the value configured in <see cref="JSRuntime.DefaultAsyncTimeout"/>. To dispatch a call with a different, or no timeout,
-        /// consider using <see cref="InvokeAsync{TValue}(string, CancellationToken, object[])" />.
-        /// </para>
-        /// </summary>
-        /// <typeparam name="TValue">The JSON-serializable return type.</typeparam>
-        /// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
-        /// <param name="args">JSON-serializable arguments.</param>
-        /// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
-        public ValueTask<TValue> InvokeAsync<TValue>(string identifier, params object[] args)
-        {
-            ThrowIfDisposed();
-
-            return _jsRuntime.InvokeAsync<TValue>(Id, identifier, args);
-        }
-
-        /// <summary>
-        /// Invokes the specified JavaScript function asynchronously.
-        /// </summary>
-        /// <typeparam name="TValue">The JSON-serializable return type.</typeparam>
-        /// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
-        /// <param name="cancellationToken">
-        /// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts
-        /// (<see cref="JSRuntime.DefaultAsyncTimeout"/>) from being applied.
-        /// </param>
-        /// <param name="args">JSON-serializable arguments.</param>
-        /// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
-        public ValueTask<TValue> InvokeAsync<TValue>(string identifier, CancellationToken cancellationToken, params object[] args)
-        {
-            ThrowIfDisposed();
-
-            return _jsRuntime.InvokeAsync<TValue>(Id, identifier, cancellationToken, args);
-        }
-
-        /// <summary>
-        /// Disposes the <see cref="JSObjectReference"/>, freeing its resources and disabling it from further use.
-        /// </summary>
-        /// <returns>A <see cref="ValueTask"/> representing the completion of the operation.</returns>
-        public async ValueTask DisposeAsync()
-        {
-            if (!_disposed)
-            {
-                _disposed = true;
-
-                await _jsRuntime.InvokeVoidAsync("DotNet.jsCallDispatcher.disposeJSObjectReferenceById", Id);
-            }
-        }
-
-        /// <summary>
-        /// Throws an exception if this instance has been disposed.
-        /// </summary>
-        protected void ThrowIfDisposed()
-        {
-            if (_disposed)
-            {
-                throw new ObjectDisposedException(GetType().Name);
-            }
-        }
-    }
-}

+ 139 - 0
src/JSInterop/Microsoft.JSInterop/src/JSObjectReferenceExtensions.cs

@@ -0,0 +1,139 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Microsoft.JSInterop
+{
+    /// <summary>
+    /// Extensions for <see cref="IJSObjectReference"/>.
+    /// </summary>
+    public static class JSObjectReferenceExtensions
+    {
+        /// <summary>
+        /// Invokes the specified JavaScript function asynchronously.
+        /// </summary>
+        /// <param name="jsObjectReference">The <see cref="IJSObjectReference"/>.</param>
+        /// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
+        /// <param name="args">JSON-serializable arguments.</param>
+        /// <returns>A <see cref="ValueTask"/> that represents the asynchronous invocation operation.</returns>
+        public static async ValueTask InvokeVoidAsync(this IJSObjectReference jsObjectReference, string identifier, params object?[] args)
+        {
+            if (jsObjectReference is null)
+            {
+                throw new ArgumentNullException(nameof(jsObjectReference));
+            }
+
+            await jsObjectReference.InvokeAsync<object>(identifier, args);
+        }
+
+        /// <summary>
+        /// Invokes the specified JavaScript function asynchronously.
+        /// <para>
+        /// <see cref="JSRuntime"/> will apply timeouts to this operation based on the value configured in <see cref="JSRuntime.DefaultAsyncTimeout"/>. To dispatch a call with a different timeout, or no timeout,
+        /// consider using <see cref="IJSObjectReference.InvokeAsync{TValue}(string, CancellationToken, object[])" />.
+        /// </para>
+        /// </summary>
+        /// <param name="jsObjectReference">The <see cref="IJSObjectReference"/>.</param>
+        /// <typeparam name="TValue">The JSON-serializable return type.</typeparam>
+        /// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
+        /// <param name="args">JSON-serializable arguments.</param>
+        /// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
+        public static ValueTask<TValue> InvokeAsync<TValue>(this IJSObjectReference jsObjectReference, string identifier, params object?[] args)
+        {
+            if (jsObjectReference is null)
+            {
+                throw new ArgumentNullException(nameof(jsObjectReference));
+            }
+
+            return jsObjectReference.InvokeAsync<TValue>(identifier, args);
+        }
+
+        /// <summary>
+        /// Invokes the specified JavaScript function asynchronously.
+        /// </summary>
+        /// <typeparam name="TValue">The JSON-serializable return type.</typeparam>
+        /// <param name="jsObjectReference">The <see cref="IJSObjectReference"/>.</param>
+        /// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
+        /// <param name="cancellationToken">
+        /// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts
+        /// (<see cref="JSRuntime.DefaultAsyncTimeout"/>) from being applied.
+        /// </param>
+        /// <param name="args">JSON-serializable arguments.</param>
+        /// <returns>An instance of <typeparamref name="TValue"/> obtained by JSON-deserializing the return value.</returns>
+        public static ValueTask<TValue> InvokeAsync<TValue>(this IJSObjectReference jsObjectReference, string identifier, CancellationToken cancellationToken, params object?[] args)
+        {
+            if (jsObjectReference is null)
+            {
+                throw new ArgumentNullException(nameof(jsObjectReference));
+            }
+
+            return jsObjectReference.InvokeAsync<TValue>(identifier, cancellationToken, args);
+        }
+
+        /// <summary>
+        /// Invokes the specified JavaScript function asynchronously.
+        /// </summary>
+        /// <param name="jsObjectReference">The <see cref="IJSObjectReference"/>.</param>
+        /// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
+        /// <param name="cancellationToken">
+        /// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts
+        /// (<see cref="JSRuntime.DefaultAsyncTimeout"/>) from being applied.
+        /// </param>
+        /// <param name="args">JSON-serializable arguments.</param>
+        /// <returns>A <see cref="ValueTask"/> that represents the asynchronous invocation operation.</returns>
+        public static async ValueTask InvokeVoidAsync(this IJSObjectReference jsObjectReference, string identifier, CancellationToken cancellationToken, params object?[] args)
+        {
+            if (jsObjectReference is null)
+            {
+                throw new ArgumentNullException(nameof(jsObjectReference));
+            }
+
+            await jsObjectReference.InvokeAsync<object>(identifier, cancellationToken, args);
+        }
+
+        /// <summary>
+        /// Invokes the specified JavaScript function asynchronously.
+        /// </summary>
+        /// <param name="jsObjectReference">The <see cref="IJSObjectReference"/>.</param>
+        /// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
+        /// <param name="timeout">The duration after which to cancel the async operation. Overrides default timeouts (<see cref="JSRuntime.DefaultAsyncTimeout"/>).</param>
+        /// <param name="args">JSON-serializable arguments.</param>
+        /// <returns>A <see cref="ValueTask"/> that represents the asynchronous invocation operation.</returns>
+        public static async ValueTask<TValue> InvokeAsync<TValue>(this IJSObjectReference jsObjectReference, string identifier, TimeSpan timeout, params object?[] args)
+        {
+            if (jsObjectReference is null)
+            {
+                throw new ArgumentNullException(nameof(jsObjectReference));
+            }
+
+            using var cancellationTokenSource = timeout == Timeout.InfiniteTimeSpan ? null : new CancellationTokenSource(timeout);
+            var cancellationToken = cancellationTokenSource?.Token ?? CancellationToken.None;
+
+            return await jsObjectReference.InvokeAsync<TValue>(identifier, cancellationToken, args);
+        }
+
+        /// <summary>
+        /// Invokes the specified JavaScript function asynchronously.
+        /// </summary>
+        /// <param name="jsObjectReference">The <see cref="IJSObjectReference"/>.</param>
+        /// <param name="identifier">An identifier for the function to invoke. For example, the value <c>"someScope.someFunction"</c> will invoke the function <c>someScope.someFunction</c> on the target instance.</param>
+        /// <param name="timeout">The duration after which to cancel the async operation. Overrides default timeouts (<see cref="JSRuntime.DefaultAsyncTimeout"/>).</param>
+        /// <param name="args">JSON-serializable arguments.</param>
+        /// <returns>A <see cref="ValueTask"/> that represents the asynchronous invocation operation.</returns>
+        public static async ValueTask InvokeVoidAsync(this IJSObjectReference jsObjectReference, string identifier, TimeSpan timeout, params object?[] args)
+        {
+            if (jsObjectReference is null)
+            {
+                throw new ArgumentNullException(nameof(jsObjectReference));
+            }
+
+            using var cancellationTokenSource = timeout == Timeout.InfiniteTimeSpan ? null : new CancellationTokenSource(timeout);
+            var cancellationToken = cancellationTokenSource?.Token ?? CancellationToken.None;
+
+            await jsObjectReference.InvokeAsync<object>(identifier, cancellationToken, args);
+        }
+    }
+}

+ 4 - 13
src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs

@@ -8,6 +8,7 @@ using System.Linq;
 using System.Text.Json;
 using System.Threading;
 using System.Threading.Tasks;
+using Microsoft.JSInterop.Implementation;
 using Microsoft.JSInterop.Infrastructure;
 
 namespace Microsoft.JSInterop
@@ -37,7 +38,8 @@ namespace Microsoft.JSInterop
                 Converters =
                 {
                     new DotNetObjectReferenceJsonConverterFactory(this),
-                    new JSObjectReferenceJsonConverter<JSObjectReference>(id => new JSObjectReference(this, id)),
+                    new JSObjectReferenceJsonConverter<IJSObjectReference, JSObjectReference>(
+                        id => new JSObjectReference(this, id)),
                 }
             };
         }
@@ -52,17 +54,6 @@ namespace Microsoft.JSInterop
         /// </summary>
         protected TimeSpan? DefaultAsyncTimeout { get; set; }
 
-        /// <summary>
-        /// Creates a <see cref="JSCallResultType"/> from the given generic type.
-        /// </summary>
-        /// <typeparam name="TResult">
-        /// The type of the result of the relevant JS interop call.
-        /// </typeparam>
-        protected static JSCallResultType ResultTypeFromGeneric<TResult>()
-            => typeof(TResult) == typeof(JSObjectReference) || typeof(TResult) == typeof(JSInProcessObjectReference) ?
-                JSCallResultType.JSObjectReference :
-                JSCallResultType.Default;
-
         /// <summary>
         /// Invokes the specified JavaScript function asynchronously.
         /// <para>
@@ -134,7 +125,7 @@ namespace Microsoft.JSInterop
                 var argsJson = args?.Any() == true ?
                     JsonSerializer.Serialize(args, JsonSerializerOptions) :
                     null;
-                var resultType = ResultTypeFromGeneric<TValue>();
+                var resultType = JSCallResultTypeHelper.FromGeneric<TValue>();
 
                 BeginInvokeJS(taskId, identifier, argsJson, resultType, targetInstanceId);
 

+ 4 - 0
src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.csproj

@@ -13,6 +13,10 @@
     <Reference Include="System.Text.Json" />
   </ItemGroup>
 
+  <ItemGroup>
+    <Compile Include="$(SharedSourceRoot)JSInterop\*.cs" />
+  </ItemGroup>
+
   <ItemGroup>
     <InternalsVisibleTo Include="Microsoft.JSInterop.Tests" />
   </ItemGroup>

+ 1 - 0
src/JSInterop/Microsoft.JSInterop/src/PublicAPI.Shipped.txt

@@ -0,0 +1 @@
+#nullable enable

+ 97 - 0
src/JSInterop/Microsoft.JSInterop/src/PublicAPI.Unshipped.txt

@@ -0,0 +1,97 @@
+#nullable enable
+Microsoft.JSInterop.DotNetObjectReference
+Microsoft.JSInterop.DotNetObjectReference<TValue>
+Microsoft.JSInterop.DotNetObjectReference<TValue>.Dispose() -> void
+Microsoft.JSInterop.DotNetObjectReference<TValue>.Value.get -> TValue!
+Microsoft.JSInterop.IJSInProcessObjectReference
+Microsoft.JSInterop.IJSInProcessObjectReference.Invoke<TValue>(string! identifier, params object?[]? args) -> TValue
+Microsoft.JSInterop.IJSInProcessRuntime
+Microsoft.JSInterop.IJSInProcessRuntime.Invoke<T>(string! identifier, params object?[]? args) -> T
+Microsoft.JSInterop.IJSObjectReference
+Microsoft.JSInterop.IJSObjectReference.InvokeAsync<TValue>(string! identifier, System.Threading.CancellationToken cancellationToken, object?[]? args) -> System.Threading.Tasks.ValueTask<TValue>
+Microsoft.JSInterop.IJSObjectReference.InvokeAsync<TValue>(string! identifier, object?[]? args) -> System.Threading.Tasks.ValueTask<TValue>
+Microsoft.JSInterop.IJSRuntime
+Microsoft.JSInterop.IJSRuntime.InvokeAsync<TValue>(string! identifier, System.Threading.CancellationToken cancellationToken, object?[]? args) -> System.Threading.Tasks.ValueTask<TValue>
+Microsoft.JSInterop.IJSRuntime.InvokeAsync<TValue>(string! identifier, object?[]? args) -> System.Threading.Tasks.ValueTask<TValue>
+Microsoft.JSInterop.IJSUnmarshalledObjectReference
+Microsoft.JSInterop.IJSUnmarshalledObjectReference.InvokeUnmarshalled<T0, T1, T2, TResult>(string! identifier, T0 arg0, T1 arg1, T2 arg2) -> TResult
+Microsoft.JSInterop.IJSUnmarshalledObjectReference.InvokeUnmarshalled<T0, T1, TResult>(string! identifier, T0 arg0, T1 arg1) -> TResult
+Microsoft.JSInterop.IJSUnmarshalledObjectReference.InvokeUnmarshalled<T0, TResult>(string! identifier, T0 arg0) -> TResult
+Microsoft.JSInterop.IJSUnmarshalledObjectReference.InvokeUnmarshalled<TResult>(string! identifier) -> TResult
+Microsoft.JSInterop.IJSUnmarshalledRuntime
+Microsoft.JSInterop.IJSUnmarshalledRuntime.InvokeUnmarshalled<T0, T1, T2, TResult>(string! identifier, T0 arg0, T1 arg1, T2 arg2) -> TResult
+Microsoft.JSInterop.IJSUnmarshalledRuntime.InvokeUnmarshalled<T0, T1, TResult>(string! identifier, T0 arg0, T1 arg1) -> TResult
+Microsoft.JSInterop.IJSUnmarshalledRuntime.InvokeUnmarshalled<T0, TResult>(string! identifier, T0 arg0) -> TResult
+Microsoft.JSInterop.IJSUnmarshalledRuntime.InvokeUnmarshalled<TResult>(string! identifier) -> TResult
+Microsoft.JSInterop.Implementation.JSInProcessObjectReference
+Microsoft.JSInterop.Implementation.JSInProcessObjectReference.Dispose() -> void
+Microsoft.JSInterop.Implementation.JSInProcessObjectReference.Invoke<TValue>(string! identifier, params object?[]? args) -> TValue
+Microsoft.JSInterop.Implementation.JSInProcessObjectReference.JSInProcessObjectReference(Microsoft.JSInterop.JSInProcessRuntime! jsRuntime, long id) -> void
+Microsoft.JSInterop.Implementation.JSObjectReference
+Microsoft.JSInterop.Implementation.JSObjectReference.DisposeAsync() -> System.Threading.Tasks.ValueTask
+Microsoft.JSInterop.Implementation.JSObjectReference.Id.get -> long
+Microsoft.JSInterop.Implementation.JSObjectReference.InvokeAsync<TValue>(string! identifier, System.Threading.CancellationToken cancellationToken, object?[]? args) -> System.Threading.Tasks.ValueTask<TValue>
+Microsoft.JSInterop.Implementation.JSObjectReference.InvokeAsync<TValue>(string! identifier, object?[]? args) -> System.Threading.Tasks.ValueTask<TValue>
+Microsoft.JSInterop.Implementation.JSObjectReference.JSObjectReference(Microsoft.JSInterop.JSRuntime! jsRuntime, long id) -> void
+Microsoft.JSInterop.Implementation.JSObjectReference.ThrowIfDisposed() -> void
+Microsoft.JSInterop.Infrastructure.DotNetDispatcher
+Microsoft.JSInterop.Infrastructure.DotNetInvocationInfo
+Microsoft.JSInterop.Infrastructure.DotNetInvocationInfo.AssemblyName.get -> string?
+Microsoft.JSInterop.Infrastructure.DotNetInvocationInfo.CallId.get -> string?
+Microsoft.JSInterop.Infrastructure.DotNetInvocationInfo.DotNetInvocationInfo(string? assemblyName, string! methodIdentifier, long dotNetObjectId, string? callId) -> void
+Microsoft.JSInterop.Infrastructure.DotNetInvocationInfo.DotNetObjectId.get -> long
+Microsoft.JSInterop.Infrastructure.DotNetInvocationInfo.MethodIdentifier.get -> string!
+Microsoft.JSInterop.Infrastructure.DotNetInvocationResult
+Microsoft.JSInterop.Infrastructure.DotNetInvocationResult.DotNetInvocationResult(System.Exception! exception, string? errorKind) -> void
+Microsoft.JSInterop.Infrastructure.DotNetInvocationResult.DotNetInvocationResult(object? result) -> void
+Microsoft.JSInterop.Infrastructure.DotNetInvocationResult.ErrorKind.get -> string?
+Microsoft.JSInterop.Infrastructure.DotNetInvocationResult.Exception.get -> System.Exception?
+Microsoft.JSInterop.Infrastructure.DotNetInvocationResult.Result.get -> object?
+Microsoft.JSInterop.Infrastructure.DotNetInvocationResult.Success.get -> bool
+Microsoft.JSInterop.JSCallResultType
+Microsoft.JSInterop.JSCallResultType.Default = 0 -> Microsoft.JSInterop.JSCallResultType
+Microsoft.JSInterop.JSCallResultType.JSObjectReference = 1 -> Microsoft.JSInterop.JSCallResultType
+Microsoft.JSInterop.JSException
+Microsoft.JSInterop.JSException.JSException(string! message) -> void
+Microsoft.JSInterop.JSException.JSException(string! message, System.Exception! innerException) -> void
+Microsoft.JSInterop.JSInProcessObjectReferenceExtensions
+Microsoft.JSInterop.JSInProcessRuntime
+Microsoft.JSInterop.JSInProcessRuntime.Invoke<TValue>(string! identifier, params object?[]? args) -> TValue
+Microsoft.JSInterop.JSInProcessRuntime.JSInProcessRuntime() -> void
+Microsoft.JSInterop.JSInProcessRuntimeExtensions
+Microsoft.JSInterop.JSInvokableAttribute
+Microsoft.JSInterop.JSInvokableAttribute.Identifier.get -> string?
+Microsoft.JSInterop.JSInvokableAttribute.JSInvokableAttribute() -> void
+Microsoft.JSInterop.JSInvokableAttribute.JSInvokableAttribute(string! identifier) -> void
+Microsoft.JSInterop.JSObjectReferenceExtensions
+Microsoft.JSInterop.JSRuntime
+Microsoft.JSInterop.JSRuntime.DefaultAsyncTimeout.get -> System.TimeSpan?
+Microsoft.JSInterop.JSRuntime.DefaultAsyncTimeout.set -> void
+Microsoft.JSInterop.JSRuntime.InvokeAsync<TValue>(string! identifier, System.Threading.CancellationToken cancellationToken, object?[]? args) -> System.Threading.Tasks.ValueTask<TValue>
+Microsoft.JSInterop.JSRuntime.InvokeAsync<TValue>(string! identifier, object?[]? args) -> System.Threading.Tasks.ValueTask<TValue>
+Microsoft.JSInterop.JSRuntime.JSRuntime() -> void
+Microsoft.JSInterop.JSRuntime.JsonSerializerOptions.get -> System.Text.Json.JsonSerializerOptions!
+Microsoft.JSInterop.JSRuntimeExtensions
+abstract Microsoft.JSInterop.JSInProcessRuntime.InvokeJS(string! identifier, string? argsJson, Microsoft.JSInterop.JSCallResultType resultType, long targetInstanceId) -> string?
+abstract Microsoft.JSInterop.JSRuntime.BeginInvokeJS(long taskId, string! identifier, string? argsJson, Microsoft.JSInterop.JSCallResultType resultType, long targetInstanceId) -> void
+abstract Microsoft.JSInterop.JSRuntime.EndInvokeDotNet(Microsoft.JSInterop.Infrastructure.DotNetInvocationInfo invocationInfo, in Microsoft.JSInterop.Infrastructure.DotNetInvocationResult invocationResult) -> void
+static Microsoft.JSInterop.DotNetObjectReference.Create<TValue>(TValue! value) -> Microsoft.JSInterop.DotNetObjectReference<TValue!>!
+static Microsoft.JSInterop.Infrastructure.DotNetDispatcher.BeginInvokeDotNet(Microsoft.JSInterop.JSRuntime! jsRuntime, Microsoft.JSInterop.Infrastructure.DotNetInvocationInfo invocationInfo, string! argsJson) -> void
+static Microsoft.JSInterop.Infrastructure.DotNetDispatcher.EndInvokeJS(Microsoft.JSInterop.JSRuntime! jsRuntime, string! arguments) -> void
+static Microsoft.JSInterop.Infrastructure.DotNetDispatcher.Invoke(Microsoft.JSInterop.JSRuntime! jsRuntime, in Microsoft.JSInterop.Infrastructure.DotNetInvocationInfo invocationInfo, string! argsJson) -> string?
+static Microsoft.JSInterop.JSInProcessObjectReferenceExtensions.InvokeVoid(this Microsoft.JSInterop.IJSInProcessObjectReference! jsObjectReference, string! identifier, params object?[]! args) -> void
+static Microsoft.JSInterop.JSInProcessRuntimeExtensions.InvokeVoid(this Microsoft.JSInterop.IJSInProcessRuntime! jsRuntime, string! identifier, params object?[]! args) -> void
+static Microsoft.JSInterop.JSObjectReferenceExtensions.InvokeAsync<TValue>(this Microsoft.JSInterop.IJSObjectReference! jsObjectReference, string! identifier, System.Threading.CancellationToken cancellationToken, params object?[]! args) -> System.Threading.Tasks.ValueTask<TValue>
+static Microsoft.JSInterop.JSObjectReferenceExtensions.InvokeAsync<TValue>(this Microsoft.JSInterop.IJSObjectReference! jsObjectReference, string! identifier, System.TimeSpan timeout, params object?[]! args) -> System.Threading.Tasks.ValueTask<TValue>
+static Microsoft.JSInterop.JSObjectReferenceExtensions.InvokeAsync<TValue>(this Microsoft.JSInterop.IJSObjectReference! jsObjectReference, string! identifier, params object?[]! args) -> System.Threading.Tasks.ValueTask<TValue>
+static Microsoft.JSInterop.JSObjectReferenceExtensions.InvokeVoidAsync(this Microsoft.JSInterop.IJSObjectReference! jsObjectReference, string! identifier, System.Threading.CancellationToken cancellationToken, params object?[]! args) -> System.Threading.Tasks.ValueTask
+static Microsoft.JSInterop.JSObjectReferenceExtensions.InvokeVoidAsync(this Microsoft.JSInterop.IJSObjectReference! jsObjectReference, string! identifier, System.TimeSpan timeout, params object?[]! args) -> System.Threading.Tasks.ValueTask
+static Microsoft.JSInterop.JSObjectReferenceExtensions.InvokeVoidAsync(this Microsoft.JSInterop.IJSObjectReference! jsObjectReference, string! identifier, params object?[]! args) -> System.Threading.Tasks.ValueTask
+static Microsoft.JSInterop.JSRuntimeExtensions.InvokeAsync<TValue>(this Microsoft.JSInterop.IJSRuntime! jsRuntime, string! identifier, System.Threading.CancellationToken cancellationToken, params object![]! args) -> System.Threading.Tasks.ValueTask<TValue>
+static Microsoft.JSInterop.JSRuntimeExtensions.InvokeAsync<TValue>(this Microsoft.JSInterop.IJSRuntime! jsRuntime, string! identifier, System.TimeSpan timeout, params object![]! args) -> System.Threading.Tasks.ValueTask<TValue>
+static Microsoft.JSInterop.JSRuntimeExtensions.InvokeAsync<TValue>(this Microsoft.JSInterop.IJSRuntime! jsRuntime, string! identifier, params object![]! args) -> System.Threading.Tasks.ValueTask<TValue>
+static Microsoft.JSInterop.JSRuntimeExtensions.InvokeVoidAsync(this Microsoft.JSInterop.IJSRuntime! jsRuntime, string! identifier, System.Threading.CancellationToken cancellationToken, params object![]! args) -> System.Threading.Tasks.ValueTask
+static Microsoft.JSInterop.JSRuntimeExtensions.InvokeVoidAsync(this Microsoft.JSInterop.IJSRuntime! jsRuntime, string! identifier, System.TimeSpan timeout, params object![]! args) -> System.Threading.Tasks.ValueTask
+static Microsoft.JSInterop.JSRuntimeExtensions.InvokeVoidAsync(this Microsoft.JSInterop.IJSRuntime! jsRuntime, string! identifier, params object![]! args) -> System.Threading.Tasks.ValueTask
+virtual Microsoft.JSInterop.JSInProcessRuntime.InvokeJS(string! identifier, string? argsJson) -> string?
+virtual Microsoft.JSInterop.JSRuntime.BeginInvokeJS(long taskId, string! identifier, string? argsJson) -> void

+ 74 - 8
src/JSInterop/Microsoft.JSInterop/test/Infrastructure/JSObjectReferenceJsonConverterTest.cs

@@ -1,7 +1,9 @@
 // Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
+using System;
 using System.Text.Json;
+using Microsoft.JSInterop.Implementation;
 using Xunit;
 
 namespace Microsoft.JSInterop.Infrastructure
@@ -9,7 +11,16 @@ namespace Microsoft.JSInterop.Infrastructure
     public class JSObjectReferenceJsonConverterTest
     {
         private readonly JSRuntime JSRuntime = new TestJSRuntime();
-        private JsonSerializerOptions JsonSerializerOptions => JSRuntime.JsonSerializerOptions;
+        private readonly JsonSerializerOptions JsonSerializerOptions;
+
+        public JSObjectReferenceJsonConverterTest()
+        {
+            JsonSerializerOptions = JSRuntime.JsonSerializerOptions;
+            JsonSerializerOptions.Converters.Add(new JSObjectReferenceJsonConverter<IJSInProcessObjectReference, JSInProcessObjectReference>(
+                id => new JSInProcessObjectReference(default!, id)));
+            JsonSerializerOptions.Converters.Add(new JSObjectReferenceJsonConverter<IJSUnmarshalledObjectReference, TestJSUnmarshalledObjectReference>(
+                id => new TestJSUnmarshalledObjectReference(id)));
+        }
 
         [Fact]
         public void Read_Throws_IfJsonIsMissingJSObjectIdProperty()
@@ -18,7 +29,7 @@ namespace Microsoft.JSInterop.Infrastructure
             var json = "{}";
 
             // Act & Assert
-            var ex = Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<JSObjectReference>(json, JsonSerializerOptions));
+            var ex = Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<IJSObjectReference>(json, JsonSerializerOptions));
             Assert.Equal("Required property __jsObjectId not found.", ex.Message);
         }
 
@@ -29,7 +40,7 @@ namespace Microsoft.JSInterop.Infrastructure
             var json = "{\"foo\":2}";
 
             // Act & Assert
-            var ex = Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<JSObjectReference>(json, JsonSerializerOptions));
+            var ex = Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<IJSObjectReference>(json, JsonSerializerOptions));
             Assert.Equal("Unexcepted JSON property foo.", ex.Message);
         }
 
@@ -40,7 +51,7 @@ namespace Microsoft.JSInterop.Infrastructure
             var json = $"{{\"__jsObjectId\":5";
 
             // Act & Assert
-            var ex = Record.Exception(() => JsonSerializer.Deserialize<JSObjectReference>(json, JsonSerializerOptions));
+            var ex = Record.Exception(() => JsonSerializer.Deserialize<IJSObjectReference>(json, JsonSerializerOptions));
             Assert.IsAssignableFrom<JsonException>(ex);
         }
 
@@ -51,19 +62,47 @@ namespace Microsoft.JSInterop.Infrastructure
             var json = $"{{\"__jsObjectId\":3,\"__jsObjectId\":7}}";
 
             // Act & Assert
-            var ex = Record.Exception(() => JsonSerializer.Deserialize<JSObjectReference>(json, JsonSerializerOptions));
+            var ex = Record.Exception(() => JsonSerializer.Deserialize<IJSObjectReference>(json, JsonSerializerOptions));
             Assert.IsAssignableFrom<JsonException>(ex);
         }
 
         [Fact]
-        public void Read_ReadsJson()
+        public void Read_ReadsJson_IJSObjectReference()
+        {
+            // Arrange
+            var expectedId = 3;
+            var json = $"{{\"__jsObjectId\":{expectedId}}}";
+
+            // Act
+            var deserialized = (JSObjectReference)JsonSerializer.Deserialize<IJSObjectReference>(json, JsonSerializerOptions)!;
+
+            // Assert
+            Assert.Equal(expectedId, deserialized?.Id);
+        }
+
+        [Fact]
+        public void Read_ReadsJson_IJSInProcessObjectReference()
         {
             // Arrange
             var expectedId = 3;
             var json = $"{{\"__jsObjectId\":{expectedId}}}";
 
             // Act
-            var deserialized = JsonSerializer.Deserialize<JSObjectReference>(json, JsonSerializerOptions)!;
+            var deserialized = (JSInProcessObjectReference)JsonSerializer.Deserialize<IJSInProcessObjectReference>(json, JsonSerializerOptions)!;
+
+            // Assert
+            Assert.Equal(expectedId, deserialized?.Id);
+        }
+
+        [Fact]
+        public void Read_ReadsJson_IJSUnmarshalledObjectReference()
+        {
+            // Arrange
+            var expectedId = 3;
+            var json = $"{{\"__jsObjectId\":{expectedId}}}";
+
+            // Act
+            var deserialized = (TestJSUnmarshalledObjectReference)JsonSerializer.Deserialize<IJSUnmarshalledObjectReference>(json, JsonSerializerOptions)!;
 
             // Assert
             Assert.Equal(expectedId, deserialized?.Id);
@@ -76,10 +115,37 @@ namespace Microsoft.JSInterop.Infrastructure
             var jsObjectRef = new JSObjectReference(JSRuntime, 7);
 
             // Act
-            var json = JsonSerializer.Serialize(jsObjectRef, JsonSerializerOptions);
+            var json = JsonSerializer.Serialize((IJSObjectReference)jsObjectRef, JsonSerializerOptions);
 
             // Assert
             Assert.Equal($"{{\"__jsObjectId\":{jsObjectRef.Id}}}", json);
         }
+
+        private class TestJSUnmarshalledObjectReference : JSInProcessObjectReference, IJSUnmarshalledObjectReference
+        {
+            public TestJSUnmarshalledObjectReference(long id) : base(default!, id)
+            {
+            }
+
+            public TResult InvokeUnmarshalled<TResult>(string identifier)
+            {
+                throw new NotImplementedException();
+            }
+
+            public TResult InvokeUnmarshalled<T0, TResult>(string identifier, T0 arg0)
+            {
+                throw new NotImplementedException();
+            }
+
+            public TResult InvokeUnmarshalled<T0, T1, TResult>(string identifier, T0 arg0, T1 arg1)
+            {
+                throw new NotImplementedException();
+            }
+
+            public TResult InvokeUnmarshalled<T0, T1, T2, TResult>(string identifier, T0 arg0, T1 arg1, T2 arg2)
+            {
+                throw new NotImplementedException();
+            }
+        }
     }
 }

+ 1 - 0
src/JSInterop/Microsoft.JSInterop/test/JSObjectReferenceTest.cs

@@ -4,6 +4,7 @@
 using System;
 using System.Threading;
 using System.Threading.Tasks;
+using Microsoft.JSInterop.Implementation;
 using Microsoft.JSInterop.Infrastructure;
 using Xunit;
 

+ 1 - 0
src/Localization/Abstractions/src/PublicAPI.Shipped.txt

@@ -0,0 +1 @@
+#nullable enable

+ 28 - 0
src/Localization/Abstractions/src/PublicAPI.Unshipped.txt

@@ -0,0 +1,28 @@
+#nullable enable
+Microsoft.Extensions.Localization.IStringLocalizer
+Microsoft.Extensions.Localization.IStringLocalizer.GetAllStrings(bool includeParentCultures) -> System.Collections.Generic.IEnumerable<Microsoft.Extensions.Localization.LocalizedString!>!
+Microsoft.Extensions.Localization.IStringLocalizer.this[string! name, params object![]! arguments].get -> Microsoft.Extensions.Localization.LocalizedString!
+Microsoft.Extensions.Localization.IStringLocalizer.this[string! name].get -> Microsoft.Extensions.Localization.LocalizedString!
+Microsoft.Extensions.Localization.IStringLocalizer<T>
+Microsoft.Extensions.Localization.IStringLocalizerFactory
+Microsoft.Extensions.Localization.IStringLocalizerFactory.Create(System.Type! resourceSource) -> Microsoft.Extensions.Localization.IStringLocalizer!
+Microsoft.Extensions.Localization.IStringLocalizerFactory.Create(string! baseName, string! location) -> Microsoft.Extensions.Localization.IStringLocalizer!
+Microsoft.Extensions.Localization.LocalizedString
+Microsoft.Extensions.Localization.LocalizedString.LocalizedString(string! name, string! value) -> void
+Microsoft.Extensions.Localization.LocalizedString.LocalizedString(string! name, string! value, bool resourceNotFound) -> void
+Microsoft.Extensions.Localization.LocalizedString.LocalizedString(string! name, string! value, bool resourceNotFound, string? searchedLocation) -> void
+Microsoft.Extensions.Localization.LocalizedString.Name.get -> string!
+Microsoft.Extensions.Localization.LocalizedString.ResourceNotFound.get -> bool
+Microsoft.Extensions.Localization.LocalizedString.SearchedLocation.get -> string?
+Microsoft.Extensions.Localization.LocalizedString.Value.get -> string!
+Microsoft.Extensions.Localization.StringLocalizer<TResourceSource>
+Microsoft.Extensions.Localization.StringLocalizer<TResourceSource>.GetAllStrings(bool includeParentCultures) -> System.Collections.Generic.IEnumerable<Microsoft.Extensions.Localization.LocalizedString!>!
+Microsoft.Extensions.Localization.StringLocalizer<TResourceSource>.StringLocalizer(Microsoft.Extensions.Localization.IStringLocalizerFactory! factory) -> void
+Microsoft.Extensions.Localization.StringLocalizerExtensions
+override Microsoft.Extensions.Localization.LocalizedString.ToString() -> string!
+static Microsoft.Extensions.Localization.LocalizedString.implicit operator string?(Microsoft.Extensions.Localization.LocalizedString! localizedString) -> string?
+static Microsoft.Extensions.Localization.StringLocalizerExtensions.GetAllStrings(this Microsoft.Extensions.Localization.IStringLocalizer! stringLocalizer) -> System.Collections.Generic.IEnumerable<Microsoft.Extensions.Localization.LocalizedString!>!
+static Microsoft.Extensions.Localization.StringLocalizerExtensions.GetString(this Microsoft.Extensions.Localization.IStringLocalizer! stringLocalizer, string! name) -> Microsoft.Extensions.Localization.LocalizedString!
+static Microsoft.Extensions.Localization.StringLocalizerExtensions.GetString(this Microsoft.Extensions.Localization.IStringLocalizer! stringLocalizer, string! name, params object![]! arguments) -> Microsoft.Extensions.Localization.LocalizedString!
+virtual Microsoft.Extensions.Localization.StringLocalizer<TResourceSource>.this[string! name, params object![]! arguments].get -> Microsoft.Extensions.Localization.LocalizedString!
+virtual Microsoft.Extensions.Localization.StringLocalizer<TResourceSource>.this[string! name].get -> Microsoft.Extensions.Localization.LocalizedString!

+ 1 - 0
src/Localization/Localization/src/PublicAPI.Shipped.txt

@@ -0,0 +1 @@
+#nullable enable

+ 37 - 0
src/Localization/Localization/src/PublicAPI.Unshipped.txt

@@ -0,0 +1,37 @@
+#nullable enable
+Microsoft.Extensions.DependencyInjection.LocalizationServiceCollectionExtensions
+Microsoft.Extensions.Localization.IResourceNamesCache
+Microsoft.Extensions.Localization.IResourceNamesCache.GetOrAdd(string! name, System.Func<string!, System.Collections.Generic.IList<string!>?>! valueFactory) -> System.Collections.Generic.IList<string!>?
+Microsoft.Extensions.Localization.LocalizationOptions
+Microsoft.Extensions.Localization.LocalizationOptions.LocalizationOptions() -> void
+Microsoft.Extensions.Localization.LocalizationOptions.ResourcesPath.get -> string!
+Microsoft.Extensions.Localization.LocalizationOptions.ResourcesPath.set -> void
+Microsoft.Extensions.Localization.ResourceLocationAttribute
+Microsoft.Extensions.Localization.ResourceLocationAttribute.ResourceLocation.get -> string!
+Microsoft.Extensions.Localization.ResourceLocationAttribute.ResourceLocationAttribute(string! resourceLocation) -> void
+Microsoft.Extensions.Localization.ResourceManagerStringLocalizer
+Microsoft.Extensions.Localization.ResourceManagerStringLocalizer.GetAllStrings(bool includeParentCultures, System.Globalization.CultureInfo! culture) -> System.Collections.Generic.IEnumerable<Microsoft.Extensions.Localization.LocalizedString!>!
+Microsoft.Extensions.Localization.ResourceManagerStringLocalizer.GetStringSafely(string! name, System.Globalization.CultureInfo? culture) -> string?
+Microsoft.Extensions.Localization.ResourceManagerStringLocalizer.ResourceManagerStringLocalizer(System.Resources.ResourceManager! resourceManager, System.Reflection.Assembly! resourceAssembly, string! baseName, Microsoft.Extensions.Localization.IResourceNamesCache! resourceNamesCache, Microsoft.Extensions.Logging.ILogger! logger) -> void
+Microsoft.Extensions.Localization.ResourceManagerStringLocalizerFactory
+Microsoft.Extensions.Localization.ResourceManagerStringLocalizerFactory.Create(System.Type! resourceSource) -> Microsoft.Extensions.Localization.IStringLocalizer!
+Microsoft.Extensions.Localization.ResourceManagerStringLocalizerFactory.Create(string! baseName, string! location) -> Microsoft.Extensions.Localization.IStringLocalizer!
+Microsoft.Extensions.Localization.ResourceNamesCache
+Microsoft.Extensions.Localization.ResourceNamesCache.GetOrAdd(string! name, System.Func<string!, System.Collections.Generic.IList<string!>?>! valueFactory) -> System.Collections.Generic.IList<string!>?
+Microsoft.Extensions.Localization.ResourceNamesCache.ResourceNamesCache() -> void
+Microsoft.Extensions.Localization.RootNamespaceAttribute
+Microsoft.Extensions.Localization.RootNamespaceAttribute.RootNamespace.get -> string!
+Microsoft.Extensions.Localization.RootNamespaceAttribute.RootNamespaceAttribute(string! rootNamespace) -> void
+static Microsoft.Extensions.DependencyInjection.LocalizationServiceCollectionExtensions.AddLocalization(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
+static Microsoft.Extensions.DependencyInjection.LocalizationServiceCollectionExtensions.AddLocalization(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services, System.Action<Microsoft.Extensions.Localization.LocalizationOptions!>! setupAction) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
+virtual Microsoft.Extensions.Localization.ResourceManagerStringLocalizer.GetAllStrings(bool includeParentCultures) -> System.Collections.Generic.IEnumerable<Microsoft.Extensions.Localization.LocalizedString!>!
+virtual Microsoft.Extensions.Localization.ResourceManagerStringLocalizer.this[string! name, params object![]! arguments].get -> Microsoft.Extensions.Localization.LocalizedString!
+virtual Microsoft.Extensions.Localization.ResourceManagerStringLocalizer.this[string! name].get -> Microsoft.Extensions.Localization.LocalizedString!
+virtual Microsoft.Extensions.Localization.ResourceManagerStringLocalizerFactory.CreateResourceManagerStringLocalizer(System.Reflection.Assembly! assembly, string! baseName) -> Microsoft.Extensions.Localization.ResourceManagerStringLocalizer!
+virtual Microsoft.Extensions.Localization.ResourceManagerStringLocalizerFactory.GetResourceLocationAttribute(System.Reflection.Assembly! assembly) -> Microsoft.Extensions.Localization.ResourceLocationAttribute?
+virtual Microsoft.Extensions.Localization.ResourceManagerStringLocalizerFactory.GetResourcePrefix(System.Reflection.TypeInfo! typeInfo) -> string!
+virtual Microsoft.Extensions.Localization.ResourceManagerStringLocalizerFactory.GetResourcePrefix(System.Reflection.TypeInfo! typeInfo, string? baseNamespace, string? resourcesRelativePath) -> string!
+virtual Microsoft.Extensions.Localization.ResourceManagerStringLocalizerFactory.GetResourcePrefix(string! baseResourceName, string! baseNamespace) -> string!
+virtual Microsoft.Extensions.Localization.ResourceManagerStringLocalizerFactory.GetResourcePrefix(string! location, string! baseName, string! resourceLocation) -> string!
+virtual Microsoft.Extensions.Localization.ResourceManagerStringLocalizerFactory.GetRootNamespaceAttribute(System.Reflection.Assembly! assembly) -> Microsoft.Extensions.Localization.RootNamespaceAttribute?
+~Microsoft.Extensions.Localization.ResourceManagerStringLocalizerFactory.ResourceManagerStringLocalizerFactory(Microsoft.Extensions.Options.IOptions<Microsoft.Extensions.Localization.LocalizationOptions!>! localizationOptions, Microsoft.Extensions.Logging.ILoggerFactory! loggerFactory) -> void

+ 2 - 0
src/Logging.AzureAppServices/src/BlobLoggerProvider.cs

@@ -5,6 +5,7 @@
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
 using System.IO;
 using System.Linq;
 using System.Net.Http;
@@ -29,6 +30,7 @@ namespace Microsoft.Extensions.Logging.AzureAppServices
         /// Creates a new instance of <see cref="BlobLoggerProvider"/>
         /// </summary>
         /// <param name="options">The options to use when creating a provider.</param>
+        [SuppressMessage("ApiDesign", "RS0022:Constructor make noninheritable base class inheritable", Justification = "Required for backwards compatibility")]
         public BlobLoggerProvider(IOptionsMonitor<AzureBlobLoggerOptions> options)
             : this(options, null)
         {

+ 2 - 0
src/Logging.AzureAppServices/src/FileLoggerProvider.cs

@@ -3,6 +3,7 @@
 // See the LICENSE file in the project root for more information.
 
 using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
 using System.IO;
 using System.Linq;
 using System.Threading;
@@ -26,6 +27,7 @@ namespace Microsoft.Extensions.Logging.AzureAppServices
         /// Creates a new instance of <see cref="FileLoggerProvider"/>.
         /// </summary>
         /// <param name="options">The options to use when creating a provider.</param>
+        [SuppressMessage("ApiDesign", "RS0022:Constructor make noninheritable base class inheritable", Justification = "Required for backwards compatibility")]
         public FileLoggerProvider(IOptionsMonitor<AzureFileLoggerOptions> options) : base(options)
         {
             var loggerOptions = options.CurrentValue;

+ 1 - 0
src/Logging.AzureAppServices/src/PublicAPI.Shipped.txt

@@ -0,0 +1 @@
+#nullable enable

+ 35 - 0
src/Logging.AzureAppServices/src/PublicAPI.Unshipped.txt

@@ -0,0 +1,35 @@
+#nullable enable
+Microsoft.Extensions.Logging.AzureAppServices.AzureBlobLoggerOptions
+Microsoft.Extensions.Logging.AzureAppServices.AzureBlobLoggerOptions.AzureBlobLoggerOptions() -> void
+Microsoft.Extensions.Logging.AzureAppServices.AzureFileLoggerOptions
+Microsoft.Extensions.Logging.AzureAppServices.AzureFileLoggerOptions.AzureFileLoggerOptions() -> void
+Microsoft.Extensions.Logging.AzureAppServices.AzureFileLoggerOptions.FileSizeLimit.get -> int?
+Microsoft.Extensions.Logging.AzureAppServices.AzureFileLoggerOptions.FileSizeLimit.set -> void
+Microsoft.Extensions.Logging.AzureAppServices.AzureFileLoggerOptions.RetainedFileCountLimit.get -> int?
+Microsoft.Extensions.Logging.AzureAppServices.AzureFileLoggerOptions.RetainedFileCountLimit.set -> void
+Microsoft.Extensions.Logging.AzureAppServices.BatchingLoggerOptions
+Microsoft.Extensions.Logging.AzureAppServices.BatchingLoggerOptions.BackgroundQueueSize.get -> int?
+Microsoft.Extensions.Logging.AzureAppServices.BatchingLoggerOptions.BackgroundQueueSize.set -> void
+Microsoft.Extensions.Logging.AzureAppServices.BatchingLoggerOptions.BatchSize.get -> int?
+Microsoft.Extensions.Logging.AzureAppServices.BatchingLoggerOptions.BatchSize.set -> void
+Microsoft.Extensions.Logging.AzureAppServices.BatchingLoggerOptions.BatchingLoggerOptions() -> void
+Microsoft.Extensions.Logging.AzureAppServices.BatchingLoggerOptions.FlushPeriod.get -> System.TimeSpan
+Microsoft.Extensions.Logging.AzureAppServices.BatchingLoggerOptions.FlushPeriod.set -> void
+Microsoft.Extensions.Logging.AzureAppServices.BatchingLoggerOptions.IncludeScopes.get -> bool
+Microsoft.Extensions.Logging.AzureAppServices.BatchingLoggerOptions.IncludeScopes.set -> void
+Microsoft.Extensions.Logging.AzureAppServices.BatchingLoggerOptions.IsEnabled.get -> bool
+Microsoft.Extensions.Logging.AzureAppServices.BatchingLoggerOptions.IsEnabled.set -> void
+Microsoft.Extensions.Logging.AzureAppServices.BatchingLoggerProvider
+Microsoft.Extensions.Logging.AzureAppServices.BatchingLoggerProvider.Dispose() -> void
+Microsoft.Extensions.Logging.AzureAppServices.BatchingLoggerProvider.IsEnabled.get -> bool
+Microsoft.Extensions.Logging.AzureAppServices.BlobLoggerProvider
+Microsoft.Extensions.Logging.AzureAppServices.FileLoggerProvider
+Microsoft.Extensions.Logging.AzureAppServicesLoggerFactoryExtensions
+~Microsoft.Extensions.Logging.AzureAppServices.AzureBlobLoggerOptions.BlobName.get -> string
+~Microsoft.Extensions.Logging.AzureAppServices.AzureBlobLoggerOptions.BlobName.set -> void
+~Microsoft.Extensions.Logging.AzureAppServices.AzureFileLoggerOptions.FileName.get -> string
+~Microsoft.Extensions.Logging.AzureAppServices.AzureFileLoggerOptions.FileName.set -> void
+~Microsoft.Extensions.Logging.AzureAppServices.BatchingLoggerProvider.CreateLogger(string categoryName) -> Microsoft.Extensions.Logging.ILogger
+~Microsoft.Extensions.Logging.AzureAppServices.BlobLoggerProvider.BlobLoggerProvider(Microsoft.Extensions.Options.IOptionsMonitor<Microsoft.Extensions.Logging.AzureAppServices.AzureBlobLoggerOptions> options) -> void
+~Microsoft.Extensions.Logging.AzureAppServices.FileLoggerProvider.FileLoggerProvider(Microsoft.Extensions.Options.IOptionsMonitor<Microsoft.Extensions.Logging.AzureAppServices.AzureFileLoggerOptions> options) -> void
+~static Microsoft.Extensions.Logging.AzureAppServicesLoggerFactoryExtensions.AddAzureWebAppDiagnostics(this Microsoft.Extensions.Logging.ILoggingBuilder builder) -> Microsoft.Extensions.Logging.ILoggingBuilder

+ 18 - 3
src/Mvc/Mvc.Core/src/Routing/UrlHelperBase.cs

@@ -1,4 +1,4 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
@@ -60,7 +60,7 @@ namespace Microsoft.AspNetCore.Mvc.Routing
                 // url doesn't start with "//" or "/\"
                 if (url[1] != '/' && url[1] != '\\')
                 {
-                    return true;
+                    return !HasControlCharacter(url.AsSpan(1));
                 }
 
                 return false;
@@ -78,15 +78,30 @@ namespace Microsoft.AspNetCore.Mvc.Routing
                 // url doesn't start with "~//" or "~/\"
                 if (url[2] != '/' && url[2] != '\\')
                 {
-                    return true;
+                    return !HasControlCharacter(url.AsSpan(2));
                 }
 
                 return false;
             }
 
             return false;
+
+            static bool HasControlCharacter(ReadOnlySpan<char> readOnlySpan)
+            {
+                // URLs may not contain ASCII control characters.
+                for (var i = 0; i < readOnlySpan.Length; i++)
+                {
+                    if (char.IsControl(readOnlySpan[i]))
+                    {
+                        return true;
+                    }
+                }
+
+                return false;
+            }
         }
 
+
         /// <inheritdoc />
         public virtual string Content(string contentPath)
         {

+ 38 - 1
src/Mvc/Mvc.Core/test/Routing/UrlHelperTestBase.cs

@@ -1,4 +1,4 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
@@ -288,6 +288,43 @@ namespace Microsoft.AspNetCore.Mvc.Routing
             Assert.False(result);
         }
 
+        [Theory]
+        [InlineData("\n")]
+        [InlineData("\\n")]
+        [InlineData("/\n")]
+        [InlineData("~\n")]
+        [InlineData("~/\n")]
+        public void IsLocalUrl_RejectsUrlWithNewLineAtStart(string url)
+        {
+            // Arrange
+            var helper = CreateUrlHelper(appRoot: string.Empty, host: "www.mysite.com", protocol: null);
+
+            // Act
+            var result = helper.IsLocalUrl(url);
+
+            // Assert
+            Assert.False(result);
+        }
+
+        [Theory]
+        [InlineData("/\r\nsomepath")]
+        [InlineData("~/\r\nsomepath")]
+        [InlineData("/some\npath")]
+        [InlineData("~/some\npath")]
+        [InlineData("\\path\b")]
+        [InlineData("~\\path\b")]
+        public void IsLocalUrl_RejectsUrlWithControlCharacters(string url)
+        {
+            // Arrange
+            var helper = CreateUrlHelper(appRoot: string.Empty, host: "www.mysite.com", protocol: null);
+
+            // Act
+            var result = helper.IsLocalUrl(url);
+
+            // Assert
+            Assert.False(result);
+        }
+
         [Fact]
         public void RouteUrlWithDictionary()
         {

+ 25 - 1
src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/.template.config/template.json

@@ -26,6 +26,31 @@
   "sources": [
     {
       "modifiers": [
+        {
+          "condition": "(!OrganizationalAuth && !IndividualAuth)",
+          "exclude": [
+            "Areas/Identity/Pages/Shared/_LoginPartial.Identity.cshtml",
+            "Areas/Identity/Pages/Shared/_LoginPartial.OrgAuth.cshtml"
+          ]
+        },
+        {
+          "condition": "(OrganizationalAuth || IndividualB2CAuth)",
+          "rename": {
+            "Areas/Identity/Pages/Shared/_LoginPartial.OrgAuth.cshtml": "Areas/Identity/Pages/Shared/_LoginPartial.cshtml"
+          },
+          "exclude": [
+            "Areas/Identity/Pages/Shared/_LoginPartial.Identity.cshtml"
+          ]
+        },
+        {
+          "condition": "(IndividualLocalAuth)",
+          "rename": {
+            "Areas/Identity/Pages/Shared/_LoginPartial.Identity.cshtml": "Areas/Identity/Pages/Shared/_LoginPartial.cshtml"
+          },
+          "exclude": [
+            "Areas/Identity/Pages/Shared/_LoginPartial.OrgAuth.cshtml"
+          ]
+        },
         {
           "condition": "(!IndividualLocalAuth || UseLocalDB)",
           "exclude": [
@@ -131,7 +156,6 @@
         {
           "condition": "(!GenerateApi)",
             "exclude": [
-              "Services/DownstreamWebApi.cs",
               "Pages/CallWebApi.razor"
             ]
         },

+ 27 - 0
src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Areas/Identity/Pages/Shared/_LoginPartial.Identity.cshtml

@@ -0,0 +1,27 @@
+@using Microsoft.AspNetCore.Identity
+@inject SignInManager<IdentityUser> SignInManager
+@inject UserManager<IdentityUser> UserManager
+@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
+
+<ul class="navbar-nav">
+@if (SignInManager.IsSignedIn(User))
+{
+    <li class="nav-item">
+        <a  class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Manage/Index" title="Manage">Hello @User.Identity.Name!</a>
+    </li>
+    <li class="nav-item">
+        <form class="form-inline" asp-area="Identity" asp-page="/Account/Logout" asp-route-returnUrl="/" method="post">
+            <button  type="submit" class="nav-link btn btn-link text-dark">Logout</button>
+        </form>
+    </li>
+}
+else
+{
+    <li class="nav-item">
+        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Register">Register</a>
+    </li>
+    <li class="nav-item">
+        <a class="nav-link text-dark" asp-area="Identity" asp-page="/Account/Login">Login</a>
+    </li>
+}
+</ul>

+ 0 - 0
src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Areas/Identity/Pages/Shared/_LoginPartial.cshtml → src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Areas/Identity/Pages/Shared/_LoginPartial.OrgAuth.cshtml


+ 1 - 1
src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Pages/_Host.cshtml

@@ -14,7 +14,7 @@
     <base href="~/" />
     <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
     <link href="css/site.css" rel="stylesheet" />
-    <link href="_content/BlazorServerWeb-CSharp/_framework/scoped.styles.css" rel="stylesheet" />
+    <link href="BlazorServerWeb-CSharp.styles.css" rel="stylesheet" />
 </head>
 <body>
     <component type="typeof(App)" render-mode="ServerPrerendered" />

+ 0 - 1
src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/_Imports.razor

@@ -4,7 +4,6 @@
 @using Microsoft.AspNetCore.Components.Forms
 @using Microsoft.AspNetCore.Components.Routing
 @using Microsoft.AspNetCore.Components.Web
-@using Microsoft.AspNetCore.Components.Web.Virtualization
 @using Microsoft.JSInterop
 @using BlazorServerWeb_CSharp
 @using BlazorServerWeb_CSharp.Shared

+ 5 - 1
src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/wwwroot/index.html

@@ -8,7 +8,11 @@
     <base href="/" />
     <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
     <link href="css/app.css" rel="stylesheet" />
-    <link href="_framework/scoped.styles.css" rel="stylesheet" />
+<!--#if (Hosted)  -->
+    <link href="ComponentsWebAssembly-CSharp.Client.styles.css" rel="stylesheet" />
+<!--#else  -->
+    <link href="ComponentsWebAssembly-CSharp.styles.css" rel="stylesheet" />
+<!--#endif  -->
     <!--#if PWA -->
     <link href="manifest.json" rel="manifest" />
     <link rel="apple-touch-icon" sizes="512x512" href="icon-512.png" />

+ 75 - 1
src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/PackIntegrationTest.cs

@@ -180,6 +180,30 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
                 Path.Combine("lib", TFM, "ClassLibrary.Views.dll"));
         }
 
+        [Fact]
+        [InitializeTestProject("PackageLibraryDirectDependency", additionalProjects: new[] { "PackageLibraryTransitiveDependency" })]
+        public async Task Pack_FailsWhenStaticWebAssetsHaveConflictingPaths()
+        {
+            Project.AddProjectFileContent(@"
+<ItemGroup>
+  <StaticWebAsset Include=""bundle\js\pkg-direct-dep.js"">
+    <SourceType></SourceType>
+    <SourceId>PackageLibraryDirectDependency</SourceId>
+    <ContentRoot>$([MSBuild]::NormalizeDirectory('$(MSBuildProjectDirectory)\bundle\'))</ContentRoot>
+    <BasePath>_content/PackageLibraryDirectDependency</BasePath>
+    <RelativePath>js/pkg-direct-dep.js</RelativePath>
+  </StaticWebAsset>
+</ItemGroup>");
+
+            Directory.CreateDirectory(Path.Combine(Project.DirectoryPath, "bundle", "js"));
+            File.WriteAllText(Path.Combine(Project.DirectoryPath, "bundle", "js", "pkg-direct-dep.js"), "console.log('bundle');");
+
+            var result = await DotnetMSBuild("Pack");
+
+            Assert.BuildFailed(result);
+        }
+
+        // If you modify this test, make sure you also modify the test below this one to assert that things are not included as content.
         [Fact]
         [InitializeTestProject("PackageLibraryDirectDependency", additionalProjects: new[] { "PackageLibraryTransitiveDependency" })]
         public async Task Pack_IncludesStaticWebAssets()
@@ -197,6 +221,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
                 {
                     Path.Combine("staticwebassets", "js", "pkg-direct-dep.js"),
                     Path.Combine("staticwebassets", "css", "site.css"),
+                    Path.Combine("staticwebassets", "PackageLibraryDirectDependency.bundle.scp.css"),
                     Path.Combine("build", "Microsoft.AspNetCore.StaticWebAssets.props"),
                     Path.Combine("build", "PackageLibraryDirectDependency.props"),
                     Path.Combine("buildMultiTargeting", "PackageLibraryDirectDependency.props"),
@@ -204,6 +229,55 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
                 });
         }
 
+        [Fact]
+        [InitializeTestProject("PackageLibraryDirectDependency", additionalProjects: new[] { "PackageLibraryTransitiveDependency" })]
+        public async Task Pack_DoesNotInclude_TransitiveBundleOrScopedCssAsStaticWebAsset()
+        {
+            var result = await DotnetMSBuild("Pack");
+
+            Assert.BuildPassed(result, allowWarnings: true);
+
+            Assert.FileExists(result, OutputPath, "PackageLibraryDirectDependency.dll");
+
+            Assert.NupkgDoesNotContain(
+                result,
+                Path.Combine("..", "TestPackageRestoreSource", "PackageLibraryDirectDependency.1.0.0.nupkg"),
+                filePaths: new[]
+                {
+                    // This is to make sure we don't include the scoped css files on the package when bundling is enabled.
+                    Path.Combine("staticwebassets", "Components", "App.razor.rz.scp.css"),
+                    Path.Combine("staticwebassets", "PackageLibraryDirectDependency.styles.css"),
+                });
+        }
+
+        [Fact]
+        [InitializeTestProject("PackageLibraryDirectDependency", additionalProjects: new[] { "PackageLibraryTransitiveDependency" })]
+        public async Task Pack_DoesNotIncludeStaticWebAssetsAsContent()
+        {
+            var result = await DotnetMSBuild("Pack");
+
+            Assert.BuildPassed(result, allowWarnings: true);
+
+            Assert.FileExists(result, OutputPath, "PackageLibraryDirectDependency.dll");
+
+            Assert.NupkgDoesNotContain(
+                result,
+                Path.Combine("..", "TestPackageRestoreSource", "PackageLibraryDirectDependency.1.0.0.nupkg"),
+                filePaths: new[]
+                {
+                    Path.Combine("content", "js", "pkg-direct-dep.js"),
+                    Path.Combine("content", "css", "site.css"),
+                    Path.Combine("content", "Components", "App.razor.css"),
+                    // This is to make sure we don't include the unscoped css file on the package.
+                    Path.Combine("content", "Components", "App.razor.css"),
+                    Path.Combine("content", "Components", "App.razor.rz.scp.css"),
+                    Path.Combine("contentFiles", "js", "pkg-direct-dep.js"),
+                    Path.Combine("contentFiles", "css", "site.css"),
+                    Path.Combine("contentFiles", "Components", "App.razor.css"),
+                    Path.Combine("contentFiles", "Components", "App.razor.rz.scp.css"),
+                });
+        }
+
         [Fact]
         [InitializeTestProject("PackageLibraryDirectDependency", additionalProjects: new[] { "PackageLibraryTransitiveDependency" })]
         public async Task Pack_StaticWebAssetsEnabledFalse_DoesNotPackAnyStaticWebAssets()
@@ -246,7 +320,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
                 filePaths: new[]
                 {
                     Path.Combine("staticwebassets", "js", "pkg-direct-dep.js"),
-                    Path.Combine("staticwebassets", "Components", "App.razor.rz.scp.css"),
+                    Path.Combine("staticwebassets", "PackageLibraryDirectDependency.bundle.scp.css"),
                     Path.Combine("staticwebassets", "css", "site.css"),
                     Path.Combine("build", "Microsoft.AspNetCore.StaticWebAssets.props"),
                     Path.Combine("build", "PackageLibraryDirectDependency.props"),

+ 72 - 4
src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/ScopedCssIntegrationTests.cs

@@ -22,6 +22,58 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
 
         public ITestOutputHelper Output { get; private set; }
 
+        [Fact]
+        [InitializeTestProject("ComponentApp", language: "C#")]
+        public async Task Build_NoOps_WhenScopedCssIsDisabled()
+        {
+            var result = await DotnetMSBuild("Build", "/p:ScopedCssEnabled=false");
+            Assert.BuildPassed(result);
+
+            Assert.FileDoesNotExist(result, IntermediateOutputPath, "scopedcss", "Components", "Pages", "Counter.razor.rz.scp.css");
+            Assert.FileDoesNotExist(result, IntermediateOutputPath, "scopedcss", "Components", "Pages", "Index.razor.rz.scp.css");
+            Assert.FileDoesNotExist(result, IntermediateOutputPath, "scopedcss", "ComponentApp.styles.css");
+            Assert.FileDoesNotExist(result, IntermediateOutputPath, "scopedcss", "Components", "Pages", "FetchData.razor.rz.scp.css");
+        }
+
+        [Fact]
+        [InitializeTestProject("ComponentApp", language: "C#")]
+        public async Task CanDisableDefaultDiscoveryConvention()
+        {
+            var result = await DotnetMSBuild("Build", "/p:EnableDefaultScopedCssItems=false");
+            Assert.BuildPassed(result);
+
+            Assert.FileDoesNotExist(result, IntermediateOutputPath, "scopedcss", "Components", "Pages", "Counter.razor.rz.scp.css");
+            Assert.FileDoesNotExist(result, IntermediateOutputPath, "scopedcss", "Components", "Pages", "Index.razor.rz.scp.css");
+            Assert.FileDoesNotExist(result, IntermediateOutputPath, "scopedcss", "ComponentApp.styles.css");
+            Assert.FileDoesNotExist(result, IntermediateOutputPath, "scopedcss", "Components", "Pages", "FetchData.razor.rz.scp.css");
+        }
+
+        [Fact]
+        [InitializeTestProject("ComponentApp", language: "C#")]
+        public async Task CanOverrideScopeIdentifiers()
+        {
+            var stylesFolder = Path.Combine(Project.DirectoryPath, "Styles", "Pages");
+            Directory.CreateDirectory(stylesFolder);
+            var styles = Path.Combine(stylesFolder, "Counter.css");
+            File.Move(Path.Combine(Project.DirectoryPath, "Components", "Pages", "Counter.razor.css"), styles);
+            Project.AddProjectFileContent(@"
+<ItemGroup>
+    <ScopedCssInput Include=""Styles\Pages\Counter.css"">
+        <RazorComponent>Components\Pages\Counter.razor</RazorComponent>
+        <CssScope>b-overriden</CssScope>
+    </ScopedCssInput>
+</ItemGroup>
+");
+            var result = await DotnetMSBuild("Build", "/p:EnableDefaultScopedCssItems=false");
+            Assert.BuildPassed(result);
+
+            var scoped = Assert.FileExists(result, IntermediateOutputPath, "scopedcss", "Styles", "Pages", "Counter.rz.scp.css");
+            Assert.FileContains(result, scoped, "b-overriden");
+            var generated = Assert.FileExists(result, IntermediateOutputPath, "Razor", "Components", "Pages", "Counter.razor.g.cs");
+            Assert.FileContains(result, generated, "b-overriden");
+            Assert.FileDoesNotExist(result, IntermediateOutputPath, "scopedcss", "Components", "Pages", "Index.razor.rz.scp.css");
+        }
+
         [Fact]
         [InitializeTestProject("ComponentApp", language: "C#")]
         public async Task Build_GeneratesTransformedFilesAndBundle_ForComponentsWithScopedCss()
@@ -31,7 +83,8 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
 
             Assert.FileExists(result, IntermediateOutputPath, "scopedcss", "Components", "Pages", "Counter.razor.rz.scp.css");
             Assert.FileExists(result, IntermediateOutputPath, "scopedcss", "Components", "Pages", "Index.razor.rz.scp.css");
-            Assert.FileExists(result, IntermediateOutputPath, "scopedcss", "_framework", "scoped.styles.css");
+            Assert.FileExists(result, IntermediateOutputPath, "scopedcss", "bundle", "ComponentApp.styles.css");
+            Assert.FileExists(result, IntermediateOutputPath, "scopedcss", "projectbundle", "ComponentApp.bundle.scp.css");
             Assert.FileDoesNotExist(result, IntermediateOutputPath, "scopedcss", "Components", "Pages", "FetchData.razor.rz.scp.css");
         }
 
@@ -65,7 +118,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
             var result = await DotnetMSBuild("Publish");
             Assert.BuildPassed(result);
 
-            Assert.FileExists(result, PublishOutputPath, "wwwroot", "_content", "ComponentApp", "_framework", "scoped.styles.css");
+            Assert.FileExists(result, PublishOutputPath, "wwwroot", "ComponentApp.styles.css");
             Assert.FileDoesNotExist(result, PublishOutputPath, "wwwroot", "_content", "ComponentApp", "Components", "Pages", "Index.razor.rz.scp.css");
             Assert.FileDoesNotExist(result, PublishOutputPath, "wwwroot", "_content", "ComponentApp", "Components", "Pages", "Counter.razor.rz.scp.css");
         }
@@ -80,7 +133,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
             result = await DotnetMSBuild("Publish", "/p:NoBuild=true");
             Assert.BuildPassed(result);
 
-            Assert.FileExists(result, PublishOutputPath, "wwwroot", "_content", "ComponentApp", "_framework", "scoped.styles.css");
+            Assert.FileExists(result, PublishOutputPath, "wwwroot", "ComponentApp.styles.css");
             Assert.FileDoesNotExist(result, PublishOutputPath, "wwwroot", "_content", "ComponentApp", "Components", "Pages", "Index.razor.rz.scp.css");
             Assert.FileDoesNotExist(result, PublishOutputPath, "wwwroot", "_content", "ComponentApp", "Components", "Pages", "Counter.razor.rz.scp.css");
         }
@@ -98,6 +151,20 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
             Assert.FileDoesNotExist(result, PublishOutputPath, "wwwroot", "_content", "ComponentApp", "_framework", "scoped.styles.css");
         }
 
+        [Fact]
+        [InitializeTestProject("ComponentApp", language: "C#")]
+        public async Task Publish_Publishes_IndividualScopedCssFiles_WhenNoBundlingIsEnabled()
+        {
+            var result = await DotnetMSBuild("Publish", args: "/p:DisableScopedCssBundling=true");
+            Assert.BuildPassed(result);
+
+            Assert.FileDoesNotExist(result, PublishOutputPath, "wwwroot", "_content", "ComponentApp", "ComponentApp.styles.css");
+
+            Assert.FileExists(result, PublishOutputPath, "wwwroot", "_content", "ComponentApp", "Components", "Pages", "Index.razor.rz.scp.css");
+            Assert.FileExists(result, PublishOutputPath, "wwwroot", "_content", "ComponentApp", "Components", "Pages", "Counter.razor.rz.scp.css");
+        }
+
+
         [Fact]
         [InitializeTestProject("ComponentApp", language: "C#")]
         public async Task Build_GeneratedComponentContainsScope()
@@ -125,7 +192,8 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
             Assert.BuildPassed(result);
 
             Assert.FileExists(result, IntermediateOutputPath, "scopedcss", "Components", "Pages", "Counter.razor.rz.scp.css");
-            var generatedBundle = Assert.FileExists(result, IntermediateOutputPath, "scopedcss", "_framework", "scoped.styles.css");
+            var generatedBundle = Assert.FileExists(result, IntermediateOutputPath, "scopedcss", "bundle", "ComponentApp.styles.css");
+            var generatedProjectBundle = Assert.FileExists(result, IntermediateOutputPath, "scopedcss", "projectbundle", "ComponentApp.bundle.scp.css");
             var generatedCounter = Assert.FileExists(result, IntermediateOutputPath, "Razor", "Components", "Pages", "Counter.razor.g.cs");
 
             var componentThumbprint = GetThumbPrint(generatedCounter);

+ 35 - 17
src/Razor/Microsoft.NET.Sdk.Razor/integrationtests/StaticWebAssetsIntegrationTest.cs

@@ -65,6 +65,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
 
             Assert.BuildPassed(result);
 
+            Assert.FileExists(result, PublishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary", "ClassLibrary.bundle.scp.css"));
             Assert.FileExists(result, PublishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary", "js", "project-transitive-dep.js"));
             Assert.FileExists(result, PublishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary", "js", "project-transitive-dep.v4.js"));
             Assert.FileExists(result, PublishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary2", "css", "site.css"));
@@ -72,7 +73,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
             Assert.FileExists(result, PublishOutputPath, Path.Combine("wwwroot", "_content", "PackageLibraryDirectDependency", "css", "site.css"));
             Assert.FileExists(result, PublishOutputPath, Path.Combine("wwwroot", "_content", "PackageLibraryDirectDependency", "js", "pkg-direct-dep.js"));
             Assert.FileExists(result, PublishOutputPath, Path.Combine("wwwroot", "_content", "PackageLibraryTransitiveDependency", "js", "pkg-transitive-dep.js"));
-            Assert.FileExists(result, PublishOutputPath, Path.Combine("wwwroot", "_content", "AppWithPackageAndP2PReference", "_framework", "scoped.styles.css"));
+            Assert.FileExists(result, PublishOutputPath, Path.Combine("wwwroot", "AppWithPackageAndP2PReference.styles.css"));
 
             // Validate that static web assets don't get published as content too on their regular path
             Assert.FileDoesNotExist(result, PublishOutputPath, Path.Combine("wwwroot", "js", "project-transitive-dep.js"));
@@ -91,22 +92,23 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
             var result = await DotnetMSBuild("Publish", $"/restore /p:PublishSingleFile=true /p:ReferenceLocallyBuiltPackages=true");
 
             Assert.BuildPassed(result);
-            var publishOutputPath = GetRidSpecificPublishOutputPath("win-x64");
-            Assert.FileExists(result, publishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary", "js", "project-transitive-dep.js"));
-            Assert.FileExists(result, publishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary", "js", "project-transitive-dep.v4.js"));
-            Assert.FileExists(result, publishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary2", "css", "site.css"));
-            Assert.FileExists(result, publishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary2", "js", "project-direct-dep.js"));
-            Assert.FileExists(result, publishOutputPath, Path.Combine("wwwroot", "_content", "PackageLibraryDirectDependency", "css", "site.css"));
-            Assert.FileExists(result, publishOutputPath, Path.Combine("wwwroot", "_content", "PackageLibraryDirectDependency", "js", "pkg-direct-dep.js"));
-            Assert.FileExists(result, publishOutputPath, Path.Combine("wwwroot", "_content", "PackageLibraryTransitiveDependency", "js", "pkg-transitive-dep.js"));
-            Assert.FileExists(result, publishOutputPath, Path.Combine("wwwroot", "_content", "AppWithPackageAndP2PReferenceAndRID", "_framework", "scoped.styles.css"));
+            var publishOutputPathWithRID = GetRidSpecificPublishOutputPath("win-x64");
+            Assert.FileExists(result, publishOutputPathWithRID, Path.Combine("wwwroot", "_content", "ClassLibrary", "ClassLibrary.bundle.scp.css"));
+            Assert.FileExists(result, publishOutputPathWithRID, Path.Combine("wwwroot", "_content", "ClassLibrary", "js", "project-transitive-dep.js"));
+            Assert.FileExists(result, publishOutputPathWithRID, Path.Combine("wwwroot", "_content", "ClassLibrary", "js", "project-transitive-dep.v4.js"));
+            Assert.FileExists(result, publishOutputPathWithRID, Path.Combine("wwwroot", "_content", "ClassLibrary2", "css", "site.css"));
+            Assert.FileExists(result, publishOutputPathWithRID, Path.Combine("wwwroot", "_content", "ClassLibrary2", "js", "project-direct-dep.js"));
+            Assert.FileExists(result, publishOutputPathWithRID, Path.Combine("wwwroot", "_content", "PackageLibraryDirectDependency", "css", "site.css"));
+            Assert.FileExists(result, publishOutputPathWithRID, Path.Combine("wwwroot", "_content", "PackageLibraryDirectDependency", "js", "pkg-direct-dep.js"));
+            Assert.FileExists(result, publishOutputPathWithRID, Path.Combine("wwwroot", "_content", "PackageLibraryTransitiveDependency", "js", "pkg-transitive-dep.js"));
+            Assert.FileExists(result, publishOutputPathWithRID, Path.Combine("wwwroot", "AppWithPackageAndP2PReferenceAndRID.styles.css"));
 
             // Validate that static web assets don't get published as content too on their regular path
-            Assert.FileDoesNotExist(result, publishOutputPath, Path.Combine("wwwroot", "js", "project-transitive-dep.js"));
-            Assert.FileDoesNotExist(result, publishOutputPath, Path.Combine("wwwroot", "js", "project-transitive-dep.v4.js"));
+            Assert.FileDoesNotExist(result, publishOutputPathWithRID, Path.Combine("wwwroot", "js", "project-transitive-dep.js"));
+            Assert.FileDoesNotExist(result, publishOutputPathWithRID, Path.Combine("wwwroot", "js", "project-transitive-dep.v4.js"));
 
             // Validate that the manifest never gets copied
-            Assert.FileDoesNotExist(result, publishOutputPath, "AppWithPackageAndP2PReference.StaticWebAssets.xml");
+            Assert.FileDoesNotExist(result, publishOutputPathWithRID, "AppWithPackageAndP2PReference.StaticWebAssets.xml");
         }
 
         [Fact]
@@ -122,6 +124,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
 
             Assert.BuildPassed(publish);
 
+            Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary", "ClassLibrary.bundle.scp.css"));
             Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary", "js", "project-transitive-dep.js"));
             Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary", "js", "project-transitive-dep.v4.js"));
             Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary2", "css", "site.css"));
@@ -129,7 +132,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
             Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "PackageLibraryDirectDependency", "css", "site.css"));
             Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "PackageLibraryDirectDependency", "js", "pkg-direct-dep.js"));
             Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "PackageLibraryTransitiveDependency", "js", "pkg-transitive-dep.js"));
-            Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "AppWithPackageAndP2PReference", "_framework", "scoped.styles.css"));
+            Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "AppWithPackageAndP2PReference.styles.css"));
         }
 
         [Fact]
@@ -145,6 +148,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
 
             Assert.BuildPassed(publish);
 
+            Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary", "ClassLibrary.bundle.scp.css"));
             Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary", "js", "project-transitive-dep.js"));
             Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary", "js", "project-transitive-dep.v4.js"));
             Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "ClassLibrary2", "css", "site.css"));
@@ -152,7 +156,7 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
             Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "PackageLibraryDirectDependency", "css", "site.css"));
             Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "PackageLibraryDirectDependency", "js", "pkg-direct-dep.js"));
             Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "PackageLibraryTransitiveDependency", "js", "pkg-transitive-dep.js"));
-            Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "_content", "AppWithPackageAndP2PReference", "_framework", "scoped.styles.css"));
+            Assert.FileExists(publish, PublishOutputPath, Path.Combine("wwwroot", "AppWithPackageAndP2PReference.styles.css"));
         }
 
         [Fact]
@@ -171,6 +175,18 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
             var path = Assert.FileExists(result, OutputPath, "SimpleMvc.dll");
         }
 
+        [Fact]
+        [InitializeTestProject("AppWithPackageAndP2PReference", language: "C#", additionalProjects: new[] { "ClassLibrary", "ClassLibrary2" })]
+        public async Task Build_Fails_WhenConflictingAssetsFoundBetweenAStaticWebAssetAndAFileInTheWebRootFolder()
+        {
+            Directory.CreateDirectory(Path.Combine(Project.DirectoryPath, "wwwroot", "_content", "ClassLibrary", "js"));
+            File.WriteAllText(Path.Combine(Project.DirectoryPath, "wwwroot", "_content", "ClassLibrary", "js", "project-transitive-dep.js"), "console.log('transitive-dep');");
+
+            var result = await DotnetMSBuild("Build");
+
+            Assert.BuildFailed(result);
+        }
+
         [Fact]
         [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/23756")]
         [InitializeTestProject("AppWithPackageAndP2PReference", language: "C#", additionalProjects: new[] { "ClassLibrary", "ClassLibrary2" })]
@@ -301,15 +317,17 @@ namespace Microsoft.AspNetCore.Razor.Design.IntegrationTests
                 Path.Combine(restorePath, "packagelibrarydirectdependency", "1.0.0", "build", "..", "staticwebassets") + Path.DirectorySeparatorChar,
                 Path.GetFullPath(Path.Combine(source, "ClassLibrary2", "wwwroot")) + Path.DirectorySeparatorChar,
                 Path.GetFullPath(Path.Combine(source, "ClassLibrary", "wwwroot")) + Path.DirectorySeparatorChar,
-                Path.GetFullPath(Path.Combine(source, "AppWithPackageAndP2PReference", IntermediateOutputPath, "scopedcss")) + Path.DirectorySeparatorChar,
+                Path.GetFullPath(Path.Combine(source, "ClassLibrary", IntermediateOutputPath, "scopedcss", "projectbundle")) + Path.DirectorySeparatorChar,
+                Path.GetFullPath(Path.Combine(source, "AppWithPackageAndP2PReference", IntermediateOutputPath, "scopedcss", "bundle")) + Path.DirectorySeparatorChar,
             };
 
             return $@"<StaticWebAssets Version=""1.0"">
-  <ContentRoot BasePath=""_content/AppWithPackageAndP2PReference"" Path=""{projects[4]}"" />
+  <ContentRoot BasePath=""_content/ClassLibrary"" Path=""{projects[4]}"" />
   <ContentRoot BasePath=""_content/ClassLibrary"" Path=""{projects[3]}"" />
   <ContentRoot BasePath=""_content/ClassLibrary2"" Path=""{projects[2]}"" />
   <ContentRoot BasePath=""_content/PackageLibraryDirectDependency"" Path=""{projects[1]}"" />
   <ContentRoot BasePath=""_content/PackageLibraryTransitiveDependency"" Path=""{projects[0]}"" />
+  <ContentRoot BasePath=""/"" Path=""{projects[5]}"" />
 </StaticWebAssets>";
         }
     }

+ 5 - 4
src/Razor/Microsoft.NET.Sdk.Razor/src/ApplyCssScopes.cs

@@ -3,7 +3,6 @@
 
 using System;
 using System.Collections.Generic;
-using System.IO;
 using System.Linq;
 using System.Text.RegularExpressions;
 using Microsoft.Build.Framework;
@@ -69,8 +68,10 @@ namespace Microsoft.AspNetCore.Razor.Tasks
 
                 if (scopeFiles.Count > 1)
                 {
-                    Log.LogError($"More than one scoped css files were found for the razor component '{component}'. Each razor component must have at most" +
-                    " a single associated scoped css file." + Environment.NewLine + string.Join(Environment.NewLine, scopeFiles.Select(f => f.ItemSpec)));
+                    Log.LogError(null, "BLAZOR101", "", component, 0, 0, 0, 0, $"More than one scoped css files were found for the razor component '{component}'. " +
+                        $"Each razor component must have at most a single associated scoped css file." +
+                        Environment.NewLine +
+                        string.Join(Environment.NewLine, scopeFiles.Select(f => f.ItemSpec)));
                 }
             }
 
@@ -84,7 +85,7 @@ namespace Microsoft.AspNetCore.Razor.Tasks
             // can update the Content item for the .razor.css file with Scoped=false and we will not consider it.
             foreach (var unmatched in unmatchedScopedCss)
             {
-                Log.LogError($"The scoped css file '{unmatched.ItemSpec}' was defined but no associated razor component was found for it.");
+                Log.LogError(null, "BLAZOR102", "", unmatched.ItemSpec, 0, 0, 0, 0, $"The scoped css file '{unmatched.ItemSpec}' was defined but no associated razor component was found for it.");
             }
 
             RazorComponentsWithScopes = razorComponentsWithScopes.ToArray();

+ 1 - 2
src/Razor/Microsoft.NET.Sdk.Razor/src/ComputeCssScope.cs

@@ -28,8 +28,7 @@ namespace Microsoft.AspNetCore.Razor.Tasks
             for (var i = 0; i < ScopedCssInput.Length; i++)
             {
                 var input = ScopedCssInput[i];
-                // Todo: Normalize path to forward slashes and lowercase before computing the hash
-                var relativePath = input.ItemSpec.Replace("\\","//");
+                var relativePath = input.ItemSpec.ToLowerInvariant().Replace("\\","//");
                 var scope = input.GetMetadata("CssScope");
                 scope = !string.IsNullOrEmpty(scope) ? scope : GenerateScope(TargetName, relativePath);
 

+ 60 - 6
src/Razor/Microsoft.NET.Sdk.Razor/src/ConcatenateCssFiles.cs

@@ -2,6 +2,7 @@
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
+using System.Collections.Generic;
 using System.IO;
 using System.Linq;
 using System.Security.Cryptography;
@@ -13,21 +14,72 @@ namespace Microsoft.AspNetCore.Razor.Tasks
 {
     public class ConcatenateCssFiles : Task
     {
+        private static readonly IComparer<ITaskItem> _fullPathComparer =
+            Comparer<ITaskItem>.Create((x, y) => StringComparer.OrdinalIgnoreCase.Compare(x.GetMetadata("FullPath"), y.GetMetadata("FullPath")));
+
+        [Required]
+        public ITaskItem[] ScopedCssFiles { get; set; }
+
+        [Required]
+        public ITaskItem[] ProjectBundles { get; set; }
+
         [Required]
-        public ITaskItem[] FilesToProcess { get; set; }
+        public string ScopedCssBundleBasePath { get; set; }
 
         [Required]
         public string OutputFile { get; set; }
 
         public override bool Execute()
         {
+            if (ProjectBundles.Length > 0)
+            {
+                Array.Sort(ProjectBundles, _fullPathComparer);
+            }
+            Array.Sort(ScopedCssFiles, _fullPathComparer);
+
             var builder = new StringBuilder();
-            var orderedFiles = FilesToProcess.OrderBy(f => f.GetMetadata("FullPath")).ToArray();
-            for (var i = 0; i < orderedFiles.Length; i++)
+            if (ProjectBundles.Length > 0)
             {
-                var current = orderedFiles[i];
-                builder.AppendLine($"/* {current.GetMetadata("BasePath").Replace("\\","/")}{current.GetMetadata("RelativePath").Replace("\\","/")} */");
-                foreach (var line in File.ReadLines(FilesToProcess[i].GetMetadata("FullPath")))
+                // We are importing bundles from other class libraries and packages, in that case we need to compute the
+                // import path relative to the position of where the final bundle will be.
+                // Our final bundle will always be at "<<CurrentBasePath>>/scoped.styles.css"
+                // Other bundles will be at "<<BundleBasePath>>/bundle.bdl.scp.css"
+                // The base and relative paths can be modified by the user, so we do a normalization process to ensure they
+                // are in the shape we expect them before we use them.
+                // We normalize path separators to '\' from '/' which is what we expect on a url. The separator can come as
+                // '\' as a result of user input or another MSBuild path normalization operation. We always want '/' since that
+                // is what is valid on the url.
+                // We remove leading and trailing '/' on all paths to ensure we can combine them properly. Users might specify their
+                // base path with or without forward and trailing slashes and we always need to make sure we combine them appropriately.
+                // These links need to be relative to the final bundle to be independent of the path where the main app is being served.
+                // For example:
+                // An app is served from the "subdir" path base, the main bundle path on disk is "MyApp/scoped.styles.css" and it uses a
+                // library with scoped components that is placed on "_content/library/bundle.bdl.scp.css".
+                // The resulting import would be "import '../_content/library/bundle.bdl.scp.css'".
+                // If we were to produce "/_content/library/bundle.bdl.scp.css" it would fail to accoutn for "subdir"
+                // We could produce shorter paths if we detected common segments between the final bundle base path and the imported bundle
+                // base paths, but its more work and it will not have a significant impact on the bundle size size.
+                var normalizedBasePath = NormalizePath(ScopedCssBundleBasePath);
+                var currentBasePathSegments = normalizedBasePath.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
+                var prefix = string.Join("/", Enumerable.Repeat("..", currentBasePathSegments.Length));
+                for (var i = 0; i < ProjectBundles.Length; i++)
+                {
+                    var bundle = ProjectBundles[i];
+                    var bundleBasePath = NormalizePath(bundle.GetMetadata("BasePath"));
+                    var relativePath = NormalizePath(bundle.GetMetadata("RelativePath"));
+                    var importPath = NormalizePath(Path.Combine(prefix, bundleBasePath, relativePath));
+
+                    builder.AppendLine($"@import '{importPath}';");
+                }
+
+                builder.AppendLine();
+            }
+
+            for (var i = 0; i < ScopedCssFiles.Length; i++)
+            {
+                var current = ScopedCssFiles[i];
+                builder.AppendLine($"/* {NormalizePath(current.GetMetadata("BasePath"))}/{NormalizePath(current.GetMetadata("RelativePath"))} */");
+                foreach (var line in File.ReadLines(current.GetMetadata("FullPath")))
                 {
                     builder.AppendLine(line);
                 }
@@ -45,6 +97,8 @@ namespace Microsoft.AspNetCore.Razor.Tasks
             return !Log.HasLoggedErrors;
         }
 
+        private string NormalizePath(string path) => path.Replace("\\", "/").Trim('/');
+
         private bool SameContent(string content, string outputFilePath)
         {
             var contentHash = GetContentHash(content);

+ 4 - 76
src/Razor/Microsoft.NET.Sdk.Razor/src/GenerateStaticWebAssetsManifest.cs

@@ -17,6 +17,7 @@ namespace Microsoft.AspNetCore.Razor.Tasks
     {
         private const string ContentRoot = "ContentRoot";
         private const string BasePath = "BasePath";
+        private const string NodePath = "Path";
         private const string SourceId = "SourceId";
 
         [Required]
@@ -82,8 +83,7 @@ namespace Microsoft.AspNetCore.Razor.Tasks
                 // single trailing separator.
                 var normalizedContentRoot = $"{contentRoot.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)}{Path.DirectorySeparatorChar}";
 
-                // At this point we already know that there are no elements with different base paths and same content roots
-                // or viceversa. Here we simply skip additional items that have the same base path and same content root.
+                // Here we simply skip additional items that have the same base path and same content root.
                 if (!nodes.Exists(e => e.Attribute("BasePath").Value.Equals(normalizedBasePath, StringComparison.OrdinalIgnoreCase) &&
                     e.Attribute("Path").Value.Equals(normalizedContentRoot, StringComparison.OrdinalIgnoreCase)))
                 {
@@ -94,7 +94,7 @@ namespace Microsoft.AspNetCore.Razor.Tasks
             }
 
             // Its important that we order the nodes here to produce a manifest deterministically.
-            return nodes.OrderBy(e=>e.Attribute(BasePath).Value);
+            return nodes.OrderBy(e=>e.Attribute(BasePath).Value).ThenBy(e => e.Attribute(NodePath).Value);
         }
 
         private XmlWriter GetXmlWriter(XmlWriterSettings settings)
@@ -116,78 +116,6 @@ namespace Microsoft.AspNetCore.Razor.Tasks
                 }
             }
 
-            // We want to validate that there are no different item groups that share either the same base path
-            // but different content roots or that share the same content root but different base paths.
-            // We pass in all the static web assets that we discovered to this task without making any distinction for
-            // duplicates, so here we skip elements for which we are already tracking an element with the same
-            // content root path and same base path.
-
-            // Case-sensitivity depends on the underlying OS so we are not going to do anything to enforce it here.
-            // Any two items that match base path and content root in a case-insensitive way won't produce an error.
-            // Any other two items will produce an error even if there is only a casing difference between either the
-            // base paths or the content roots.
-            var basePaths = new Dictionary<string, ITaskItem>(StringComparer.OrdinalIgnoreCase);
-            var contentRootPaths = new Dictionary<string, ITaskItem>(StringComparer.OrdinalIgnoreCase);
-
-            for (var i = 0; i < ContentRootDefinitions.Length; i++)
-            {
-                var contentRootDefinition = ContentRootDefinitions[i];
-                var basePath = contentRootDefinition.GetMetadata(BasePath);
-                var contentRoot = contentRootDefinition.GetMetadata(ContentRoot);
-                var sourceId = contentRootDefinition.GetMetadata(SourceId);
-
-                if (basePaths.TryGetValue(basePath, out var existingBasePath))
-                {
-                    var existingBasePathContentRoot = existingBasePath.GetMetadata(ContentRoot);
-                    var existingSourceId = existingBasePath.GetMetadata(SourceId);
-                    if (!string.Equals(contentRoot, existingBasePathContentRoot, StringComparison.OrdinalIgnoreCase) &&
-                        // We want to check this case to allow for client-side blazor projects to have multiple different content
-                        // root sources exposed under the same base path while still requiring unique base paths/content roots across
-                        // project/package boundaries.
-                        !string.Equals(sourceId, existingSourceId, StringComparison.OrdinalIgnoreCase))
-                    {
-                        // Case:
-                        // Item2: /_content/Library, project:/project/aspnetContent2
-                        // Item1: /_content/Library, package:/package/aspnetContent1
-                        Log.LogError($"Duplicate base paths '{basePath}' for content root paths '{contentRoot}' and '{existingBasePathContentRoot}'. " +
-                            $"('{contentRootDefinition.ItemSpec}', '{existingBasePath.ItemSpec}')");
-                        return false;
-                    }
-
-                    // It was a duplicate, so we skip it.
-                    // Case:
-                    // Item1: /_content/Library, project:/project/aspnetContent
-                    // Item2: /_content/Library, project:/project/aspnetContent
-
-                    // It was a separate content root exposed from the same project/package, so we skip it.
-                    // Case:
-                    // Item1: /_content/Library, project:/project/aspnetContent/bin/debug/netstandard2.1/dist
-                    // Item2: /_content/Library, project:/project/wwwroot
-                }
-                else
-                {
-                    if (contentRootPaths.TryGetValue(contentRoot, out var existingContentRoot))
-                    {
-                        // Case:
-                        // Item1: /_content/Library1, /package/aspnetContent
-                        // Item2: /_content/Library2, /package/aspnetContent
-                        Log.LogError($"Duplicate content root paths '{contentRoot}' for base paths '{basePath}' and '{existingContentRoot.GetMetadata(BasePath)}' " +
-                            $"('{contentRootDefinition.ItemSpec}', '{existingContentRoot.ItemSpec}')");
-                        return false;
-                    }
-                }
-
-                if (!basePaths.ContainsKey(basePath))
-                {
-                    basePaths.Add(basePath, contentRootDefinition);
-                }
-
-                if (!contentRootPaths.ContainsKey(contentRoot))
-                {
-                    contentRootPaths.Add(contentRoot, contentRootDefinition);
-                }
-            }
-
             return true;
         }
 
@@ -203,4 +131,4 @@ namespace Microsoft.AspNetCore.Razor.Tasks
             return true;
         }
     }
-}
+}

+ 19 - 18
src/Razor/Microsoft.NET.Sdk.Razor/src/GenerateStaticWebAsssetsPropsFile.cs

@@ -3,6 +3,7 @@
 using System;
 using System.Collections.Generic;
 using System.IO;
+using System.Linq;
 using System.Text;
 using System.Xml;
 using System.Xml.Linq;
@@ -42,19 +43,22 @@ namespace Microsoft.AspNetCore.Razor.Tasks
                 return !Log.HasLoggedErrors;
             }
 
-            var template = StaticWebAssets[0];
+            var itemGroup = new XElement("ItemGroup");
+            var orderedAssets = StaticWebAssets.OrderBy(e => e.GetMetadata(BasePath), StringComparer.OrdinalIgnoreCase)
+                .ThenBy(e => e.GetMetadata(RelativePath), StringComparer.OrdinalIgnoreCase);
+            foreach(var element in orderedAssets)
+            {
+                itemGroup.Add(new XElement("StaticWebAsset",
+                    new XAttribute("Include", @$"$(MSBuildThisFileDirectory)..\staticwebassets\{Normalize(element.GetMetadata(RelativePath))}"),
+                    new XElement(SourceType, "Package"),
+                    new XElement(SourceId, element.GetMetadata(SourceId)),
+                    new XElement(ContentRoot, @"$(MSBuildThisFileDirectory)..\staticwebassets\"),
+                    new XElement(BasePath, element.GetMetadata(BasePath)),
+                    new XElement(RelativePath, element.GetMetadata(RelativePath))));
+            }
 
             var document = new XDocument(new XDeclaration("1.0", "utf-8", "yes"));
-            var root = new XElement(
-                "Project",
-                new XElement("ItemGroup",
-                    new XElement("StaticWebAsset",
-                        new XAttribute("Include", @"$(MSBuildThisFileDirectory)..\staticwebassets\**"),
-                        new XElement(SourceType, "Package"),
-                        new XElement(SourceId, template.GetMetadata(SourceId)),
-                        new XElement(ContentRoot, @"$(MSBuildThisFileDirectory)..\staticwebassets\"),
-                        new XElement(BasePath, template.GetMetadata(BasePath)),
-                        new XElement(RelativePath, "%(RecursiveDir)%(FileName)%(Extension)"))));
+            var root = new XElement("Project", itemGroup);
 
             document.Add(root);
 
@@ -74,6 +78,8 @@ namespace Microsoft.AspNetCore.Razor.Tasks
             }
 
             return !Log.HasLoggedErrors;
+
+            static string Normalize(string relativePath) => relativePath.Replace("/", "\\").TrimStart('\\');
         }
 
         private XmlWriter GetXmlWriter(XmlWriterSettings settings)
@@ -105,12 +111,7 @@ namespace Microsoft.AspNetCore.Razor.Tasks
                 }
 
                 if (!ValidateMetadataMatches(firstAsset, webAsset, SourceId) ||
-                    !ValidateMetadataMatches(firstAsset, webAsset, SourceType) ||
-                    // Now that we support generated assets we need to be able to support multiple content roots.
-                    // We need to change this check for one that ensures that no two files end up in the same final destination
-                    //!ValidateMetadataMatches(firstAsset, webAsset, ContentRoot) ||
-                    // See https://github.com/dotnet/aspnetcore/issues/24257
-                    !ValidateMetadataMatches(firstAsset, webAsset, BasePath))
+                    !ValidateMetadataMatches(firstAsset, webAsset, SourceType))
                 {
                     return false;
                 }
@@ -123,7 +124,7 @@ namespace Microsoft.AspNetCore.Razor.Tasks
         {
             var referenceMetadata = reference.GetMetadata(metadata);
             var candidateMetadata = candidate.GetMetadata(metadata);
-            if (!string.Equals(referenceMetadata, candidateMetadata, System.StringComparison.Ordinal))
+            if (!string.Equals(referenceMetadata, candidateMetadata, StringComparison.Ordinal))
             {
                 Log.LogError($"Static web assets have different '{metadata}' metadata values '{referenceMetadata}' and '{candidateMetadata}' for '{reference.ItemSpec}' and '{candidate.ItemSpec}'.");
                 return false;

+ 11 - 2
src/Razor/Microsoft.NET.Sdk.Razor/src/ResolveAllScopedCssAssets.cs

@@ -16,21 +16,30 @@ namespace Microsoft.AspNetCore.Razor.Tasks
         [Output]
         public ITaskItem[] ScopedCssAssets { get; set; }
 
+        [Output]
+        public ITaskItem[] ScopedCssProjectBundles { get; set; }
+
         public override bool Execute()
         {
             var scopedCssAssets = new List<ITaskItem>();
+            var scopedCssProjectBundles = new List<ITaskItem>();
 
             for (var i = 0; i < StaticWebAssets.Length; i++)
             {
                 var swa = StaticWebAssets[i];
-                var fullPath = swa.GetMetadata("RelativePath");
-                if (fullPath.EndsWith(".rz.scp.css", StringComparison.OrdinalIgnoreCase))
+                var path = swa.GetMetadata("RelativePath");
+                if (path.EndsWith(".rz.scp.css", StringComparison.OrdinalIgnoreCase))
                 {
                     scopedCssAssets.Add(swa);
                 }
+                else if (path.EndsWith(".bundle.scp.css", StringComparison.OrdinalIgnoreCase))
+                {
+                    scopedCssProjectBundles.Add(swa);
+                }
             }
 
             ScopedCssAssets = scopedCssAssets.ToArray();
+            ScopedCssProjectBundles = scopedCssProjectBundles.ToArray();
 
             return !Log.HasLoggedErrors;
         }

+ 4 - 4
src/Razor/Microsoft.NET.Sdk.Razor/src/ValidateStaticWebAssetsUniquePaths.cs

@@ -1,4 +1,4 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System;
@@ -34,7 +34,7 @@ namespace Microsoft.AspNetCore.Razor.Tasks
                 }
                 else
                 {
-                    var webRootPath = GetWebRootPath(
+                    var webRootPath = GetWebRootPath("/wwwroot",
                         contentRootDefinition.GetMetadata(BasePath),
                         contentRootDefinition.GetMetadata(RelativePath));
 
@@ -57,7 +57,7 @@ namespace Microsoft.AspNetCore.Razor.Tasks
             {
                 var webRootFile = WebRootFiles[i];
                 var relativePath = webRootFile.GetMetadata(TargetPath);
-                var webRootFileWebRootPath = GetWebRootPath("/", relativePath);
+                var webRootFileWebRootPath = GetWebRootPath("", "/", relativePath);
                 if (assetsByWebRootPaths.TryGetValue(webRootFileWebRootPath, out var existingAsset))
                 {
                     Log.LogError($"The static web asset '{existingAsset.ItemSpec}' has a conflicting web root path '{webRootFileWebRootPath}' with the project file '{webRootFile.ItemSpec}'.");
@@ -69,7 +69,7 @@ namespace Microsoft.AspNetCore.Razor.Tasks
         }
 
         // Normalizes /base/relative \base\relative\ base\relative and so on to /base/relative
-        private string GetWebRootPath(string basePath, string relativePath) => $"/{Path.Combine(basePath, relativePath.TrimStart('.').TrimStart('/')).Replace("\\", "/").Trim('/')}";
+        private string GetWebRootPath(string webRoot, string basePath, string relativePath) => $"{webRoot}/{Path.Combine(basePath, relativePath.TrimStart('.').TrimStart('/')).Replace("\\", "/").Trim('/')}";
 
         private bool EnsureRequiredMetadata(ITaskItem item, string metadataName)
         {

+ 178 - 67
src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.ScopedCss.targets

@@ -27,7 +27,7 @@ Copyright (c) .NET Foundation. All rights reserved.
     * Computing the scopes will happen very early on the pipeline and it will generate all the input that the compiler needs to do its job
       independently.
 * For web applications (Blazor webassembly and Blazor server) the main project is responsible for producing the final CSS bundle and making
-  it available during development and production behind _framework/scoped.styles.css
+  it available during development and production behind $(PackageId).styles.css
 * For razor class libraries we will add the list of ScopedCss to the list of available static web assets imported by the project, the main project
   will then discover these assets and add them to the ScopedCss files to process in the final bundle.
 * For packing in razor class libraries, the ScopedCss files will get processed and added as static web assets to the pack.
@@ -51,16 +51,62 @@ Integration with static web assets:
 <UsingTask TaskName="Microsoft.AspNetCore.Razor.Tasks.ConcatenateCssFiles" AssemblyFile="$(RazorSdkBuildTasksAssembly)" />
 
 <PropertyGroup>
+
+  <PrepareForRunDependsOn>
+    _PrepareForScopedCss;
+    $(PrepareForRunDependsOn)
+  </PrepareForRunDependsOn>
+
+  <_PrepareForScopedCssDependsOn Condition="'$(DisableScopedCssBundling)' != 'true'">
+    _PrepareForBundling;
+    ResolveStaticWebAssetsInputs;
+    _CollectAllScopedCssAssets;
+    BundleScopedCssFiles;
+    $(_PrepareForScopedCssDependsOn)
+  </_PrepareForScopedCssDependsOn>
+  <!-- Order between this definition and the above one is important to make sure we don't create a circular reference loop. -->
+  <_PrepareForScopedCssDependsOn>
+    _GenerateScopedCssFiles;
+    $(_PrepareForScopedCssDependsOn)
+  </_PrepareForScopedCssDependsOn>
+
+  <ResolveCurrentProjectStaticWebAssetsInputsDependsOn>
+    $(ResolveCurrentProjectStaticWebAssetsInputsDependsOn);
+    _AddGeneratedScopedCssFiles;
+  </ResolveCurrentProjectStaticWebAssetsInputsDependsOn>
+
+  <!-- We want to include the bundle as part of the list of static web assets when bundling is enabled and remove all the individual scoped
+       css files.
+  -->
+  <ResolveStaticWebAssetsInputsDependsOn Condition="'$(DisableScopedCssBundling)' != 'true'">
+    $(ResolveStaticWebAssetsInputsDependsOn);
+    _AddScopedCssBundles;
+  </ResolveStaticWebAssetsInputsDependsOn>
+
+  <!-- When used as a reference, the app will have been built before and as a result the list of static web assets will include the bundle
+  instead of the individual files. We need to correct that in GetCopyToOuputDirectoryItems -->
+
+  <GetCurrentProjectStaticWebAssetsDependsOn Condition="'$(DisableScopedCssBundling)' != 'true'">
+    $(GetCurrentProjectStaticWebAssetsDependsOn);
+    _AddScopedCssBundles;
+    _AddGeneratedScopedCssFilesForReference;
+  </GetCurrentProjectStaticWebAssetsDependsOn>
+
+  <GenerateStaticWebAssetsPackTargetsDependsOn>
+    $(GenerateStaticWebAssetsPackTargetsDependsOn);
+    _AddScopedCssBundles;
+    _AddGeneratedScopedCssFilesForReference;
+  </GenerateStaticWebAssetsPackTargetsDependsOn>
+
   <!-- We are going to use .rz.scp.css as the extension to mark scoped css files that come from packages or that have been pre-procesed by
        referenced class libraries. This way, we can use that information to adjust the build pipeline without having to rely on external
        sources like an additional itemgroup or metadata.
   -->
   <_ScopedCssExtension>.rz.scp.css</_ScopedCssExtension>
-  <ResolveStaticWebAssetsInputsDependsOn>$(ResolveStaticWebAssetsInputsDependsOn);_CollectAllScopedCssAssets;AddScopedCssBundle</ResolveStaticWebAssetsInputsDependsOn>
-  <ResolveCurrentProjectStaticWebAssetsInputsDependsOn>$(ResolveCurrentProjectStaticWebAssetsInputsDependsOn);_AddGeneratedScopedCssFiles</ResolveCurrentProjectStaticWebAssetsInputsDependsOn>
-  <GetCurrentProjectStaticWebAssetsDependsOn Condition="'$(UsingMicrosoftNETSdkBlazorWebAssembly)' == 'true'">$(GetCurrentProjectStaticWebAssetsDependsOn);IncludeScopedCssBundle;</GetCurrentProjectStaticWebAssetsDependsOn>
 </PropertyGroup>
 
+<Target Name="_PrepareForScopedCss" DependsOnTargets="$(_PrepareForScopedCssDependsOn)" />
+
 <Target Name="ResolveScopedCssInputs">
     <!--
   Gathers input source files for Razor component generation. This is a separate target so that we can avoid
@@ -73,13 +119,13 @@ Integration with static web assets:
     <Output TaskParameter="DiscoveredScopedCssInputs" ItemName="_DiscoveredScopedCssInputs" />
   </DiscoverDefaultScopedCssItems>
 
-  <ItemGroup Condition="'$(EnableDefaultScopedCssItems)'=='true'">
+  <ItemGroup Condition="'$(EnableDefaultScopedCssItems)' == 'true'">
     <ScopedCssInput Include="@(_DiscoveredScopedCssInputs)" />
   </ItemGroup>
 
   <ItemGroup>
     <Content Remove="@(ScopedCssInput)" />
-    <Content Include="@(ScopedCssInput)" CopyToPublishDirectory="Never" />
+    <Content Include="@(ScopedCssInput)" Pack="false" CopyToPublishDirectory="Never" />
   </ItemGroup>
 </Target>
 
@@ -116,6 +162,13 @@ Integration with static web assets:
     </_ScopedCss>
     <_ScopedCssOutputs Include="%(_ScopedCss.OutputFile)" />
   </ItemGroup>
+
+  <!-- https://github.com/dotnet/project-system/blob/master/docs/up-to-date-check.md -->
+  <ItemGroup>
+    <UpToDateCheckInput Include="%(_ScopedCss.Identity)" />
+    <UpToDateCheckBuilt Include="%(_ScopedCss.OutputFile)" Original="%(_ScopedCss.Identity)" />
+  </ItemGroup>
+
 </Target>
 
 <!-- Transforms the original scoped CSS files into their scoped versions on their designated output paths -->
@@ -139,7 +192,7 @@ Integration with static web assets:
   In the hosted blazor webassembly case, we want to include the bundle within the assets returned to the host, so we wire up this task
   to `GetCurrentProjectStaticWebAssetsDependsOn` so that contents are replaced and shared with the host application.
 
-  Normally, _CollectAllScopedCssAssets will find all the scoped css files from referenced packages, class libraries and the current project. When AddScopedCssBundle
+  Normally, _CollectAllScopedCssAssets will find all the scoped css files from referenced packages, class libraries and the current project. When _AddScopedCssBundles
   runs, it will remove all those static web assets and add the bundle asset.
   When _CollectAllScopedCssAssets runs as part of a hosted blazor webassembly app, only the current project and package assets are removed from the list of
   static web assets. If the host also decides to generate a bundle, there will be a bundle for the razor client app and another bundle for the host and they will
@@ -154,118 +207,176 @@ Integration with static web assets:
   If one single bundle is desired, bundling can be disabled in the Blazor application and the host will create a single big bundle file.
 
 -->
-<Target Name="AddScopedCssBundle" Condition="'$(ScopedCssDisableBundling)' != 'true'" DependsOnTargets="_CollectAllScopedCssAssets">
+
+<Target Name="_PrepareForBundling" DependsOnTargets="ResolveStaticWebAssetsConfiguration">
   <PropertyGroup>
-    <_ScopedCssOutputPath>$(_ScopedCssIntermediatePath)_framework\scoped.styles.css</_ScopedCssOutputPath>
-    <_ScopedCssOutputFullPath>$([System.IO.Path]::Combine('$(MSBuildProjectFileDirectory)', '$(_ScopedCssIntermediatePath)_framework\scoped.styles.css'))</_ScopedCssOutputFullPath>
+    <!-- This bundle represents the bundle for the entire application dependency graph which includes the application scoped css files and all the scoped css files from
+         projects and packages that this app references -->
+    <_ScopedCssBundleContentRoot>$(_ScopedCssIntermediatePath)bundle\</_ScopedCssBundleContentRoot>
+    <_ScopedCssOutputPath>$(_ScopedCssIntermediatePath)bundle\$(PackageId).styles.css</_ScopedCssOutputPath>
+    <_ScopedCssOutputFullPath>$([System.IO.Path]::Combine('$(MSBuildProjectFileDirectory)', '$(_ScopedCssIntermediatePath)bundle\$(PackageId).styles.css'))</_ScopedCssOutputFullPath>
+
+    <!-- This bundle represents the bundle for the scoped css files in this project, without references to other projects or package scoped css files. This bundle is used by projects
+         referencing this project that import it through an import rule into their app bundle -->
+    <_ScopedCssProjectBundleContentRoot>$(_ScopedCssIntermediatePath)projectbundle\</_ScopedCssProjectBundleContentRoot>
+    <_ScopedCssProjectOutputPath>$(_ScopedCssIntermediatePath)projectbundle\$(PackageId).bundle.scp.css</_ScopedCssProjectOutputPath>
+    <_ScopedCssProjectOutputFullPath>$([System.IO.Path]::Combine('$(MSBuildProjectFileDirectory)', '$(_ScopedCssIntermediatePath)projectbundle\$(PackageId).bundle.scp.css'))</_ScopedCssProjectOutputFullPath>
+    <!-- We want the scoped css bundle path to always point to the root path of the app, overriding the default base path unless it is not explicitly overriden
+    by the user. This is so that when you are developing a server-side application or in the future potentially an ASP.NET application using css isolation,
+    you don't have to make the urls in your files relative to "_content/$(PackageId).styles.css".
+    If the user chooses to override the base path explicitly, we place the bundle at the root of the defined base path, this allows Blazor WebAssembly applications to be hosted
+    on different paths other than the root path and for the bundle to behave as expected
+    -->
+    <_ScopedCssBundleBasePath>/</_ScopedCssBundleBasePath>
+    <_ScopedCssBundleBasePath Condition="'$(StaticWebAssetBasePath)' != '_content/$(PackageId)'">$(StaticWebAssetBasePath)</_ScopedCssBundleBasePath>
   </PropertyGroup>
+</Target>
+
+<Target Name="_ComputeCssBundles" DependsOnTargets="_PrepareForBundling;_CollectAllScopedCssAssets">
   <ItemGroup>
-  <!-- When bundling is enabled we want to remove all identified generated scoped css files from the list of static web assets so that
-       they are not copied to the output folder. -->
-    <StaticWebAsset Remove="@(_AllScopedCss)" Condition="'$(ScopedCssDisableBundling)' != 'true'" />
     <!-- https://github.com/dotnet/aspnetcore/issues/24245 -->
-    <StaticWebAsset Include="$(_ScopedCssOutputPath)" Condition="@(_AllScopedCss) != ''">
+    <_AppBundleStaticWebAsset Include="$(_ScopedCssOutputPath)" Condition="@(_AllScopedCss) != ''">
       <SourceType></SourceType>
       <SourceId>$(PackageId)</SourceId>
-      <ContentRoot>$(_ScopedCssIntermediatePath)</ContentRoot>
-      <BasePath>$(StaticWebAssetBasePath)</BasePath>
-      <RelativePath>_framework/scoped.styles.css</RelativePath>
-    </StaticWebAsset>
-    <_ExternalStaticWebAsset Include="$(_ScopedCssOutputPath)" Condition="@(_AllScopedCss) != ''">
-      <SourceType>generated</SourceType>
+      <ContentRoot>$(_ScopedCssBundleContentRoot)</ContentRoot>
+      <BasePath>$(_ScopedCssBundleBasePath)</BasePath>
+      <RelativePath>$(PackageId).styles.css</RelativePath>
+    </_AppBundleStaticWebAsset>
+
+    <!-- We include the project bundle as an SWA too so that other targets can be aware of the file and take it into account when making decissions.
+        The application bundle is the only one that gets a special treatment and is allowed to be on the "/" by default when the SWA default path hasn't
+        changed.
+    -->
+    <_ProjectBundleStaticWebAsset Include="$(_ScopedCssProjectOutputPath)" Condition="@(_ScopedCss) != ''">
+      <SourceType></SourceType>
       <SourceId>$(PackageId)</SourceId>
-      <ContentRoot>$(_ScopedCssIntermediatePath)</ContentRoot>
+      <ContentRoot>$(_ScopedCssProjectBundleContentRoot)</ContentRoot>
       <BasePath>$(StaticWebAssetBasePath)</BasePath>
-      <RelativePath>_framework/scoped.styles.css</RelativePath>
+      <RelativePath>$(PackageId).bundle.scp.css</RelativePath>
+    </_ProjectBundleStaticWebAsset>
+
+  </ItemGroup>
+</Target>
+
+<Target Name="_AddScopedCssBundles" Condition="'$(DisableScopedCssBundling)' != 'true'" DependsOnTargets="_ComputeCssBundles">
+  <ItemGroup>
+  <!-- When bundling is enabled we want to remove all identified generated scoped css files from the list of static web assets so that
+       they are not copied to the output folder. -->
+    <StaticWebAsset Remove="@(_DiscoveredScopedCssFiles)" />
+    <!-- https://github.com/dotnet/aspnetcore/issues/24245 -->
+    <StaticWebAsset Include="@(_AppBundleStaticWebAsset)" />
+    <!-- We include the project bundle as an SWA too so that other targets can be aware of the file and take it into account when making decissions.
+        The application bundle is the only one that gets a special treatment and is allowed to be on the "/" by default when the SWA default path hasn't
+        changed.
+    -->
+    <StaticWebAsset Include="@(_ProjectBundleStaticWebAsset)" />
+
+    <_ExternalStaticWebAsset Include="@(_AppBundleStaticWebAsset)">
+      <SourceType>generated</SourceType>
     </_ExternalStaticWebAsset>
+
   </ItemGroup>
+
 </Target>
 
 <!-- This target runs as part of ResolveStaticWebAssetInputs and collects all the generated scoped css files. When bundling is enabled
-     these files are removed from the list of static web assets by '_AddScopedCssBundle' -->
+     these files are removed from the list of static web assets by '_AddScopedCssBundles' -->
 
 <Target Name="_CollectAllScopedCssAssets">
   <ResolveAllScopedCssAssets StaticWebAssets="@(StaticWebAsset)">
-    <Output TaskParameter="ScopedCssAssets" ItemName="_AllScopedCss" />
+    <Output TaskParameter="ScopedCssAssets" ItemName="_DiscoveredScopedCssFiles" />
+    <Output TaskParameter="ScopedCssProjectBundles" ItemName="_ScopedCssProjectBundles" />
   </ResolveAllScopedCssAssets>
+  <ItemGroup>
+    <_AllScopedCss Include="@(_ScopedCssProjectBundles);@(_DiscoveredScopedCssFiles)" />
+  </ItemGroup>
+
+  <!-- https://github.com/dotnet/project-system/blob/master/docs/up-to-date-check.md -->
+  <ItemGroup>
+    <UpToDateCheckInput Include="@(_AllScopedCss)" />
+    <UpToDateCheckBuilt Include="$(_ScopedCssOutputFullPath)" />
+    <UpToDateCheckBuilt Include="$(_ScopedCssProjectOutputFullPath)" />
+  </ItemGroup>
+
 </Target>
 
 <!-- This target is only called as part of GetCurrentProjectStaticWebAssets which is only invoked on referenced projects to get the list
      of their assets. We return the list of css outputs we will produce and let the main app do the final bundling. -->
 
-<Target Name="_AddGeneratedScopedCssFiles" DependsOnTargets="_ResolveScopedCssOutputs">
-  <PropertyGroup>
-    <StaticWebAssetBasePath Condition="$(StaticWebAssetBasePath) == ''">_content/$(PackageId)</StaticWebAssetBasePath>
-  </PropertyGroup>
+<Target Name="_AddGeneratedScopedCssFiles" DependsOnTargets="_ResolveScopedCssOutputs;ResolveStaticWebAssetsConfiguration">
+  <!-- We do this in two steps because we will be modifying the list of static web assets if we bundle the files and these assets need to be
+  available when called from GetCurrentProjectStaticWebAssets -->
   <ItemGroup>
-    <StaticWebAsset Include="%(_ScopedCss.OutputFile)" Condition="@(_ScopedCss) != ''">
+    <_ScopedCssStaticWebAsset Include="%(_ScopedCss.OutputFile)" Condition="@(_ScopedCss) != ''">
       <SourceType></SourceType>
       <SourceId>$(PackageId)</SourceId>
       <ContentRoot>$(IntermediateOutputPath)scopedcss\</ContentRoot>
       <BasePath>$(StaticWebAssetBasePath)</BasePath>
       <RelativePath>$([MSBuild]::MakeRelative('$(_ScopedCssIntermediatePath)','%(_ScopedCss.OutputFile)'))</RelativePath>
-    </StaticWebAsset>
+    </_ScopedCssStaticWebAsset>
+    <StaticWebAsset Include="@(_ScopedCssStaticWebAsset)" />
   </ItemGroup>
 </Target>
 
-<Target Name="IncludeScopedCssBundle" Condition="'$(ScopedCssDisableBundling)' != 'true'" DependsOnTargets="_CollectAllScopedCssAssets;_AddGeneratedScopedCssFiles">
-  <PropertyGroup>
-    <_ScopedCssOutputPath>$(_ScopedCssIntermediatePath)_framework\scoped.styles.css</_ScopedCssOutputPath>
-    <_ScopedCssOutputFullPath>$([System.IO.Path]::Combine('$(MSBuildProjectFileDirectory)', '$(_ScopedCssIntermediatePath)_framework\scoped.styles.css'))</_ScopedCssOutputFullPath>
-  </PropertyGroup>
+<Target Name="_AddGeneratedScopedCssFilesForReference" Condition="'$(DisableScopedCssBundling)' != 'true'" DependsOnTargets="_PrepareForBundling;_AddGeneratedScopedCssFiles">
   <ItemGroup>
-  <!-- When bundling is enabled we want to remove all identified generated scoped css files from the list of static web assets so that
-       they are not copied to the output folder. -->
-    <StaticWebAsset Remove="@(_AllScopedCss)" Condition="'$(ScopedCssDisableBundling)' != 'true'" />
-    <!-- https://github.com/dotnet/aspnetcore/issues/24245 -->
-    <StaticWebAsset Include="$(_ScopedCssOutputPath)" Condition="@(_AllScopedCss) != ''">
-      <SourceType></SourceType>
-      <SourceId>$(PackageId)</SourceId>
-      <ContentRoot>$(_ScopedCssIntermediatePath)</ContentRoot>
-      <BasePath>$(StaticWebAssetBasePath)</BasePath>
-      <RelativePath>_framework/scoped.styles.css</RelativePath>
-    </StaticWebAsset>
-    <_ExternalStaticWebAsset Include="$(_ScopedCssOutputPath)" Condition="@(_AllScopedCss) != ''">
-      <SourceType>generated</SourceType>
-      <SourceId>$(PackageId)</SourceId>
-      <ContentRoot>$(_ScopedCssIntermediatePath)</ContentRoot>
-      <BasePath>$(StaticWebAssetBasePath)</BasePath>
-      <RelativePath>_framework/scoped.styles.css</RelativePath>
-    </_ExternalStaticWebAsset>
+    <StaticWebAsset Remove="@(_ScopedCssStaticWebAsset)" />
+    <Staticwebasset Remove="$(_ScopedCssOutputPath)" />
   </ItemGroup>
 </Target>
 
-<Target Name="BundleScopedCssFiles" Condition="'$(ScopedCssDisableBundling)' != 'true' and '@(_AllScopedCss)' != ''" BeforeTargets="GetCopyToOutputDirectoryItems;_StaticWebAssetsComputeFilesToPublish" DependsOnTargets="_GenerateScopedCssFiles">
+<Target Name="BundleScopedCssFiles" Condition="'$(DisableScopedCssBundling)' != 'true'">
   <!-- Incrementalism is built into the task itself. -->
-  <ConcatenateCssFiles FilesToProcess="@(_AllScopedCss)" OutputFile="$(_ScopedCssOutputPath)" />
-</Target>
+  <ItemGroup>
+    <_CurrentProjectDiscoveredScopedCssFiles Include="@(_DiscoveredScopedCssFiles)" Condition="'%(SourceType)' == ''" />
+  </ItemGroup>
+  <!-- This is the bundle for the app, we will always generate it when there are scoped css files for the current project or
+       we detected existing bundles available. If some other project/package didn't bundle their assets, we will not be including
+       them in this bundle. -->
+  <ConcatenateCssFiles
+    Condition="'@(_ScopedCssProjectBundles)' != '' or '@(_ScopedCss)' != ''"
+    ScopedCssFiles="@(_CurrentProjectDiscoveredScopedCssFiles)"
+    ProjectBundles="@(_ScopedCssProjectBundles)"
+    ScopedCssBundleBasePath="$(_ScopedCssBundleBasePath)"
+    OutputFile="$(_ScopedCssOutputPath)" />
+  <!-- This is the project bundle, we will only generate it when there are scoped files defined in the project. This bundle will be used
+       when the project is referenced from another project or packed as a package (Razor Class Library). If some other project/package
+       didn't bundle their assets, we will not be including them in this bundle. -->
+  <ConcatenateCssFiles
+    Condition="'@(_ScopedCss)' != ''"
+    ScopedCssFiles="@(_CurrentProjectDiscoveredScopedCssFiles)"
+    ProjectBundles="@()"
+    ScopedCssBundleBasePath="$(_ScopedCssBundleBasePath)"
+    OutputFile="$(_ScopedCssProjectOutputPath)" />
 
-<Target Name="_RemoveBundleFromOutput" BeforeTargets="GetCopyToOutputDirectoryItems" DependsOnTargets="BundleScopedCssFiles">
   <ItemGroup>
-    <StaticWebAsset Remove="$(_ScopedCssOutputFullPath)" />
-    <StaticWebAsset Include="@(_AllScopedCss)" Condition="'%(SourceType)' == ''" />
+    <FileWrites Include="$(_ScopedCssOutputPath)" />
+    <FileWrites Condition="'@(_ScopedCss)' != ''" Include="$(_ScopedCssProjectOutputPath)" />
   </ItemGroup>
 </Target>
 
-<Target Name="_AddBundleToStaticWebAssetsPublishedFile" Condition="'$(ScopedCssDisableBundling)' != 'true' and '@(_AllScopedCss)' != ''" BeforeTargets="_StaticWebAssetsComputeFilesToPublish" DependsOnTargets="_CollectAllScopedCssAssets">
+<Target Name="_AddAppBundleToStaticWebAssetsPublishedFiles" BeforeTargets="_StaticWebAssetsComputeFilesToPublish" Condition="'$(DisableScopedCssBundling)' != 'true' and '@(_AllScopedCss)' != ''" DependsOnTargets="_CollectAllScopedCssAssets">
   <ItemGroup>
     <!-- Manually add the file to the publish flow. See https://github.com/dotnet/aspnetcore/issues/24245 -->
     <_ExternalPublishStaticWebAsset Include="$(_ScopedCssOutputFullPath)" ExcludeFromSingleFile="true">
       <SourceType>generated</SourceType>
       <SourceId>$(PackageId)</SourceId>
-      <ContentRoot>$(_ScopedCssIntermediatePath)</ContentRoot>
-      <BasePath>$(StaticWebAssetBasePath)</BasePath>
+      <ContentRoot>$(_ScopedCssBundleContentRoot)</ContentRoot>
+      <BasePath>$(_ScopedCssBundleBasePath)</BasePath>
       <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
-      <RelativePath>$([MSBuild]::MakeRelative('$(MSBuildProjectDirectory)',$([MSBuild]::NormalizePath('wwwroot/$(StaticWebAssetBasePath)/_framework/scoped.styles.css'))))</RelativePath>
+      <RelativePath>$([MSBuild]::MakeRelative('$(MSBuildProjectDirectory)',$([MSBuild]::NormalizePath('wwwroot/$(_ScopedCssBundleBasePath)/$(PackageId).styles.css'))))</RelativePath>
     </_ExternalPublishStaticWebAsset>
   </ItemGroup>
 </Target>
 
-<Target Name="_AdjustIsolatedCssPackageContents" BeforeTargets="_RemoveWebRootContentFromPackaging;_CreateStaticWebAssetsCustomPropsCacheFile" DependsOnTargets="_CollectAllScopedCssAssets">
+<Target Name="_AddScopedCssFilesToStaticWebAssetsPublishedFiles" BeforeTargets="_StaticWebAssetsComputeFilesToPublish" Condition="'$(DisableScopedCssBundling)' == 'true' and '@(_ScopedCss)' != ''" DependsOnTargets="_CollectAllScopedCssAssets">
   <ItemGroup>
-    <_CurrentProjectStaticWebAsset Remove="$(_ScopedCssOutputFullPath)" />
-    <StaticWebAsset Remove="$(_ScopedCssOutputFullPath)" />
-    <StaticWebAsset Include="@(_AllScopedCss)" Condition="'%(SourceType)' == ''" />
+    <!-- Manually add the scoped css files to the publish flow. See https://github.com/dotnet/aspnetcore/issues/24245 -->
+    <_ExternalPublishStaticWebAsset Include="@(_ScopedCssStaticWebAsset)" ExcludeFromSingleFile="true">
+      <SourceType>generated</SourceType>
+      <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
+      <RelativePath>$([MSBuild]::MakeRelative('$(MSBuildProjectDirectory)','$([MSBuild]::NormalizePath('wwwroot\%(BasePath)\%(RelativePath)'))'))</RelativePath>
+    </_ExternalPublishStaticWebAsset>
   </ItemGroup>
 </Target>
 

+ 34 - 14
src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Microsoft.NET.Sdk.Razor.StaticWebAssets.targets

@@ -50,8 +50,8 @@ Copyright (c) .NET Foundation. All rights reserved.
   <PropertyGroup>
     <GenerateStaticWebAssetsManifestDependsOn>
       ResolveStaticWebAssetsInputs;
-      _CreateStaticWebAssetsInputsCacheFile;
       $(GenerateStaticWebAssetsManifestDependsOn)
+      _CreateStaticWebAssetsInputsCacheFile;
     </GenerateStaticWebAssetsManifestDependsOn>
 
     <GetCurrentProjectStaticWebAssetsDependsOn>
@@ -59,11 +59,21 @@ Copyright (c) .NET Foundation. All rights reserved.
       $(GetCurrentProjectStaticWebAssetsDependsOn)
     </GetCurrentProjectStaticWebAssetsDependsOn>
 
+    <PrepareForRunDependsOn>
+      GenerateStaticWebAssetsManifest;
+      $(PrepareForRunDependsOn);
+    </PrepareForRunDependsOn>
+
     <GetCopyToOutputDirectoryItemsDependsOn>
+      _IncludeGeneratedStaticWebAssetsManifest;
       $(GetCopyToOutputDirectoryItemsDependsOn);
-      GenerateStaticWebAssetsManifest;
     </GetCopyToOutputDirectoryItemsDependsOn>
 
+    <ResolveCurrentProjectStaticWebAssetsInputsDependsOn>
+      ResolveStaticWebAssetsConfiguration;
+      $(ResolveCurrentProjectStaticWebAssetsInputsDependsOn)
+    </ResolveCurrentProjectStaticWebAssetsInputsDependsOn>
+
     <ResolveStaticWebAssetsInputsDependsOn>
       ResolveCurrentProjectStaticWebAssetsInputs;
       ResolveReferencedProjectsStaticWebAssets;
@@ -76,8 +86,9 @@ Copyright (c) .NET Foundation. All rights reserved.
     </ResolveReferencedProjectsStaticWebAssetsDependsOn>
 
     <GenerateStaticWebAssetsPackTargetsDependsOn>
+      ResolveStaticWebAssetsInputs;
+      $(GenerateStaticWebAssetsPackTargetsDependsOn);
       _CreateStaticWebAssetsCustomPropsCacheFile;
-      $(GenerateStaticWebAssetsPackTargetsDependsOn)
     </GenerateStaticWebAssetsPackTargetsDependsOn>
 
     <TargetsForTfmSpecificContentInPackage>
@@ -124,6 +135,12 @@ Copyright (c) .NET Foundation. All rights reserved.
       Condition="!Exists('$(_StaticWebAssetsIntermediateOutputPath)')" />
   </Target>
 
+  <Target Name="ResolveStaticWebAssetsConfiguration">
+    <PropertyGroup>
+      <StaticWebAssetBasePath Condition="'$(StaticWebAssetBasePath)' == ''">_content/$(PackageId)</StaticWebAssetBasePath>
+    </PropertyGroup>
+  </Target>
+
   <!--
     ============================================================
                                         Static web assets development manifest generation targets
@@ -196,22 +213,25 @@ Copyright (c) .NET Foundation. All rights reserved.
       ContentRootDefinitions="@(_ExternalStaticWebAsset)"
       TargetManifestPath="$(_GeneratedStaticWebAssetsDevelopmentManifest)" />
 
+    <ItemGroup>
+      <FileWrites Include="$(_GeneratedStaticWebAssetsDevelopmentManifest)" />
+    </ItemGroup>
+
+  </Target>
+
+  <Target Name="_IncludeGeneratedStaticWebAssetsManifest">
       <!-- This is the list of inputs that will be used for generating the manifest used during development. -->
     <ItemGroup>
       <ContentWithTargetPath
         Include="$(_GeneratedStaticWebAssetsDevelopmentManifest)"
         Condition="'@(_ExternalStaticWebAsset->Count())' != '0'">
-
+        <Pack>false</Pack>
         <TargetPath>$(TargetName).StaticWebAssets.xml</TargetPath>
         <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
         <CopyToPublishDirectory>Never</CopyToPublishDirectory>
       </ContentWithTargetPath>
     </ItemGroup>
 
-    <ItemGroup>
-      <FileWrites Include="$(_GeneratedStaticWebAssetsDevelopmentManifest)" />
-    </ItemGroup>
-
   </Target>
 
   <!--
@@ -275,11 +295,6 @@ Copyright (c) .NET Foundation. All rights reserved.
   </Target>
 
   <Target Name="ResolveCurrentProjectStaticWebAssetsInputs" DependsOnTargets="$(ResolveCurrentProjectStaticWebAssetsInputsDependsOn)">
-
-      <PropertyGroup>
-        <StaticWebAssetBasePath Condition="$(StaticWebAssetBasePath) == ''">_content/$(PackageId)</StaticWebAssetBasePath>
-      </PropertyGroup>
-
       <ItemGroup>
 
       <_ThisProjectStaticWebAsset
@@ -356,7 +371,7 @@ Copyright (c) .NET Foundation. All rights reserved.
 
   <Target
     Name="_CreateStaticWebAssetsCustomPropsCacheFile"
-    DependsOnTargets="ResolveStaticWebAssetsInputs;_PrepareForStaticWebAssets">
+    DependsOnTargets="_PrepareForStaticWebAssets">
 
     <ItemGroup>
       <!-- This is the list of inputs that will be used for generating the props file that will be packed with
@@ -409,6 +424,11 @@ Copyright (c) .NET Foundation. All rights reserved.
       <_CurrentProjectHasStaticWebAssets Condition="'@(_CurrentProjectStaticWebAsset->Count())' != '0'">true</_CurrentProjectHasStaticWebAssets>
     </PropertyGroup>
 
+    <!-- Validate that there are no path conflicts between the assets we are about to pack. -->
+    <ValidateStaticWebAssetsUniquePaths Condition="'$(_CurrentProjectHasStaticWebAssets)' == 'true'"
+      StaticWebAssets="@(_CurrentProjectStaticWebAsset)"
+      WebRootFiles="" />
+
     <!-- Generates a props file that goes in build\Microsoft.AspNetCore.StaticWebAssets.props
          and that describes the static web assets for the package.
      -->

+ 3 - 3
src/Razor/Microsoft.NET.Sdk.Razor/src/build/netstandard2.0/Sdk.Razor.CurrentVersion.targets

@@ -67,7 +67,7 @@ Copyright (c) .NET Foundation. All rights reserved.
     <StaticWebAssetsEnabled Condition="'$(StaticWebAssetsEnabled)' == ''">$(_Targeting30OrNewerRazorLangVersion)</StaticWebAssetsEnabled>
 
     <!-- Controls whether or not the scoped css feature is enabled. By default is enabled for net5.0 applications and RazorLangVersion 5 or above -->
-    <ScopedCssEnabled Condition="'$(ScopedCssEnabled)' == ''">$(_Targeting30OrNewerRazorLangVersion)</ScopedCssEnabled>
+    <ScopedCssEnabled Condition="'$(ScopedCssEnabled)' == '' and '$(StaticWebAssetsEnabled)' == 'true'">$(_Targeting30OrNewerRazorLangVersion)</ScopedCssEnabled>
   </PropertyGroup>
 
 
@@ -352,10 +352,10 @@ Copyright (c) .NET Foundation. All rights reserved.
 
   <Import Project="Microsoft.NET.Sdk.Razor.Component.targets" Condition="'$(_Targeting30OrNewerRazorLangVersion)' == 'true'" />
 
-  <Import Project="Microsoft.NET.Sdk.Razor.StaticWebAssets.targets" Condition="'$(StaticWebAssetsEnabled)' == 'true'" />
-
   <Import Project="Microsoft.NET.Sdk.Razor.ScopedCss.targets" Condition="'$(ScopedCssEnabled)' == 'true'" />
 
+  <Import Project="Microsoft.NET.Sdk.Razor.StaticWebAssets.targets" Condition="'$(StaticWebAssetsEnabled)' == 'true'" />
+
   <Import Project="Microsoft.NET.Sdk.Razor.GenerateAssemblyInfo.targets" />
 
   <Import Project="Microsoft.NET.Sdk.Razor.MvcApplicationPartsDiscovery.targets" Condition="'$(_TargetingNETCoreApp30OrLater)' == 'true'" />

+ 160 - 0
src/Razor/Microsoft.NET.Sdk.Razor/test/ApplyCssScopesTest.cs

@@ -0,0 +1,160 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using Microsoft.AspNetCore.Razor.Tasks;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+using Moq;
+using Xunit;
+
+namespace Microsoft.NET.Sdk.Razor.Test
+{
+    public class ApplyAllCssScopesTest
+    {
+        [Fact]
+        public void ApplyAllCssScopes_AppliesScopesToRazorFiles()
+        {
+            // Arrange
+            var taskInstance = new ApplyCssScopes()
+            {
+                RazorComponents = new[]
+                {
+                    new TaskItem("TestFiles/Pages/Counter.razor"),
+                    new TaskItem("TestFiles/Pages/Index.razor"),
+                },
+                ScopedCss = new[]
+                {
+                    new TaskItem("TestFiles/Pages/Index.razor.css", new Dictionary<string, string> { ["CssScope"] = "index-scope" }),
+                    new TaskItem("TestFiles/Pages/Counter.razor.css", new Dictionary<string, string> { ["CssScope"] = "counter-scope" }),
+                }
+            };
+
+            // Act
+            var result = taskInstance.Execute();
+
+            // Assert
+            Assert.True(result);
+            Assert.Equal(2, taskInstance.RazorComponentsWithScopes.Length);
+            Assert.Single(taskInstance.RazorComponentsWithScopes, rcws => rcws.ItemSpec == "TestFiles/Pages/Index.razor" && rcws.GetMetadata("CssScope") == "index-scope");
+            Assert.Single(taskInstance.RazorComponentsWithScopes, rcws => rcws.ItemSpec == "TestFiles/Pages/Counter.razor" && rcws.GetMetadata("CssScope") == "counter-scope");
+        }
+
+        [Fact]
+        public void DoesNotApplyCssScopes_ToRazorComponentsWithoutAssociatedFiles()
+        {
+            // Arrange
+            var taskInstance = new ApplyCssScopes()
+            {
+                RazorComponents = new[]
+                {
+                    new TaskItem("TestFiles/Pages/Counter.razor"),
+                    new TaskItem("TestFiles/Pages/Index.razor"),
+                    new TaskItem("TestFiles/Pages/FetchData.razor"),
+                },
+                ScopedCss = new[]
+                {
+                    new TaskItem("TestFiles/Pages/Index.razor.css", new Dictionary<string, string> { ["CssScope"] = "index-scope" }),
+                    new TaskItem("TestFiles/Pages/Counter.razor.css", new Dictionary<string, string> { ["CssScope"] = "counter-scope" })
+                }
+            };
+
+            // Act
+            var result = taskInstance.Execute();
+
+            // Assert
+            Assert.True(result);
+            Assert.DoesNotContain(taskInstance.RazorComponentsWithScopes, rcws => rcws.ItemSpec == "TestFiles/Pages/Fetchdata.razor");
+        }
+
+        [Fact]
+        public void ApplyAllCssScopes_FailsWhenTheScopedCss_DoesNotMatchTheRazorComponent()
+        {
+            // Arrange
+            var taskInstance = new ApplyCssScopes()
+            {
+                RazorComponents = new[]
+                {
+                    new TaskItem("TestFiles/Pages/Counter.razor"),
+                    new TaskItem("TestFiles/Pages/Index.razor"),
+                },
+                ScopedCss = new[]
+                {
+                    new TaskItem("TestFiles/Pages/Index.razor.css", new Dictionary<string, string> { ["CssScope"] = "index-scope" }),
+                    new TaskItem("TestFiles/Pages/Counter.razor.css", new Dictionary<string, string> { ["CssScope"] = "counter-scope" }),
+                    new TaskItem("TestFiles/Pages/Profile.razor.css", new Dictionary<string, string> { ["CssScope"] = "profile-scope" }),
+                }
+            };
+
+            taskInstance.BuildEngine = Mock.Of<IBuildEngine>();
+
+            // Act
+            var result = taskInstance.Execute();
+
+            // Assert
+            Assert.False(result);
+        }
+
+        [Fact]
+        public void ScopedCssCanDefineAssociatedRazorComponentFile()
+        {
+            // Arrange
+            var taskInstance = new ApplyCssScopes()
+            {
+                RazorComponents = new[]
+                {
+                    new TaskItem("TestFiles/Pages/FetchData.razor")
+                },
+                ScopedCss = new[]
+                {
+                    new TaskItem("TestFiles/Pages/Profile.razor.css", new Dictionary<string, string>
+                    {
+                        ["CssScope"] = "fetchdata-scope",
+                        ["RazorComponent"] = "TestFiles/Pages/FetchData.razor"
+                    })
+                }
+            };
+
+            // Act
+            var result = taskInstance.Execute();
+
+            // Assert
+            Assert.True(result);
+            var rcws = Assert.Single(taskInstance.RazorComponentsWithScopes);
+            Assert.Equal("TestFiles/Pages/FetchData.razor", rcws.ItemSpec);
+            Assert.Equal("fetchdata-scope", rcws.GetMetadata("CssScope"));
+        }
+
+        [Fact]
+        public void ApplyAllCssScopes_FailsWhenMultipleScopedCssFiles_MatchTheSameRazorComponent()
+        {
+            // Arrange
+            var taskInstance = new ApplyCssScopes()
+            {
+                RazorComponents = new[]
+                {
+                    new TaskItem("TestFiles/Pages/Counter.razor"),
+                    new TaskItem("TestFiles/Pages/Index.razor"),
+                },
+                ScopedCss = new[]
+                {
+                    new TaskItem("TestFiles/Pages/Index.razor.css", new Dictionary<string, string> { ["CssScope"] = "index-scope" }),
+                    new TaskItem("TestFiles/Pages/Counter.razor.css", new Dictionary<string, string> { ["CssScope"] = "counter-scope" }),
+                    new TaskItem("TestFiles/Pages/Profile.razor.css", new Dictionary<string, string>
+                    {
+                        ["CssScope"] = "conflict-scope",
+                        ["RazorComponent"] = "TestFiles/Pages/Index.razor"
+                    }),
+                }
+            };
+
+            taskInstance.BuildEngine = Mock.Of<IBuildEngine>();
+
+            // Act
+            var result = taskInstance.Execute();
+
+            // Assert
+            Assert.False(result);
+        }
+    }
+}

+ 139 - 0
src/Razor/Microsoft.NET.Sdk.Razor/test/ComputeCssScopesTests.cs

@@ -0,0 +1,139 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Build.Utilities;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Razor.Tasks
+{
+    public class ComputeCssScopesTests
+    {
+        [Fact]
+        public void ComputesScopes_ComputesUniqueScopes_ForCssFiles()
+        {
+            // Arrange
+            var taskInstance = new ComputeCssScope()
+            {
+                ScopedCssInput = new[]
+                {
+                    new TaskItem("TestFiles/Pages/Counter.razor.css"),
+                    new TaskItem("TestFiles/Pages/Index.razor.css"),
+                    new TaskItem("TestFiles/Pages/Profile.razor.css"),
+                },
+                TargetName = "Test"
+            };
+
+            // Act
+            var result = taskInstance.Execute();
+
+            // Assert
+            Assert.True(result);
+            Assert.Equal(3, taskInstance.ScopedCss.Length);
+            Assert.All(taskInstance.ScopedCss, item =>
+            {
+                var scope = item.GetMetadata("CssScope");
+                Assert.NotEmpty(scope);
+                Assert.Matches("b-[a-z0-9]+", scope);
+            });
+
+            Assert.Equal(3, new HashSet<string>(taskInstance.ScopedCss.Select(s => s.GetMetadata("CssScope"))).Count);
+        }
+
+        [Fact]
+        public void ComputesScopes_ScopeVariesByTargetName()
+        {
+            // Arrange
+            var taskInstance = new ComputeCssScope()
+            {
+                ScopedCssInput = new[]
+                {
+                    new TaskItem("TestFiles/Pages/Counter.razor.css"),
+                    new TaskItem("TestFiles/Pages/Index.razor.css"),
+                    new TaskItem("TestFiles/Pages/Profile.razor.css"),
+                },
+                TargetName = "Test"
+            };
+
+            // Act
+            taskInstance.Execute();
+            var existing = taskInstance.ScopedCss.Select(s => s.GetMetadata("CssScope")).ToArray();
+
+            taskInstance.TargetName = "AnotherLibrary";
+            var result = taskInstance.Execute();
+
+            // Assert
+            Assert.All(taskInstance.ScopedCss, newScoped => Assert.DoesNotContain(newScoped.GetMetadata("ScopedCss"), existing));
+        }
+
+        [Fact]
+        public void ComputesScopes_IsDeterministic()
+        {
+            // Arrange
+            var taskInstance = new ComputeCssScope()
+            {
+                ScopedCssInput = new[]
+                {
+                    new TaskItem("TestFiles/Pages/Counter.razor.css"),
+                    new TaskItem("TestFiles/Pages/Index.razor.css"),
+                    new TaskItem("TestFiles/Pages/Profile.razor.css"),
+                },
+                TargetName = "Test"
+            };
+
+            // Act
+            taskInstance.Execute();
+            var existing = taskInstance.ScopedCss.Select(s => s.GetMetadata("CssScope")).OrderBy(id => id).ToArray();
+
+            var result = taskInstance.Execute();
+
+            // Assert
+            Assert.Equal(existing, taskInstance.ScopedCss.Select(newScoped => newScoped.GetMetadata("CssScope")).OrderBy(id => id).ToArray());
+        }
+
+        [Fact]
+        public void ComputesScopes_VariesByPath()
+        {
+            // Arrange
+            var taskInstance = new ComputeCssScope()
+            {
+                ScopedCssInput = new[]
+                {
+                    new TaskItem("TestFiles/Pages/Index.razor.css"),
+                    new TaskItem("TestFiles/Index.razor.css"),
+                },
+                TargetName = "Test"
+            };
+
+            // Act
+            var result = taskInstance.Execute();
+
+            // Assert
+            Assert.True(result);
+            Assert.Equal(2, taskInstance.ScopedCss.Length);
+            Assert.NotEqual(taskInstance.ScopedCss[0].GetMetadata("CssScope"), taskInstance.ScopedCss[1].GetMetadata("CssScope"));
+        }
+
+        [Fact]
+        public void ComputesScopes_PreservesUserDefinedScopes()
+        {
+            // Arrange
+            var taskInstance = new ComputeCssScope()
+            {
+                ScopedCssInput = new[]
+                {
+                    new TaskItem("TestFiles/Pages/Index.razor.css", new Dictionary<string,string>{ ["CssScope"] = "b-predefined" }),                },
+                TargetName = "Test"
+            };
+
+            // Act
+            var result = taskInstance.Execute();
+
+            // Assert
+            Assert.True(result);
+            var scopedCss = Assert.Single(taskInstance.ScopedCss);
+            Assert.Equal("b-predefined", scopedCss.GetMetadata("CssScope"));
+        }
+    }
+}

+ 394 - 0
src/Razor/Microsoft.NET.Sdk.Razor/test/ConcatenateFilesTest.cs

@@ -0,0 +1,394 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Razor.Tasks
+{
+    public class ConcatenateCssFilesTest
+    {
+        private static readonly string BundleContent =
+@"/* _content/Test/TestFiles/Generated/Counter.razor.rz.scp.css */
+.counter {
+    font-size: 2rem;
+}
+/* _content/Test/TestFiles/Generated/Index.razor.rz.scp.css */
+.index {
+    font-weight: bold;
+}
+";
+
+        private static readonly string BundleWithImportsContent =
+@"@import '_content/Test/TestFiles/Generated/lib.bundle.scp.css';
+@import 'TestFiles/Generated/package.bundle.scp.css';
+
+/* _content/Test/TestFiles/Generated/Counter.razor.rz.scp.css */
+.counter {
+    font-size: 2rem;
+}
+/* _content/Test/TestFiles/Generated/Index.razor.rz.scp.css */
+.index {
+    font-weight: bold;
+}
+";
+
+        private static readonly string UpdatedBundleContent =
+@"/* _content/Test/TestFiles/Generated/Counter.razor.rz.scp.css */
+.counter {
+    font-size: 2rem;
+}
+/* _content/Test/TestFiles/Generated/FetchData.razor.rz.scp.css */
+.fetchData {
+    font-family: Helvetica;
+}
+/* _content/Test/TestFiles/Generated/Index.razor.rz.scp.css */
+.index {
+    font-weight: bold;
+}
+";
+
+        [Fact]
+        public void BundlesScopedCssFiles_ProducesEmpyBundleIfNoFilesAvailable()
+        {
+            // Arrange
+            var expectedFile = Path.Combine(Directory.GetCurrentDirectory(), $"{Guid.NewGuid():N}.css");
+            var taskInstance = new ConcatenateCssFiles()
+            {
+                ScopedCssFiles = Array.Empty<ITaskItem>(),
+                ProjectBundles = Array.Empty<ITaskItem>(),
+                OutputFile = expectedFile
+            };
+
+            // Act
+            var result = taskInstance.Execute();
+
+            // Assert
+            Assert.True(result);
+            Assert.True(File.Exists(expectedFile));
+
+            Assert.Empty(File.ReadAllText(expectedFile));
+        }
+
+        [Fact]
+        public void BundlesScopedCssFiles_ProducesBundle()
+        {
+            // Arrange
+            var expectedFile = Path.Combine(Directory.GetCurrentDirectory(), $"{Guid.NewGuid():N}.css");
+            var taskInstance = new ConcatenateCssFiles()
+            {
+                ScopedCssFiles = new[]
+                {
+                    new TaskItem(
+                        "TestFiles/Generated/Counter.razor.rz.scp.css",
+                        new Dictionary<string,string>
+                        {
+                            ["BasePath"] = "_content/Test/",
+                            ["RelativePath"] = "TestFiles/Generated/Counter.razor.rz.scp.css",
+                        }),
+                    new TaskItem(
+                        "TestFiles/Generated/Index.razor.rz.scp.css",
+                        new Dictionary<string,string>
+                        {
+                            ["BasePath"] = "_content/Test/",
+                            ["RelativePath"] = "TestFiles/Generated/Index.razor.rz.scp.css",
+                        }),
+                },
+                ProjectBundles = Array.Empty<ITaskItem>(),
+                OutputFile = expectedFile
+            };
+
+            // Act
+            var result = taskInstance.Execute();
+
+            // Assert
+            Assert.True(result);
+            Assert.True(File.Exists(expectedFile));
+
+            var actualContents = File.ReadAllText(expectedFile);
+            Assert.Equal(BundleContent, actualContents, ignoreLineEndingDifferences: true);
+        }
+
+        [Fact]
+        public void BundlesScopedCssFiles_IncludesOtherBundles()
+        {
+            // Arrange
+            var expectedFile = Path.Combine(Directory.GetCurrentDirectory(), $"{Guid.NewGuid():N}.css");
+            var taskInstance = new ConcatenateCssFiles()
+            {
+                ScopedCssFiles = new[]
+                {
+                    new TaskItem(
+                        "TestFiles/Generated/Counter.razor.rz.scp.css",
+                        new Dictionary<string, string>
+                        {
+                            ["BasePath"] = "_content/Test/",
+                            ["RelativePath"] = "TestFiles/Generated/Counter.razor.rz.scp.css",
+                        }),
+                    new TaskItem(
+                        "TestFiles/Generated/Index.razor.rz.scp.css",
+                        new Dictionary<string, string>
+                        {
+                            ["BasePath"] = "_content/Test/",
+                            ["RelativePath"] = "TestFiles/Generated/Index.razor.rz.scp.css",
+                        }),
+                },
+                ProjectBundles = new[]
+                {
+                    new TaskItem(
+                        "TestFiles/Generated/lib.bundle.scp.css",
+                        new Dictionary<string, string>
+                        {
+                            ["BasePath"] = "_content/Test/",
+                            ["RelativePath"] = "TestFiles/Generated/lib.bundle.scp.css",
+                        }),
+                    new TaskItem(
+                        "TestFiles/Generated/package.bundle.scp.css",
+                        new Dictionary<string, string>
+                        {
+                            ["BasePath"] = "",
+                            ["RelativePath"] = "TestFiles/Generated/package.bundle.scp.css",
+                        }),
+                },
+                ScopedCssBundleBasePath = "/",
+                OutputFile = expectedFile
+            };
+
+            // Act
+            var result = taskInstance.Execute();
+
+            // Assert
+            Assert.True(result);
+            Assert.True(File.Exists(expectedFile));
+
+            var actualContents = File.ReadAllText(expectedFile);
+            Assert.Equal(BundleWithImportsContent, actualContents, ignoreLineEndingDifferences: true);
+        }
+
+        [Theory]
+        [InlineData("", "", "TestFiles/Generated/lib.bundle.scp.css")]
+        [InlineData("/", "/", "TestFiles/Generated/lib.bundle.scp.css")]
+        [InlineData("app", "_content", "../_content/TestFiles/Generated/lib.bundle.scp.css")]
+        [InlineData("app", "/_content", "../_content/TestFiles/Generated/lib.bundle.scp.css")]
+        [InlineData("app", "/_content/", "../_content/TestFiles/Generated/lib.bundle.scp.css")]
+        [InlineData("/app", "_content", "../_content/TestFiles/Generated/lib.bundle.scp.css")]
+        [InlineData("/app", "/_content", "../_content/TestFiles/Generated/lib.bundle.scp.css")]
+        [InlineData("/app", "/_content/", "../_content/TestFiles/Generated/lib.bundle.scp.css")]
+        [InlineData("app/", "_content", "../_content/TestFiles/Generated/lib.bundle.scp.css")]
+        [InlineData("app/", "/_content", "../_content/TestFiles/Generated/lib.bundle.scp.css")]
+        [InlineData("app/", "/_content/", "../_content/TestFiles/Generated/lib.bundle.scp.css")]
+        [InlineData("/company/app/", "_content", "../../_content/TestFiles/Generated/lib.bundle.scp.css")]
+        [InlineData("/company/app/", "/_content", "../../_content/TestFiles/Generated/lib.bundle.scp.css")]
+        [InlineData("/company/app/", "/_content/", "../../_content/TestFiles/Generated/lib.bundle.scp.css")]
+
+        public void BundlesScopedCssFiles_HandlesBasePathCombinationsCorrectly(string finalBasePath, string libraryBasePath, string expectedImport)
+        {
+            // Arrange
+            var expectedContent = BundleWithImportsContent
+                .Replace("_content/Test/TestFiles/Generated/lib.bundle.scp.css", expectedImport)
+                .Replace("@import 'TestFiles/Generated/package.bundle.scp.css';", "")
+                .Replace("\r\n", "\n")
+                .Replace("\n\n", "\n");
+
+            var expectedFile = Path.Combine(Directory.GetCurrentDirectory(), $"{Guid.NewGuid():N}.css");
+            var taskInstance = new ConcatenateCssFiles()
+            {
+                ScopedCssFiles = new[]
+                {
+                    new TaskItem(
+                        "TestFiles/Generated/Counter.razor.rz.scp.css",
+                        new Dictionary<string,string>
+                        {
+                            ["BasePath"] = "_content/Test/",
+                            ["RelativePath"] = "TestFiles/Generated/Counter.razor.rz.scp.css",
+                        }),
+                    new TaskItem(
+                        "TestFiles/Generated/Index.razor.rz.scp.css",
+                        new Dictionary<string,string>
+                        {
+                            ["BasePath"] = "_content/Test/",
+                            ["RelativePath"] = "TestFiles/Generated/Index.razor.rz.scp.css",
+                        })
+                },
+                ProjectBundles = new[]
+                {
+                    new TaskItem(
+                        "TestFiles/Generated/lib.bundle.scp.css",
+                        new Dictionary<string, string>
+                        {
+                            ["BasePath"] = libraryBasePath,
+                            ["RelativePath"] = "TestFiles/Generated/lib.bundle.scp.css",
+                        }),
+                },
+                ScopedCssBundleBasePath = finalBasePath,
+                OutputFile = expectedFile
+            };
+
+            // Act
+            var result = taskInstance.Execute();
+
+            // Assert
+            Assert.True(result);
+            Assert.True(File.Exists(expectedFile));
+
+            var actualContents = File.ReadAllText(expectedFile);
+            Assert.Equal(expectedContent, actualContents, ignoreLineEndingDifferences: true);
+        }
+
+        [Fact]
+        public void BundlesScopedCssFiles_BundlesFilesInOrder()
+        {
+            // Arrange
+            var expectedFile = Path.Combine(Directory.GetCurrentDirectory(), $"{Guid.NewGuid():N}.css");
+            var taskInstance = new ConcatenateCssFiles()
+            {
+                ScopedCssFiles = new[]
+                {
+                    new TaskItem(
+                        "TestFiles/Generated/Index.razor.rz.scp.css",
+                        new Dictionary<string,string>
+                        {
+                            ["BasePath"] = "_content/Test/",
+                            ["RelativePath"] = "TestFiles/Generated/Index.razor.rz.scp.css",
+                        }),
+                    new TaskItem(
+                        "TestFiles/Generated/Counter.razor.rz.scp.css",
+                        new Dictionary<string,string>
+                        {
+                            ["BasePath"] = "_content/Test/",
+                            ["RelativePath"] = "TestFiles/Generated/Counter.razor.rz.scp.css",
+                        }),
+                },
+                ProjectBundles = Array.Empty<ITaskItem>(),
+                OutputFile = expectedFile
+            };
+
+            // Act
+            var result = taskInstance.Execute();
+
+            // Assert
+            Assert.True(result);
+            Assert.True(File.Exists(expectedFile));
+
+            var actualContents = File.ReadAllText(expectedFile);
+            Assert.Equal(BundleContent, actualContents, ignoreLineEndingDifferences: true);
+        }
+
+        [Fact]
+        public void BundlesScopedCssFiles_DoesNotOverrideBundleForSameContents()
+        {
+            // Arrange
+            var expectedFile = Path.Combine(Directory.GetCurrentDirectory(), $"{Guid.NewGuid():N}.css");
+            var taskInstance = new ConcatenateCssFiles()
+            {
+                ScopedCssFiles = new[]
+                {
+                    new TaskItem(
+                        "TestFiles/Generated/Index.razor.rz.scp.css",
+                        new Dictionary<string,string>
+                        {
+                            ["BasePath"] = "_content/Test/",
+                            ["RelativePath"] = "TestFiles/Generated/Index.razor.rz.scp.css",
+                        }),
+                    new TaskItem(
+                        "TestFiles/Generated/Counter.razor.rz.scp.css",
+                        new Dictionary<string,string>
+                        {
+                            ["BasePath"] = "_content/Test/",
+                            ["RelativePath"] = "TestFiles/Generated/Counter.razor.rz.scp.css",
+                        }),
+                },
+                ProjectBundles = Array.Empty<ITaskItem>(),
+                OutputFile = expectedFile
+            };
+
+            // Act
+            var result = taskInstance.Execute();
+
+            var lastModified = File.GetLastWriteTimeUtc(expectedFile);
+
+            taskInstance.Execute();
+
+            // Assert
+            Assert.True(result);
+            Assert.True(File.Exists(expectedFile));
+            var actualContents = File.ReadAllText(expectedFile);
+            Assert.Equal(BundleContent, actualContents, ignoreLineEndingDifferences: true);
+
+            Assert.Equal(lastModified, File.GetLastWriteTimeUtc(expectedFile));
+        }
+
+        [Fact]
+        public void BundlesScopedCssFiles_UpdatesBundleWhenContentsChange()
+        {
+            // Arrange
+            var expectedFile = Path.Combine(Directory.GetCurrentDirectory(), $"{Guid.NewGuid():N}.css");
+            var taskInstance = new ConcatenateCssFiles()
+            {
+                ScopedCssFiles = new[]
+                {
+                    new TaskItem(
+                        "TestFiles/Generated/Index.razor.rz.scp.css",
+                        new Dictionary<string,string>
+                        {
+                            ["BasePath"] = "_content/Test/",
+                            ["RelativePath"] = "TestFiles/Generated/Index.razor.rz.scp.css",
+                        }),
+                    new TaskItem(
+                        "TestFiles/Generated/Counter.razor.rz.scp.css",
+                        new Dictionary<string,string>
+                        {
+                            ["BasePath"] = "_content/Test/",
+                            ["RelativePath"] = "TestFiles/Generated/Counter.razor.rz.scp.css",
+                        }),
+                },
+                ProjectBundles = Array.Empty<ITaskItem>(),
+                OutputFile = expectedFile
+            };
+
+            // Act
+            var result = taskInstance.Execute();
+
+            var lastModified = File.GetLastWriteTimeUtc(expectedFile);
+
+            taskInstance.ScopedCssFiles = new[]
+            {
+                new TaskItem(
+                    "TestFiles/Generated/Index.razor.rz.scp.css",
+                    new Dictionary<string,string>
+                    {
+                        ["BasePath"] = "_content/Test/",
+                        ["RelativePath"] = "TestFiles/Generated/Index.razor.rz.scp.css",
+                    }),
+                new TaskItem(
+                    "TestFiles/Generated/Counter.razor.rz.scp.css",
+                    new Dictionary<string,string>
+                    {
+                        ["BasePath"] = "_content/Test/",
+                        ["RelativePath"] = "TestFiles/Generated/Counter.razor.rz.scp.css",
+                    }),
+                new TaskItem(
+                    "TestFiles/Generated/FetchData.razor.rz.scp.css",
+                    new Dictionary<string,string>
+                    {
+                        ["BasePath"] = "_content/Test/",
+                        ["RelativePath"] = "TestFiles/Generated/FetchData.razor.rz.scp.css",
+                    }),
+            };
+
+            taskInstance.Execute();
+
+            // Assert
+            Assert.True(result);
+            Assert.True(File.Exists(expectedFile));
+            var actualContents = File.ReadAllText(expectedFile);
+            Assert.Equal(UpdatedBundleContent, actualContents, ignoreLineEndingDifferences: true);
+
+            Assert.NotEqual(lastModified, File.GetLastWriteTimeUtc(expectedFile));
+        }
+    }
+}

+ 56 - 0
src/Razor/Microsoft.NET.Sdk.Razor/test/DiscoverDefaultScopedCssItemsTests.cs

@@ -0,0 +1,56 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using Microsoft.Build.Utilities;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Razor.Tasks
+{
+    public class DiscoverDefaultScopedCssItemsTests
+    {
+        [Fact]
+        public void DiscoversScopedCssFiles_BasedOnTheirExtension()
+        {
+            // Arrange
+            var taskInstance = new DiscoverDefaultScopedCssItems()
+            {
+                Content = new[]
+                {
+                    new TaskItem("TestFiles/Pages/Counter.razor.css"),
+                    new TaskItem("TestFiles/Pages/Index.razor.css"),
+                    new TaskItem("TestFiles/Pages/Profile.razor.css"),
+                }
+            };
+
+            // Act
+            var result = taskInstance.Execute();
+
+            // Assert
+            Assert.True(result);
+            Assert.Equal(3, taskInstance.DiscoveredScopedCssInputs.Length);
+        }
+
+        [Fact]
+        public void DiscoversScopedCssFiles_SkipsFilesWithScopedAttributeWithAFalseValue()
+        {
+            // Arrange
+            var taskInstance = new DiscoverDefaultScopedCssItems()
+            {
+                Content = new[]
+                {
+                    new TaskItem("TestFiles/Pages/Counter.razor.css"),
+                    new TaskItem("TestFiles/Pages/Index.razor.css"),
+                    new TaskItem("TestFiles/Pages/Profile.razor.css", new Dictionary<string,string>{ ["Scoped"] = "false" }),
+                }
+            };
+
+            // Act
+            var result = taskInstance.Execute();
+
+            // Assert
+            Assert.True(result);
+            Assert.Equal(2, taskInstance.DiscoveredScopedCssInputs.Length);
+        }
+    }
+}

+ 2 - 84
src/Razor/Microsoft.NET.Sdk.Razor/test/GenerateAspNetCoreStaticAssetsManifestTest.cs

@@ -1,4 +1,4 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 using System.Collections.Generic;
@@ -72,55 +72,14 @@ namespace Microsoft.AspNetCore.Razor.Tasks
             Assert.Equal($"Missing required metadata 'ContentRoot' for '{Path.Combine("wwwroot", "sample.js")}'.", message);
         }
 
-        [Fact]
-        public void ReturnsError_ForDuplicateBasePaths()
-        {
-            // Arrange
-            var errorMessages = new List<string>();
-            var buildEngine = new Mock<IBuildEngine>();
-            buildEngine.Setup(e => e.LogErrorEvent(It.IsAny<BuildErrorEventArgs>()))
-                .Callback<BuildErrorEventArgs>(args => errorMessages.Add(args.Message));
-
-            var task = new GenerateStaticWebAssetsManifest
-            {
-                BuildEngine = buildEngine.Object,
-                ContentRootDefinitions = new TaskItem[]
-                {
-                    CreateItem(Path.Combine("wwwroot","sample.js"), new Dictionary<string,string>
-                    {
-                        ["BasePath"] = "MyLibrary",
-                        ["ContentRoot"] = Path.Combine("nuget", "MyLibrary"),
-                        ["SourceId"] = "MyLibrary"
-                    }),
-                    CreateItem(Path.Combine("wwwroot", "otherLib.js"), new Dictionary<string,string>
-                    {
-                        ["BasePath"] = "MyLibrary",
-                        ["ContentRoot"] = Path.Combine("nuget", "MyOtherLibrary"),
-                        ["SourceId"] = "MyOtherLibrary"
-                    })
-                }
-            };
-
-            // Act
-            var result = task.Execute();
-
-            // Assert
-            Assert.False(result);
-            var message = Assert.Single(errorMessages);
-            Assert.Equal(
-                $"Duplicate base paths 'MyLibrary' for content root paths '{Path.Combine("nuget", "MyOtherLibrary")}' and '{Path.Combine("nuget", "MyLibrary")}'. " +
-                $"('{Path.Combine("wwwroot", "otherLib.js")}', '{Path.Combine("wwwroot", "sample.js")}')",
-                message);
-        }
-
         [Fact]
         public void AllowsMultipleContentRootsWithSameBasePath_ForTheSameSourceId()
         {
             // Arrange
             var file = Path.GetTempFileName();
             var expectedDocument = $@"<StaticWebAssets Version=""1.0"">
-  <ContentRoot BasePath=""Blazor.Client"" Path=""{Path.Combine(".", "nuget", $"Blazor.Client{Path.DirectorySeparatorChar}")}"" />
   <ContentRoot BasePath=""Blazor.Client"" Path=""{Path.Combine(".", "nuget", "bin", "debug", $"netstandard2.1{Path.DirectorySeparatorChar}")}"" />
+  <ContentRoot BasePath=""Blazor.Client"" Path=""{Path.Combine(".", "nuget", $"Blazor.Client{Path.DirectorySeparatorChar}")}"" />
 </StaticWebAssets>";
 
             var buildEngine = new Mock<IBuildEngine>();
@@ -165,47 +124,6 @@ namespace Microsoft.AspNetCore.Razor.Tasks
             }
         }
 
-        [Fact]
-        public void ReturnsError_ForDuplicateContentRoots()
-        {
-            // Arrange
-            var errorMessages = new List<string>();
-            var buildEngine = new Mock<IBuildEngine>();
-            buildEngine.Setup(e => e.LogErrorEvent(It.IsAny<BuildErrorEventArgs>()))
-                .Callback<BuildErrorEventArgs>(args => errorMessages.Add(args.Message));
-
-            var task = new GenerateStaticWebAssetsManifest
-            {
-                BuildEngine = buildEngine.Object,
-                ContentRootDefinitions = new TaskItem[]
-                {
-                    CreateItem(Path.Combine("wwwroot","sample.js"), new Dictionary<string,string>
-                    {
-                        ["BasePath"] = "MyLibrary",
-                        ["SourceId"] = "MyLibrary",
-                        ["ContentRoot"] = Path.Combine(".", "MyLibrary")
-                    }),
-                    CreateItem(Path.Combine("wwwroot", "otherLib.js"), new Dictionary<string,string>
-                    {
-                        ["BasePath"] = "MyOtherLibrary",
-                        ["SourceId"] = "MyOtherLibrary",
-                        ["ContentRoot"] = Path.Combine(".", "MyLibrary")
-                    })
-                }
-            };
-
-            // Act
-            var result = task.Execute();
-
-            // Assert
-            Assert.False(result);
-            var message = Assert.Single(errorMessages);
-            Assert.Equal(
-                $"Duplicate content root paths '{Path.Combine(".", "MyLibrary")}' for base paths 'MyOtherLibrary' and 'MyLibrary' " +
-                $"('{Path.Combine("wwwroot", "otherLib.js")}', '{Path.Combine("wwwroot", "sample.js")}')",
-                message);
-        }
-
         [Fact]
         public void Generates_EmptyManifest_WhenNoItems_Passed()
         {

+ 70 - 46
src/Razor/Microsoft.NET.Sdk.Razor/test/GenerateStaticWebAssetsPropsFileTest.cs

@@ -316,64 +316,80 @@ namespace Microsoft.AspNetCore.Razor.Tasks
         }
 
         [Fact]
-        public void Fails_WhenStaticWebAsset_HaveDifferentBasePath()
+        public void WritesPropsFile_WhenThereIsAtLeastOneStaticAsset()
         {
             // Arrange
-            var expectedError = "Static web assets have different 'BasePath' metadata values " +
-                "'_content/mylibrary' and '_content/mylibrary2' " +
-                $"for '{Path.Combine("wwwroot", "js", "sample.js")}' and '{Path.Combine("wwwroot", "css", "site.css")}'.";
-
-            var errorMessages = new List<string>();
-            var buildEngine = new Mock<IBuildEngine>();
-            buildEngine.Setup(e => e.LogErrorEvent(It.IsAny<BuildErrorEventArgs>()))
-                .Callback<BuildErrorEventArgs>(args => errorMessages.Add(args.Message));
+            var file = Path.GetTempFileName();
+            var expectedDocument = @"<Project>
+  <ItemGroup>
+    <StaticWebAsset Include=""$(MSBuildThisFileDirectory)..\staticwebassets\js\sample.js"">
+      <SourceType>Package</SourceType>
+      <SourceId>MyLibrary</SourceId>
+      <ContentRoot>$(MSBuildThisFileDirectory)..\staticwebassets\</ContentRoot>
+      <BasePath>_content/mylibrary</BasePath>
+      <RelativePath>js/sample.js</RelativePath>
+    </StaticWebAsset>
+  </ItemGroup>
+</Project>";
 
-            var task = new GenerateStaticWebAsssetsPropsFile
+            try
             {
-                BuildEngine = buildEngine.Object,
-                StaticWebAssets = new TaskItem[]
+                var buildEngine = new Mock<IBuildEngine>();
+
+                var task = new GenerateStaticWebAsssetsPropsFile
                 {
-                    CreateItem(Path.Combine("wwwroot","js","sample.js"), new Dictionary<string,string>
-                    {
-                        ["SourceType"] = "",
-                        ["SourceId"] = "MyLibrary",
-                        ["ContentRoot"] = @"$(MSBuildThisFileDirectory)..\staticwebassets",
-                        ["BasePath"] = "_content/mylibrary",
-                        ["RelativePath"] = Path.Combine("js", "sample.js"),
-                    }),
-                    CreateItem(Path.Combine("wwwroot","css","site.css"), new Dictionary<string,string>
+                    BuildEngine = buildEngine.Object,
+                    TargetPropsFilePath = file,
+                    StaticWebAssets = new TaskItem[]
                     {
-                        ["SourceType"] = "",
-                        ["SourceId"] = "MyLibrary",
-                        ["ContentRoot"] = @"$(MSBuildThisFileDirectory)..\staticwebassets",
-                        ["BasePath"] = "_content/mylibrary2",
-                        ["RelativePath"] = Path.Combine("css", "site.css"),
-                    })
-                }
-            };
+                        CreateItem(Path.Combine("wwwroot","js","sample.js"), new Dictionary<string,string>
+                        {
+                            ["SourceType"] = "",
+                            ["SourceId"] = "MyLibrary",
+                            ["ContentRoot"] = @"$(MSBuildThisFileDirectory)..\staticwebassets",
+                            ["BasePath"] = "_content/mylibrary",
+                            ["RelativePath"] = Path.Combine("js", "sample.js").Replace("\\","/"),
+                        }),
+                    }
+                };
 
-            // Act
-            var result = task.Execute();
+                // Act
+                var result = task.Execute();
 
-            // Assert
-            Assert.False(result);
-            var message = Assert.Single(errorMessages);
-            Assert.Equal(expectedError, message);
+                // Assert
+                Assert.True(result);
+                var document = File.ReadAllText(file);
+                Assert.Equal(expectedDocument, document, ignoreLineEndingDifferences: true);
+            }
+            finally
+            {
+                if (File.Exists(file))
+                {
+                    File.Delete(file);
+                }
+            }
         }
 
         [Fact]
-        public void WritesPropsFile_WhenThereIsAtLeastOneStaticAsset()
+        public void WritesIndividualItems_WithTheirRespectiveBaseAndRelativePaths()
         {
             // Arrange
             var file = Path.GetTempFileName();
             var expectedDocument = @"<Project>
   <ItemGroup>
-    <StaticWebAsset Include=""$(MSBuildThisFileDirectory)..\staticwebassets\**"">
+    <StaticWebAsset Include=""$(MSBuildThisFileDirectory)..\staticwebassets\App.styles.css"">
+      <SourceType>Package</SourceType>
+      <SourceId>MyLibrary</SourceId>
+      <ContentRoot>$(MSBuildThisFileDirectory)..\staticwebassets\</ContentRoot>
+      <BasePath>/</BasePath>
+      <RelativePath>App.styles.css</RelativePath>
+    </StaticWebAsset>
+    <StaticWebAsset Include=""$(MSBuildThisFileDirectory)..\staticwebassets\js\sample.js"">
       <SourceType>Package</SourceType>
       <SourceId>MyLibrary</SourceId>
       <ContentRoot>$(MSBuildThisFileDirectory)..\staticwebassets\</ContentRoot>
       <BasePath>_content/mylibrary</BasePath>
-      <RelativePath>%(RecursiveDir)%(FileName)%(Extension)</RelativePath>
+      <RelativePath>js/sample.js</RelativePath>
     </StaticWebAsset>
   </ItemGroup>
 </Project>";
@@ -388,14 +404,22 @@ namespace Microsoft.AspNetCore.Razor.Tasks
                     TargetPropsFilePath = file,
                     StaticWebAssets = new TaskItem[]
                     {
-                    CreateItem(Path.Combine("wwwroot","js","sample.js"), new Dictionary<string,string>
-                    {
-                        ["SourceType"] = "",
-                        ["SourceId"] = "MyLibrary",
-                        ["ContentRoot"] = @"$(MSBuildThisFileDirectory)..\staticwebassets",
-                        ["BasePath"] = "_content/mylibrary",
-                        ["RelativePath"] = Path.Combine("js", "sample.js"),
-                    }),
+                        CreateItem(Path.Combine("wwwroot","js","sample.js"), new Dictionary<string,string>
+                        {
+                            ["SourceType"] = "",
+                            ["SourceId"] = "MyLibrary",
+                            ["ContentRoot"] = @"$(MSBuildThisFileDirectory)..\staticwebassets",
+                            ["BasePath"] = "_content/mylibrary",
+                            ["RelativePath"] = Path.Combine("js", "sample.js").Replace("\\","/"),
+                        }),
+                        CreateItem(Path.Combine("wwwroot","App.styles.css"), new Dictionary<string,string>
+                        {
+                            ["SourceType"] = "",
+                            ["SourceId"] = "MyLibrary",
+                            ["ContentRoot"] = @"$(MSBuildThisFileDirectory)..\staticwebassets",
+                            ["BasePath"] = "/",
+                            ["RelativePath"] = "App.styles.css",
+                        }),
                     }
                 };
 

+ 10 - 1
src/Razor/Microsoft.NET.Sdk.Razor/test/Microsoft.NET.Sdk.Razor.Test.csproj

@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
     <TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
@@ -13,4 +13,13 @@
     <Reference Include="Microsoft.NET.Sdk.Razor" />
   </ItemGroup>
 
+  <ItemGroup>
+    <None Update="TestFiles\Generated\*.rz.scp.css">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+    <None Update="TestFiles\Generated\*.bundle.scp.css">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </None>
+  </ItemGroup>
+
 </Project>

+ 124 - 0
src/Razor/Microsoft.NET.Sdk.Razor/test/ResolveAllScopedCssAssetsTest.cs

@@ -0,0 +1,124 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System.Collections.Generic;
+using Microsoft.AspNetCore.Razor.Tasks;
+using Microsoft.Build.Utilities;
+using Xunit;
+
+namespace Microsoft.NET.Sdk.Razor.Test
+{
+    public class ResolveAllScopedCssAssetsTest
+    {
+        [Fact]
+        public void ResolveAllScopedCssAssets_IgnoresRegularCssFiles()
+        {
+            // Arrange
+            var taskInstance = new ResolveAllScopedCssAssets()
+            {
+                StaticWebAssets = new[]
+                {
+                    new TaskItem("TestFiles/Pages/Counter.razor.rz.scp.css", new Dictionary<string,string>
+                    {
+                        ["RelativePath"] = "Pages/Counter.razor.rz.scp.css"
+                    }),
+                    new TaskItem("site.css", new Dictionary<string,string>
+                    {
+                        ["RelativePath"] = "site.css"
+                    }),
+                }
+            };
+
+            // Act
+            var result = taskInstance.Execute();
+
+            // Assert
+            Assert.True(result);
+            var scopedCssAsset = Assert.Single(taskInstance.ScopedCssAssets);
+            Assert.NotEqual("site.css", scopedCssAsset.ItemSpec);
+        }
+
+        [Fact]
+        public void ResolveAllScopedCssAssets_DetectsScopedCssFiles()
+        {
+            // Arrange
+            var taskInstance = new ResolveAllScopedCssAssets()
+            {
+                StaticWebAssets = new[]
+                {
+                    new TaskItem("TestFiles/Pages/Counter.razor.rz.scp.css", new Dictionary<string,string>
+                    {
+                        ["RelativePath"] = "Pages/Counter.razor.rz.scp.css"
+                    }),
+                    new TaskItem("site.css", new Dictionary<string,string>
+                    {
+                        ["RelativePath"] = "site.css"
+                    }),
+                }
+            };
+
+            // Act
+            var result = taskInstance.Execute();
+
+            // Assert
+            Assert.True(result);
+            var scopedCssAsset = Assert.Single(taskInstance.ScopedCssAssets);
+            Assert.Equal("TestFiles/Pages/Counter.razor.rz.scp.css", scopedCssAsset.ItemSpec);
+        }
+
+        [Fact]
+        public void ResolveAllScopedCssAssets_DetectsScopedCssProjectBundleFiles()
+        {
+            // Arrange
+            var taskInstance = new ResolveAllScopedCssAssets()
+            {
+                StaticWebAssets = new[]
+                {
+                    new TaskItem("Folder/Project.bundle.scp.css", new Dictionary<string,string>
+                    {
+                        ["RelativePath"] = "Project.bundle.scp.css"
+                    }),
+                    new TaskItem("site.css", new Dictionary<string,string>
+                    {
+                        ["RelativePath"] = "site.css"
+                    }),
+                }
+            };
+
+            // Act
+            var result = taskInstance.Execute();
+
+            // Assert
+            Assert.True(result);
+            var scopedCssBundle = Assert.Single(taskInstance.ScopedCssProjectBundles);
+            Assert.Equal("Folder/Project.bundle.scp.css", scopedCssBundle.ItemSpec);
+        }
+
+        [Fact]
+        public void ResolveAllScopedCssAssets_IgnoresScopedCssApplicationBundleFiles()
+        {
+            // Arrange
+            var taskInstance = new ResolveAllScopedCssAssets()
+            {
+                StaticWebAssets = new[]
+                {
+                    new TaskItem("Folder/Project.styles.css", new Dictionary<string,string>
+                    {
+                        ["RelativePath"] = "Project.styles.css"
+                    }),
+                    new TaskItem("site.css", new Dictionary<string,string>
+                    {
+                        ["RelativePath"] = "site.css"
+                    }),
+                }
+            };
+
+            // Act
+            var result = taskInstance.Execute();
+
+            // Assert
+            Assert.True(result);
+            Assert.Empty(taskInstance.ScopedCssProjectBundles);
+        }
+    }
+}

+ 3 - 0
src/Razor/Microsoft.NET.Sdk.Razor/test/TestFiles/Generated/Counter.razor.rz.scp.css

@@ -0,0 +1,3 @@
+.counter {
+    font-size: 2rem;
+}

+ 3 - 0
src/Razor/Microsoft.NET.Sdk.Razor/test/TestFiles/Generated/FetchData.razor.rz.scp.css

@@ -0,0 +1,3 @@
+.fetchData {
+    font-family: Helvetica;
+}

+ 3 - 0
src/Razor/Microsoft.NET.Sdk.Razor/test/TestFiles/Generated/Index.razor.rz.scp.css

@@ -0,0 +1,3 @@
+.index {
+    font-weight: bold;
+}

+ 3 - 0
src/Razor/Microsoft.NET.Sdk.Razor/test/TestFiles/Generated/lib.bundle.scp.css

@@ -0,0 +1,3 @@
+.counter {
+    font-size: 2rem;
+}

+ 3 - 0
src/Razor/Microsoft.NET.Sdk.Razor/test/TestFiles/Generated/package.bundle.scp.css

@@ -0,0 +1,3 @@
+.counter {
+    font-size: 2rem;
+}

+ 85 - 9
src/Razor/Microsoft.NET.Sdk.Razor/test/ValidateStaticWebAssetsUniquePathsTest.cs

@@ -1,6 +1,7 @@
-// Copyright (c) .NET Foundation. All rights reserved.
+// Copyright (c) .NET Foundation. All rights reserved.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
+using System;
 using System.Collections.Generic;
 using System.IO;
 using Microsoft.Build.Framework;
@@ -26,28 +27,103 @@ namespace Microsoft.AspNetCore.Razor.Tasks
                 BuildEngine = buildEngine.Object,
                 StaticWebAssets = new TaskItem[]
                 {
-                    CreateItem(Path.Combine(".", "Library", "wwwroot", "sample.js"), new Dictionary<string,string>
+                    CreateItem(Path.Combine("wwroot", "js", "project-transitive-dep.js"), new Dictionary<string,string>
                     {
-                        ["BasePath"] = "/",
-                        ["RelativePath"] = "/sample.js",
-                    })
+                        ["BasePath"] = "_content/ClassLibrary",
+                        ["RelativePath"] = "js/project-transitive-dep.js",
+                        ["SourceId"] = "ClassLibrary",
+                        ["SourceType"] = "Project",
+                    }),
                 },
                 WebRootFiles = new TaskItem[]
                 {
-                    CreateItem(Path.Combine(".", "App", "wwwroot", "sample.js"), new Dictionary<string,string>
+                    CreateItem(Path.Combine("wwwroot", "_content", "ClassLibrary", "js", "project-transitive-dep.js"), new Dictionary<string,string>
                     {
-                        ["TargetPath"] = "/SAMPLE.js",
+                        ["CopyToPublishDirectory"] = "PreserveNewest",
+                        ["ExcludeFromSingleFile"] = "true",
+                        ["OriginalItemSpec"] = Path.Combine("wwwroot", "_content", "ClassLibrary", "js", "project-transitive-dep.js"),
+                        ["TargetPath"] = Path.Combine("wwwroot", "_content", "ClassLibrary", "js", "project-transitive-dep.js"),
                     })
                 }
             };
 
+            var expectedMessage = $"The static web asset '{Path.Combine("wwroot", "js", "project-transitive-dep.js")}' " +
+                "has a conflicting web root path '/wwwroot/_content/ClassLibrary/js/project-transitive-dep.js' with the " +
+                $"project file '{Path.Combine("wwwroot", "_content", "ClassLibrary", "js", "project-transitive-dep.js")}'.";
+
             // Act
             var result = task.Execute();
 
             // Assert
             Assert.False(result);
             var message = Assert.Single(errorMessages);
-            Assert.Equal($"The static web asset '{Path.Combine(".", "Library", "wwwroot", "sample.js")}' has a conflicting web root path '/SAMPLE.js' with the project file '{Path.Combine(".", "App", "wwwroot", "sample.js")}'.", message);
+            Assert.Equal(expectedMessage, message);
+        }
+
+        [Fact]
+        public void AllowsAssetsHavingTheSameBasePathAcrossDifferentSources_WhenTheirFinalDestinationPathIsDifferent()
+        {
+            // Arrange
+            var task = new ValidateStaticWebAssetsUniquePaths
+            {
+                StaticWebAssets = new TaskItem[]
+                {
+                    CreateItem(Path.Combine("wwwroot","sample.js"), new Dictionary<string,string>
+                    {
+                        ["BasePath"] = "MyLibrary",
+                        ["ContentRoot"] = Path.Combine("nuget", "MyLibrary"),
+                        ["RelativePath"] = "sample.js",
+                        ["SourceId"] = "MyLibrary"
+                    }),
+                    CreateItem(Path.Combine("wwwroot", "otherLib.js"), new Dictionary<string,string>
+                    {
+                        ["BasePath"] = "MyLibrary",
+                        ["ContentRoot"] = Path.Combine("nuget", "MyOtherLibrary"),
+                        ["RelativePath"] = "otherLib.js",
+                        ["SourceId"] = "MyOtherLibrary"
+                    })
+                },
+                WebRootFiles = Array.Empty<TaskItem>()
+            };
+
+            // Act
+            var result = task.Execute();
+
+            // Assert
+            Assert.True(result);
+        }
+
+        [Fact]
+        public void AllowsAssetsHavingTheSameContentRootAndDifferentBasePathsAcrossDifferentSources_WhenTheirFinalDestinationPathIsDifferent()
+        {
+            // Arrange
+            var task = new ValidateStaticWebAssetsUniquePaths
+            {
+                StaticWebAssets = new TaskItem[]
+                {
+                    CreateItem(Path.Combine("wwwroot","sample.js"), new Dictionary<string,string>
+                    {
+                        ["BasePath"] = "MyLibrary",
+                        ["SourceId"] = "MyLibrary",
+                        ["RelativePath"] = "sample.js",
+                        ["ContentRoot"] = Path.Combine(".", "MyLibrary")
+                    }),
+                    CreateItem(Path.Combine("wwwroot", "otherLib.js"), new Dictionary<string,string>
+                    {
+                        ["BasePath"] = "MyOtherLibrary",
+                        ["SourceId"] = "MyOtherLibrary",
+                        ["RelativePath"] = "otherlib.js",
+                        ["ContentRoot"] = Path.Combine(".", "MyLibrary")
+                    })
+                },
+                WebRootFiles = Array.Empty<TaskItem>()
+            };
+
+            // Act
+            var result = task.Execute();
+
+            // Assert
+            Assert.True(result);
         }
 
         [Fact]
@@ -83,7 +159,7 @@ namespace Microsoft.AspNetCore.Razor.Tasks
             // Assert
             Assert.False(result);
             var message = Assert.Single(errorMessages);
-            Assert.Equal($"Conflicting assets with the same path '/sample.js' for content root paths '{Path.Combine(".", "Library", "bin", "dist", "sample.js")}' and '{Path.Combine(".", "Library", "wwwroot", "sample.js")}'.", message);
+            Assert.Equal($"Conflicting assets with the same path '/wwwroot/sample.js' for content root paths '{Path.Combine(".", "Library", "bin", "dist", "sample.js")}' and '{Path.Combine(".", "Library", "wwwroot", "sample.js")}'.", message);
         }
 
         [Fact]

+ 1 - 0
src/Servers/IIS/AspNetCoreModuleV2/CommonLib/ConfigurationSection.h

@@ -30,6 +30,7 @@
 #define CS_ENABLED                                       L"enabled"
 #define CS_ASPNETCORE_HANDLER_CALL_STARTUP_HOOK          L"callStartupHook"
 #define CS_ASPNETCORE_HANDLER_STACK_SIZE                 L"stackSize"
+#define CS_ASPNETCORE_SUPPRESS_RECYCLE_ON_STARTUP_TIMEOUT L"suppressRecycleOnStartupTimeout"
 #define CS_ASPNETCORE_DETAILEDERRORS                     L"ASPNETCORE_DETAILEDERRORS"
 #define CS_ASPNETCORE_ENVIRONMENT                        L"ASPNETCORE_ENVIRONMENT"
 #define CS_DOTNET_ENVIRONMENT                            L"DOTNET_ENVIRONMENT"

+ 1 - 0
src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.cpp

@@ -66,6 +66,7 @@ InProcessOptions::InProcessOptions(const ConfigurationSource &configurationSourc
     m_fSetCurrentDirectory = equals_ignore_case(find_element(handlerSettings, CS_ASPNETCORE_HANDLER_SET_CURRENT_DIRECTORY).value_or(L"true"), L"true");
     m_fCallStartupHook = equals_ignore_case(find_element(handlerSettings, CS_ASPNETCORE_HANDLER_CALL_STARTUP_HOOK).value_or(L"true"), L"true");
     m_strStackSize = find_element(handlerSettings, CS_ASPNETCORE_HANDLER_STACK_SIZE).value_or(L"1048576");
+    m_fSuppressRecycleOnStartupTimeout = equals_ignore_case(find_element(handlerSettings, CS_ASPNETCORE_SUPPRESS_RECYCLE_ON_STARTUP_TIMEOUT).value_or(L"false"), L"true");
 
     m_dwStartupTimeLimitInMS = aspNetCoreSection->GetRequiredLong(CS_ASPNETCORE_PROCESS_STARTUP_TIME_LIMIT) * 1000;
     m_dwShutdownTimeLimitInMS = aspNetCoreSection->GetRequiredLong(CS_ASPNETCORE_PROCESS_SHUTDOWN_TIME_LIMIT) * 1000;

+ 7 - 0
src/Servers/IIS/AspNetCoreModuleV2/InProcessRequestHandler/InProcessOptions.h

@@ -118,6 +118,12 @@ public:
         return m_strStackSize;
     }
 
+    bool
+    QuerySuppressRecycleOnStartupTimeout() const
+    {
+        return m_fSuppressRecycleOnStartupTimeout;
+    }
+
     InProcessOptions(const ConfigurationSource &configurationSource, IHttpSite* pSite);
 
     static
@@ -139,6 +145,7 @@ private:
     bool                           m_fWindowsAuthEnabled;
     bool                           m_fBasicAuthEnabled;
     bool                           m_fAnonymousAuthEnabled;
+    bool                           m_fSuppressRecycleOnStartupTimeout;
     DWORD                          m_dwStartupTimeLimitInMS;
     DWORD                          m_dwShutdownTimeLimitInMS;
     DWORD                          m_dwMaxRequestBodySize;

Some files were not shown because too many files changed in this diff