diff --git a/.azure/pipelines/stress.yml b/.azure/pipelines/stress.yml new file mode 100644 index 0000000000..93346d96f2 --- /dev/null +++ b/.azure/pipelines/stress.yml @@ -0,0 +1,35 @@ +# This configuration builds the repository and runs stress +pr: none + +# Don't run CI for this config +trigger: none + +variables: +- name: DOTNET_SKIP_FIRST_TIME_EXPERIENCE + value: true +- name: _TeamName + value: AspNetCore +- ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - name: _BuildArgs + value: /p:TeamName=$(_TeamName) + /p:OfficialBuildId=$(Build.BuildNumber) +- ${{ if or(eq(variables['System.TeamProject'], 'public'), in(variables['Build.Reason'], 'PullRequest')) }}: + - name: _BuildArgs + value: '' +jobs: +- template: jobs/default-build.yml + parameters: + condition: ne(variables['SkipTests'], 'true') + jobName: Windows_Stress_Test + jobDisplayName: "Windows Stress test" + agentOs: Windows + isTestingJob: true + steps: + - script: .\src\Servers\Kestrel\stress\build.cmd -ci -c Release + displayName: Build Repo + - script: .\.dotnet\dotnet.exe run --project .\src\Servers\Kestrel\stress\HttpStress.csproj -c Release -aspnetlog + displayName: Run stress + artifacts: + - name: Windows_Test_Stress_Logs + path: artifacts/log/ + publishOnError: true diff --git a/Directory.Build.props b/Directory.Build.props index d7c5acd6b0..06e288aa81 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -59,11 +59,9 @@ $(MSBuildThisFileDirectory)src\Shared\ $(RepoRoot)src\submodules\googletest\ + true - - - false diff --git a/Directory.Build.targets b/Directory.Build.targets index 0da788da27..5006951f11 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -19,6 +19,11 @@ true + + true + true + + diff --git a/clean.ps1 b/clean.ps1 index 98dd53af40..955b03c41d 100644 --- a/clean.ps1 +++ b/clean.ps1 @@ -38,4 +38,5 @@ if ($Help) { exit 0 } -git clean -dix -e .dotnet/ -e .tools/ -e src/SignalR/clients/ts/FunctionalTests/node_modules/ @GitArguments +git clean -dix -e .dotnet/ -e .tools/ @GitArguments +git checkout -- $(git ls-files -d) diff --git a/eng/Baseline.Designer.props b/eng/Baseline.Designer.props index 80bc739545..f35501e1de 100644 --- a/eng/Baseline.Designer.props +++ b/eng/Baseline.Designer.props @@ -2,7 +2,7 @@ $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - 2.2.5 + 2.2.6 @@ -77,12 +77,12 @@ - 2.2.5 + 2.2.6 - 2.2.5 + 2.2.6 @@ -649,6 +649,11 @@ 2.2.0 + + + 2.2.6 + + 2.2.0 @@ -932,7 +937,7 @@ - 2.2.0 + 2.2.6 @@ -943,13 +948,13 @@ - 2.2.2 + 2.2.6 - + diff --git a/eng/Baseline.xml b/eng/Baseline.xml index 49a2c044e3..0946044016 100644 --- a/eng/Baseline.xml +++ b/eng/Baseline.xml @@ -4,7 +4,7 @@ This file contains a list of all the packages and their versions which were rele build of ASP.NET Core 2.2.x. Update this list when preparing for a new patch. --> - + @@ -12,8 +12,8 @@ build of ASP.NET Core 2.2.x. Update this list when preparing for a new patch. - - + + @@ -74,6 +74,7 @@ build of ASP.NET Core 2.2.x. Update this list when preparing for a new patch. + @@ -100,8 +101,8 @@ build of ASP.NET Core 2.2.x. Update this list when preparing for a new patch. - - + + diff --git a/eng/Dependencies.props b/eng/Dependencies.props index 754b192df3..3f6d8d595c 100644 --- a/eng/Dependencies.props +++ b/eng/Dependencies.props @@ -84,6 +84,7 @@ and are generated based on the last package release. + diff --git a/eng/PatchConfig.props b/eng/PatchConfig.props index 6c515f6dac..c31e71c5e4 100644 --- a/eng/PatchConfig.props +++ b/eng/PatchConfig.props @@ -63,4 +63,8 @@ Later on, this will be checked using this condition: Microsoft.AspNetCore.Server.IIS; + + + + diff --git a/eng/ProjectReferences.props b/eng/ProjectReferences.props index 932cec9dce..a54507bbb0 100644 --- a/eng/ProjectReferences.props +++ b/eng/ProjectReferences.props @@ -73,6 +73,7 @@ + @@ -87,7 +88,6 @@ - diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index fb70b43094..8ff491b896 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -9,432 +9,432 @@ --> - + https://github.com/aspnet/Blazor - 7e2a0091dd81c0cb17ceb7bdc4d67aed67dbec66 + 9045a3861427fc95575c07bd0455c98731567969 - + https://github.com/aspnet/AspNetCore-Tooling - a23fdc5348f411e8fcc29761fb03cf74b1c4f970 + d95774f01eb5f86d3f9ad00d2341deb74b024f37 - + https://github.com/aspnet/AspNetCore-Tooling - a23fdc5348f411e8fcc29761fb03cf74b1c4f970 + d95774f01eb5f86d3f9ad00d2341deb74b024f37 - + https://github.com/aspnet/AspNetCore-Tooling - a23fdc5348f411e8fcc29761fb03cf74b1c4f970 + d95774f01eb5f86d3f9ad00d2341deb74b024f37 - + https://github.com/aspnet/AspNetCore-Tooling - a23fdc5348f411e8fcc29761fb03cf74b1c4f970 + d95774f01eb5f86d3f9ad00d2341deb74b024f37 - + https://github.com/aspnet/EntityFrameworkCore - c916044ea14b17159b7c2cb5f5848bf43f15efc7 + 751fb9dcf92357260128fc55afeb1e1735791297 - + https://github.com/aspnet/EntityFrameworkCore - c916044ea14b17159b7c2cb5f5848bf43f15efc7 + 751fb9dcf92357260128fc55afeb1e1735791297 - + https://github.com/aspnet/EntityFrameworkCore - c916044ea14b17159b7c2cb5f5848bf43f15efc7 + 751fb9dcf92357260128fc55afeb1e1735791297 - + https://github.com/aspnet/EntityFrameworkCore - c916044ea14b17159b7c2cb5f5848bf43f15efc7 + 751fb9dcf92357260128fc55afeb1e1735791297 - + https://github.com/aspnet/EntityFrameworkCore - c916044ea14b17159b7c2cb5f5848bf43f15efc7 + 751fb9dcf92357260128fc55afeb1e1735791297 - + https://github.com/aspnet/EntityFrameworkCore - c916044ea14b17159b7c2cb5f5848bf43f15efc7 + 751fb9dcf92357260128fc55afeb1e1735791297 - + https://github.com/aspnet/EntityFrameworkCore - c916044ea14b17159b7c2cb5f5848bf43f15efc7 + 751fb9dcf92357260128fc55afeb1e1735791297 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/dotnet/corefx - a393ffee970c6d962c5830bb1c6fa4c3a3e268a6 + c14b80faff694bae4e085bad221e2e83410e5f33 - + https://github.com/dotnet/corefx - a393ffee970c6d962c5830bb1c6fa4c3a3e268a6 + c14b80faff694bae4e085bad221e2e83410e5f33 - + https://github.com/dotnet/corefx - a393ffee970c6d962c5830bb1c6fa4c3a3e268a6 + c14b80faff694bae4e085bad221e2e83410e5f33 - + https://github.com/dotnet/corefx - a393ffee970c6d962c5830bb1c6fa4c3a3e268a6 + c14b80faff694bae4e085bad221e2e83410e5f33 - + https://github.com/dotnet/corefx - a393ffee970c6d962c5830bb1c6fa4c3a3e268a6 + c14b80faff694bae4e085bad221e2e83410e5f33 https://github.com/dotnet/corefx a28176b5ec68b6da1472934fe9493790d1665cae - + https://github.com/dotnet/corefx - a393ffee970c6d962c5830bb1c6fa4c3a3e268a6 + c14b80faff694bae4e085bad221e2e83410e5f33 - + https://github.com/dotnet/corefx - a393ffee970c6d962c5830bb1c6fa4c3a3e268a6 + c14b80faff694bae4e085bad221e2e83410e5f33 - + https://github.com/dotnet/corefx - a393ffee970c6d962c5830bb1c6fa4c3a3e268a6 + c14b80faff694bae4e085bad221e2e83410e5f33 - + https://github.com/dotnet/corefx - a393ffee970c6d962c5830bb1c6fa4c3a3e268a6 + c14b80faff694bae4e085bad221e2e83410e5f33 - + https://github.com/dotnet/corefx - a393ffee970c6d962c5830bb1c6fa4c3a3e268a6 + c14b80faff694bae4e085bad221e2e83410e5f33 - + https://github.com/dotnet/corefx - a393ffee970c6d962c5830bb1c6fa4c3a3e268a6 + c14b80faff694bae4e085bad221e2e83410e5f33 - + https://github.com/dotnet/corefx - a393ffee970c6d962c5830bb1c6fa4c3a3e268a6 + c14b80faff694bae4e085bad221e2e83410e5f33 - + https://github.com/dotnet/corefx - a393ffee970c6d962c5830bb1c6fa4c3a3e268a6 + c14b80faff694bae4e085bad221e2e83410e5f33 - + https://github.com/dotnet/corefx - a393ffee970c6d962c5830bb1c6fa4c3a3e268a6 + c14b80faff694bae4e085bad221e2e83410e5f33 - + https://github.com/dotnet/corefx - a393ffee970c6d962c5830bb1c6fa4c3a3e268a6 + c14b80faff694bae4e085bad221e2e83410e5f33 - + https://github.com/dotnet/corefx - a393ffee970c6d962c5830bb1c6fa4c3a3e268a6 + c14b80faff694bae4e085bad221e2e83410e5f33 - + https://github.com/dotnet/corefx - a393ffee970c6d962c5830bb1c6fa4c3a3e268a6 + c14b80faff694bae4e085bad221e2e83410e5f33 - + https://github.com/dotnet/corefx - a393ffee970c6d962c5830bb1c6fa4c3a3e268a6 + c14b80faff694bae4e085bad221e2e83410e5f33 - + https://github.com/dotnet/corefx - a393ffee970c6d962c5830bb1c6fa4c3a3e268a6 + c14b80faff694bae4e085bad221e2e83410e5f33 - + https://github.com/dotnet/corefx - a393ffee970c6d962c5830bb1c6fa4c3a3e268a6 + c14b80faff694bae4e085bad221e2e83410e5f33 - + https://github.com/dotnet/corefx - a393ffee970c6d962c5830bb1c6fa4c3a3e268a6 + c14b80faff694bae4e085bad221e2e83410e5f33 - + https://github.com/dotnet/corefx - a393ffee970c6d962c5830bb1c6fa4c3a3e268a6 + c14b80faff694bae4e085bad221e2e83410e5f33 - + https://github.com/dotnet/core-setup - 3ca720b8a2dc8f711a4762729d5c6a223ecc523a + 2bb2dcaeffb1dfeda077354449868ddac254bc3d - + https://github.com/dotnet/core-setup - 3ca720b8a2dc8f711a4762729d5c6a223ecc523a + 2bb2dcaeffb1dfeda077354449868ddac254bc3d - + https://github.com/dotnet/core-setup - 3ca720b8a2dc8f711a4762729d5c6a223ecc523a + 2bb2dcaeffb1dfeda077354449868ddac254bc3d - + https://github.com/dotnet/core-setup - 3ca720b8a2dc8f711a4762729d5c6a223ecc523a + 2bb2dcaeffb1dfeda077354449868ddac254bc3d - + https://github.com/dotnet/corefx - a393ffee970c6d962c5830bb1c6fa4c3a3e268a6 + c14b80faff694bae4e085bad221e2e83410e5f33 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/dotnet/arcade - a65d0966dc28861394ce78cfdcb9d5dff370957c + fb27fd4d8a2b67d4333e33d4b898c65171c9f3c1 - + https://github.com/dotnet/arcade - a65d0966dc28861394ce78cfdcb9d5dff370957c + fb27fd4d8a2b67d4333e33d4b898c65171c9f3c1 - + https://github.com/dotnet/arcade - a65d0966dc28861394ce78cfdcb9d5dff370957c + fb27fd4d8a2b67d4333e33d4b898c65171c9f3c1 - + https://github.com/aspnet/Extensions - 340377c1b06f9eb34d018f84bea69119fb622093 + 8bf6089b4491bc4387cdfec64ab56e7cc39030f8 - + https://github.com/dotnet/roslyn - 17aaaf42dfcf86952acf0c7f788887623e7f4095 + 342275182023200bcd42b3d9919e984039577b44 diff --git a/eng/Versions.props b/eng/Versions.props index 41af8c2290..6aee4469b6 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -50,117 +50,117 @@ --> - 1.0.0-beta.19356.1 + 1.0.0-beta.19365.4 - 3.3.0-beta2-19359-05 + 3.3.0-beta2-19363-02 - 3.0.0-preview8-27911-04 - 3.0.0-preview8-27911-04 - 3.0.0-preview8-27911-04 - 2.1.0-preview8-27911-04 + 3.0.0-preview8-27914-06 + 3.0.0-preview8-27914-06 + 3.0.0-preview8-27914-06 + 2.1.0-preview8-27914-06 - 1.0.0-preview8.19361.1 - 4.6.0-preview8.19361.1 - 4.6.0-preview8.19361.1 - 4.6.0-preview8.19361.1 - 4.6.0-preview8.19361.1 + 1.0.0-preview8.19364.1 + 4.6.0-preview8.19364.1 + 4.6.0-preview8.19364.1 + 4.6.0-preview8.19364.1 + 4.6.0-preview8.19364.1 4.7.0-preview6.19264.9 - 4.6.0-preview8.19361.1 - 4.6.0-preview8.19361.1 - 4.6.0-preview8.19361.1 - 4.6.0-preview8.19361.1 - 4.6.0-preview8.19361.1 - 1.7.0-preview8.19361.1 - 4.6.0-preview8.19361.1 - 4.6.0-preview8.19361.1 - 4.6.0-preview8.19361.1 - 4.6.0-preview8.19361.1 - 4.6.0-preview8.19361.1 - 4.6.0-preview8.19361.1 - 4.6.0-preview8.19361.1 - 4.6.0-preview8.19361.1 - 4.6.0-preview8.19361.1 - 4.6.0-preview8.19361.1 - 4.6.0-preview8.19361.1 + 4.6.0-preview8.19364.1 + 4.6.0-preview8.19364.1 + 4.6.0-preview8.19364.1 + 4.6.0-preview8.19364.1 + 4.6.0-preview8.19364.1 + 1.7.0-preview8.19364.1 + 4.6.0-preview8.19364.1 + 4.6.0-preview8.19364.1 + 4.6.0-preview8.19364.1 + 4.6.0-preview8.19364.1 + 4.6.0-preview8.19364.1 + 4.6.0-preview8.19364.1 + 4.6.0-preview8.19364.1 + 4.6.0-preview8.19364.1 + 4.6.0-preview8.19364.1 + 4.6.0-preview8.19364.1 + 4.6.0-preview8.19364.1 - 3.0.0-preview8.19361.1 + 3.0.0-preview8.19364.1 - 0.10.0-preview8.19355.1 + 0.10.0-preview8.19365.1 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 + 3.0.0-preview8.19365.1 - 3.0.0-preview8.19361.7 - 3.0.0-preview8.19361.7 - 3.0.0-preview8.19361.7 - 3.0.0-preview8.19361.7 - 3.0.0-preview8.19361.7 - 3.0.0-preview8.19361.7 - 3.0.0-preview8.19361.7 + 3.0.0-preview8.19365.5 + 3.0.0-preview8.19365.5 + 3.0.0-preview8.19365.5 + 3.0.0-preview8.19365.5 + 3.0.0-preview8.19365.5 + 3.0.0-preview8.19365.5 + 3.0.0-preview8.19365.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 - 3.0.0-preview8.19361.5 + 3.0.0-preview8.19365.2 + 3.0.0-preview8.19365.2 + 3.0.0-preview8.19365.2 + 3.0.0-preview8.19365.2 4.5.0 4.4.0 + 0.3.0-alpha.19317.1 4.3.0 4.3.2 4.5.2 @@ -201,16 +202,16 @@ 3.0.0 3.0.0 3.19.8 - 5.3.0 - 5.3.0 - 5.3.0 + 5.5.0 + 5.5.0 + 5.5.0 2.2.1 1.0.1 3.0.1 3.0.1 11.1.0 1.4.0 - 5.3.0 + 5.5.0 2.1.1 2.2.0 diff --git a/eng/common/SigningValidation.proj b/eng/common/SigningValidation.proj index 7045fb6fb9..3d0ac80af3 100644 --- a/eng/common/SigningValidation.proj +++ b/eng/common/SigningValidation.proj @@ -3,7 +3,7 @@ + + + $(WorkItemDirectory) + $(WorkItemCommand) --bdn-arguments="--anyCategories $(BDNCategories) $(ExtraBenchmarkDotNetArguments) $(CoreRunArgument) --artifacts $(ArtifactsDirectory) --partition-count $(PartitionCount) --partition-index %(HelixWorkItem.Index)" + 4:00 + + + + + $(WorkItemDirectory) + $(WorkItemCommand) --bdn-arguments="--anyCategories $(BDNCategories) $(ExtraBenchmarkDotNetArguments) $(CoreRunArgument) --artifacts $(ArtifactsDirectory)" + 4:00 + + + \ No newline at end of file diff --git a/eng/common/performance/performance-setup.ps1 b/eng/common/performance/performance-setup.ps1 new file mode 100644 index 0000000000..7e5441f797 --- /dev/null +++ b/eng/common/performance/performance-setup.ps1 @@ -0,0 +1,91 @@ +Param( + [string] $SourceDirectory=$env:BUILD_SOURCESDIRECTORY, + [string] $CoreRootDirectory, + [string] $Architecture="x64", + [string] $Framework="netcoreapp3.0", + [string] $CompilationMode="Tiered", + [string] $Repository=$env:BUILD_REPOSITORY_NAME, + [string] $Branch=$env:BUILD_SOURCEBRANCH, + [string] $CommitSha=$env:BUILD_SOURCEVERSION, + [string] $BuildNumber=$env:BUILD_BUILDNUMBER, + [string] $RunCategories="coreclr corefx", + [string] $Csproj="src\benchmarks\micro\MicroBenchmarks.csproj", + [string] $Kind="micro", + [switch] $Internal, + [string] $Configurations="CompilationMode=$CompilationMode" +) + +$RunFromPerformanceRepo = ($Repository -eq "dotnet/performance") +$UseCoreRun = ($CoreRootDirectory -ne [string]::Empty) + +$PayloadDirectory = (Join-Path $SourceDirectory "Payload") +$PerformanceDirectory = (Join-Path $PayloadDirectory "performance") +$WorkItemDirectory = (Join-Path $SourceDirectory "workitem") +$ExtraBenchmarkDotNetArguments = "--iterationCount 1 --warmupCount 0 --invocationCount 1 --unrollFactor 1 --strategy ColdStart --stopOnFirstError true" +$Creator = $env:BUILD_DEFINITIONNAME +$PerfLabArguments = "" +$HelixSourcePrefix = "pr" + +$Queue = "Windows.10.Amd64.ClientRS4.DevEx.15.8.Open" + +if ($Framework.StartsWith("netcoreapp")) { + $Queue = "Windows.10.Amd64.ClientRS4.Open" +} + +if ($Internal) { + $Queue = "Windows.10.Amd64.ClientRS5.Perf" + $PerfLabArguments = "--upload-to-perflab-container" + $ExtraBenchmarkDotNetArguments = "" + $Creator = "" + $HelixSourcePrefix = "official" +} + +$CommonSetupArguments="--frameworks $Framework --queue $Queue --build-number $BuildNumber --build-configs $Configurations" +$SetupArguments = "--repository https://github.com/$Repository --branch $Branch --get-perf-hash --commit-sha $CommitSha $CommonSetupArguments" + +if ($RunFromPerformanceRepo) { + $SetupArguments = "--perf-hash $CommitSha $CommonSetupArguments" + + robocopy $SourceDirectory $PerformanceDirectory /E /XD $PayloadDirectory $SourceDirectory\artifacts $SourceDirectory\.git +} +else { + git clone --branch master --depth 1 --quiet https://github.com/dotnet/performance $PerformanceDirectory +} + +if ($UseCoreRun) { + $NewCoreRoot = (Join-Path $PayloadDirectory "Core_Root") + Move-Item -Path $CoreRootDirectory -Destination $NewCoreRoot +} + +$DocsDir = (Join-Path $PerformanceDirectory "docs") +robocopy $DocsDir $WorkItemDirectory + +# Set variables that we will need to have in future steps +$ci = $true + +. "$PSScriptRoot\..\pipeline-logging-functions.ps1" + +# Directories +Write-PipelineSetVariable -Name 'PayloadDirectory' -Value "$PayloadDirectory" -IsMultiJobVariable $false +Write-PipelineSetVariable -Name 'PerformanceDirectory' -Value "$PerformanceDirectory" -IsMultiJobVariable $false +Write-PipelineSetVariable -Name 'WorkItemDirectory' -Value "$WorkItemDirectory" -IsMultiJobVariable $false + +# Script Arguments +Write-PipelineSetVariable -Name 'Python' -Value "py -3" -IsMultiJobVariable $false +Write-PipelineSetVariable -Name 'ExtraBenchmarkDotNetArguments' -Value "$ExtraBenchmarkDotNetArguments" -IsMultiJobVariable $false +Write-PipelineSetVariable -Name 'SetupArguments' -Value "$SetupArguments" -IsMultiJobVariable $false +Write-PipelineSetVariable -Name 'PerfLabArguments' -Value "$PerfLabArguments" -IsMultiJobVariable $false +Write-PipelineSetVariable -Name 'BDNCategories' -Value "$RunCategories" -IsMultiJobVariable $false +Write-PipelineSetVariable -Name 'TargetCsproj' -Value "$Csproj" -IsMultiJobVariable $false +Write-PipelineSetVariable -Name 'Kind' -Value "$Kind" -IsMultiJobVariable $false +Write-PipelineSetVariable -Name 'Architecture' -Value "$Architecture" -IsMultiJobVariable $false +Write-PipelineSetVariable -Name 'UseCoreRun' -Value "$UseCoreRun" -IsMultiJobVariable $false +Write-PipelineSetVariable -Name 'RunFromPerfRepo' -Value "$RunFromPerformanceRepo" -IsMultiJobVariable $false + +# Helix Arguments +Write-PipelineSetVariable -Name 'Creator' -Value "$Creator" -IsMultiJobVariable $false +Write-PipelineSetVariable -Name 'Queue' -Value "$Queue" -IsMultiJobVariable $false +Write-PipelineSetVariable -Name 'HelixSourcePrefix' -Value "$HelixSourcePrefix" -IsMultiJobVariable $false +Write-PipelineSetVariable -Name '_BuildConfig' -Value "$Architecture.$Kind.$Framework" -IsMultiJobVariable $false + +exit 0 \ No newline at end of file diff --git a/eng/common/performance/performance-setup.sh b/eng/common/performance/performance-setup.sh new file mode 100644 index 0000000000..126da5f76d --- /dev/null +++ b/eng/common/performance/performance-setup.sh @@ -0,0 +1,176 @@ +#!/usr/bin/env bash + +source_directory=$BUILD_SOURCESDIRECTORY +core_root_directory= +architecture=x64 +framework=netcoreapp3.0 +compilation_mode=tiered +repository=$BUILD_REPOSITORY_NAME +branch=$BUILD_SOURCEBRANCH +commit_sha=$BUILD_SOURCEVERSION +build_number=$BUILD_BUILDNUMBER +internal=false +kind="micro" +run_categories="coreclr corefx" +csproj="src\benchmarks\micro\MicroBenchmarks.csproj" +configurations= +run_from_perf_repo=false +use_core_run=true + +while (($# > 0)); do + lowerI="$(echo $1 | awk '{print tolower($0)}')" + case $lowerI in + --sourcedirectory) + source_directory=$2 + shift 2 + ;; + --corerootdirectory) + core_root_directory=$2 + shift 2 + ;; + --architecture) + architecture=$2 + shift 2 + ;; + --framework) + framework=$2 + shift 2 + ;; + --compilationmode) + compilation_mode=$2 + shift 2 + ;; + --repository) + repository=$2 + shift 2 + ;; + --branch) + branch=$2 + shift 2 + ;; + --commitsha) + commit_sha=$2 + shift 2 + ;; + --buildnumber) + build_number=$2 + shift 2 + ;; + --kind) + kind=$2 + shift 2 + ;; + --runcategories) + run_categories=$2 + shift 2 + ;; + --csproj) + csproj=$2 + shift 2 + ;; + --internal) + internal=true + shift 1 + ;; + --configurations) + configurations=$2 + shift 2 + ;; + --help) + echo "Common settings:" + echo " --corerootdirectory Directory where Core_Root exists, if running perf testing with --corerun" + echo " --architecture Architecture of the testing being run" + echo " --configurations List of key=value pairs that will be passed to perf testing infrastructure." + echo " ex: --configurations \"CompilationMode=Tiered OptimzationLevel=PGO\"" + echo " --help Print help and exit" + echo "" + echo "Advanced settings:" + echo " --framework The framework to run, if not running in master" + echo " --compliationmode The compilation mode if not passing --configurations" + echo " --sourcedirectory The directory of the sources. Defaults to env:BUILD_SOURCESDIRECTORY" + echo " --repository The name of the repository in the / format. Defaults to env:BUILD_REPOSITORY_NAME" + echo " --branch The name of the branch. Defaults to env:BUILD_SOURCEBRANCH" + echo " --commitsha The commit sha1 to run against. Defaults to env:BUILD_SOURCEVERSION" + echo " --buildnumber The build number currently running. Defaults to env:BUILD_BUILDNUMBER" + echo " --csproj The relative path to the benchmark csproj whose tests should be run. Defaults to src\benchmarks\micro\MicroBenchmarks.csproj" + echo " --kind Related to csproj. The kind of benchmarks that should be run. Defaults to micro" + echo " --runcategories Related to csproj. Categories of benchmarks to run. Defaults to \"coreclr corefx\"" + echo " --internal If the benchmarks are running as an official job." + echo "" + exit 0 + ;; + esac +done + +if [[ "$repository" == "dotnet/performance" ]]; then + run_from_perf_repo=true +fi + +if [ -z "$configurations" ]; then + configurations="CompliationMode=$compilation_mode" +fi + +if [ -z "$core_root_directory" ]; then + use_core_run=false +fi + +payload_directory=$source_directory/Payload +performance_directory=$payload_directory/performance +workitem_directory=$source_directory/workitem +extra_benchmark_dotnet_arguments="--iterationCount 1 --warmupCount 0 --invocationCount 1 --unrollFactor 1 --strategy ColdStart --stopOnFirstError true" +perflab_arguments= +queue=Ubuntu.1804.Amd64.Open +creator=$BUILD_DEFINITIONNAME +helix_source_prefix="pr" + +if [[ "$internal" == true ]]; then + perflab_arguments="--upload-to-perflab-container" + helix_source_prefix="official" + creator= + extra_benchmark_dotnet_arguments= + + if [[ "$architecture" = "arm64" ]]; then + queue=Ubuntu.1804.Arm64.Perf + else + queue=Ubuntu.1804.Amd64.Perf + fi +fi + +common_setup_arguments="--frameworks $framework --queue $queue --build-number $build_number --build-configs $configurations" +setup_arguments="--repository https://github.com/$repository --branch $branch --get-perf-hash --commit-sha $commit_sha $common_setup_arguments" + +if [[ "$run_from_perf_repo" = true ]]; then + payload_directory= + workitem_directory=$source_directory + performance_directory=$workitem_directory + setup_arguments="--perf-hash $commit_sha $common_setup_arguments" +else + git clone --branch master --depth 1 --quiet https://github.com/dotnet/performance $performance_directory + + docs_directory=$performance_directory/docs + mv $docs_directory $workitem_directory +fi + +if [[ "$use_core_run" = true ]]; then + new_core_root=$payload_directory/Core_Root + mv $core_root_directory $new_core_root +fi + +# Make sure all of our variables are available for future steps +echo "##vso[task.setvariable variable=UseCoreRun]$use_core_run" +echo "##vso[task.setvariable variable=Architecture]$architecture" +echo "##vso[task.setvariable variable=PayloadDirectory]$payload_directory" +echo "##vso[task.setvariable variable=PerformanceDirectory]$performance_directory" +echo "##vso[task.setvariable variable=WorkItemDirectory]$workitem_directory" +echo "##vso[task.setvariable variable=Queue]$queue" +echo "##vso[task.setvariable variable=SetupArguments]$setup_arguments" +echo "##vso[task.setvariable variable=Python]python3" +echo "##vso[task.setvariable variable=PerfLabArguments]$perflab_arguments" +echo "##vso[task.setvariable variable=ExtraBenchmarkDotNetArguments]$extra_benchmark_dotnet_arguments" +echo "##vso[task.setvariable variable=BDNCategories]$run_categories" +echo "##vso[task.setvariable variable=TargetCsproj]$csproj" +echo "##vso[task.setvariable variable=RunFromPerfRepo]$run_from_perf_repo" +echo "##vso[task.setvariable variable=Creator]$creator" +echo "##vso[task.setvariable variable=HelixSourcePrefix]$helix_source_prefix" +echo "##vso[task.setvariable variable=Kind]$kind" +echo "##vso[task.setvariable variable=_BuildConfig]$architecture.$kind.$framework" \ No newline at end of file diff --git a/eng/common/pipeline-logging-functions.ps1 b/eng/common/pipeline-logging-functions.ps1 index 7b61376f8a..af5f48aace 100644 --- a/eng/common/pipeline-logging-functions.ps1 +++ b/eng/common/pipeline-logging-functions.ps1 @@ -77,13 +77,14 @@ function Write-PipelineTaskError { [string]$Name, [string]$Value, [switch]$Secret, - [switch]$AsOutput) - + [switch]$AsOutput, + [bool]$IsMultiJobVariable=$true) + if($ci) { Write-LoggingCommand -Area 'task' -Event 'setvariable' -Data $Value -Properties @{ 'variable' = $Name 'isSecret' = $Secret - 'isOutput' = 'true' + 'isOutput' = $IsMultiJobVariable } -AsOutput:$AsOutput } } diff --git a/eng/common/post-build/trigger-subscriptions.ps1 b/eng/common/post-build/trigger-subscriptions.ps1 index db8a839457..1a91dab037 100644 --- a/eng/common/post-build/trigger-subscriptions.ps1 +++ b/eng/common/post-build/trigger-subscriptions.ps1 @@ -19,14 +19,14 @@ function Get-Headers([string]$accept, [string]$barToken) { } # Get all the $SourceRepo subscriptions -$normalizedSurceRepo = $SourceRepo.Replace('dnceng@', '') -$getSubscriptionsApiEndpoint = "$maestroEndpoint/api/subscriptions?sourceRepository=$normalizedSurceRepo&api-version=$apiVersion" +$normalizedSourceRepo = $SourceRepo.Replace('dnceng@', '') +$getSubscriptionsApiEndpoint = "$maestroEndpoint/api/subscriptions?sourceRepository=$normalizedSourceRepo&api-version=$apiVersion" $headers = Get-Headers 'application/json' $barToken $subscriptions = Invoke-WebRequest -Uri $getSubscriptionsApiEndpoint -Headers $headers | ConvertFrom-Json if (!$subscriptions) { - Write-Host "No subscriptions found for source repo '$normalizedSurceRepo' in channel '$ChannelId'" + Write-Host "No subscriptions found for source repo '$normalizedSourceRepo' in channel '$ChannelId'" return } diff --git a/eng/common/templates/job/performance.yml b/eng/common/templates/job/performance.yml new file mode 100644 index 0000000000..ef809253d1 --- /dev/null +++ b/eng/common/templates/job/performance.yml @@ -0,0 +1,93 @@ +parameters: + steps: [] # optional -- any additional steps that need to happen before pulling down the performance repo and sending the performance benchmarks to helix (ie building your repo) + variables: [] # optional -- list of additional variables to send to the template + jobName: '' # required -- job name + displayName: '' # optional -- display name for the job. Will use jobName if not passed + pool: '' # required -- name of the Build pool + container: '' # required -- name of the container + extraSetupParameters: '' # optional -- extra arguments to pass to the setup script + frameworks: ['netcoreapp3.0'] # optional -- list of frameworks to run against + continueOnError: 'false' # optional -- determines whether to continue the build if the step errors + dependsOn: '' # optional -- dependencies of the job + timeoutInMinutes: 320 # optional -- timeout for the job + enableTelemetry: false # optional -- enable for telemetry + +jobs: +- template: ../jobs/jobs.yml + parameters: + dependsOn: ${{ parameters.dependsOn }} + enableTelemetry: ${{ parameters.enableTelemetry }} + enablePublishBuildArtifacts: true + continueOnError: ${{ parameters.continueOnError }} + + jobs: + - job: '${{ parameters.jobName }}' + + ${{ if ne(parameters.displayName, '') }}: + displayName: '${{ parameters.displayName }}' + ${{ if eq(parameters.displayName, '') }}: + displayName: '${{ parameters.jobName }}' + + timeoutInMinutes: ${{ parameters.timeoutInMinutes }} + + variables: + + - ${{ each variable in parameters.variables }}: + - ${{ if ne(variable.name, '') }}: + - name: ${{ variable.name }} + value: ${{ variable.value }} + - ${{ if ne(variable.group, '') }}: + - group: ${{ variable.group }} + + - IsInternal: '' + - HelixApiAccessToken: '' + - HelixPreCommand: '' + + - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - ${{ if eq(variables['Agent.Os'], 'Windows_NT') }}: + - HelixPreCommand: 'set "PERFLAB_UPLOAD_TOKEN=$(PerfCommandUploadToken)"' + - IsInternal: -Internal + - ${{ if ne(variables['Agent.Os'], 'Windows_NT') }}: + - HelixPreCommand: 'export PERFLAB_UPLOAD_TOKEN="$(PerfCommandUploadTokenLinux)"' + - IsInternal: --internal + - group: DotNet-HelixApi-Access + - group: dotnet-benchview + + workspace: + clean: all + pool: + ${{ parameters.pool }} + container: ${{ parameters.container }} + strategy: + matrix: + ${{ each framework in parameters.frameworks }}: + ${{ framework }}: + _Framework: ${{ framework }} + steps: + - checkout: self + clean: true + # Run all of the steps to setup repo + - ${{ each step in parameters.steps }}: + - ${{ step }} + - powershell: $(Build.SourcesDirectory)\eng\common\performance\performance-setup.ps1 $(IsInternal) -Framework $(_Framework) ${{ parameters.extraSetupParameters }} + displayName: Performance Setup (Windows) + condition: and(succeeded(), eq(variables['Agent.Os'], 'Windows_NT')) + continueOnError: ${{ parameters.continueOnError }} + - script: $(Build.SourcesDirectory)/eng/common/performance/performance-setup.sh $(IsInternal) --framework $(_Framework) ${{ parameters.extraSetupParameters }} + displayName: Performance Setup (Unix) + condition: and(succeeded(), ne(variables['Agent.Os'], 'Windows_NT')) + continueOnError: ${{ parameters.continueOnError }} + - script: $(Python) $(PerformanceDirectory)/scripts/ci_setup.py $(SetupArguments) + displayName: Run ci setup script + # Run perf testing in helix + - template: /eng/common/templates/steps/perf-send-to-helix.yml + parameters: + HelixSource: '$(HelixSourcePrefix)/$(Build.Repository.Name)/$(Build.SourceBranch)' # sources must start with pr/, official/, prodcon/, or agent/ + HelixType: 'test/performance/$(Kind)/$(_Framework)/$(Architecture)' + HelixAccessToken: $(HelixApiAccessToken) + HelixTargetQueues: $(Queue) + HelixPreCommands: $(HelixPreCommand) + Creator: $(Creator) + WorkItemTimeout: 4:00 # 4 hours + WorkItemDirectory: '$(WorkItemDirectory)' # WorkItemDirectory can not be empty, so we send it some docs to keep it happy + CorrelationPayloadDirectory: '$(PayloadDirectory)' # it gets checked out to a folder with shorter path than WorkItemDirectory so we can avoid file name too long exceptions \ No newline at end of file diff --git a/eng/common/templates/post-build/channels/internal-servicing.yml b/eng/common/templates/post-build/channels/internal-servicing.yml index 808d46b17f..50ad724fc0 100644 --- a/eng/common/templates/post-build/channels/internal-servicing.yml +++ b/eng/common/templates/post-build/channels/internal-servicing.yml @@ -81,10 +81,10 @@ stages: /p:IsInternalBuild=$(IsInternalBuild) /p:RepositoryName=$(Build.Repository.Name) /p:CommitSha=$(Build.SourceVersion) + /p:AzureStorageAccountName=$(ProxyBackedFeedsAccountName) + /p:AzureStorageAccountKey=$(dotnetfeed-storage-access-key-1) + /p:AzureDevOpsFeedsBaseUrl=$(dotnetfeed-internal-private-feed-url) /p:NugetPath=$(Agent.BuildDirectory)\Nuget\NuGet.exe - /p:AzdoTargetFeedPAT='$(dn-bot-dnceng-unviersal-packages-rw)' - /p:TargetFeedPAT='$(dn-bot-dnceng-unviersal-packages-rw)' - /p:AzureStorageTargetFeedPAT='$(dotnetfeed-storage-access-key-1)' /p:BARBuildId=$(BARBuildId) /p:MaestroApiEndpoint='https://maestro-prod.westus2.cloudapp.azure.com' /p:BuildAssetRegistryToken='$(MaestroAccessToken)' @@ -167,4 +167,4 @@ stages: - template: ../promote-build.yml parameters: - ChannelId: ${{ variables.InternalServicing_30_Channel_Id }} \ No newline at end of file + ChannelId: ${{ variables.InternalServicing_30_Channel_Id }} diff --git a/eng/common/templates/post-build/channels/public-dev-release.yml b/eng/common/templates/post-build/channels/public-dev-release.yml index 79c6822db7..bdc631016b 100644 --- a/eng/common/templates/post-build/channels/public-dev-release.yml +++ b/eng/common/templates/post-build/channels/public-dev-release.yml @@ -77,6 +77,7 @@ stages: filePath: eng\common\sdk-task.ps1 arguments: -task PublishArtifactsInManifest -restore -msbuildEngine dotnet /p:ChannelId=$(PublicDevRelease_30_Channel_Id) + /p:ArtifactsCategory=.NetCore /p:IsStableBuild=$(IsStableBuild) /p:IsInternalBuild=$(IsInternalBuild) /p:RepositoryName=$(Build.Repository.Name) diff --git a/eng/common/templates/post-build/channels/public-release.yml b/eng/common/templates/post-build/channels/public-release.yml index 25923020df..574cb1c2b9 100644 --- a/eng/common/templates/post-build/channels/public-release.yml +++ b/eng/common/templates/post-build/channels/public-release.yml @@ -81,10 +81,10 @@ stages: /p:IsInternalBuild=$(IsInternalBuild) /p:RepositoryName=$(Build.Repository.Name) /p:CommitSha=$(Build.SourceVersion) - /p:NugetPath=$(Agent.BuildDirectory)/Nuget/NuGet.exe - /p:AzdoTargetFeedPAT='$(dn-bot-dnceng-unviersal-packages-rw)' - /p:TargetFeedPAT='$(dn-bot-dnceng-unviersal-packages-rw)' - /p:AzureStorageTargetFeedPAT='$(dotnetfeed-storage-access-key-1)' + /p:AzureStorageAccountName=$(ProxyBackedFeedsAccountName) + /p:AzureStorageAccountKey=$(dotnetfeed-storage-access-key-1) + /p:AzureDevOpsFeedsBaseUrl=$(dotnetfeed-internal-private-feed-url) + /p:NugetPath=$(Agent.BuildDirectory)\Nuget\NuGet.exe /p:BARBuildId=$(BARBuildId) /p:MaestroApiEndpoint='https://maestro-prod.westus2.cloudapp.azure.com' /p:BuildAssetRegistryToken='$(MaestroAccessToken)' diff --git a/eng/common/templates/post-build/channels/public-validation-release.yml b/eng/common/templates/post-build/channels/public-validation-release.yml index 114477d3ad..f12f402ad9 100644 --- a/eng/common/templates/post-build/channels/public-validation-release.yml +++ b/eng/common/templates/post-build/channels/public-validation-release.yml @@ -48,6 +48,7 @@ stages: filePath: eng\common\sdk-task.ps1 arguments: -task PublishArtifactsInManifest -restore -msbuildEngine dotnet /p:ChannelId=$(PublicValidationRelease_30_Channel_Id) + /p:ArtifactsCategory=.NetCoreValidation /p:IsStableBuild=$(IsStableBuild) /p:IsInternalBuild=$(IsInternalBuild) /p:RepositoryName=$(Build.Repository.Name) diff --git a/eng/common/templates/post-build/common-variables.yml b/eng/common/templates/post-build/common-variables.yml index 8283467352..42df4ae77e 100644 --- a/eng/common/templates/post-build/common-variables.yml +++ b/eng/common/templates/post-build/common-variables.yml @@ -14,5 +14,8 @@ variables: # Whether the build is internal or not IsInternalBuild: ${{ and(ne(variables['System.TeamProject'], 'public'), contains(variables['Build.SourceBranch'], 'internal')) }} + # Storage account name for proxy-backed feeds + ProxyBackedFeedsAccountName: dotnetfeed + SourceLinkCLIVersion: 3.0.0 SymbolToolVersion: 1.0.1 diff --git a/eng/common/templates/steps/perf-send-to-helix.yml b/eng/common/templates/steps/perf-send-to-helix.yml new file mode 100644 index 0000000000..b3ea9acf1f --- /dev/null +++ b/eng/common/templates/steps/perf-send-to-helix.yml @@ -0,0 +1,66 @@ +# Please remember to update the documentation if you make changes to these parameters! +parameters: + HelixSource: 'pr/default' # required -- sources must start with pr/, official/, prodcon/, or agent/ + HelixType: 'tests/default/' # required -- Helix telemetry which identifies what type of data this is; should include "test" for clarity and must end in '/' + HelixBuild: $(Build.BuildNumber) # required -- the build number Helix will use to identify this -- automatically set to the AzDO build number + HelixTargetQueues: '' # required -- semicolon delimited list of Helix queues to test on; see https://helix.dot.net/ for a list of queues + HelixAccessToken: '' # required -- access token to make Helix API requests; should be provided by the appropriate variable group + HelixPreCommands: '' # optional -- commands to run before Helix work item execution + HelixPostCommands: '' # optional -- commands to run after Helix work item execution + WorkItemDirectory: '' # optional -- a payload directory to zip up and send to Helix; requires WorkItemCommand; incompatible with XUnitProjects + CorrelationPayloadDirectory: '' # optional -- a directory to zip up and send to Helix as a correlation payload + IncludeDotNetCli: false # optional -- true will download a version of the .NET CLI onto the Helix machine as a correlation payload; requires DotNetCliPackageType and DotNetCliVersion + DotNetCliPackageType: '' # optional -- either 'sdk' or 'runtime'; determines whether the sdk or runtime will be sent to Helix; see https://raw.githubusercontent.com/dotnet/core/master/release-notes/releases.json + DotNetCliVersion: '' # optional -- version of the CLI to send to Helix; based on this: https://raw.githubusercontent.com/dotnet/core/master/release-notes/releases.json + EnableXUnitReporter: false # optional -- true enables XUnit result reporting to Mission Control + WaitForWorkItemCompletion: true # optional -- true will make the task wait until work items have been completed and fail the build if work items fail. False is "fire and forget." + Creator: '' # optional -- if the build is external, use this to specify who is sending the job + DisplayNamePrefix: 'Send job to Helix' # optional -- rename the beginning of the displayName of the steps in AzDO + condition: succeeded() # optional -- condition for step to execute; defaults to succeeded() + continueOnError: false # optional -- determines whether to continue the build if the step errors; defaults to false + +steps: + - powershell: $(Build.SourcesDirectory)\eng\common\msbuild.ps1 $(Build.SourcesDirectory)\eng\common\performance\perfhelixpublish.proj /restore /t:Test /bl:$(Build.SourcesDirectory)\artifacts\log\$env:BuildConfig\SendToHelix.binlog + displayName: ${{ parameters.DisplayNamePrefix }} (Windows) + env: + BuildConfig: $(_BuildConfig) + HelixSource: ${{ parameters.HelixSource }} + HelixType: ${{ parameters.HelixType }} + HelixBuild: ${{ parameters.HelixBuild }} + HelixTargetQueues: ${{ parameters.HelixTargetQueues }} + HelixAccessToken: ${{ parameters.HelixAccessToken }} + HelixPreCommands: ${{ parameters.HelixPreCommands }} + HelixPostCommands: ${{ parameters.HelixPostCommands }} + WorkItemDirectory: ${{ parameters.WorkItemDirectory }} + CorrelationPayloadDirectory: ${{ parameters.CorrelationPayloadDirectory }} + IncludeDotNetCli: ${{ parameters.IncludeDotNetCli }} + DotNetCliPackageType: ${{ parameters.DotNetCliPackageType }} + DotNetCliVersion: ${{ parameters.DotNetCliVersion }} + EnableXUnitReporter: ${{ parameters.EnableXUnitReporter }} + WaitForWorkItemCompletion: ${{ parameters.WaitForWorkItemCompletion }} + Creator: ${{ parameters.Creator }} + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + condition: and(${{ parameters.condition }}, eq(variables['Agent.Os'], 'Windows_NT')) + continueOnError: ${{ parameters.continueOnError }} + - script: $BUILD_SOURCESDIRECTORY/eng/common/msbuild.sh $BUILD_SOURCESDIRECTORY/eng/common/performance/perfhelixpublish.proj /restore /t:Test /bl:$BUILD_SOURCESDIRECTORY/artifacts/log/$BuildConfig/SendToHelix.binlog + displayName: ${{ parameters.DisplayNamePrefix }} (Unix) + env: + BuildConfig: $(_BuildConfig) + HelixSource: ${{ parameters.HelixSource }} + HelixType: ${{ parameters.HelixType }} + HelixBuild: ${{ parameters.HelixBuild }} + HelixTargetQueues: ${{ parameters.HelixTargetQueues }} + HelixAccessToken: ${{ parameters.HelixAccessToken }} + HelixPreCommands: ${{ parameters.HelixPreCommands }} + HelixPostCommands: ${{ parameters.HelixPostCommands }} + WorkItemDirectory: ${{ parameters.WorkItemDirectory }} + CorrelationPayloadDirectory: ${{ parameters.CorrelationPayloadDirectory }} + IncludeDotNetCli: ${{ parameters.IncludeDotNetCli }} + DotNetCliPackageType: ${{ parameters.DotNetCliPackageType }} + DotNetCliVersion: ${{ parameters.DotNetCliVersion }} + EnableXUnitReporter: ${{ parameters.EnableXUnitReporter }} + WaitForWorkItemCompletion: ${{ parameters.WaitForWorkItemCompletion }} + Creator: ${{ parameters.Creator }} + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + condition: and(${{ parameters.condition }}, ne(variables['Agent.Os'], 'Windows_NT')) + continueOnError: ${{ parameters.continueOnError }} diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index 60741f0390..9abaac015f 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -84,7 +84,7 @@ function Exec-Process([string]$command, [string]$commandArgs) { return $global:LASTEXITCODE = $process.ExitCode } finally { - # If we didn't finish then an error occured or the user hit ctrl-c. Either + # If we didn't finish then an error occurred or the user hit ctrl-c. Either # way kill the process if (-not $finished) { $process.Kill() @@ -147,7 +147,7 @@ function InitializeDotNetCli([bool]$install) { # It also ensures that VS msbuild will use the downloaded sdk targets. $env:PATH = "$dotnetRoot;$env:PATH" - # Make Sure that our bootstrapped dotnet cli is avaliable in future steps of the Azure Pipelines build + # Make Sure that our bootstrapped dotnet cli is available in future steps of the Azure Pipelines build Write-PipelinePrependPath -Path $dotnetRoot Write-PipelineSetVariable -Name 'DOTNET_MULTILEVEL_LOOKUP' -Value '0' Write-PipelineSetVariable -Name 'DOTNET_SKIP_FIRST_TIME_EXPERIENCE' -Value '1' diff --git a/global.json b/global.json index 8daa21248f..4c70738470 100644 --- a/global.json +++ b/global.json @@ -24,7 +24,7 @@ }, "msbuild-sdks": { "Yarn.MSBuild": "1.15.2", - "Microsoft.DotNet.Arcade.Sdk": "1.0.0-beta.19356.1", - "Microsoft.DotNet.Helix.Sdk": "2.0.0-beta.19356.1" + "Microsoft.DotNet.Arcade.Sdk": "1.0.0-beta.19365.4", + "Microsoft.DotNet.Helix.Sdk": "2.0.0-beta.19365.4" } } diff --git a/src/Mvc/Mvc.Analyzers/src/Startup/BuildServiceProviderValidator.cs b/src/Analyzers/Analyzers/src/BuildServiceProviderAnalzyer.cs similarity index 52% rename from src/Mvc/Mvc.Analyzers/src/Startup/BuildServiceProviderValidator.cs rename to src/Analyzers/Analyzers/src/BuildServiceProviderAnalzyer.cs index ef6d430eee..edd78751b9 100644 --- a/src/Mvc/Mvc.Analyzers/src/Startup/BuildServiceProviderValidator.cs +++ b/src/Analyzers/Analyzers/src/BuildServiceProviderAnalzyer.cs @@ -1,41 +1,42 @@ // 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.Concurrent; -using System.Linq; +using System.Diagnostics; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; namespace Microsoft.AspNetCore.Analyzers { - internal class BuildServiceProviderValidator : StartupDiagnosticValidator + internal class BuildServiceProviderValidator { - public static BuildServiceProviderValidator CreateAndInitialize(CompilationAnalysisContext context, ConcurrentBag analyses) + private readonly StartupAnalysis _context; + + public BuildServiceProviderValidator(StartupAnalysis context) { - if (analyses == null) - { - throw new ArgumentNullException(nameof(analyses)); - } + _context = context; + } - var validator = new BuildServiceProviderValidator(); + public void AnalyzeSymbol(SymbolAnalysisContext context) + { + Debug.Assert(context.Symbol.Kind == SymbolKind.NamedType); + Debug.Assert(StartupFacts.IsStartupClass(_context.StartupSymbols, (INamedTypeSymbol)context.Symbol)); - foreach (var serviceAnalysis in analyses.OfType()) + var type = (INamedTypeSymbol)context.Symbol; + + foreach (var serviceAnalysis in _context.GetRelatedAnalyses(type)) { foreach (var serviceItem in serviceAnalysis.Services) { if (serviceItem.UseMethod.Name == "BuildServiceProvider") { context.ReportDiagnostic(Diagnostic.Create( - StartupAnalzyer.BuildServiceProviderShouldNotCalledInConfigureServicesMethod, + StartupAnalzyer.Diagnostics.BuildServiceProviderShouldNotCalledInConfigureServicesMethod, serviceItem.Operation.Syntax.GetLocation(), serviceItem.UseMethod.Name, serviceAnalysis.ConfigureServicesMethod.Name)); } } } - - return validator; } } } diff --git a/src/Analyzers/Analyzers/src/Microsoft.AspNetCore.Analyzers.csproj b/src/Analyzers/Analyzers/src/Microsoft.AspNetCore.Analyzers.csproj index 2a6daa68bd..feec3324be 100644 --- a/src/Analyzers/Analyzers/src/Microsoft.AspNetCore.Analyzers.csproj +++ b/src/Analyzers/Analyzers/src/Microsoft.AspNetCore.Analyzers.csproj @@ -15,6 +15,7 @@ false false $(MSBuildProjectName).nuspec + Enable diff --git a/src/Mvc/Mvc.Analyzers/src/Startup/ConfigureMethodAnalysis.cs b/src/Analyzers/Analyzers/src/MiddlewareAnalysis.cs similarity index 51% rename from src/Mvc/Mvc.Analyzers/src/Startup/ConfigureMethodAnalysis.cs rename to src/Analyzers/Analyzers/src/MiddlewareAnalysis.cs index 394dd23bff..789dbcc352 100644 --- a/src/Mvc/Mvc.Analyzers/src/Startup/ConfigureMethodAnalysis.cs +++ b/src/Analyzers/Analyzers/src/MiddlewareAnalysis.cs @@ -1,18 +1,23 @@ // 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.Immutable; using Microsoft.CodeAnalysis; namespace Microsoft.AspNetCore.Analyzers { - internal abstract class ConfigureMethodAnalysis : StartupComputedAnalysis + internal class MiddlewareAnalysis { - protected ConfigureMethodAnalysis(IMethodSymbol configureMethod) - : base(configureMethod.ContainingType) + public MiddlewareAnalysis(IMethodSymbol configureMethod, ImmutableArray middleware) { ConfigureMethod = configureMethod; + Middleware = middleware; } + public INamedTypeSymbol StartupType => ConfigureMethod.ContainingType; + public IMethodSymbol ConfigureMethod { get; } + + public ImmutableArray Middleware { get; } } } diff --git a/src/Analyzers/Analyzers/src/MiddlewareAnalyzer.cs b/src/Analyzers/Analyzers/src/MiddlewareAnalyzer.cs new file mode 100644 index 0000000000..0a8deb5dc4 --- /dev/null +++ b/src/Analyzers/Analyzers/src/MiddlewareAnalyzer.cs @@ -0,0 +1,48 @@ +// 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.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.AspNetCore.Analyzers +{ + internal class MiddlewareAnalyzer + { + private readonly StartupAnalysisBuilder _context; + + public MiddlewareAnalyzer(StartupAnalysisBuilder context) + { + _context = context; + } + + public void AnalyzeConfigureMethod(OperationBlockStartAnalysisContext context) + { + var configureMethod = (IMethodSymbol)context.OwningSymbol; + var middleware = ImmutableArray.CreateBuilder(); + + // Note: this is a simple source-order implementation. We don't attempt perform data flow + // analysis in order to determine the actual order in which middleware are ordered. + // + // This can currently be confused by things like Map(...) + context.RegisterOperationAction(context => + { + // We're looking for usage of extension methods, so we need to look at the 'this' parameter + // rather than invocation.Instance. + if (context.Operation is IInvocationOperation invocation && + invocation.Instance == null && + invocation.Arguments.Length >= 1 && + invocation.Arguments[0].Parameter?.Type == _context.StartupSymbols.IApplicationBuilder) + { + middleware.Add(new MiddlewareItem(invocation)); + } + }, OperationKind.Invocation); + + context.RegisterOperationBlockEndAction(context => + { + _context.ReportAnalysis(new MiddlewareAnalysis(configureMethod, middleware.ToImmutable())); + }); + } + } +} diff --git a/src/Mvc/Mvc.Analyzers/src/Startup/MiddlewareItem.cs b/src/Analyzers/Analyzers/src/MiddlewareItem.cs similarity index 100% rename from src/Mvc/Mvc.Analyzers/src/Startup/MiddlewareItem.cs rename to src/Analyzers/Analyzers/src/MiddlewareItem.cs diff --git a/src/Mvc/Mvc.Analyzers/src/Startup/ConfigureServicesMethodAnalysis.cs b/src/Analyzers/Analyzers/src/OptionsAnalysis.cs similarity index 53% rename from src/Mvc/Mvc.Analyzers/src/Startup/ConfigureServicesMethodAnalysis.cs rename to src/Analyzers/Analyzers/src/OptionsAnalysis.cs index ebe773474b..08f0e11e95 100644 --- a/src/Mvc/Mvc.Analyzers/src/Startup/ConfigureServicesMethodAnalysis.cs +++ b/src/Analyzers/Analyzers/src/OptionsAnalysis.cs @@ -1,18 +1,23 @@ // 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.Immutable; using Microsoft.CodeAnalysis; namespace Microsoft.AspNetCore.Analyzers { - internal abstract class ConfigureServicesMethodAnalysis : StartupComputedAnalysis + internal class OptionsAnalysis { - protected ConfigureServicesMethodAnalysis(IMethodSymbol configureServicesMethod) - : base(configureServicesMethod.ContainingType) + public OptionsAnalysis(IMethodSymbol configureServicesMethod, ImmutableArray options) { ConfigureServicesMethod = configureServicesMethod; + Options = options; } + public INamedTypeSymbol StartupType => ConfigureServicesMethod.ContainingType; + public IMethodSymbol ConfigureServicesMethod { get; } + + public ImmutableArray Options { get; } } } diff --git a/src/Analyzers/Analyzers/src/OptionsAnalyzer.cs b/src/Analyzers/Analyzers/src/OptionsAnalyzer.cs new file mode 100644 index 0000000000..a6649c0194 --- /dev/null +++ b/src/Analyzers/Analyzers/src/OptionsAnalyzer.cs @@ -0,0 +1,43 @@ +// 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.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.AspNetCore.Analyzers +{ + internal class OptionsAnalyzer + { + private readonly StartupAnalysisBuilder _context; + + public OptionsAnalyzer(StartupAnalysisBuilder context) + { + _context = context; + } + + public void AnalyzeConfigureServices(OperationBlockStartAnalysisContext context) + { + var configureServicesMethod = (IMethodSymbol)context.OwningSymbol; + var options = ImmutableArray.CreateBuilder(); + context.RegisterOperationAction(context => + { + if (context.Operation is ISimpleAssignmentOperation operation && + operation.Value.ConstantValue.HasValue && + operation.Target is IPropertyReferenceOperation property && + property.Property?.ContainingType?.Name != null && + property.Property.ContainingType.Name.EndsWith("Options")) + { + options.Add(new OptionsItem(property.Property, operation.Value.ConstantValue.Value)); + } + + }, OperationKind.SimpleAssignment); + + context.RegisterOperationBlockEndAction(context => + { + _context.ReportAnalysis(new OptionsAnalysis(configureServicesMethod, options.ToImmutable())); + }); + } + } +} diff --git a/src/Analyzers/Analyzers/src/OptionsFacts.cs b/src/Analyzers/Analyzers/src/OptionsFacts.cs new file mode 100644 index 0000000000..fb89a0224d --- /dev/null +++ b/src/Analyzers/Analyzers/src/OptionsFacts.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; +using Microsoft.CodeAnalysis; + +namespace Microsoft.AspNetCore.Analyzers +{ + internal static class OptionsFacts + { + public static bool IsEndpointRoutingExplicitlyDisabled(OptionsAnalysis analysis) + { + for (var i = 0; i < analysis.Options.Length; i++) + { + var item = analysis.Options[i]; + if (string.Equals(item.OptionsType.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat), SymbolNames.MvcOptions.MetadataName) && + string.Equals(item.Property.Name, SymbolNames.MvcOptions.EnableEndpointRoutingPropertyName, StringComparison.Ordinal)) + { + return item.ConstantValue as bool? == false; + } + } + + return false; + } + } +} diff --git a/src/Analyzers/Analyzers/src/OptionsItem.cs b/src/Analyzers/Analyzers/src/OptionsItem.cs new file mode 100644 index 0000000000..d39850339f --- /dev/null +++ b/src/Analyzers/Analyzers/src/OptionsItem.cs @@ -0,0 +1,22 @@ +// 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.CodeAnalysis; + +namespace Microsoft.AspNetCore.Analyzers +{ + internal class OptionsItem + { + public OptionsItem(IPropertySymbol property, object constantValue) + { + Property = property; + ConstantValue = constantValue; + } + + public INamedTypeSymbol OptionsType => Property.ContainingType; + + public IPropertySymbol Property { get; } + + public object ConstantValue { get; } + } +} diff --git a/src/Analyzers/Analyzers/src/ServicesAnalysis.cs b/src/Analyzers/Analyzers/src/ServicesAnalysis.cs new file mode 100644 index 0000000000..2cb58ca5d5 --- /dev/null +++ b/src/Analyzers/Analyzers/src/ServicesAnalysis.cs @@ -0,0 +1,23 @@ +// 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.Immutable; +using Microsoft.CodeAnalysis; + +namespace Microsoft.AspNetCore.Analyzers +{ + internal class ServicesAnalysis + { + public ServicesAnalysis(IMethodSymbol configureServicesMethod, ImmutableArray services) + { + ConfigureServicesMethod = configureServicesMethod; + Services = services; + } + + public INamedTypeSymbol StartupType => ConfigureServicesMethod.ContainingType; + + public IMethodSymbol ConfigureServicesMethod { get; } + + public ImmutableArray Services { get; } + } +} diff --git a/src/Analyzers/Analyzers/src/ServicesAnalyzer.cs b/src/Analyzers/Analyzers/src/ServicesAnalyzer.cs new file mode 100644 index 0000000000..dc79cf4727 --- /dev/null +++ b/src/Analyzers/Analyzers/src/ServicesAnalyzer.cs @@ -0,0 +1,43 @@ +// 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.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.AspNetCore.Analyzers +{ + internal class ServicesAnalyzer + { + private readonly StartupAnalysisBuilder _context; + + public ServicesAnalyzer(StartupAnalysisBuilder context) + { + _context = context; + } + + public void AnalyzeConfigureServices(OperationBlockStartAnalysisContext context) + { + var configureServicesMethod = (IMethodSymbol)context.OwningSymbol; + var services = ImmutableArray.CreateBuilder(); + context.RegisterOperationAction(context => + { + // We're looking for usage of extension methods, so we need to look at the 'this' parameter + // rather than invocation.Instance. + if (context.Operation is IInvocationOperation invocation && + invocation.Instance == null && + invocation.Arguments.Length >= 1 && + invocation.Arguments[0].Parameter?.Type == _context.StartupSymbols.IServiceCollection) + { + services.Add(new ServicesItem(invocation)); + } + }, OperationKind.Invocation); + + context.RegisterOperationBlockEndAction(context => + { + _context.ReportAnalysis(new ServicesAnalysis(configureServicesMethod, services.ToImmutable())); + }); + } + } +} diff --git a/src/Mvc/Mvc.Analyzers/src/Startup/ServicesItem.cs b/src/Analyzers/Analyzers/src/ServicesItem.cs similarity index 100% rename from src/Mvc/Mvc.Analyzers/src/Startup/ServicesItem.cs rename to src/Analyzers/Analyzers/src/ServicesItem.cs diff --git a/src/Analyzers/Analyzers/src/StartupAnalysis.cs b/src/Analyzers/Analyzers/src/StartupAnalysis.cs new file mode 100644 index 0000000000..9ddc03e04e --- /dev/null +++ b/src/Analyzers/Analyzers/src/StartupAnalysis.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.Immutable; +using Microsoft.CodeAnalysis; + +namespace Microsoft.AspNetCore.Analyzers +{ + internal class StartupAnalysis + { + private ImmutableDictionary> _analysesByType; + + public StartupAnalysis( + StartupSymbols startupSymbols, + ImmutableDictionary> analysesByType) + { + StartupSymbols = startupSymbols; + _analysesByType = analysesByType; + } + + public StartupSymbols StartupSymbols { get; } + + public T? GetRelatedSingletonAnalysis(INamedTypeSymbol type) where T : class + { + if (_analysesByType.TryGetValue(type, out var list)) + { + for (var i = 0; i < list.Length; i++) + { + if (list[i] is T item) + { + return item; + } + } + } + + return null; + } + + public ImmutableArray GetRelatedAnalyses(INamedTypeSymbol type) where T : class + { + var items = ImmutableArray.CreateBuilder(); + if (_analysesByType.TryGetValue(type, out var list)) + { + for (var i = 0; i < list.Length; i++) + { + if (list[i] is T item) + { + items.Add(item); + } + } + } + + return items.ToImmutable(); + } + } +} diff --git a/src/Analyzers/Analyzers/src/StartupAnalysisBuilder.cs b/src/Analyzers/Analyzers/src/StartupAnalysisBuilder.cs new file mode 100644 index 0000000000..e5b1217dd7 --- /dev/null +++ b/src/Analyzers/Analyzers/src/StartupAnalysisBuilder.cs @@ -0,0 +1,71 @@ +// 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.Collections.Immutable; +using Microsoft.CodeAnalysis; + +namespace Microsoft.AspNetCore.Analyzers +{ + internal class StartupAnalysisBuilder + { + private readonly Dictionary> _analysesByType; + private readonly StartupAnalzyer _analyzer; + private readonly object _lock; + + public StartupAnalysisBuilder(StartupAnalzyer analyzer, StartupSymbols startupSymbols) + { + _analyzer = analyzer; + StartupSymbols = startupSymbols; + + _analysesByType = new Dictionary>(); + _lock = new object(); + } + + public StartupSymbols StartupSymbols { get; } + + public StartupAnalysis Build() + { + lock (_lock) + { + return new StartupAnalysis( + StartupSymbols, + _analysesByType.ToImmutableDictionary( + k => k.Key, + v => v.Value.ToImmutableArray())); + } + } + + public void ReportAnalysis(ServicesAnalysis analysis) + { + ReportAnalysisCore(analysis.StartupType, analysis); + _analyzer.OnServicesAnalysisCompleted(analysis); + } + + public void ReportAnalysis(OptionsAnalysis analysis) + { + ReportAnalysisCore(analysis.StartupType, analysis); + _analyzer.OnOptionsAnalysisCompleted(analysis); + } + + public void ReportAnalysis(MiddlewareAnalysis analysis) + { + ReportAnalysisCore(analysis.StartupType, analysis); + _analyzer.OnMiddlewareAnalysisCompleted(analysis); + } + + private void ReportAnalysisCore(INamedTypeSymbol type, object analysis) + { + lock (_lock) + { + if (!_analysesByType.TryGetValue(type, out var list)) + { + list = new List(); + _analysesByType.Add(type, list); + } + + list.Add(analysis); + } + } + } +} diff --git a/src/Analyzers/Analyzers/src/StartupAnalyzer.Diagnostics.cs b/src/Analyzers/Analyzers/src/StartupAnalyzer.Diagnostics.cs new file mode 100644 index 0000000000..0a04924177 --- /dev/null +++ b/src/Analyzers/Analyzers/src/StartupAnalyzer.Diagnostics.cs @@ -0,0 +1,47 @@ +// 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.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.AspNetCore.Analyzers +{ + public partial class StartupAnalzyer : DiagnosticAnalyzer + { + internal static class Diagnostics + { + public static readonly ImmutableArray SupportedDiagnostics; + + static Diagnostics() + { + SupportedDiagnostics = ImmutableArray.Create(new[] + { + // ASP + BuildServiceProviderShouldNotCalledInConfigureServicesMethod, + + // MVC + UnsupportedUseMvcWithEndpointRouting, + }); + } + + internal readonly static DiagnosticDescriptor BuildServiceProviderShouldNotCalledInConfigureServicesMethod = new DiagnosticDescriptor( + "ASP0000", + "Do not call 'IServiceCollection.BuildServiceProvider' in 'ConfigureServices'", + "Calling 'BuildServiceProvider' from application code results in an additional copy of singleton services being created. Consider alternatives such as dependency injecting services as parameters to 'Configure'.", + "Design", + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + helpLinkUri: "https://aka.ms/AA5k895"); + + internal readonly static DiagnosticDescriptor UnsupportedUseMvcWithEndpointRouting = new DiagnosticDescriptor( + "MVC1005", + "Cannot use UseMvc with Endpoint Routing.", + "Using '{0}' to configure MVC is not supported while using Endpoint Routing. To continue using '{0}', please set 'MvcOptions.EnableEndpointRounting = false' inside '{1}'.", + "Usage", + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + helpLinkUri: "https://aka.ms/YJggeFn"); + } + } +} diff --git a/src/Analyzers/Analyzers/src/StartupAnalyzer.Events.cs b/src/Analyzers/Analyzers/src/StartupAnalyzer.Events.cs new file mode 100644 index 0000000000..9e2a554948 --- /dev/null +++ b/src/Analyzers/Analyzers/src/StartupAnalyzer.Events.cs @@ -0,0 +1,48 @@ +// 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 Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.AspNetCore.Analyzers +{ + // Events for testability. Allows us to unit test the data we gather from analysis. + public partial class StartupAnalzyer : DiagnosticAnalyzer + { + internal event EventHandler? ConfigureServicesMethodFound; + + internal void OnConfigureServicesMethodFound(IMethodSymbol method) + { + ConfigureServicesMethodFound?.Invoke(this, method); + } + + internal event EventHandler? ServicesAnalysisCompleted; + + internal void OnServicesAnalysisCompleted(ServicesAnalysis analysis) + { + ServicesAnalysisCompleted?.Invoke(this, analysis); + } + + internal event EventHandler? OptionsAnalysisCompleted; + + internal void OnOptionsAnalysisCompleted(OptionsAnalysis analysis) + { + OptionsAnalysisCompleted?.Invoke(this, analysis); + } + + internal event EventHandler? ConfigureMethodFound; + + internal void OnConfigureMethodFound(IMethodSymbol method) + { + ConfigureMethodFound?.Invoke(this, method); + } + + internal event EventHandler? MiddlewareAnalysisCompleted; + + internal void OnMiddlewareAnalysisCompleted(MiddlewareAnalysis analysis) + { + MiddlewareAnalysisCompleted?.Invoke(this, analysis); + } + } +} diff --git a/src/Analyzers/Analyzers/src/StartupAnalzyer.cs b/src/Analyzers/Analyzers/src/StartupAnalzyer.cs new file mode 100644 index 0000000000..3cbb83c180 --- /dev/null +++ b/src/Analyzers/Analyzers/src/StartupAnalzyer.cs @@ -0,0 +1,90 @@ +// 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.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.AspNetCore.Analyzers +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public partial class StartupAnalzyer : DiagnosticAnalyzer + { + public override ImmutableArray SupportedDiagnostics => Diagnostics.SupportedDiagnostics; + + public override void Initialize(AnalysisContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.RegisterCompilationStartAction(OnCompilationStart); + } + + private void OnCompilationStart(CompilationStartAnalysisContext context) + { + var symbols = new StartupSymbols(context.Compilation); + + // Don't run analyzer if ASP.NET Core types cannot be found + if (!symbols.HasRequiredSymbols) + { + return; + } + + context.RegisterSymbolStartAction(context => + { + var type = (INamedTypeSymbol)context.Symbol; + if (!StartupFacts.IsStartupClass(symbols, type)) + { + // Not a startup class, nothing to do. + return; + } + + // This analyzer fans out a bunch of jobs. The context will capture the results of doing analysis + // on the startup code, so that other analyzers that run later can examine them. + var builder = new StartupAnalysisBuilder(this, symbols); + + var services = new ServicesAnalyzer(builder); + var options = new OptionsAnalyzer(builder); + var middleware = new MiddlewareAnalyzer(builder); + + context.RegisterOperationBlockStartAction(context => + { + if (context.OwningSymbol.Kind != SymbolKind.Method) + { + return; + } + + var method = (IMethodSymbol)context.OwningSymbol; + if (StartupFacts.IsConfigureServices(symbols, method)) + { + OnConfigureServicesMethodFound(method); + + services.AnalyzeConfigureServices(context); + options.AnalyzeConfigureServices(context); + } + + if (StartupFacts.IsConfigure(symbols, method)) + { + OnConfigureMethodFound(method); + + middleware.AnalyzeConfigureMethod(context); + } + }); + + // Run after analyses have had a chance to finish to add diagnostics. + context.RegisterSymbolEndAction(context => + { + var analysis = builder.Build(); + new UseMvcAnalyzer(analysis).AnalyzeSymbol(context); + new BuildServiceProviderValidator(analysis).AnalyzeSymbol(context); + }); + + }, SymbolKind.NamedType); + } + } +} diff --git a/src/Analyzers/Analyzers/src/StartupSymbols.cs b/src/Analyzers/Analyzers/src/StartupSymbols.cs index 9b98302b46..e1beb5a74d 100644 --- a/src/Analyzers/Analyzers/src/StartupSymbols.cs +++ b/src/Analyzers/Analyzers/src/StartupSymbols.cs @@ -11,6 +11,7 @@ namespace Microsoft.AspNetCore.Analyzers { IApplicationBuilder = compilation.GetTypeByMetadataName(SymbolNames.IApplicationBuilder.MetadataName); IServiceCollection = compilation.GetTypeByMetadataName(SymbolNames.IServiceCollection.MetadataName); + MvcOptions = compilation.GetTypeByMetadataName(SymbolNames.MvcOptions.MetadataName); } public bool HasRequiredSymbols => IApplicationBuilder != null && IServiceCollection != null; @@ -18,5 +19,7 @@ namespace Microsoft.AspNetCore.Analyzers public INamedTypeSymbol IApplicationBuilder { get; } public INamedTypeSymbol IServiceCollection { get; } + + public INamedTypeSymbol MvcOptions { get; } } } diff --git a/src/Analyzers/Analyzers/src/SymbolNames.cs b/src/Analyzers/Analyzers/src/SymbolNames.cs index 0442d96c75..eb544f1e67 100644 --- a/src/Analyzers/Analyzers/src/SymbolNames.cs +++ b/src/Analyzers/Analyzers/src/SymbolNames.cs @@ -41,5 +41,12 @@ namespace Microsoft.AspNetCore.Analyzers public const string UseSignalRMethodName = "UseSignalR"; } + + public static class MvcOptions + { + public const string MetadataName = "Microsoft.AspNetCore.Mvc.MvcOptions"; + + public const string EnableEndpointRoutingPropertyName = "EnableEndpointRouting"; + } } } diff --git a/src/Analyzers/Analyzers/src/UseMvcAnalyzer.cs b/src/Analyzers/Analyzers/src/UseMvcAnalyzer.cs new file mode 100644 index 0000000000..4cbb702ad1 --- /dev/null +++ b/src/Analyzers/Analyzers/src/UseMvcAnalyzer.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. + +using System.Diagnostics; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.AspNetCore.Analyzers +{ + internal class UseMvcAnalyzer + { + private readonly StartupAnalysis _context; + + public UseMvcAnalyzer(StartupAnalysis context) + { + _context = context; + } + + public void AnalyzeSymbol(SymbolAnalysisContext context) + { + Debug.Assert(context.Symbol.Kind == SymbolKind.NamedType); + Debug.Assert(StartupFacts.IsStartupClass(_context.StartupSymbols, (INamedTypeSymbol)context.Symbol)); + + var type = (INamedTypeSymbol)context.Symbol; + + var optionsAnalysis = _context.GetRelatedSingletonAnalysis(type); + if (optionsAnalysis == null) + { + return; + } + + // Find the middleware analysis foreach of the Configure methods defined by this class and validate. + // + // Note that this doesn't attempt to handle inheritance scenarios. + foreach (var middlewareAnalysis in _context.GetRelatedAnalyses(type)) + { + foreach (var middlewareItem in middlewareAnalysis.Middleware) + { + if (middlewareItem.UseMethod.Name == "UseMvc" || middlewareItem.UseMethod.Name == "UseMvcWithDefaultRoute") + { + // Report a diagnostic if it's unclear that the user turned off Endpoint Routing. + if (!OptionsFacts.IsEndpointRoutingExplicitlyDisabled(optionsAnalysis)) + { + context.ReportDiagnostic(Diagnostic.Create( + StartupAnalzyer.Diagnostics.UnsupportedUseMvcWithEndpointRouting, + middlewareItem.Operation.Syntax.GetLocation(), + middlewareItem.UseMethod.Name, + optionsAnalysis.ConfigureServicesMethod.Name)); + } + } + } + } + } + } +} diff --git a/src/Analyzers/Analyzers/test/AnalyzerTestBase.cs b/src/Analyzers/Analyzers/test/AnalyzerTestBase.cs index dee9dd3227..9ec94add87 100644 --- a/src/Analyzers/Analyzers/test/AnalyzerTestBase.cs +++ b/src/Analyzers/Analyzers/test/AnalyzerTestBase.cs @@ -17,6 +17,11 @@ namespace Microsoft.AspNetCore.Analyzers public TestSource Read(string source) { + if (!source.EndsWith(".cs")) + { + source = source + ".cs"; + } + var filePath = Path.Combine(ProjectDirectory, "TestFiles", GetType().Name, source); if (!File.Exists(filePath)) { diff --git a/src/Analyzers/Analyzers/test/AnalyzersDiagnosticAnalyzerRunner.cs b/src/Analyzers/Analyzers/test/AnalyzersDiagnosticAnalyzerRunner.cs new file mode 100644 index 0000000000..57a8ce8a69 --- /dev/null +++ b/src/Analyzers/Analyzers/test/AnalyzersDiagnosticAnalyzerRunner.cs @@ -0,0 +1,31 @@ +// 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.Tasks; +using Microsoft.AspNetCore.Analyzer.Testing; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.AspNetCore.Analyzers +{ + internal class AnalyzersDiagnosticAnalyzerRunner : DiagnosticAnalyzerRunner + { + public AnalyzersDiagnosticAnalyzerRunner(DiagnosticAnalyzer analyzer) + { + Analyzer = analyzer; + } + + public DiagnosticAnalyzer Analyzer { get; } + + public Task GetDiagnosticsAsync(string source) + { + return GetDiagnosticsAsync(sources: new[] { source }, Analyzer, Array.Empty()); + } + + public Task GetDiagnosticsAsync(Project project) + { + return GetDiagnosticsAsync(new[] { project }, Analyzer, Array.Empty()); + } + } +} diff --git a/src/Mvc/Mvc.Analyzers/test/StartupAnalyzerTest.cs b/src/Analyzers/Analyzers/test/StartupAnalyzerTest.cs similarity index 70% rename from src/Mvc/Mvc.Analyzers/test/StartupAnalyzerTest.cs rename to src/Analyzers/Analyzers/test/StartupAnalyzerTest.cs index 16c7af384d..c471b56a56 100644 --- a/src/Mvc/Mvc.Analyzers/test/StartupAnalyzerTest.cs +++ b/src/Analyzers/Analyzers/test/StartupAnalyzerTest.cs @@ -1,39 +1,38 @@ -// 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.Concurrent; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Analyzer.Testing; -using Microsoft.AspNetCore.Mvc; using Microsoft.CodeAnalysis; using Xunit; namespace Microsoft.AspNetCore.Analyzers { - public class StartupAnalyzerTest + public class StartupAnalyzerTest : AnalyzerTestBase { public StartupAnalyzerTest() { StartupAnalyzer = new StartupAnalzyer(); - StartupAnalyzer.StartupFilePredicate = path => path.Equals("Test.cs", StringComparison.Ordinal); - Runner = new MvcDiagnosticAnalyzerRunner(StartupAnalyzer); + Runner = new AnalyzersDiagnosticAnalyzerRunner(StartupAnalyzer); - Analyses = new ConcurrentBag(); + Analyses = new ConcurrentBag(); ConfigureServicesMethods = new ConcurrentBag(); ConfigureMethods = new ConcurrentBag(); - StartupAnalyzer.AnalysisStarted += (sender, analysis) => Analyses.Add(analysis); + StartupAnalyzer.ServicesAnalysisCompleted += (sender, analysis) => Analyses.Add(analysis); + StartupAnalyzer.OptionsAnalysisCompleted += (sender, analysis) => Analyses.Add(analysis); + StartupAnalyzer.MiddlewareAnalysisCompleted += (sender, analysis) => Analyses.Add(analysis); StartupAnalyzer.ConfigureServicesMethodFound += (sender, method) => ConfigureServicesMethods.Add(method); StartupAnalyzer.ConfigureMethodFound += (sender, method) => ConfigureMethods.Add(method); } private StartupAnalzyer StartupAnalyzer { get; } - private MvcDiagnosticAnalyzerRunner Runner { get; } + private AnalyzersDiagnosticAnalyzerRunner Runner { get; } - private ConcurrentBag Analyses { get; } + private ConcurrentBag Analyses { get; } private ConcurrentBag ConfigureServicesMethods { get; } @@ -43,7 +42,7 @@ namespace Microsoft.AspNetCore.Analyzers public async Task StartupAnalyzer_FindsStartupMethods_StartupSignatures_Standard() { // Arrange - var source = ReadSource("StartupSignatures_Standard"); + var source = Read("StartupSignatures_Standard"); // Act var diagnostics = await Runner.GetDiagnosticsAsync(source.Source); @@ -59,7 +58,7 @@ namespace Microsoft.AspNetCore.Analyzers public async Task StartupAnalyzer_FindsStartupMethods_StartupSignatures_MoreVariety() { // Arrange - var source = ReadSource("StartupSignatures_MoreVariety"); + var source = Read("StartupSignatures_MoreVariety"); // Act var diagnostics = await Runner.GetDiagnosticsAsync(source.Source); @@ -81,14 +80,14 @@ namespace Microsoft.AspNetCore.Analyzers public async Task StartupAnalyzer_MvcOptionsAnalysis_UseMvc_FindsEndpointRoutingDisabled() { // Arrange - var source = ReadSource("MvcOptions_UseMvcWithDefaultRouteAndEndpointRoutingDisabled"); + var source = Read("MvcOptions_UseMvcWithDefaultRouteAndEndpointRoutingDisabled"); // Act var diagnostics = await Runner.GetDiagnosticsAsync(source.Source); // Assert - var mvcOptionsAnalysis = Assert.Single(Analyses.OfType()); - Assert.False(mvcOptionsAnalysis.EndpointRoutingEnabled); + var optionsAnalysis = Assert.Single(Analyses.OfType()); + Assert.True(OptionsFacts.IsEndpointRoutingExplicitlyDisabled(optionsAnalysis)); var middlewareAnalysis = Assert.Single(Analyses.OfType()); var middleware = Assert.Single(middlewareAnalysis.Middleware); @@ -101,14 +100,14 @@ namespace Microsoft.AspNetCore.Analyzers public async Task StartupAnalyzer_MvcOptionsAnalysis_AddMvcOptions_FindsEndpointRoutingDisabled() { // Arrange - var source = ReadSource("MvcOptions_UseMvcWithDefaultRouteAndAddMvcOptionsEndpointRoutingDisabled"); + var source = Read("MvcOptions_UseMvcWithDefaultRouteAndAddMvcOptionsEndpointRoutingDisabled"); // Act var diagnostics = await Runner.GetDiagnosticsAsync(source.Source); // Assert - var mvcOptionsAnalysis = Assert.Single(Analyses.OfType()); - Assert.False(mvcOptionsAnalysis.EndpointRoutingEnabled); + var optionsAnalysis = Assert.Single(Analyses.OfType()); + Assert.True(OptionsFacts.IsEndpointRoutingExplicitlyDisabled(optionsAnalysis)); var middlewareAnalysis = Assert.Single(Analyses.OfType()); var middleware = Assert.Single(middlewareAnalysis.Middleware); @@ -124,14 +123,14 @@ namespace Microsoft.AspNetCore.Analyzers public async Task StartupAnalyzer_MvcOptionsAnalysis_FindsEndpointRoutingEnabled(string sourceFileName, string mvcMiddlewareName) { // Arrange - var source = ReadSource(sourceFileName); + var source = Read(sourceFileName); // Act var diagnostics = await Runner.GetDiagnosticsAsync(source.Source); // Assert - var mvcOptionsAnalysis = Assert.Single(Analyses.OfType()); - Assert.Null(mvcOptionsAnalysis.EndpointRoutingEnabled); + var optionsAnalysis = Assert.Single(Analyses.OfType()); + Assert.False(OptionsFacts.IsEndpointRoutingExplicitlyDisabled(optionsAnalysis)); var middlewareAnalysis = Assert.Single(Analyses.OfType()); var middleware = Assert.Single(middlewareAnalysis.Middleware); @@ -141,7 +140,7 @@ namespace Microsoft.AspNetCore.Analyzers diagnostics, diagnostic => { - Assert.Same(StartupAnalzyer.UnsupportedUseMvcWithEndpointRouting, diagnostic.Descriptor); + Assert.Same(StartupAnalzyer.Diagnostics.UnsupportedUseMvcWithEndpointRouting, diagnostic.Descriptor); AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location); }); } @@ -150,14 +149,14 @@ namespace Microsoft.AspNetCore.Analyzers public async Task StartupAnalyzer_MvcOptionsAnalysis_MultipleMiddleware() { // Arrange - var source = ReadSource("MvcOptions_UseMvcWithOtherMiddleware"); + var source = Read("MvcOptions_UseMvcWithOtherMiddleware"); // Act var diagnostics = await Runner.GetDiagnosticsAsync(source.Source); // Assert - var mvcOptionsAnalysis = Assert.Single(Analyses.OfType()); - Assert.Null(mvcOptionsAnalysis.EndpointRoutingEnabled); + var optionsAnalysis = Assert.Single(Analyses.OfType()); + Assert.False(OptionsFacts.IsEndpointRoutingExplicitlyDisabled(optionsAnalysis)); var middlewareAnalysis = Assert.Single(Analyses.OfType()); @@ -173,7 +172,7 @@ namespace Microsoft.AspNetCore.Analyzers diagnostics, diagnostic => { - Assert.Same(StartupAnalzyer.UnsupportedUseMvcWithEndpointRouting, diagnostic.Descriptor); + Assert.Same(StartupAnalzyer.Diagnostics.UnsupportedUseMvcWithEndpointRouting, diagnostic.Descriptor); AnalyzerAssert.DiagnosticLocation(source.DefaultMarkerLocation, diagnostic.Location); }); } @@ -182,38 +181,39 @@ namespace Microsoft.AspNetCore.Analyzers public async Task StartupAnalyzer_MvcOptionsAnalysis_MultipleUseMvc() { // Arrange - var source = ReadSource("MvcOptions_UseMvcMultiple"); + var source = Read("MvcOptions_UseMvcMultiple"); // Act var diagnostics = await Runner.GetDiagnosticsAsync(source.Source); // Assert - var mvcOptionsAnalysis = Assert.Single(Analyses.OfType()); - Assert.Null(mvcOptionsAnalysis.EndpointRoutingEnabled); + var optionsAnalysis = Assert.Single(Analyses.OfType()); + Assert.False(OptionsFacts.IsEndpointRoutingExplicitlyDisabled(optionsAnalysis)); Assert.Collection( diagnostics, diagnostic => { - Assert.Same(StartupAnalzyer.UnsupportedUseMvcWithEndpointRouting, diagnostic.Descriptor); + Assert.Same(StartupAnalzyer.Diagnostics.UnsupportedUseMvcWithEndpointRouting, diagnostic.Descriptor); AnalyzerAssert.DiagnosticLocation(source.MarkerLocations["MM1"], diagnostic.Location); }, diagnostic => { - Assert.Same(StartupAnalzyer.UnsupportedUseMvcWithEndpointRouting, diagnostic.Descriptor); + Assert.Same(StartupAnalzyer.Diagnostics.UnsupportedUseMvcWithEndpointRouting, diagnostic.Descriptor); AnalyzerAssert.DiagnosticLocation(source.MarkerLocations["MM2"], diagnostic.Location); }, diagnostic => { - Assert.Same(StartupAnalzyer.UnsupportedUseMvcWithEndpointRouting, diagnostic.Descriptor); + Assert.Same(StartupAnalzyer.Diagnostics.UnsupportedUseMvcWithEndpointRouting, diagnostic.Descriptor); AnalyzerAssert.DiagnosticLocation(source.MarkerLocations["MM3"], diagnostic.Location); }); } + [Fact] public async Task StartupAnalyzer_ServicesAnalysis_CallBuildServiceProvider() { // Arrange - var source = ReadSource("ConfigureServices_BuildServiceProvider"); + var source = Read("ConfigureServices_BuildServiceProvider"); // Act var diagnostics = await Runner.GetDiagnosticsAsync(source.Source); @@ -224,13 +224,9 @@ namespace Microsoft.AspNetCore.Analyzers Assert.Collection(diagnostics, diagnostic => { - Assert.Same(StartupAnalzyer.BuildServiceProviderShouldNotCalledInConfigureServicesMethod, diagnostic.Descriptor); + Assert.Same(StartupAnalzyer.Diagnostics.BuildServiceProviderShouldNotCalledInConfigureServicesMethod, diagnostic.Descriptor); AnalyzerAssert.DiagnosticLocation(source.MarkerLocations["MM1"], diagnostic.Location); }); } - private TestSource ReadSource(string fileName) - { - return MvcTestSource.Read(nameof(StartupAnalyzerTest), fileName); - } } } diff --git a/src/Mvc/Mvc.Analyzers/test/TestFiles/StartupAnalyzerTest/ConfigureServices_BuildServiceProvider.cs b/src/Analyzers/Analyzers/test/TestFiles/StartupAnalyzerTest/ConfigureServices_BuildServiceProvider.cs similarity index 100% rename from src/Mvc/Mvc.Analyzers/test/TestFiles/StartupAnalyzerTest/ConfigureServices_BuildServiceProvider.cs rename to src/Analyzers/Analyzers/test/TestFiles/StartupAnalyzerTest/ConfigureServices_BuildServiceProvider.cs diff --git a/src/Mvc/Mvc.Analyzers/test/TestFiles/StartupAnalyzerTest/MvcOptions_UseMvc.cs b/src/Analyzers/Analyzers/test/TestFiles/StartupAnalyzerTest/MvcOptions_UseMvc.cs similarity index 100% rename from src/Mvc/Mvc.Analyzers/test/TestFiles/StartupAnalyzerTest/MvcOptions_UseMvc.cs rename to src/Analyzers/Analyzers/test/TestFiles/StartupAnalyzerTest/MvcOptions_UseMvc.cs diff --git a/src/Mvc/Mvc.Analyzers/test/TestFiles/StartupAnalyzerTest/MvcOptions_UseMvcAndConfiguredRoutes.cs b/src/Analyzers/Analyzers/test/TestFiles/StartupAnalyzerTest/MvcOptions_UseMvcAndConfiguredRoutes.cs similarity index 100% rename from src/Mvc/Mvc.Analyzers/test/TestFiles/StartupAnalyzerTest/MvcOptions_UseMvcAndConfiguredRoutes.cs rename to src/Analyzers/Analyzers/test/TestFiles/StartupAnalyzerTest/MvcOptions_UseMvcAndConfiguredRoutes.cs diff --git a/src/Mvc/Mvc.Analyzers/test/TestFiles/StartupAnalyzerTest/MvcOptions_UseMvcMultiple.cs b/src/Analyzers/Analyzers/test/TestFiles/StartupAnalyzerTest/MvcOptions_UseMvcMultiple.cs similarity index 100% rename from src/Mvc/Mvc.Analyzers/test/TestFiles/StartupAnalyzerTest/MvcOptions_UseMvcMultiple.cs rename to src/Analyzers/Analyzers/test/TestFiles/StartupAnalyzerTest/MvcOptions_UseMvcMultiple.cs diff --git a/src/Mvc/Mvc.Analyzers/test/TestFiles/StartupAnalyzerTest/MvcOptions_UseMvcWithDefaultRoute.cs b/src/Analyzers/Analyzers/test/TestFiles/StartupAnalyzerTest/MvcOptions_UseMvcWithDefaultRoute.cs similarity index 100% rename from src/Mvc/Mvc.Analyzers/test/TestFiles/StartupAnalyzerTest/MvcOptions_UseMvcWithDefaultRoute.cs rename to src/Analyzers/Analyzers/test/TestFiles/StartupAnalyzerTest/MvcOptions_UseMvcWithDefaultRoute.cs diff --git a/src/Mvc/Mvc.Analyzers/test/TestFiles/StartupAnalyzerTest/MvcOptions_UseMvcWithDefaultRouteAndAddMvcOptionsEndpointRoutingDisabled.cs b/src/Analyzers/Analyzers/test/TestFiles/StartupAnalyzerTest/MvcOptions_UseMvcWithDefaultRouteAndAddMvcOptionsEndpointRoutingDisabled.cs similarity index 100% rename from src/Mvc/Mvc.Analyzers/test/TestFiles/StartupAnalyzerTest/MvcOptions_UseMvcWithDefaultRouteAndAddMvcOptionsEndpointRoutingDisabled.cs rename to src/Analyzers/Analyzers/test/TestFiles/StartupAnalyzerTest/MvcOptions_UseMvcWithDefaultRouteAndAddMvcOptionsEndpointRoutingDisabled.cs diff --git a/src/Mvc/Mvc.Analyzers/test/TestFiles/StartupAnalyzerTest/MvcOptions_UseMvcWithDefaultRouteAndEndpointRoutingDisabled.cs b/src/Analyzers/Analyzers/test/TestFiles/StartupAnalyzerTest/MvcOptions_UseMvcWithDefaultRouteAndEndpointRoutingDisabled.cs similarity index 100% rename from src/Mvc/Mvc.Analyzers/test/TestFiles/StartupAnalyzerTest/MvcOptions_UseMvcWithDefaultRouteAndEndpointRoutingDisabled.cs rename to src/Analyzers/Analyzers/test/TestFiles/StartupAnalyzerTest/MvcOptions_UseMvcWithDefaultRouteAndEndpointRoutingDisabled.cs diff --git a/src/Mvc/Mvc.Analyzers/test/TestFiles/StartupAnalyzerTest/MvcOptions_UseMvcWithOtherMiddleware.cs b/src/Analyzers/Analyzers/test/TestFiles/StartupAnalyzerTest/MvcOptions_UseMvcWithOtherMiddleware.cs similarity index 100% rename from src/Mvc/Mvc.Analyzers/test/TestFiles/StartupAnalyzerTest/MvcOptions_UseMvcWithOtherMiddleware.cs rename to src/Analyzers/Analyzers/test/TestFiles/StartupAnalyzerTest/MvcOptions_UseMvcWithOtherMiddleware.cs diff --git a/src/Mvc/Mvc.Analyzers/test/TestFiles/StartupAnalyzerTest/StartupSignatures_MoreVariety.cs b/src/Analyzers/Analyzers/test/TestFiles/StartupAnalyzerTest/StartupSignatures_MoreVariety.cs similarity index 100% rename from src/Mvc/Mvc.Analyzers/test/TestFiles/StartupAnalyzerTest/StartupSignatures_MoreVariety.cs rename to src/Analyzers/Analyzers/test/TestFiles/StartupAnalyzerTest/StartupSignatures_MoreVariety.cs diff --git a/src/Mvc/Mvc.Analyzers/test/TestFiles/StartupAnalyzerTest/StartupSignatures_Standard.cs b/src/Analyzers/Analyzers/test/TestFiles/StartupAnalyzerTest/StartupSignatures_Standard.cs similarity index 100% rename from src/Mvc/Mvc.Analyzers/test/TestFiles/StartupAnalyzerTest/StartupSignatures_Standard.cs rename to src/Analyzers/Analyzers/test/TestFiles/StartupAnalyzerTest/StartupSignatures_Standard.cs diff --git a/src/Antiforgery/src/Properties/Resources.Designer.cs b/src/Antiforgery/src/Properties/Resources.Designer.cs deleted file mode 100644 index 83811ea2dc..0000000000 --- a/src/Antiforgery/src/Properties/Resources.Designer.cs +++ /dev/null @@ -1,254 +0,0 @@ -// -namespace Microsoft.AspNetCore.Antiforgery -{ - using System.Globalization; - using System.Reflection; - using System.Resources; - - internal static class Resources - { - private static readonly ResourceManager _resourceManager - = new ResourceManager("Microsoft.AspNetCore.Antiforgery.Resources", typeof(Resources).GetTypeInfo().Assembly); - - /// - /// The provided identity of type '{0}' is marked {1} = {2} but does not have a value for {3}. By default, the antiforgery system requires that all authenticated identities have a unique {3}. If it is not possible to provide a unique {3} for this identity, consider extending {4} by overriding the {5} or a custom type that can provide some form of unique identifier for the current user. - /// - internal static string AntiforgeryTokenValidator_AuthenticatedUserWithoutUsername - { - get => GetString("AntiforgeryTokenValidator_AuthenticatedUserWithoutUsername"); - } - - /// - /// The provided identity of type '{0}' is marked {1} = {2} but does not have a value for {3}. By default, the antiforgery system requires that all authenticated identities have a unique {3}. If it is not possible to provide a unique {3} for this identity, consider extending {4} by overriding the {5} or a custom type that can provide some form of unique identifier for the current user. - /// - internal static string FormatAntiforgeryTokenValidator_AuthenticatedUserWithoutUsername(object p0, object p1, object p2, object p3, object p4, object p5) - => string.Format(CultureInfo.CurrentCulture, GetString("AntiforgeryTokenValidator_AuthenticatedUserWithoutUsername"), p0, p1, p2, p3, p4, p5); - - /// - /// The provided antiforgery token failed a custom data check. - /// - internal static string AntiforgeryToken_AdditionalDataCheckFailed - { - get => GetString("AntiforgeryToken_AdditionalDataCheckFailed"); - } - - /// - /// The provided antiforgery token failed a custom data check. - /// - internal static string FormatAntiforgeryToken_AdditionalDataCheckFailed() - => GetString("AntiforgeryToken_AdditionalDataCheckFailed"); - - /// - /// The provided antiforgery token was meant for a different claims-based user than the current user. - /// - internal static string AntiforgeryToken_ClaimUidMismatch - { - get => GetString("AntiforgeryToken_ClaimUidMismatch"); - } - - /// - /// The provided antiforgery token was meant for a different claims-based user than the current user. - /// - internal static string FormatAntiforgeryToken_ClaimUidMismatch() - => GetString("AntiforgeryToken_ClaimUidMismatch"); - - /// - /// The antiforgery token could not be decrypted. - /// - internal static string AntiforgeryToken_DeserializationFailed - { - get => GetString("AntiforgeryToken_DeserializationFailed"); - } - - /// - /// The antiforgery token could not be decrypted. - /// - internal static string FormatAntiforgeryToken_DeserializationFailed() - => GetString("AntiforgeryToken_DeserializationFailed"); - - /// - /// The antiforgery cookie token and request token do not match. - /// - internal static string AntiforgeryToken_SecurityTokenMismatch - { - get => GetString("AntiforgeryToken_SecurityTokenMismatch"); - } - - /// - /// The antiforgery cookie token and request token do not match. - /// - internal static string FormatAntiforgeryToken_SecurityTokenMismatch() - => GetString("AntiforgeryToken_SecurityTokenMismatch"); - - /// - /// Validation of the provided antiforgery token failed. The cookie token and the request token were swapped. - /// - internal static string AntiforgeryToken_TokensSwapped - { - get => GetString("AntiforgeryToken_TokensSwapped"); - } - - /// - /// Validation of the provided antiforgery token failed. The cookie token and the request token were swapped. - /// - internal static string FormatAntiforgeryToken_TokensSwapped() - => GetString("AntiforgeryToken_TokensSwapped"); - - /// - /// The provided antiforgery token was meant for user "{0}", but the current user is "{1}". - /// - internal static string AntiforgeryToken_UsernameMismatch - { - get => GetString("AntiforgeryToken_UsernameMismatch"); - } - - /// - /// The provided antiforgery token was meant for user "{0}", but the current user is "{1}". - /// - internal static string FormatAntiforgeryToken_UsernameMismatch(object p0, object p1) - => string.Format(CultureInfo.CurrentCulture, GetString("AntiforgeryToken_UsernameMismatch"), p0, p1); - - /// - /// The antiforgery cookie token is invalid. - /// - internal static string Antiforgery_CookieToken_IsInvalid - { - get => GetString("Antiforgery_CookieToken_IsInvalid"); - } - - /// - /// The antiforgery cookie token is invalid. - /// - internal static string FormatAntiforgery_CookieToken_IsInvalid() - => GetString("Antiforgery_CookieToken_IsInvalid"); - - /// - /// The required antiforgery cookie "{0}" is not present. - /// - internal static string Antiforgery_CookieToken_MustBeProvided - { - get => GetString("Antiforgery_CookieToken_MustBeProvided"); - } - - /// - /// The required antiforgery cookie "{0}" is not present. - /// - internal static string FormatAntiforgery_CookieToken_MustBeProvided(object p0) - => string.Format(CultureInfo.CurrentCulture, GetString("Antiforgery_CookieToken_MustBeProvided"), p0); - - /// - /// The required antiforgery cookie token must be provided. - /// - internal static string Antiforgery_CookieToken_MustBeProvided_Generic - { - get => GetString("Antiforgery_CookieToken_MustBeProvided_Generic"); - } - - /// - /// The required antiforgery cookie token must be provided. - /// - internal static string FormatAntiforgery_CookieToken_MustBeProvided_Generic() - => GetString("Antiforgery_CookieToken_MustBeProvided_Generic"); - - /// - /// The required antiforgery form field "{0}" is not present. - /// - internal static string Antiforgery_FormToken_MustBeProvided - { - get => GetString("Antiforgery_FormToken_MustBeProvided"); - } - - /// - /// The required antiforgery form field "{0}" is not present. - /// - internal static string FormatAntiforgery_FormToken_MustBeProvided(object p0) - => string.Format(CultureInfo.CurrentCulture, GetString("Antiforgery_FormToken_MustBeProvided"), p0); - - /// - /// The required antiforgery header value "{0}" is not present. - /// - internal static string Antiforgery_HeaderToken_MustBeProvided - { - get => GetString("Antiforgery_HeaderToken_MustBeProvided"); - } - - /// - /// The required antiforgery header value "{0}" is not present. - /// - internal static string FormatAntiforgery_HeaderToken_MustBeProvided(object p0) - => string.Format(CultureInfo.CurrentCulture, GetString("Antiforgery_HeaderToken_MustBeProvided"), p0); - - /// - /// The required antiforgery request token was not provided in either form field "{0}" or header value "{1}". - /// - internal static string Antiforgery_RequestToken_MustBeProvided - { - get => GetString("Antiforgery_RequestToken_MustBeProvided"); - } - - /// - /// The required antiforgery request token was not provided in either form field "{0}" or header value "{1}". - /// - internal static string FormatAntiforgery_RequestToken_MustBeProvided(object p0, object p1) - => string.Format(CultureInfo.CurrentCulture, GetString("Antiforgery_RequestToken_MustBeProvided"), p0, p1); - - /// - /// The required antiforgery request token must be provided. - /// - internal static string Antiforgery_RequestToken_MustBeProvided_Generic - { - get => GetString("Antiforgery_RequestToken_MustBeProvided_Generic"); - } - - /// - /// The required antiforgery request token must be provided. - /// - internal static string FormatAntiforgery_RequestToken_MustBeProvided_Generic() - => GetString("Antiforgery_RequestToken_MustBeProvided_Generic"); - - /// - /// The antiforgery system has the configuration value {optionName} = {value}, but the current request is not an SSL request. - /// - internal static string Antiforgery_RequiresSSL - { - get => GetString("Antiforgery_RequiresSSL"); - } - - /// - /// The antiforgery system has the configuration value {optionName} = {value}, but the current request is not an SSL request. - /// - internal static string FormatAntiforgery_RequiresSSL(object optionName, object value) - => string.Format(CultureInfo.CurrentCulture, GetString("Antiforgery_RequiresSSL", "optionName", "value"), optionName, value); - - /// - /// Value cannot be null or empty. - /// - internal static string ArgumentCannotBeNullOrEmpty - { - get => GetString("ArgumentCannotBeNullOrEmpty"); - } - - /// - /// Value cannot be null or empty. - /// - internal static string FormatArgumentCannotBeNullOrEmpty() - => GetString("ArgumentCannotBeNullOrEmpty"); - - private static string GetString(string name, params string[] formatterNames) - { - var value = _resourceManager.GetString(name); - - System.Diagnostics.Debug.Assert(value != null); - - if (formatterNames != null) - { - for (var i = 0; i < formatterNames.Length; i++) - { - value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); - } - } - - return value; - } - } -} diff --git a/src/Components/Analyzers/src/ComponentFacts.cs b/src/Components/Analyzers/src/ComponentFacts.cs index 031df5586f..75403ce282 100644 --- a/src/Components/Analyzers/src/ComponentFacts.cs +++ b/src/Components/Analyzers/src/ComponentFacts.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; namespace Microsoft.AspNetCore.Components.Analyzers { @@ -85,5 +86,26 @@ namespace Microsoft.AspNetCore.Components.Analyzers return property.GetAttributes().Any(a => a.AttributeClass == symbols.CascadingParameterAttribute); } + + public static bool IsComponent(ComponentSymbols symbols, Compilation compilation, INamedTypeSymbol type) + { + if (symbols is null) + { + throw new ArgumentNullException(nameof(symbols)); + } + + if (type is null) + { + throw new ArgumentNullException(nameof(type)); + } + + var conversion = compilation.ClassifyConversion(symbols.IComponentType, type); + if (!conversion.Exists || !conversion.IsExplicit) + { + return false; + } + + return true; + } } } diff --git a/src/Components/Analyzers/src/ComponentParameterUsageAnalyzer.cs b/src/Components/Analyzers/src/ComponentParameterUsageAnalyzer.cs new file mode 100644 index 0000000000..0d45edcabc --- /dev/null +++ b/src/Components/Analyzers/src/ComponentParameterUsageAnalyzer.cs @@ -0,0 +1,104 @@ +// 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.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.AspNetCore.Components.Analyzers +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public class ComponentParameterUsageAnalyzer : DiagnosticAnalyzer + { + public ComponentParameterUsageAnalyzer() + { + SupportedDiagnostics = ImmutableArray.Create(new[] + { + DiagnosticDescriptors.ComponentParametersShouldNotBeSetOutsideOfTheirDeclaredComponent, + }); + } + + public override ImmutableArray SupportedDiagnostics { get; } + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); + context.RegisterCompilationStartAction(context => + { + if (!ComponentSymbols.TryCreate(context.Compilation, out var symbols)) + { + // Types we need are not defined. + return; + } + + context.RegisterOperationBlockStartAction(startBlockContext => + { + startBlockContext.RegisterOperationAction(context => + { + var assignmentOperation = (IAssignmentOperation)context.Operation; + var leftHandSide = assignmentOperation.Target; + if (leftHandSide == null) + { + // Malformed assignment, no left hand side. + return; + } + + if (leftHandSide.Kind != OperationKind.PropertyReference) + { + // We don't want to capture situations where a user does + // MyOtherProperty = aComponent.SomeParameter + return; + } + + var propertyReference = (IPropertyReferenceOperation)leftHandSide; + var componentProperty = (IPropertySymbol)propertyReference.Member; + + if (!ComponentFacts.IsParameter(symbols, componentProperty)) + { + // This is not a property reference that we care about, it is not decorated with [Parameter]. + return; + } + + var propertyContainingType = componentProperty.ContainingType; + if (!ComponentFacts.IsComponent(symbols, context.Compilation, propertyContainingType)) + { + // Someone referenced a property as [Parameter] inside something that is not a component. + return; + } + + var assignmentContainingType = startBlockContext.OwningSymbol?.ContainingType; + if (assignmentContainingType == null) + { + // Assignment location has no containing type. Most likely we're operating on malformed code, don't try and validate. + return; + } + + var conversion = context.Compilation.ClassifyConversion(propertyContainingType, assignmentContainingType); + if (conversion.Exists && conversion.IsIdentity) + { + // The assignment is taking place inside of the declaring component. + return; + } + + if (conversion.Exists && conversion.IsExplicit) + { + // The assignment is taking place within the components type hierarchy. This means the user is setting this in a supported + // scenario. + return; + } + + // At this point the user is referencing a component parameter outside of its declaring class. + + context.ReportDiagnostic(Diagnostic.Create( + DiagnosticDescriptors.ComponentParametersShouldNotBeSetOutsideOfTheirDeclaredComponent, + propertyReference.Syntax.GetLocation(), + propertyReference.Member.Name)); + }, OperationKind.SimpleAssignment, OperationKind.CompoundAssignment, OperationKind.CoalesceAssignment); + }); + }); + } + } +} diff --git a/src/Components/Analyzers/src/ComponentSymbols.cs b/src/Components/Analyzers/src/ComponentSymbols.cs index ba1bfc4869..d115e27f53 100644 --- a/src/Components/Analyzers/src/ComponentSymbols.cs +++ b/src/Components/Analyzers/src/ComponentSymbols.cs @@ -2,7 +2,6 @@ // 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 Microsoft.CodeAnalysis; namespace Microsoft.AspNetCore.Components.Analyzers @@ -30,6 +29,13 @@ namespace Microsoft.AspNetCore.Components.Analyzers return false; } + var icomponentType = compilation.GetTypeByMetadataName(ComponentsApi.IComponent.MetadataName); + if (icomponentType == null) + { + symbols = null; + return false; + } + var dictionary = compilation.GetTypeByMetadataName("System.Collections.Generic.Dictionary`2"); var @string = compilation.GetSpecialType(SpecialType.System_String); var @object = compilation.GetSpecialType(SpecialType.System_Object); @@ -41,18 +47,24 @@ namespace Microsoft.AspNetCore.Components.Analyzers var parameterCaptureUnmatchedValuesRuntimeType = dictionary.Construct(@string, @object); - symbols = new ComponentSymbols(parameterAttribute, cascadingParameterAttribute, parameterCaptureUnmatchedValuesRuntimeType); + symbols = new ComponentSymbols( + parameterAttribute, + cascadingParameterAttribute, + parameterCaptureUnmatchedValuesRuntimeType, + icomponentType); return true; } private ComponentSymbols( INamedTypeSymbol parameterAttribute, INamedTypeSymbol cascadingParameterAttribute, - INamedTypeSymbol parameterCaptureUnmatchedValuesRuntimeType) + INamedTypeSymbol parameterCaptureUnmatchedValuesRuntimeType, + INamedTypeSymbol icomponentType) { ParameterAttribute = parameterAttribute; CascadingParameterAttribute = cascadingParameterAttribute; ParameterCaptureUnmatchedValuesRuntimeType = parameterCaptureUnmatchedValuesRuntimeType; + IComponentType = icomponentType; } public INamedTypeSymbol ParameterAttribute { get; } @@ -61,5 +73,7 @@ namespace Microsoft.AspNetCore.Components.Analyzers public INamedTypeSymbol ParameterCaptureUnmatchedValuesRuntimeType { get; } public INamedTypeSymbol CascadingParameterAttribute { get; } + + public INamedTypeSymbol IComponentType { get; } } } diff --git a/src/Components/Analyzers/src/ComponentsApi.cs b/src/Components/Analyzers/src/ComponentsApi.cs index 83eb39cc2c..73ceff39fd 100644 --- a/src/Components/Analyzers/src/ComponentsApi.cs +++ b/src/Components/Analyzers/src/ComponentsApi.cs @@ -22,5 +22,11 @@ namespace Microsoft.AspNetCore.Components.Analyzers public static readonly string FullTypeName = "Microsoft.AspNetCore.Components.CascadingParameterAttribute"; public static readonly string MetadataName = FullTypeName; } + + public static class IComponent + { + public static readonly string FullTypeName = "Microsoft.AspNetCore.Components.IComponent"; + public static readonly string MetadataName = FullTypeName; + } } } diff --git a/src/Components/Analyzers/src/DiagnosticDescriptors.cs b/src/Components/Analyzers/src/DiagnosticDescriptors.cs index 74632adc6f..a34102f39b 100644 --- a/src/Components/Analyzers/src/DiagnosticDescriptors.cs +++ b/src/Components/Analyzers/src/DiagnosticDescriptors.cs @@ -46,5 +46,14 @@ namespace Microsoft.AspNetCore.Components.Analyzers DiagnosticSeverity.Warning, isEnabledByDefault: true, description: new LocalizableResourceString(nameof(Resources.ComponentParametersShouldBePublic_Description), Resources.ResourceManager, typeof(Resources))); + + public static readonly DiagnosticDescriptor ComponentParametersShouldNotBeSetOutsideOfTheirDeclaredComponent = new DiagnosticDescriptor( + "BL0005", + new LocalizableResourceString(nameof(Resources.ComponentParameterShouldNotBeSetOutsideOfTheirDeclaredComponent_Title), Resources.ResourceManager, typeof(Resources)), + new LocalizableResourceString(nameof(Resources.ComponentParameterShouldNotBeSetOutsideOfTheirDeclaredComponent_Format), Resources.ResourceManager, typeof(Resources)), + "Usage", + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + description: new LocalizableResourceString(nameof(Resources.ComponentParameterShouldNotBeSetOutsideOfTheirDeclaredComponent_Description), Resources.ResourceManager, typeof(Resources))); } } diff --git a/src/Components/Analyzers/src/Microsoft.AspNetCore.Components.Analyzers.csproj b/src/Components/Analyzers/src/Microsoft.AspNetCore.Components.Analyzers.csproj index c84bc990f2..a83904f245 100644 --- a/src/Components/Analyzers/src/Microsoft.AspNetCore.Components.Analyzers.csproj +++ b/src/Components/Analyzers/src/Microsoft.AspNetCore.Components.Analyzers.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 @@ -20,17 +20,4 @@ - - - True - True - Resources.resx - - - - ResXFileCodeGenerator - Resources.Designer.cs - - - diff --git a/src/Components/Analyzers/src/Resources.Designer.cs b/src/Components/Analyzers/src/Resources.Designer.cs deleted file mode 100644 index 13c55946a3..0000000000 --- a/src/Components/Analyzers/src/Resources.Designer.cs +++ /dev/null @@ -1,180 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Microsoft.AspNetCore.Components.Analyzers { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.AspNetCore.Components.Analyzers.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to Component parameters with CaptureUnmatchedValues must be a correct type.. - /// - internal static string ComponentParameterCaptureUnmatchedValuesHasWrongType_Description { - get { - return ResourceManager.GetString("ComponentParameterCaptureUnmatchedValuesHasWrongType_Description", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Component parameter '{0}' defines CaptureUnmatchedValues but has an unsupported type '{1}'. Use a type assignable from '{2}'.. - /// - internal static string ComponentParameterCaptureUnmatchedValuesHasWrongType_Format { - get { - return ResourceManager.GetString("ComponentParameterCaptureUnmatchedValuesHasWrongType_Format", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Component parameter with CaptureUnmatchedValues has the wrong type. - /// - internal static string ComponentParameterCaptureUnmatchedValuesHasWrongType_Title { - get { - return ResourceManager.GetString("ComponentParameterCaptureUnmatchedValuesHasWrongType_Title", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Components may only define a single parameter with CaptureUnmatchedValues.. - /// - internal static string ComponentParameterCaptureUnmatchedValuesMustBeUnique_Description { - get { - return ResourceManager.GetString("ComponentParameterCaptureUnmatchedValuesMustBeUnique_Description", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Component type '{0}' defines properties multiple parameters with CaptureUnmatchedValues. Properties: {1}{2}. - /// - internal static string ComponentParameterCaptureUnmatchedValuesMustBeUnique_Format { - get { - return ResourceManager.GetString("ComponentParameterCaptureUnmatchedValuesMustBeUnique_Format", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Component has multiple CaptureUnmatchedValues parameters. - /// - internal static string ComponentParameterCaptureUnmatchedValuesMustBeUnique_Title { - get { - return ResourceManager.GetString("ComponentParameterCaptureUnmatchedValuesMustBeUnique_Title", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Component parameters should have public setters.. - /// - internal static string ComponentParameterSettersShouldBePublic_Description { - get { - return ResourceManager.GetString("ComponentParameterSettersShouldBePublic_Description", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Component parameter '{0}' should have a public setter.. - /// - internal static string ComponentParameterSettersShouldBePublic_Format { - get { - return ResourceManager.GetString("ComponentParameterSettersShouldBePublic_Format", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Component parameter should have public setters.. - /// - internal static string ComponentParameterSettersShouldBePublic_Title { - get { - return ResourceManager.GetString("ComponentParameterSettersShouldBePublic_Title", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Component parameter '{0}' should be public.. - /// - internal static string ComponentParameterShouldBePublic_Format { - get { - return ResourceManager.GetString("ComponentParameterShouldBePublic_Format", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Component parameter should be public.. - /// - internal static string ComponentParameterShouldBePublic_Title { - get { - return ResourceManager.GetString("ComponentParameterShouldBePublic_Title", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Component parameters should be public.. - /// - internal static string ComponentParametersShouldBePublic_Description { - get { - return ResourceManager.GetString("ComponentParametersShouldBePublic_Description", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Make component parameters public.. - /// - internal static string ComponentParametersShouldBePublic_FixTitle { - get { - return ResourceManager.GetString("ComponentParametersShouldBePublic_FixTitle", resourceCulture); - } - } - } -} diff --git a/src/Components/Analyzers/src/Resources.resx b/src/Components/Analyzers/src/Resources.resx index 1c4319cb5b..648adb2c3b 100644 --- a/src/Components/Analyzers/src/Resources.resx +++ b/src/Components/Analyzers/src/Resources.resx @@ -156,4 +156,13 @@ Make component parameters public. + + Component parameters should not be set outside of their declared component. Doing so may result in unexpected behavior at runtime. + + + Component parameter '{0}' should not be set outside of its component. + + + Component parameter should not be set outside of its component. + \ No newline at end of file diff --git a/src/Components/Analyzers/test/ComponentParameterUsageAnalyzerTest.cs b/src/Components/Analyzers/test/ComponentParameterUsageAnalyzerTest.cs new file mode 100644 index 0000000000..db664a2e76 --- /dev/null +++ b/src/Components/Analyzers/test/ComponentParameterUsageAnalyzerTest.cs @@ -0,0 +1,298 @@ +// 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.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using TestHelper; +using Xunit; + +namespace Microsoft.AspNetCore.Components.Analyzers +{ + public class ComponentParameterUsageAnalyzerTest : DiagnosticVerifier + { + public ComponentParameterUsageAnalyzerTest() + { + ComponentTestSource = $@" + namespace ConsoleApplication1 + {{ + using {typeof(ParameterAttribute).Namespace}; + class TestComponent : IComponent + {{ + [Parameter] public string TestProperty {{ get; set; }} + public string NonParameter {{ get; set; }} + }} + }}" + ComponentsTestDeclarations.Source; + } + + private string ComponentTestSource { get; } + + [Fact] + public void ComponentPropertySimpleAssignment_Warns() + { + var test = $@" + namespace ConsoleApplication1 + {{ + using {typeof(ParameterAttribute).Namespace}; + class OtherComponent : IComponent + {{ + private TestComponent _testComponent; + void Render() + {{ + _testComponent = new TestComponent(); + _testComponent.TestProperty = ""Hello World""; + }} + }} + }}" + ComponentTestSource; + + VerifyCSharpDiagnostic(test, + new DiagnosticResult + { + Id = DiagnosticDescriptors.ComponentParametersShouldNotBeSetOutsideOfTheirDeclaredComponent.Id, + Message = "Component parameter 'TestProperty' should not be set outside of its component.", + Severity = DiagnosticSeverity.Warning, + Locations = new[] + { + new DiagnosticResultLocation("Test0.cs", 11, 17) + } + }); + } + + [Fact] + public void ComponentPropertyCoalesceAssignment__Warns() + { + var test = $@" + namespace ConsoleApplication1 + {{ + using {typeof(ParameterAttribute).Namespace}; + class OtherComponent : IComponent + {{ + private TestComponent _testComponent; + void Render() + {{ + _testComponent = new TestComponent(); + _testComponent.TestProperty ??= ""Hello World""; + }} + }} + }}" + ComponentTestSource; + + VerifyCSharpDiagnostic(test, + new DiagnosticResult + { + Id = DiagnosticDescriptors.ComponentParametersShouldNotBeSetOutsideOfTheirDeclaredComponent.Id, + Message = "Component parameter 'TestProperty' should not be set outside of its component.", + Severity = DiagnosticSeverity.Warning, + Locations = new[] + { + new DiagnosticResultLocation("Test0.cs", 11, 17) + } + }); + } + + [Fact] + public void ComponentPropertyCompoundAssignment__Warns() + { + var test = $@" + namespace ConsoleApplication1 + {{ + using {typeof(ParameterAttribute).Namespace}; + class OtherComponent : IComponent + {{ + private TestComponent _testComponent; + void Render() + {{ + _testComponent = new TestComponent(); + _testComponent.TestProperty += ""Hello World""; + }} + }} + }}" + ComponentTestSource; + + VerifyCSharpDiagnostic(test, + new DiagnosticResult + { + Id = DiagnosticDescriptors.ComponentParametersShouldNotBeSetOutsideOfTheirDeclaredComponent.Id, + Message = "Component parameter 'TestProperty' should not be set outside of its component.", + Severity = DiagnosticSeverity.Warning, + Locations = new[] + { + new DiagnosticResultLocation("Test0.cs", 11, 17) + } + }); + } + + [Fact] + public void ComponentPropertyExpression_Ignores() + { + var test = $@" + namespace ConsoleApplication1 + {{ + using {typeof(ParameterAttribute).Namespace}; + class TypeName + {{ + void Method() + {{ + System.IO.Console.WriteLine(new TestComponent().TestProperty); + }} + }} + }}" + ComponentTestSource; + + VerifyCSharpDiagnostic(test); + } + + [Fact] + public void ComponentPropertyExpressionInStatement_Ignores() + { + var test = $@" + namespace ConsoleApplication1 + {{ + using {typeof(ParameterAttribute).Namespace}; + class TypeName + {{ + void Method() + {{ + var testComponent = new TestComponent(); + for (var i = 0; i < testComponent.TestProperty.Length; i++) + {{ + }} + }} + }} + }}" + ComponentTestSource; + + VerifyCSharpDiagnostic(test); + } + + [Fact] + public void RetrievalOfComponentPropertyValueInAssignment_Ignores() + { + var test = $@" + namespace ConsoleApplication1 + {{ + using {typeof(ParameterAttribute).Namespace}; + class TypeName + {{ + void Method() + {{ + var testComponent = new TestComponent(); + AnotherProperty = testComponent.TestProperty; + }} + + public string AnotherProperty {{ get; set; }} + }} + }}" + ComponentTestSource; + + VerifyCSharpDiagnostic(test); + } + + [Fact] + public void ShadowedComponentPropertyAssignment_Ignores() + { + var test = $@" + namespace ConsoleApplication1 + {{ + using {typeof(ParameterAttribute).Namespace}; + class TypeName + {{ + void Method() + {{ + var testComponent = new InheritedComponent(); + testComponent.TestProperty = ""Hello World""; + }} + }} + + class InheritedComponent : TestComponent + {{ + public new string TestProperty {{ get; set; }} + }} + }}" + ComponentTestSource; + + VerifyCSharpDiagnostic(test); + } + + [Fact] + public void InheritedImplicitComponentPropertyAssignment_Ignores() + { + var test = $@" + namespace ConsoleApplication1 + {{ + using {typeof(ParameterAttribute).Namespace}; + class TypeName : TestComponent + {{ + void Method() + {{ + this.TestProperty = ""Hello World""; + }} + }} + }}" + ComponentTestSource; + + VerifyCSharpDiagnostic(test); + } + + [Fact] + public void ImplicitComponentPropertyAssignment_Ignores() + { + var test = $@" + namespace ConsoleApplication1 + {{ + using {typeof(ParameterAttribute).Namespace}; + class TypeName : IComponent + {{ + void Method() + {{ + TestProperty = ""Hello World""; + }} + + [Parameter] public string TestProperty {{ get; set; }} + }} + }}" + ComponentTestSource; + + VerifyCSharpDiagnostic(test); + } + + [Fact] + public void ComponentPropertyAssignment_NonParameter_Ignores() + { + var test = $@" + namespace ConsoleApplication1 + {{ + using {typeof(ParameterAttribute).Namespace}; + class OtherComponent : IComponent + {{ + private TestComponent _testComponent; + void Render() + {{ + _testComponent = new TestComponent(); + _testComponent.NonParameter = ""Hello World""; + }} + }} + }}" + ComponentTestSource; + + VerifyCSharpDiagnostic(test); + } + + [Fact] + public void NonComponentPropertyAssignment_Ignores() + { + var test = $@" + namespace ConsoleApplication1 + {{ + using {typeof(ParameterAttribute).Namespace}; + class OtherComponent : IComponent + {{ + private SomethingElse _testNonComponent; + void Render() + {{ + _testNonComponent = new NotAComponent(); + _testNonComponent.TestProperty = ""Hello World""; + }} + }} + class NotAComponent + {{ + [Parameter] public string TestProperty {{ get; set; }} + }} + }}" + ComponentTestSource; + + VerifyCSharpDiagnostic(test); + } + + protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer() => new ComponentParameterUsageAnalyzer(); + } +} diff --git a/src/Components/Analyzers/test/ComponentsTestDeclarations.cs b/src/Components/Analyzers/test/ComponentsTestDeclarations.cs index e09f156ff4..3c71a82566 100644 --- a/src/Components/Analyzers/test/ComponentsTestDeclarations.cs +++ b/src/Components/Analyzers/test/ComponentsTestDeclarations.cs @@ -16,6 +16,10 @@ namespace Microsoft.AspNetCore.Components.Analyzers public class {typeof(CascadingParameterAttribute).Name} : System.Attribute {{ }} + + public interface {typeof(IComponent).Name} + {{ + }} }} "; } diff --git a/src/Components/Blazor/Blazor/ref/Microsoft.AspNetCore.Blazor.netstandard2.0.cs b/src/Components/Blazor/Blazor/ref/Microsoft.AspNetCore.Blazor.netstandard2.0.cs index 7c6500f008..3897a0bf62 100644 --- a/src/Components/Blazor/Blazor/ref/Microsoft.AspNetCore.Blazor.netstandard2.0.cs +++ b/src/Components/Blazor/Blazor/ref/Microsoft.AspNetCore.Blazor.netstandard2.0.cs @@ -62,7 +62,7 @@ namespace Microsoft.AspNetCore.Blazor.Rendering public override Microsoft.AspNetCore.Components.Dispatcher Dispatcher { get { throw null; } } public System.Threading.Tasks.Task AddComponentAsync(System.Type componentType, string domElementSelector) { throw null; } public System.Threading.Tasks.Task AddComponentAsync(string domElementSelector) where TComponent : Microsoft.AspNetCore.Components.IComponent { throw null; } - public override System.Threading.Tasks.Task DispatchEventAsync(int eventHandlerId, Microsoft.AspNetCore.Components.Rendering.EventFieldInfo eventFieldInfo, Microsoft.AspNetCore.Components.UIEventArgs eventArgs) { throw null; } + public override System.Threading.Tasks.Task DispatchEventAsync(ulong eventHandlerId, Microsoft.AspNetCore.Components.Rendering.EventFieldInfo eventFieldInfo, Microsoft.AspNetCore.Components.UIEventArgs eventArgs) { throw null; } protected override void Dispose(bool disposing) { } protected override void HandleException(System.Exception exception) { } protected override System.Threading.Tasks.Task UpdateDisplayAsync(in Microsoft.AspNetCore.Components.Rendering.RenderBatch batch) { throw null; } diff --git a/src/Components/Blazor/Blazor/src/Rendering/WebAssemblyRenderer.cs b/src/Components/Blazor/Blazor/src/Rendering/WebAssemblyRenderer.cs index 84e84fb609..528816e492 100644 --- a/src/Components/Blazor/Blazor/src/Rendering/WebAssemblyRenderer.cs +++ b/src/Components/Blazor/Blazor/src/Rendering/WebAssemblyRenderer.cs @@ -120,7 +120,7 @@ namespace Microsoft.AspNetCore.Blazor.Rendering } /// - public override Task DispatchEventAsync(int eventHandlerId, EventFieldInfo eventFieldInfo, UIEventArgs eventArgs) + public override Task DispatchEventAsync(ulong eventHandlerId, EventFieldInfo eventFieldInfo, UIEventArgs eventArgs) { // Be sure we only run one event handler at once. Although they couldn't run // simultaneously anyway (there's only one thread), they could run nested on @@ -183,12 +183,12 @@ namespace Microsoft.AspNetCore.Blazor.Rendering readonly struct IncomingEventInfo { - public readonly int EventHandlerId; + public readonly ulong EventHandlerId; public readonly EventFieldInfo EventFieldInfo; public readonly UIEventArgs EventArgs; public readonly TaskCompletionSource TaskCompletionSource; - public IncomingEventInfo(int eventHandlerId, EventFieldInfo eventFieldInfo, UIEventArgs eventArgs) + public IncomingEventInfo(ulong eventHandlerId, EventFieldInfo eventFieldInfo, UIEventArgs eventArgs) { EventHandlerId = eventHandlerId; EventFieldInfo = eventFieldInfo; diff --git a/src/Components/Blazor/BlazorExtension/src/Microsoft.VisualStudio.BlazorExtension.csproj b/src/Components/Blazor/BlazorExtension/src/Microsoft.VisualStudio.BlazorExtension.csproj index b803dd969f..10f7b7f541 100644 --- a/src/Components/Blazor/BlazorExtension/src/Microsoft.VisualStudio.BlazorExtension.csproj +++ b/src/Components/Blazor/BlazorExtension/src/Microsoft.VisualStudio.BlazorExtension.csproj @@ -26,6 +26,9 @@ true false true + + + false diff --git a/src/Components/Blazor/Templates/src/content/BlazorWasm-CSharp/Client/Shared/SurveyPrompt.razor b/src/Components/Blazor/Templates/src/content/BlazorWasm-CSharp/Client/Shared/SurveyPrompt.razor index 986d57321a..5baad704a9 100644 --- a/src/Components/Blazor/Templates/src/content/BlazorWasm-CSharp/Client/Shared/SurveyPrompt.razor +++ b/src/Components/Blazor/Templates/src/content/BlazorWasm-CSharp/Client/Shared/SurveyPrompt.razor @@ -11,5 +11,5 @@ @code { // Demonstrates how a parent component can supply parameters - [Parameter] string Title { get; set; } + [Parameter] public string Title { get; set; } } diff --git a/src/Components/Blazor/testassets/StandaloneApp/Pages/FetchData.razor b/src/Components/Blazor/testassets/StandaloneApp/Pages/FetchData.razor index 9024a800a7..9a741bddfb 100644 --- a/src/Components/Blazor/testassets/StandaloneApp/Pages/FetchData.razor +++ b/src/Components/Blazor/testassets/StandaloneApp/Pages/FetchData.razor @@ -44,7 +44,7 @@ else } @code { - [Parameter] DateTime StartDate { get; set; } + [Parameter] public DateTime StartDate { get; set; } WeatherForecast[] forecasts; diff --git a/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.Manual.cs b/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.Manual.cs index 4c25bec3fa..be1a95b1dd 100644 --- a/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.Manual.cs +++ b/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.Manual.cs @@ -99,7 +99,7 @@ namespace Microsoft.AspNetCore.Components public string Name { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; }} [Microsoft.AspNetCore.Components.ParameterAttribute] public T Value { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; }} - public void Configure(Microsoft.AspNetCore.Components.RenderHandle renderHandle) { } + public void Attach(Microsoft.AspNetCore.Components.RenderHandle renderHandle) { } public System.Threading.Tasks.Task SetParametersAsync(Microsoft.AspNetCore.Components.ParameterCollection parameters) { throw null; } } @@ -114,7 +114,7 @@ namespace Microsoft.AspNetCore.Components public System.Type Page { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; }} [Microsoft.AspNetCore.Components.ParameterAttribute] public System.Collections.Generic.IDictionary PageParameters { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; }} - public void Configure(Microsoft.AspNetCore.Components.RenderHandle renderHandle) { } + public void Attach(Microsoft.AspNetCore.Components.RenderHandle renderHandle) { } public System.Threading.Tasks.Task SetParametersAsync(Microsoft.AspNetCore.Components.ParameterCollection parameters) { throw null; } } } @@ -255,7 +255,7 @@ namespace Microsoft.AspNetCore.Components.Routing public RenderFragment ChildContent { get; set; } [Microsoft.AspNetCore.Components.ParameterAttribute] public Microsoft.AspNetCore.Components.Routing.NavLinkMatch Match { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; }} - public void Configure(Microsoft.AspNetCore.Components.RenderHandle renderHandle) { } + public void Attach(Microsoft.AspNetCore.Components.RenderHandle renderHandle) { } public void Dispose() { } public System.Threading.Tasks.Task SetParametersAsync(Microsoft.AspNetCore.Components.ParameterCollection parameters) { throw null; } } @@ -271,7 +271,7 @@ namespace Microsoft.AspNetCore.Components.Routing public Microsoft.AspNetCore.Components.RenderFragment NotAuthorizedContent { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; }} [Microsoft.AspNetCore.Components.ParameterAttribute] public Microsoft.AspNetCore.Components.RenderFragment AuthorizingContent { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } private set { throw null; }} - public void Configure(Microsoft.AspNetCore.Components.RenderHandle renderHandle) { } + public void Attach(Microsoft.AspNetCore.Components.RenderHandle renderHandle) { } public void Dispose() { } protected virtual void Render(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder, System.Type handler, System.Collections.Generic.IDictionary parameters) { } public System.Threading.Tasks.Task SetParametersAsync(Microsoft.AspNetCore.Components.ParameterCollection parameters) { throw null; } diff --git a/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs b/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs index 9405897b79..9cf260bf92 100644 --- a/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs +++ b/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs @@ -25,6 +25,53 @@ namespace Microsoft.AspNetCore.Components public static partial class BindAttributes { } + public static partial class BindConverter + { + public static bool FormatValue(bool value, System.Globalization.CultureInfo culture = null) { throw null; } + public static string FormatValue(System.DateTime value, System.Globalization.CultureInfo culture = null) { throw null; } + public static string FormatValue(System.DateTime value, string format, System.Globalization.CultureInfo culture = null) { throw null; } + public static string FormatValue(System.DateTimeOffset value, System.Globalization.CultureInfo culture = null) { throw null; } + public static string FormatValue(System.DateTimeOffset value, string format, System.Globalization.CultureInfo culture = null) { throw null; } + public static string FormatValue(decimal value, System.Globalization.CultureInfo culture = null) { throw null; } + public static string FormatValue(double value, System.Globalization.CultureInfo culture = null) { throw null; } + public static string FormatValue(int value, System.Globalization.CultureInfo culture = null) { throw null; } + public static string FormatValue(long value, System.Globalization.CultureInfo culture = null) { throw null; } + public static bool? FormatValue(bool? value, System.Globalization.CultureInfo culture = null) { throw null; } + public static string FormatValue(System.DateTimeOffset? value, System.Globalization.CultureInfo culture = null) { throw null; } + public static string FormatValue(System.DateTimeOffset? value, string format, System.Globalization.CultureInfo culture = null) { throw null; } + public static string FormatValue(System.DateTime? value, System.Globalization.CultureInfo culture = null) { throw null; } + public static string FormatValue(System.DateTime? value, string format, System.Globalization.CultureInfo culture = null) { throw null; } + public static string FormatValue(decimal? value, System.Globalization.CultureInfo culture = null) { throw null; } + public static string FormatValue(double? value, System.Globalization.CultureInfo culture = null) { throw null; } + public static string FormatValue(int? value, System.Globalization.CultureInfo culture = null) { throw null; } + public static string FormatValue(long? value, System.Globalization.CultureInfo culture = null) { throw null; } + public static string FormatValue(float? value, System.Globalization.CultureInfo culture = null) { throw null; } + public static string FormatValue(float value, System.Globalization.CultureInfo culture = null) { throw null; } + public static string FormatValue(string value, System.Globalization.CultureInfo culture = null) { throw null; } + public static object FormatValue(T value, System.Globalization.CultureInfo culture = null) { throw null; } + public static bool TryConvertToBool(object obj, System.Globalization.CultureInfo culture, out bool value) { throw null; } + public static bool TryConvertToDateTime(object obj, System.Globalization.CultureInfo culture, out System.DateTime value) { throw null; } + public static bool TryConvertToDateTime(object obj, System.Globalization.CultureInfo culture, string format, out System.DateTime value) { throw null; } + public static bool TryConvertToDateTimeOffset(object obj, System.Globalization.CultureInfo culture, out System.DateTimeOffset value) { throw null; } + public static bool TryConvertToDateTimeOffset(object obj, System.Globalization.CultureInfo culture, string format, out System.DateTimeOffset value) { throw null; } + public static bool TryConvertToDecimal(object obj, System.Globalization.CultureInfo culture, out decimal value) { throw null; } + public static bool TryConvertToDouble(object obj, System.Globalization.CultureInfo culture, out double value) { throw null; } + public static bool TryConvertToFloat(object obj, System.Globalization.CultureInfo culture, out float value) { throw null; } + public static bool TryConvertToInt(object obj, System.Globalization.CultureInfo culture, out int value) { throw null; } + public static bool TryConvertToLong(object obj, System.Globalization.CultureInfo culture, out long value) { throw null; } + public static bool TryConvertToNullableBool(object obj, System.Globalization.CultureInfo culture, out bool? value) { throw null; } + public static bool TryConvertToNullableDateTime(object obj, System.Globalization.CultureInfo culture, out System.DateTime? value) { throw null; } + public static bool TryConvertToNullableDateTime(object obj, System.Globalization.CultureInfo culture, string format, out System.DateTime? value) { throw null; } + public static bool TryConvertToNullableDateTimeOffset(object obj, System.Globalization.CultureInfo culture, out System.DateTimeOffset? value) { throw null; } + public static bool TryConvertToNullableDateTimeOffset(object obj, System.Globalization.CultureInfo culture, string format, out System.DateTimeOffset? value) { throw null; } + public static bool TryConvertToNullableDecimal(object obj, System.Globalization.CultureInfo culture, out decimal? value) { throw null; } + public static bool TryConvertToNullableDouble(object obj, System.Globalization.CultureInfo culture, out double? value) { throw null; } + public static bool TryConvertToNullableFloat(object obj, System.Globalization.CultureInfo culture, out float? value) { throw null; } + public static bool TryConvertToNullableInt(object obj, System.Globalization.CultureInfo culture, out int? value) { throw null; } + public static bool TryConvertToNullableLong(object obj, System.Globalization.CultureInfo culture, out long? value) { throw null; } + public static bool TryConvertToString(object obj, System.Globalization.CultureInfo culture, out string value) { throw null; } + public static bool TryConvertTo(object obj, System.Globalization.CultureInfo culture, out T value) { throw null; } + } [System.AttributeUsageAttribute(System.AttributeTargets.Class, AllowMultiple=true, Inherited=true)] public sealed partial class BindElementAttribute : System.Attribute { @@ -63,7 +110,7 @@ namespace Microsoft.AspNetCore.Components protected virtual void BuildRenderTree(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder) { } protected System.Threading.Tasks.Task InvokeAsync(System.Action workItem) { throw null; } protected System.Threading.Tasks.Task InvokeAsync(System.Func workItem) { throw null; } - void Microsoft.AspNetCore.Components.IComponent.Configure(Microsoft.AspNetCore.Components.RenderHandle renderHandle) { } + void Microsoft.AspNetCore.Components.IComponent.Attach(Microsoft.AspNetCore.Components.RenderHandle renderHandle) { } System.Threading.Tasks.Task Microsoft.AspNetCore.Components.IHandleAfterRender.OnAfterRenderAsync() { throw null; } System.Threading.Tasks.Task Microsoft.AspNetCore.Components.IHandleEvent.HandleEventAsync(Microsoft.AspNetCore.Components.EventCallbackWorkItem callback, object arg) { throw null; } protected virtual void OnAfterRender() { } @@ -104,6 +151,11 @@ namespace Microsoft.AspNetCore.Components public string __internalId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } } [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] + public readonly partial struct ElementReference + { + private readonly object _dummy; + } + [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public readonly partial struct EventCallback { private readonly object _dummy; @@ -307,7 +359,7 @@ namespace Microsoft.AspNetCore.Components } public partial interface IComponent { - void Configure(Microsoft.AspNetCore.Components.RenderHandle renderHandle); + void Attach(Microsoft.AspNetCore.Components.RenderHandle renderHandle); System.Threading.Tasks.Task SetParametersAsync(Microsoft.AspNetCore.Components.ParameterCollection parameters); } public partial interface IComponentContext @@ -347,7 +399,7 @@ namespace Microsoft.AspNetCore.Components { protected LayoutComponentBase() { } [Microsoft.AspNetCore.Components.ParameterAttribute] - protected Microsoft.AspNetCore.Components.RenderFragment Body { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public Microsoft.AspNetCore.Components.RenderFragment Body { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } } [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public readonly partial struct MarkupString @@ -672,7 +724,7 @@ namespace Microsoft.AspNetCore.Components.Rendering { private readonly object _dummy; public Microsoft.AspNetCore.Components.RenderTree.ArrayRange DisposedComponentIDs { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } - public Microsoft.AspNetCore.Components.RenderTree.ArrayRange DisposedEventHandlerIDs { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + public Microsoft.AspNetCore.Components.RenderTree.ArrayRange DisposedEventHandlerIDs { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } public Microsoft.AspNetCore.Components.RenderTree.ArrayRange ReferenceFrames { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } public Microsoft.AspNetCore.Components.RenderTree.ArrayRange UpdatedComponents { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } } @@ -683,7 +735,7 @@ namespace Microsoft.AspNetCore.Components.Rendering public event System.UnhandledExceptionEventHandler UnhandledSynchronizationException { add { } remove { } } protected internal virtual void AddToRenderQueue(int componentId, Microsoft.AspNetCore.Components.RenderFragment renderFragment) { } protected internal int AssignRootComponentId(Microsoft.AspNetCore.Components.IComponent component) { throw null; } - public virtual System.Threading.Tasks.Task DispatchEventAsync(int eventHandlerId, Microsoft.AspNetCore.Components.Rendering.EventFieldInfo fieldInfo, Microsoft.AspNetCore.Components.UIEventArgs eventArgs) { throw null; } + public virtual System.Threading.Tasks.Task DispatchEventAsync(ulong eventHandlerId, Microsoft.AspNetCore.Components.Rendering.EventFieldInfo fieldInfo, Microsoft.AspNetCore.Components.UIEventArgs eventArgs) { throw null; } public void Dispose() { } protected virtual void Dispose(bool disposing) { } protected abstract void HandleException(System.Exception exception); @@ -716,7 +768,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree public ArrayRange(T[] array, int count) { throw null; } public Microsoft.AspNetCore.Components.RenderTree.ArrayRange Clone() { throw null; } } - public partial class RenderTreeBuilder + public partial class RenderTreeBuilder : System.IDisposable { public const string ChildContent = "ChildContent"; public RenderTreeBuilder(Microsoft.AspNetCore.Components.Rendering.Renderer renderer) { } @@ -747,6 +799,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree public void OpenElement(int sequence, string elementName) { } public void SetKey(object value) { } public void SetUpdatesAttributeName(string updatesAttributeName) { } + void System.IDisposable.Dispose() { } } [System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)] public readonly partial struct RenderTreeDiff @@ -781,17 +834,17 @@ namespace Microsoft.AspNetCore.Components.RenderTree PermutationListEntry = 9, PermutationListEnd = 10, } - public enum RenderTreeFrameType + public enum RenderTreeFrameType : short { - None = 0, - Element = 1, - Text = 2, - Attribute = 3, - Component = 4, - Region = 5, - ElementReferenceCapture = 6, - ComponentReferenceCapture = 7, - Markup = 8, + None = (short)0, + Element = (short)1, + Text = (short)2, + Attribute = (short)3, + Component = (short)4, + Region = (short)5, + ElementReferenceCapture = (short)6, + ComponentReferenceCapture = (short)7, + Markup = (short)8, } } namespace Microsoft.AspNetCore.Components.Routing diff --git a/src/Components/Components/src/Auth/AuthorizeViewCore.cs b/src/Components/Components/src/Auth/AuthorizeViewCore.cs index 07e2cd1c48..4b1f2f581f 100644 --- a/src/Components/Components/src/Auth/AuthorizeViewCore.cs +++ b/src/Components/Components/src/Auth/AuthorizeViewCore.cs @@ -102,10 +102,27 @@ namespace Microsoft.AspNetCore.Components private async Task IsAuthorizedAsync(ClaimsPrincipal user) { var authorizeData = GetAuthorizeData(); + EnsureNoAuthenticationSchemeSpecified(authorizeData); + var policy = await AuthorizationPolicy.CombineAsync( AuthorizationPolicyProvider, authorizeData); var result = await AuthorizationService.AuthorizeAsync(user, Resource, policy); return result.Succeeded; } + + private static void EnsureNoAuthenticationSchemeSpecified(IAuthorizeData[] authorizeData) + { + // It's not meaningful to specify a nonempty scheme, since by the time Components + // authorization runs, we already have a specific ClaimsPrincipal (we're stateful). + // To avoid any confusion, ensure the developer isn't trying to specify a scheme. + for (var i = 0; i < authorizeData.Length; i++) + { + var entry = authorizeData[i]; + if (!string.IsNullOrEmpty(entry.AuthenticationSchemes)) + { + throw new NotSupportedException($"The authorization data specifies an authentication scheme with value '{entry.AuthenticationSchemes}'. Authentication schemes cannot be specified for components."); + } + } + } } } diff --git a/src/Components/Components/src/BindConverter.cs b/src/Components/Components/src/BindConverter.cs new file mode 100644 index 0000000000..f77689bde2 --- /dev/null +++ b/src/Components/Components/src/BindConverter.cs @@ -0,0 +1,1418 @@ +// 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.Concurrent; +using System.ComponentModel; +using System.Globalization; +using System.Reflection; + +namespace Microsoft.AspNetCore.Components +{ + /// + /// Performs conversions during binding. + /// + // + // Perf: our conversion routines present a regular API surface that allows us to specialize on types to avoid boxing. + // for instance, many of these types could be cast to IFormattable to do the appropriate formatting, but that's going + // to allocate. + public static class BindConverter + { + private static object BoxedTrue = true; + private static object BoxedFalse = false; + + private delegate object BindFormatter(T value, CultureInfo culture); + private delegate object BindFormatterWithFormat(T value, CultureInfo culture, string format); + + internal delegate bool BindParser(object obj, CultureInfo culture, out T value); + internal delegate bool BindParserWithFormat(object obj, CultureInfo culture, string format, out T value); + + /// + /// Formats the provided as a . + /// + /// The value to format. + /// + /// The to use while formatting. Defaults to . + /// + /// The formatted value. + public static string FormatValue(string value, CultureInfo culture = null) => FormatStringValueCore(value, culture); + + private static string FormatStringValueCore(string value, CultureInfo culture) + { + return value; + } + + /// + /// Formats the provided for inclusion in an attribute. + /// + /// The value to format. + /// + /// The to use while formatting. Defaults to . + /// + /// The formatted value. + public static bool FormatValue(bool value, CultureInfo culture = null) + { + // Formatting for bool is special-cased. We need to produce a boolean value for conditional attributes + // to work. + return value; + } + + // Used with generics + private static object FormatBoolValueCore(bool value, CultureInfo culture) + { + // Formatting for bool is special-cased. We need to produce a boolean value for conditional attributes + // to work. + return value ? BoxedTrue : BoxedFalse; + } + + /// + /// Formats the provided for inclusion in an attribute. + /// + /// The value to format. + /// + /// The to use while formatting. Defaults to . + /// + /// The formatted value. + public static bool? FormatValue(bool? value, CultureInfo culture = null) + { + // Formatting for bool is special-cased. We need to produce a boolean value for conditional attributes + // to work. + return value == null ? (bool?)null : value.Value; + } + + // Used with generics + private static object FormatNullableBoolValueCore(bool? value, CultureInfo culture) + { + // Formatting for bool is special-cased. We need to produce a boolean value for conditional attributes + // to work. + return value == null ? null : value.Value ? BoxedTrue : BoxedFalse; + } + + /// + /// Formats the provided for inclusion in an attribute. + /// + /// The value to format. + /// + /// The to use while formatting. Defaults to . + /// + /// The formatted value. + public static string FormatValue(int value, CultureInfo culture = null) => FormatIntValueCore(value, culture); + + private static string FormatIntValueCore(int value, CultureInfo culture) + { + return value.ToString(culture ?? CultureInfo.CurrentCulture); + } + + /// + /// Formats the provided for inclusion in an attribute. + /// + /// The value to format. + /// + /// The to use while formatting. Defaults to . + /// + /// The formatted value. + public static string FormatValue(int? value, CultureInfo culture = null) => FormatNullableIntValueCore(value, culture); + + private static string FormatNullableIntValueCore(int? value, CultureInfo culture) + { + if (value == null) + { + return null; + } + + return value.Value.ToString(culture ?? CultureInfo.CurrentCulture); + } + + /// + /// Formats the provided for inclusion in an attribute. + /// + /// The value to format. + /// + /// The to use while formatting. Defaults to . + /// + /// The formatted value. + public static string FormatValue(long value, CultureInfo culture = null) => FormatLongValueCore(value, culture); + + private static string FormatLongValueCore(long value, CultureInfo culture) + { + return value.ToString(culture ?? CultureInfo.CurrentCulture); + } + + /// + /// Formats the provided for inclusion in an attribute. + /// + /// The value to format. + /// + /// The to use while formatting. Defaults to . + /// + /// The formatted value. + public static string FormatValue(long? value, CultureInfo culture = null) => FormatNullableLongValueCore(value, culture); + + private static string FormatNullableLongValueCore(long? value, CultureInfo culture) + { + if (value == null) + { + return null; + } + + return value.Value.ToString(culture ?? CultureInfo.CurrentCulture); + } + + /// + /// Formats the provided for inclusion in an attribute. + /// + /// The value to format. + /// + /// The to use while formatting. Defaults to . + /// + /// The formatted value. + public static string FormatValue(float value, CultureInfo culture = null) => FormatFloatValueCore(value, culture); + + private static string FormatFloatValueCore(float value, CultureInfo culture) + { + return value.ToString(culture ?? CultureInfo.CurrentCulture); + } + + /// + /// Formats the provided for inclusion in an attribute. + /// + /// The value to format. + /// + /// The to use while formatting. Defaults to . + /// + /// The formatted value. + public static string FormatValue(float? value, CultureInfo culture = null) => FormatNullableFloatValueCore(value, culture); + + private static string FormatNullableFloatValueCore(float? value, CultureInfo culture) + { + if (value == null) + { + return null; + } + + return value.Value.ToString(culture ?? CultureInfo.CurrentCulture); + } + + /// + /// Formats the provided for inclusion in an attribute. + /// + /// The value to format. + /// + /// The to use while formatting. Defaults to . + /// + /// The formatted value. + public static string FormatValue(double value, CultureInfo culture = null) => FormatDoubleValueCore(value, culture); + + private static string FormatDoubleValueCore(double value, CultureInfo culture) + { + return value.ToString(culture ?? CultureInfo.CurrentCulture); + } + + /// + /// Formats the provided for inclusion in an attribute. + /// + /// The value to format. + /// + /// The to use while formatting. Defaults to . + /// + /// The formatted value. + public static string FormatValue(double? value, CultureInfo culture = null) => FormatNullableDoubleValueCore(value, culture); + + private static string FormatNullableDoubleValueCore(double? value, CultureInfo culture) + { + if (value == null) + { + return null; + } + + return value.Value.ToString(culture ?? CultureInfo.CurrentCulture); + } + + /// + /// Formats the provided for inclusion in an attribute. + /// + /// The value to format. + /// + /// The to use while formatting. Defaults to . + /// + /// The formatted value. + public static string FormatValue(decimal value, CultureInfo culture = null) => FormatDecimalValueCore(value, culture); + + private static string FormatDecimalValueCore(decimal value, CultureInfo culture) + { + return value.ToString(culture ?? CultureInfo.CurrentCulture); + } + + /// + /// Formats the provided as a . + /// + /// The value to format. + /// + /// The to use while formatting. Defaults to . + /// + /// The formatted value. + public static string FormatValue(decimal? value, CultureInfo culture = null) => FormatNullableDecimalValueCore(value, culture); + + private static string FormatNullableDecimalValueCore(decimal? value, CultureInfo culture) + { + if (value == null) + { + return null; + } + + return value.Value.ToString(culture ?? CultureInfo.CurrentCulture); + } + + /// + /// Formats the provided as a . + /// + /// The value to format. + /// + /// The to use while formatting. Defaults to . + /// + /// The formatted value. + public static string FormatValue(DateTime value, CultureInfo culture = null) => FormatDateTimeValueCore(value, format: null, culture); + + /// + /// Formats the provided as a . + /// + /// The value to format. + /// The format to use. Provided to . + /// + /// The to use while formatting. Defaults to . + /// + /// The formatted value. + public static string FormatValue(DateTime value, string format, CultureInfo culture = null) => FormatDateTimeValueCore(value, format, culture); + + private static string FormatDateTimeValueCore(DateTime value, string format, CultureInfo culture) + { + if (format != null) + { + return value.ToString(format, culture ?? CultureInfo.CurrentCulture); + } + + return value.ToString(culture ?? CultureInfo.CurrentCulture); + } + + private static string FormatDateTimeValueCore(DateTime value, CultureInfo culture) + { + return value.ToString(culture ?? CultureInfo.CurrentCulture); + } + + /// + /// Formats the provided as a . + /// + /// The value to format. + /// + /// The to use while formatting. Defaults to . + /// + /// The formatted value. + public static string FormatValue(DateTime? value, CultureInfo culture = null) => FormatNullableDateTimeValueCore(value, format: null, culture); + + /// + /// Formats the provided as a . + /// + /// The value to format. + /// The format to use. Provided to . + /// + /// The to use while formatting. Defaults to . + /// + /// The formatted value. + public static string FormatValue(DateTime? value, string format, CultureInfo culture = null) => FormatNullableDateTimeValueCore(value, format, culture); + + private static string FormatNullableDateTimeValueCore(DateTime? value, string format, CultureInfo culture) + { + if (value == null) + { + return null; + } + + if (format != null) + { + return value.Value.ToString(format, culture ?? CultureInfo.CurrentCulture); + } + + return value.Value.ToString(culture ?? CultureInfo.CurrentCulture); + } + + private static string FormatNullableDateTimeValueCore(DateTime? value, CultureInfo culture) + { + if (value == null) + { + return null; + } + + return value.Value.ToString(culture ?? CultureInfo.CurrentCulture); + } + + /// + /// Formats the provided as a . + /// + /// The value to format. + /// + /// The to use while formatting. Defaults to . + /// + /// The formatted value. + public static string FormatValue(DateTimeOffset value, CultureInfo culture = null) => FormatDateTimeOffsetValueCore(value, format: null, culture); + + + /// + /// Formats the provided as a . + /// + /// The value to format. + /// The format to use. Provided to . + /// + /// The to use while formatting. Defaults to . + /// + /// The formatted value. + public static string FormatValue(DateTimeOffset value, string format, CultureInfo culture = null) => FormatDateTimeOffsetValueCore(value, format, culture); + + private static string FormatDateTimeOffsetValueCore(DateTimeOffset value, string format, CultureInfo culture) + { + if (format != null) + { + return value.ToString(format, culture ?? CultureInfo.CurrentCulture); + } + + return value.ToString(culture ?? CultureInfo.CurrentCulture); + } + + private static string FormatDateTimeOffsetValueCore(DateTimeOffset value, CultureInfo culture) + { + return value.ToString(culture ?? CultureInfo.CurrentCulture); + } + + /// + /// Formats the provided as a . + /// + /// The value to format. + /// + /// The to use while formatting. Defaults to . + /// + /// The formatted value. + public static string FormatValue(DateTimeOffset? value, CultureInfo culture = null) => FormatNullableDateTimeOffsetValueCore(value, format: null, culture); + + /// + /// Formats the provided as a . + /// + /// The value to format. + /// The format to use. Provided to . + /// + /// The to use while formatting. Defaults to . + /// + /// The formatted value. + public static string FormatValue(DateTimeOffset? value, string format, CultureInfo culture = null) => FormatNullableDateTimeOffsetValueCore(value, format, culture); + + private static string FormatNullableDateTimeOffsetValueCore(DateTimeOffset? value, string format, CultureInfo culture) + { + if (value == null) + { + return null; + } + + if (format != null) + { + return value.Value.ToString(format, culture ?? CultureInfo.CurrentCulture); + } + + return value.Value.ToString(culture ?? CultureInfo.CurrentCulture); + } + + private static string FormatNullableDateTimeOffsetValueCore(DateTimeOffset? value, CultureInfo culture) + { + if (value == null) + { + return null; + } + + return value.Value.ToString(culture ?? CultureInfo.CurrentCulture); + } + + private static string FormatEnumValueCore(T value, CultureInfo culture) where T : struct, Enum + { + return value.ToString(); // The overload that acccepts a culture is [Obsolete] + } + + private static string FormatNullableEnumValueCore(T? value, CultureInfo culture) where T : struct, Enum + { + if (value == null) + { + return null; + } + + return value.Value.ToString(); // The overload that acccepts a culture is [Obsolete] + } + + /// + /// Formats the provided as a . + /// + /// The value to format. + /// + /// The to use while formatting. Defaults to . + /// + /// The formatted value. + public static object FormatValue(T value, CultureInfo culture = null) + { + var formatter = FormatterDelegateCache.Get(); + return formatter(value, culture); + } + + /// + /// Attempts to convert a value to a . + /// + /// The object to convert. + /// The to use for conversion. + /// The converted value. + /// true if conversion is successful, otherwise false. + public static bool TryConvertToString(object obj, CultureInfo culture, out string value) + { + return ConvertToStringCore(obj, culture, out value); + } + + internal readonly static BindParser ConvertToString = ConvertToStringCore; + + private static bool ConvertToStringCore(object obj, CultureInfo culture, out string value) + { + // We expect the input to already be a string. + value = (string)obj; + return true; + } + + /// + /// Attempts to convert a value to a . + /// + /// The object to convert. + /// The to use for conversion. + /// The converted value. + /// true if conversion is successful, otherwise false. + public static bool TryConvertToBool(object obj, CultureInfo culture, out bool value) + { + return ConvertToBoolCore(obj, culture, out value); + } + + /// + /// Attempts to convert a value to a nullable . + /// + /// The object to convert. + /// The to use for conversion. + /// The converted value. + /// true if conversion is successful, otherwise false. + public static bool TryConvertToNullableBool(object obj, CultureInfo culture, out bool? value) + { + return ConvertToNullableBoolCore(obj, culture, out value); + } + + internal static BindParser ConvertToBool = ConvertToBoolCore; + internal static BindParser ConvertToNullableBool = ConvertToNullableBoolCore; + + private static bool ConvertToBoolCore(object obj, CultureInfo culture, out bool value) + { + // We expect the input to already be a bool. + value = (bool)obj; + return true; + } + + private static bool ConvertToNullableBoolCore(object obj, CultureInfo culture, out bool? value) + { + // We expect the input to already be a bool. + value = (bool?)obj; + return true; + } + + /// + /// Attempts to convert a value to a . + /// + /// The object to convert. + /// The to use for conversion. + /// The converted value. + /// true if conversion is successful, otherwise false. + public static bool TryConvertToInt(object obj, CultureInfo culture, out int value) + { + return ConvertToIntCore(obj, culture, out value); + } + + /// + /// Attempts to convert a value to a nullable . + /// + /// The object to convert. + /// The to use for conversion. + /// The converted value. + /// true if conversion is successful, otherwise false. + public static bool TryConvertToNullableInt(object obj, CultureInfo culture, out int? value) + { + return ConvertToNullableIntCore(obj, culture, out value); + } + + internal static BindParser ConvertToInt = ConvertToIntCore; + internal static BindParser ConvertToNullableInt = ConvertToNullableIntCore; + + private static bool ConvertToIntCore(object obj, CultureInfo culture, out int value) + { + var text = (string)obj; + if (string.IsNullOrEmpty(text)) + { + value = default; + return false; + } + + if (!int.TryParse(text, NumberStyles.Number, culture ?? CultureInfo.CurrentCulture, out var converted)) + { + value = default; + return false; + } + + value = converted; + return true; + } + + private static bool ConvertToNullableIntCore(object obj, CultureInfo culture, out int? value) + { + var text = (string)obj; + if (string.IsNullOrEmpty(text)) + { + value = default; + return true; + } + + if (!int.TryParse(text, NumberStyles.Number, culture ?? CultureInfo.CurrentCulture, out var converted)) + { + value = default; + return false; + } + + value = converted; + return true; + } + + /// + /// Attempts to convert a value to a . + /// + /// The object to convert. + /// The to use for conversion. + /// The converted value. + /// true if conversion is successful, otherwise false. + public static bool TryConvertToLong(object obj, CultureInfo culture, out long value) + { + return ConvertToLongCore(obj, culture, out value); + } + + /// + /// Attempts to convert a value to a nullable . + /// + /// The object to convert. + /// The to use for conversion. + /// The converted value. + /// true if conversion is successful, otherwise false. + public static bool TryConvertToNullableLong(object obj, CultureInfo culture, out long? value) + { + return ConvertToNullableLongCore(obj, culture, out value); + } + + internal static BindParser ConvertToLong = ConvertToLongCore; + internal static BindParser ConvertToNullableLong = ConvertToNullableLongCore; + + private static bool ConvertToLongCore(object obj, CultureInfo culture, out long value) + { + var text = (string)obj; + if (string.IsNullOrEmpty(text)) + { + value = default; + return false; + } + + if (!long.TryParse(text, NumberStyles.Number, culture ?? CultureInfo.CurrentCulture, out var converted)) + { + value = default; + return false; + } + + value = converted; + return true; + } + + private static bool ConvertToNullableLongCore(object obj, CultureInfo culture, out long? value) + { + var text = (string)obj; + if (string.IsNullOrEmpty(text)) + { + value = default; + return true; + } + + if (!long.TryParse(text, NumberStyles.Number, culture ?? CultureInfo.CurrentCulture, out var converted)) + { + value = default; + return false; + } + + value = converted; + return true; + } + + /// + /// Attempts to convert a value to a . + /// + /// The object to convert. + /// The to use for conversion. + /// The converted value. + /// true if conversion is successful, otherwise false. + public static bool TryConvertToFloat(object obj, CultureInfo culture, out float value) + { + return ConvertToFloatCore(obj, culture, out value); + } + + /// + /// Attempts to convert a value to a nullable . + /// + /// The object to convert. + /// The to use for conversion. + /// The converted value. + /// true if conversion is successful, otherwise false. + public static bool TryConvertToNullableFloat(object obj, CultureInfo culture, out float? value) + { + return ConvertToNullableFloatCore(obj, culture, out value); + } + + internal static BindParser ConvertToFloat = ConvertToFloatCore; + internal static BindParser ConvertToNullableFloat = ConvertToNullableFloatCore; + + private static bool ConvertToFloatCore(object obj, CultureInfo culture, out float value) + { + var text = (string)obj; + if (string.IsNullOrEmpty(text)) + { + value = default; + return false; + } + + if (!float.TryParse(text, NumberStyles.Number, culture ?? CultureInfo.CurrentCulture, out var converted)) + { + value = default; + return false; + } + + if (float.IsInfinity(converted) || float.IsNaN(converted)) + { + value = default; + return false; + } + + value = converted; + return true; + } + + private static bool ConvertToNullableFloatCore(object obj, CultureInfo culture, out float? value) + { + var text = (string)obj; + if (string.IsNullOrEmpty(text)) + { + value = default; + return true; + } + + if (!float.TryParse(text, NumberStyles.Number, culture ?? CultureInfo.CurrentCulture, out var converted)) + { + value = default; + return false; + } + + if (float.IsInfinity(converted) || float.IsNaN(converted)) + { + value = default; + return false; + } + + value = converted; + return true; + } + + /// + /// Attempts to convert a value to a . + /// + /// The object to convert. + /// The to use for conversion. + /// The converted value. + /// true if conversion is successful, otherwise false. + public static bool TryConvertToDouble(object obj, CultureInfo culture, out double value) + { + return ConvertToDoubleCore(obj, culture, out value); + } + + /// + /// Attempts to convert a value to a nullable . + /// + /// The object to convert. + /// The to use for conversion. + /// The converted value. + /// true if conversion is successful, otherwise false. + public static bool TryConvertToNullableDouble(object obj, CultureInfo culture, out double? value) + { + return ConvertToNullableDoubleCore(obj, culture, out value); + } + + internal static BindParser ConvertToDoubleDelegate = ConvertToDoubleCore; + internal static BindParser ConvertToNullableDoubleDelegate = ConvertToNullableDoubleCore; + + private static bool ConvertToDoubleCore(object obj, CultureInfo culture, out double value) + { + var text = (string)obj; + if (string.IsNullOrEmpty(text)) + { + value = default; + return false; + } + + if (!double.TryParse(text, NumberStyles.Number, culture ?? CultureInfo.CurrentCulture, out var converted)) + { + value = default; + return false; + } + + if (double.IsInfinity(converted) || double.IsNaN(converted)) + { + value = default; + return false; + } + + value = converted; + return true; + } + + private static bool ConvertToNullableDoubleCore(object obj, CultureInfo culture, out double? value) + { + var text = (string)obj; + if (string.IsNullOrEmpty(text)) + { + value = default; + return true; + } + + if (!double.TryParse(text, NumberStyles.Number, culture ?? CultureInfo.CurrentCulture, out var converted)) + { + value = default; + return false; + } + + if (double.IsInfinity(converted) || double.IsNaN(converted)) + { + value = default; + return false; + } + + value = converted; + return true; + } + + /// + /// Attempts to convert a value to a . + /// + /// The object to convert. + /// The to use for conversion. + /// The converted value. + /// true if conversion is successful, otherwise false. + public static bool TryConvertToDecimal(object obj, CultureInfo culture, out decimal value) + { + return ConvertToDecimalCore(obj, culture, out value); + } + + /// + /// Attempts to convert a value to a nullable . + /// + /// The object to convert. + /// The to use for conversion. + /// The converted value. + /// true if conversion is successful, otherwise false. + public static bool TryConvertToNullableDecimal(object obj, CultureInfo culture, out decimal? value) + { + return ConvertToNullableDecimalCore(obj, culture, out value); + } + + internal static BindParser ConvertToDecimal = ConvertToDecimalCore; + internal static BindParser ConvertToNullableDecimal = ConvertToNullableDecimalCore; + + private static bool ConvertToDecimalCore(object obj, CultureInfo culture, out decimal value) + { + var text = (string)obj; + if (string.IsNullOrEmpty(text)) + { + value = default; + return false; + } + + if (!decimal.TryParse(text, NumberStyles.Number, culture ?? CultureInfo.CurrentCulture, out var converted)) + { + value = default; + return false; + } + + value = converted; + return true; + } + + private static bool ConvertToNullableDecimalCore(object obj, CultureInfo culture, out decimal? value) + { + var text = (string)obj; + if (string.IsNullOrEmpty(text)) + { + value = default; + return true; + } + + if (!decimal.TryParse(text, NumberStyles.Number, culture ?? CultureInfo.CurrentCulture, out var converted)) + { + value = default; + return false; + } + + value = converted; + return true; + } + + /// + /// Attempts to convert a value to a . + /// + /// The object to convert. + /// The to use for conversion. + /// The converted value. + /// true if conversion is successful, otherwise false. + public static bool TryConvertToDateTime(object obj, CultureInfo culture, out DateTime value) + { + return ConvertToDateTimeCore(obj, culture, out value); + } + + /// + /// Attempts to convert a value to a . + /// + /// The object to convert. + /// The to use for conversion. + /// The format string to use in conversion. + /// The converted value. + /// true if conversion is successful, otherwise false. + public static bool TryConvertToDateTime(object obj, CultureInfo culture, string format, out DateTime value) + { + return ConvertToDateTimeCore(obj, culture, format, out value); + } + + /// + /// Attempts to convert a value to a nullable . + /// + /// The object to convert. + /// The to use for conversion. + /// The converted value. + /// true if conversion is successful, otherwise false. + public static bool TryConvertToNullableDateTime(object obj, CultureInfo culture, out DateTime? value) + { + return ConvertToNullableDateTimeCore(obj, culture, out value); + } + + /// + /// Attempts to convert a value to a nullable . + /// + /// The object to convert. + /// The to use for conversion. + /// The format string to use in conversion. + /// The converted value. + /// true if conversion is successful, otherwise false. + public static bool TryConvertToNullableDateTime(object obj, CultureInfo culture, string format, out DateTime? value) + { + return ConvertToNullableDateTimeCore(obj, culture, format, out value); + } + + internal static BindParser ConvertToDateTime = ConvertToDateTimeCore; + internal static BindParserWithFormat ConvertToDateTimeWithFormat = ConvertToDateTimeCore; + internal static BindParser ConvertToNullableDateTime = ConvertToNullableDateTimeCore; + internal static BindParserWithFormat ConvertToNullableDateTimeWithFormat = ConvertToNullableDateTimeCore; + + private static bool ConvertToDateTimeCore(object obj, CultureInfo culture, out DateTime value) + { + return ConvertToDateTimeCore(obj, culture, format: null, out value); + } + + private static bool ConvertToDateTimeCore(object obj, CultureInfo culture, string format, out DateTime value) + { + var text = (string)obj; + if (string.IsNullOrEmpty(text)) + { + value = default; + return false; + } + + if (format != null && DateTime.TryParseExact(text, format, culture ?? CultureInfo.CurrentCulture, DateTimeStyles.None, out var converted)) + { + value = converted; + return true; + } + else if (format == null && DateTime.TryParse(text, culture ?? CultureInfo.CurrentCulture, DateTimeStyles.None, out converted)) + { + value = converted; + return true; + } + + value = default; + return false; + } + + private static bool ConvertToNullableDateTimeCore(object obj, CultureInfo culture, out DateTime? value) + { + return ConvertToNullableDateTimeCore(obj, culture, format: null, out value); + } + + private static bool ConvertToNullableDateTimeCore(object obj, CultureInfo culture, string format, out DateTime? value) + { + var text = (string)obj; + if (string.IsNullOrEmpty(text)) + { + value = default; + return true; + } + + if (format != null && DateTime.TryParseExact(text, format, culture ?? CultureInfo.CurrentCulture, DateTimeStyles.None, out var converted)) + { + value = converted; + return true; + } + else if (format == null && DateTime.TryParse(text, culture ?? CultureInfo.CurrentCulture, DateTimeStyles.None, out converted)) + { + value = converted; + return true; + } + + value = default; + return false; + } + + /// + /// Attempts to convert a value to a . + /// + /// The object to convert. + /// The to use for conversion. + /// The converted value. + /// true if conversion is successful, otherwise false. + public static bool TryConvertToDateTimeOffset(object obj, CultureInfo culture, out DateTimeOffset value) + { + return ConvertToDateTimeOffsetCore(obj, culture, out value); + } + + /// + /// Attempts to convert a value to a . + /// + /// The object to convert. + /// The to use for conversion. + /// The format string to use in conversion. + /// The converted value. + /// true if conversion is successful, otherwise false. + public static bool TryConvertToDateTimeOffset(object obj, CultureInfo culture, string format, out DateTimeOffset value) + { + return ConvertToDateTimeOffsetCore(obj, culture, format, out value); + } + + /// + /// Attempts to convert a value to a nullable . + /// + /// The object to convert. + /// The to use for conversion. + /// The converted value. + /// true if conversion is successful, otherwise false. + public static bool TryConvertToNullableDateTimeOffset(object obj, CultureInfo culture, out DateTimeOffset? value) + { + return ConvertToNullableDateTimeOffsetCore(obj, culture, out value); + } + + /// + /// Attempts to convert a value to a nullable . + /// + /// The object to convert. + /// The to use for conversion. + /// The format string to use in conversion. + /// The converted value. + /// true if conversion is successful, otherwise false. + public static bool TryConvertToNullableDateTimeOffset(object obj, CultureInfo culture, string format, out DateTimeOffset? value) + { + return ConvertToNullableDateTimeOffsetCore(obj, culture, format, out value); + } + + internal static BindParser ConvertToDateTimeOffset = ConvertToDateTimeOffsetCore; + internal static BindParserWithFormat ConvertToDateTimeOffsetWithFormat = ConvertToDateTimeOffsetCore; + internal static BindParser ConvertToNullableDateTimeOffset = ConvertToNullableDateTimeOffsetCore; + internal static BindParserWithFormat ConvertToNullableDateTimeOffsetWithFormat = ConvertToNullableDateTimeOffsetCore; + + private static bool ConvertToDateTimeOffsetCore(object obj, CultureInfo culture, out DateTimeOffset value) + { + return ConvertToDateTimeOffsetCore(obj, culture, format: null, out value); + } + + private static bool ConvertToDateTimeOffsetCore(object obj, CultureInfo culture, string format, out DateTimeOffset value) + { + var text = (string)obj; + if (string.IsNullOrEmpty(text)) + { + value = default; + return false; + } + + if (format != null && DateTimeOffset.TryParseExact(text, format, culture ?? CultureInfo.CurrentCulture, DateTimeStyles.None, out var converted)) + { + value = converted; + return true; + } + else if (format == null && DateTimeOffset.TryParse(text, culture ?? CultureInfo.CurrentCulture, DateTimeStyles.None, out converted)) + { + value = converted; + return true; + } + + value = default; + return false; + } + + private static bool ConvertToNullableDateTimeOffsetCore(object obj, CultureInfo culture, out DateTimeOffset? value) + { + return ConvertToNullableDateTimeOffsetCore(obj, culture, format: null, out value); + } + + private static bool ConvertToNullableDateTimeOffsetCore(object obj, CultureInfo culture, string format, out DateTimeOffset? value) + { + var text = (string)obj; + if (string.IsNullOrEmpty(text)) + { + value = default; + return true; + } + + if (format != null && DateTimeOffset.TryParseExact(text, format, culture ?? CultureInfo.CurrentCulture, DateTimeStyles.None, out var converted)) + { + value = converted; + return true; + } + else if (format == null && DateTimeOffset.TryParse(text, culture ?? CultureInfo.CurrentCulture, DateTimeStyles.None, out converted)) + { + value = converted; + return true; + } + + value = default; + return false; + } + + private static bool ConvertToEnum(object obj, CultureInfo culture, out T value) where T : struct, Enum + { + var text = (string)obj; + if (string.IsNullOrEmpty(text)) + { + value = default; + return true; + } + + if (!Enum.TryParse(text, out var converted)) + { + value = default; + return false; + } + + if (!Enum.IsDefined(typeof(T), converted)) + { + value = default; + return false; + } + + value = converted; + return true; + } + + private static bool ConvertToNullableEnum(object obj, CultureInfo culture, out T? value) where T : struct, Enum + { + var text = (string)obj; + if (string.IsNullOrEmpty(text)) + { + value = default; + return true; + } + + if (!Enum.TryParse(text, out var converted)) + { + value = default; + return false; + } + + if (!Enum.IsDefined(typeof(T), converted)) + { + value = default; + return false; + } + + value = converted; + return true; + } + + /// + /// Attempts to convert a value to a value of type . + /// + /// The object to convert. + /// The to use for conversion. + /// The converted value. + /// true if conversion is successful, otherwise false. + public static bool TryConvertTo(object obj, CultureInfo culture, out T value) + { + var converter = ParserDelegateCache.Get(); + return converter(obj, culture, out value); + } + + private static class FormatterDelegateCache + { + private readonly static ConcurrentDictionary _cache = new ConcurrentDictionary(); + + private static MethodInfo _formatEnumValue; + private static MethodInfo _formatNullableEnumValue; + + public static BindFormatter Get() + { + if (!_cache.TryGetValue(typeof(T), out var formattter)) + { + // We need to replicate all of the primitive cases that we handle here so that they will behave the same way. + // The result will be cached. + if (typeof(T) == typeof(string)) + { + formattter = (BindFormatter)FormatStringValueCore; + } + else if (typeof(T) == typeof(bool)) + { + formattter = (BindFormatter)FormatBoolValueCore; + } + else if (typeof(T) == typeof(bool?)) + { + formattter = (BindFormatter)FormatNullableBoolValueCore; + } + else if (typeof(T) == typeof(int)) + { + formattter = (BindFormatter)FormatIntValueCore; + } + else if (typeof(T) == typeof(int?)) + { + formattter = (BindFormatter)FormatNullableIntValueCore; + } + else if (typeof(T) == typeof(long)) + { + formattter = (BindFormatter)FormatLongValueCore; + } + else if (typeof(T) == typeof(long?)) + { + formattter = (BindFormatter)FormatNullableLongValueCore; + } + else if (typeof(T) == typeof(float)) + { + formattter = (BindFormatter)FormatFloatValueCore; + } + else if (typeof(T) == typeof(float?)) + { + formattter = (BindFormatter)FormatNullableFloatValueCore; + } + else if (typeof(T) == typeof(double)) + { + formattter = (BindFormatter)FormatDoubleValueCore; + } + else if (typeof(T) == typeof(double?)) + { + formattter = (BindFormatter)FormatNullableDoubleValueCore; + } + else if (typeof(T) == typeof(decimal)) + { + formattter = (BindFormatter)FormatDecimalValueCore; + } + else if (typeof(T) == typeof(decimal?)) + { + formattter = (BindFormatter)FormatNullableDecimalValueCore; + } + else if (typeof(T) == typeof(DateTime)) + { + formattter = (BindFormatter)FormatDateTimeValueCore; + } + else if (typeof(T) == typeof(DateTime?)) + { + formattter = (BindFormatter)FormatNullableDateTimeValueCore; + } + else if (typeof(T) == typeof(DateTimeOffset)) + { + formattter = (BindFormatter)FormatDateTimeOffsetValueCore; + } + else if (typeof(T) == typeof(DateTimeOffset?)) + { + formattter = (BindFormatter)FormatNullableDateTimeOffsetValueCore; + } + else if (typeof(T).IsEnum) + { + // We have to deal invoke this dynamically to work around the type constraint on Enum.TryParse. + var method = _formatEnumValue ??= typeof(BindConverter).GetMethod(nameof(FormatEnumValueCore), BindingFlags.NonPublic | BindingFlags.Static); + formattter = method.MakeGenericMethod(typeof(T)).CreateDelegate(typeof(BindFormatter), target: null); + } + else if (Nullable.GetUnderlyingType(typeof(T)) is Type innerType && innerType.IsEnum) + { + // We have to deal invoke this dynamically to work around the type constraint on Enum.TryParse. + var method = _formatNullableEnumValue ??= typeof(BindConverter).GetMethod(nameof(FormatNullableEnumValueCore), BindingFlags.NonPublic | BindingFlags.Static); + formattter = method.MakeGenericMethod(innerType).CreateDelegate(typeof(BindFormatter), target: null); + } + else + { + formattter = MakeTypeConverterFormatter(); + } + + _cache.TryAdd(typeof(T), formattter); + } + + return (BindFormatter)formattter; + } + + private static BindFormatter MakeTypeConverterFormatter() + { + var typeConverter = TypeDescriptor.GetConverter(typeof(T)); + if (typeConverter == null || !typeConverter.CanConvertTo(typeof(string))) + { + throw new InvalidOperationException( + $"The type '{typeof(T).FullName}' does not have an associated {typeof(TypeConverter).Name} that supports " + + $"conversion to a string. " + + $"Apply '{typeof(TypeConverterAttribute).Name}' to the type to register a converter."); + } + + return FormatWithTypeConverter; + + string FormatWithTypeConverter(T value, CultureInfo culture) + { + // We intentionally close-over the TypeConverter to cache it. The TypeDescriptor infrastructure is slow. + return typeConverter.ConvertToString(context: null, culture ?? CultureInfo.CurrentCulture, value); + } + } + } + + internal static class ParserDelegateCache + { + private readonly static ConcurrentDictionary _cache = new ConcurrentDictionary(); + + private static MethodInfo _convertToEnum; + private static MethodInfo _convertToNullableEnum; + + public static BindParser Get() + { + if (!_cache.TryGetValue(typeof(T), out var parser)) + { + // We need to replicate all of the primitive cases that we handle here so that they will behave the same way. + // The result will be cached. + if (typeof(T) == typeof(string)) + { + parser = ConvertToString; + } + else if (typeof(T) == typeof(bool)) + { + parser = ConvertToBool; + } + else if (typeof(T) == typeof(bool?)) + { + parser = ConvertToNullableBool; + } + else if (typeof(T) == typeof(int)) + { + parser = ConvertToInt; + } + else if (typeof(T) == typeof(int?)) + { + parser = ConvertToNullableInt; + } + else if (typeof(T) == typeof(long)) + { + parser = ConvertToLong; + } + else if (typeof(T) == typeof(long?)) + { + parser = ConvertToNullableLong; + } + else if (typeof(T) == typeof(float)) + { + parser = ConvertToFloat; + } + else if (typeof(T) == typeof(float?)) + { + parser = ConvertToNullableFloat; + } + else if (typeof(T) == typeof(double)) + { + parser = ConvertToDoubleDelegate; + } + else if (typeof(T) == typeof(double?)) + { + parser = ConvertToNullableDoubleDelegate; + } + else if (typeof(T) == typeof(decimal)) + { + parser = ConvertToDecimal; + } + else if (typeof(T) == typeof(decimal?)) + { + parser = ConvertToNullableDecimal; + } + else if (typeof(T) == typeof(DateTime)) + { + parser = ConvertToDateTime; + } + else if (typeof(T) == typeof(DateTime?)) + { + parser = ConvertToNullableDateTime; + } + else if (typeof(T) == typeof(DateTimeOffset)) + { + parser = ConvertToDateTime; + } + else if (typeof(T) == typeof(DateTimeOffset?)) + { + parser = ConvertToNullableDateTime; + } + else if (typeof(T).IsEnum) + { + // We have to deal invoke this dynamically to work around the type constraint on Enum.TryParse. + var method = _convertToEnum ??= typeof(BindConverter).GetMethod(nameof(ConvertToEnum), BindingFlags.NonPublic | BindingFlags.Static); + parser = method.MakeGenericMethod(typeof(T)).CreateDelegate(typeof(BindParser), target: null); + } + else if (Nullable.GetUnderlyingType(typeof(T)) is Type innerType && innerType.IsEnum) + { + // We have to deal invoke this dynamically to work around the type constraint on Enum.TryParse. + var method = _convertToNullableEnum ??= typeof(BindConverter).GetMethod(nameof(ConvertToNullableEnum), BindingFlags.NonPublic | BindingFlags.Static); + parser = method.MakeGenericMethod(innerType).CreateDelegate(typeof(BindParser), target: null); + } + else + { + parser = MakeTypeConverterConverter(); + } + + _cache.TryAdd(typeof(T), parser); + } + + return (BindParser)parser; + } + + private static BindParser MakeTypeConverterConverter() + { + var typeConverter = TypeDescriptor.GetConverter(typeof(T)); + if (typeConverter == null || !typeConverter.CanConvertFrom(typeof(string))) + { + throw new InvalidOperationException( + $"The type '{typeof(T).FullName}' does not have an associated {typeof(TypeConverter).Name} that supports " + + $"conversion from a string. " + + $"Apply '{typeof(TypeConverterAttribute).Name}' to the type to register a converter."); + } + + return ConvertWithTypeConverter; + + bool ConvertWithTypeConverter(object obj, CultureInfo culture, out T value) + { + // We intentionally close-over the TypeConverter to cache it. The TypeDescriptor infrastructure is slow. + var converted = typeConverter.ConvertFrom(context: null, culture ?? CultureInfo.CurrentCulture, obj); + if (converted == null) + { + value = default; + return true; + } + + value = (T)converted; + return true; + } + } + } + } +} diff --git a/src/Components/Components/src/CascadingValue.cs b/src/Components/Components/src/CascadingValue.cs index 07a8e79c31..0547d2d38f 100644 --- a/src/Components/Components/src/CascadingValue.cs +++ b/src/Components/Components/src/CascadingValue.cs @@ -50,7 +50,7 @@ namespace Microsoft.AspNetCore.Components bool ICascadingValueComponent.CurrentValueIsFixed => IsFixed; /// - public void Configure(RenderHandle renderHandle) + public void Attach(RenderHandle renderHandle) { _renderHandle = renderHandle; } diff --git a/src/Components/Components/src/ComponentBase.cs b/src/Components/Components/src/ComponentBase.cs index 045216b722..23dd8734e7 100644 --- a/src/Components/Components/src/ComponentBase.cs +++ b/src/Components/Components/src/ComponentBase.cs @@ -158,7 +158,7 @@ namespace Microsoft.AspNetCore.Components protected Task InvokeAsync(Func workItem) => _renderHandle.Dispatcher.InvokeAsync(workItem); - void IComponent.Configure(RenderHandle renderHandle) + void IComponent.Attach(RenderHandle renderHandle) { // This implicitly means a ComponentBase can only be associated with a single // renderer. That's the only use case we have right now. If there was ever a need, diff --git a/src/Components/Components/src/ComponentFactory.cs b/src/Components/Components/src/ComponentFactory.cs index 78f3b7dbfd..aebf96bc06 100644 --- a/src/Components/Components/src/ComponentFactory.cs +++ b/src/Components/Components/src/ComponentFactory.cs @@ -1,44 +1,41 @@ -// 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 Microsoft.AspNetCore.Components.Reflection; using System; using System.Collections.Concurrent; -using System.Collections.Generic; using System.Linq; using System.Reflection; +using Microsoft.AspNetCore.Components.Reflection; namespace Microsoft.AspNetCore.Components { + /// + /// The property on this type is used as a static global cache. Ensure any changes to this type + /// are thread safe and can be safely cached statically. + /// internal class ComponentFactory { - private readonly static BindingFlags _injectablePropertyBindingFlags + private static readonly BindingFlags _injectablePropertyBindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; - private readonly IServiceProvider _serviceProvider; - private readonly IDictionary> _cachedInitializers - = new ConcurrentDictionary>(); + private readonly ConcurrentDictionary> _cachedInitializers + = new ConcurrentDictionary>(); - public ComponentFactory(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider - ?? throw new ArgumentNullException(nameof(serviceProvider)); - } + public static readonly ComponentFactory Instance = new ComponentFactory(); - public IComponent InstantiateComponent(Type componentType) + public IComponent InstantiateComponent(IServiceProvider serviceProvider, Type componentType) { - if (!typeof(IComponent).IsAssignableFrom(componentType)) + var instance = Activator.CreateInstance(componentType); + if (!(instance is IComponent component)) { - throw new ArgumentException($"The type {componentType.FullName} does not " + - $"implement {nameof(IComponent)}.", nameof(componentType)); + throw new ArgumentException($"The type {componentType.FullName} does not implement {nameof(IComponent)}.", nameof(componentType)); } - var instance = (IComponent)Activator.CreateInstance(componentType); - PerformPropertyInjection(instance); - return instance; + PerformPropertyInjection(serviceProvider, component); + return component; } - private void PerformPropertyInjection(IComponent instance) + private void PerformPropertyInjection(IServiceProvider serviceProvider, IComponent instance) { // This is thread-safe because _cachedInitializers is a ConcurrentDictionary. // We might generate the initializer more than once for a given type, but would @@ -47,18 +44,19 @@ namespace Microsoft.AspNetCore.Components if (!_cachedInitializers.TryGetValue(instanceType, out var initializer)) { initializer = CreateInitializer(instanceType); - _cachedInitializers[instanceType] = initializer; + _cachedInitializers.TryAdd(instanceType, initializer); } - initializer(instance); + initializer(serviceProvider, instance); } - private Action CreateInitializer(Type type) + private Action CreateInitializer(Type type) { // Do all the reflection up front var injectableProperties = MemberAssignment.GetPropertiesIncludingInherited(type, _injectablePropertyBindingFlags) - .Where(p => p.GetCustomAttribute() != null); + .Where(p => p.IsDefined(typeof(InjectAttribute))); + var injectables = injectableProperties.Select(property => ( propertyName: property.Name, @@ -66,23 +64,25 @@ namespace Microsoft.AspNetCore.Components setter: MemberAssignment.CreatePropertySetter(type, property) )).ToArray(); + return Initialize; + // Return an action whose closure can write all the injected properties // without any further reflection calls (just typecasts) - return instance => + void Initialize(IServiceProvider serviceProvider, IComponent component) { - foreach (var injectable in injectables) + foreach (var (propertyName, propertyType, setter) in injectables) { - var serviceInstance = _serviceProvider.GetService(injectable.propertyType); + var serviceInstance = serviceProvider.GetService(propertyType); if (serviceInstance == null) { throw new InvalidOperationException($"Cannot provide a value for property " + - $"'{injectable.propertyName}' on type '{type.FullName}'. There is no " + - $"registered service of type '{injectable.propertyType}'."); + $"'{propertyName}' on type '{type.FullName}'. There is no " + + $"registered service of type '{propertyType}'."); } - injectable.setter.SetValue(instance, serviceInstance); + setter.SetValue(component, serviceInstance); } - }; + } } } } diff --git a/src/Components/Components/src/ComponentResolver.cs b/src/Components/Components/src/ComponentResolver.cs deleted file mode 100644 index 4e107da3db..0000000000 --- a/src/Components/Components/src/ComponentResolver.cs +++ /dev/null @@ -1,73 +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.Collections.Generic; -using System.Linq; -using System.Reflection; - -namespace Microsoft.AspNetCore.Components -{ - /// - /// Resolves components for an application. - /// - internal static class ComponentResolver - { - /// - /// Lists all the types - /// - /// - /// - public static IEnumerable ResolveComponents(Assembly appAssembly) - { - var componentsAssembly = typeof(IComponent).Assembly; - - return EnumerateAssemblies(appAssembly.GetName(), componentsAssembly, new HashSet(new AssemblyComparer())) - .SelectMany(a => a.ExportedTypes) - .Where(t => typeof(IComponent).IsAssignableFrom(t)); - } - - private static IEnumerable EnumerateAssemblies( - AssemblyName assemblyName, - Assembly componentAssembly, - HashSet visited) - { - var assembly = Assembly.Load(assemblyName); - if (visited.Contains(assembly)) - { - // Avoid traversing visited assemblies. - yield break; - } - visited.Add(assembly); - var references = assembly.GetReferencedAssemblies(); - if (!references.Any(r => string.Equals(r.FullName, componentAssembly.FullName, StringComparison.Ordinal))) - { - // Avoid traversing references that don't point to Components (like netstandard2.0) - yield break; - } - else - { - yield return assembly; - - // Look at the list of transitive dependencies for more components. - foreach (var reference in references.SelectMany(r => EnumerateAssemblies(r, componentAssembly, visited))) - { - yield return reference; - } - } - } - - private class AssemblyComparer : IEqualityComparer - { - public bool Equals(Assembly x, Assembly y) - { - return string.Equals(x?.FullName, y?.FullName, StringComparison.Ordinal); - } - - public int GetHashCode(Assembly obj) - { - return obj.FullName.GetHashCode(); - } - } - } -} diff --git a/src/Components/Components/src/ElementReference.cs b/src/Components/Components/src/ElementReference.cs new file mode 100644 index 0000000000..92affa1249 --- /dev/null +++ b/src/Components/Components/src/ElementReference.cs @@ -0,0 +1,101 @@ +// 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.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading; + +namespace Microsoft.AspNetCore.Components +{ + /// + /// Represents a reference to a rendered element. + /// + [JsonConverter(typeof(ElementReferenceConverter))] + public readonly struct ElementReference + { + private static long _nextIdForWebAssemblyOnly = 1; + + /// + /// Gets a unique identifier for . + /// + /// + /// The Id is unique at least within the scope of a given user/circuit. + /// This property is public to support Json serialization and should not be used by user code. + /// + internal string Id { get; } + + private ElementReference(string id) + { + Id = id; + } + + internal static ElementReference CreateWithUniqueId() + => new ElementReference(CreateUniqueId()); + + private static string CreateUniqueId() + { + if (PlatformInfo.IsWebAssembly) + { + // On WebAssembly there's only one user, so it's fine to expose the number + // of IDs that have been assigned, and this is cheaper than creating a GUID. + // It's unfortunate that this still involves a heap allocation. If that becomes + // a problem we could extend RenderTreeFrame to have both "string" and "long" + // fields for ElementRefCaptureId, of which only one would be in use depending + // on the platform. + var id = Interlocked.Increment(ref _nextIdForWebAssemblyOnly); + return id.ToString(CultureInfo.InvariantCulture); + } + else + { + // For remote rendering, it's important not to disclose any cross-user state, + // such as the number of IDs that have been assigned. + return Guid.NewGuid().ToString("D", CultureInfo.InvariantCulture); + } + } + + private sealed class ElementReferenceConverter : JsonConverter + { + private static readonly JsonEncodedText IdProperty = JsonEncodedText.Encode("__internalId"); + + public override ElementReference Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + string id = null; + while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) + { + if (reader.TokenType == JsonTokenType.PropertyName) + { + if (reader.ValueTextEquals(IdProperty.EncodedUtf8Bytes)) + { + reader.Read(); + id = reader.GetString(); + } + else + { + throw new JsonException($"Unexpected JSON property '{reader.GetString()}'."); + } + } + else + { + throw new JsonException($"Unexcepted JSON Token {reader.TokenType}."); + } + } + + if (id is null) + { + throw new JsonException("__internalId is required."); + } + + return new ElementReference(id); + } + + public override void Write(Utf8JsonWriter writer, ElementReference value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + writer.WriteString(IdProperty, value.Id); + writer.WriteEndObject(); + } + } + } +} diff --git a/src/Components/Components/src/EventCallbackFactoryBinderExtensions.cs b/src/Components/Components/src/EventCallbackFactoryBinderExtensions.cs index 0f8a6d547e..a007c47f09 100644 --- a/src/Components/Components/src/EventCallbackFactoryBinderExtensions.cs +++ b/src/Components/Components/src/EventCallbackFactoryBinderExtensions.cs @@ -2,10 +2,8 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Concurrent; -using System.ComponentModel; using System.Globalization; -using System.Reflection; +using static Microsoft.AspNetCore.Components.BindConverter; namespace Microsoft.AspNetCore.Components { @@ -24,406 +22,6 @@ namespace Microsoft.AspNetCore.Components // For now we're not necessarily handling this correctly since we parse the same way for number and text. public static class EventCallbackFactoryBinderExtensions { - private delegate bool BindConverter(object obj, CultureInfo culture, out T value); - private delegate bool BindConverterWithFormat(object obj, CultureInfo culture, string format, out T value); - - // Perf: conversion delegates are written as static funcs so we can prevent - // allocations for these simple cases. - private readonly static BindConverter ConvertToString = ConvertToStringCore; - - private static bool ConvertToStringCore(object obj, CultureInfo culture, out string value) - { - // We expect the input to already be a string. - value = (string)obj; - return true; - } - - private static BindConverter ConvertToBool = ConvertToBoolCore; - private static BindConverter ConvertToNullableBool = ConvertToNullableBoolCore; - - private static bool ConvertToBoolCore(object obj, CultureInfo culture, out bool value) - { - // We expect the input to already be a bool. - value = (bool)obj; - return true; - } - - private static bool ConvertToNullableBoolCore(object obj, CultureInfo culture, out bool? value) - { - // We expect the input to already be a bool. - value = (bool?)obj; - return true; - } - - private static BindConverter ConvertToInt = ConvertToIntCore; - private static BindConverter ConvertToNullableInt = ConvertToNullableIntCore; - - private static bool ConvertToIntCore(object obj, CultureInfo culture, out int value) - { - var text = (string)obj; - if (string.IsNullOrEmpty(text)) - { - value = default; - return false; - } - - if (!int.TryParse(text, NumberStyles.Number, culture ?? CultureInfo.CurrentCulture, out var converted)) - { - value = default; - return false; - } - - value = converted; - return true; - } - - private static bool ConvertToNullableIntCore(object obj, CultureInfo culture, out int? value) - { - var text = (string)obj; - if (string.IsNullOrEmpty(text)) - { - value = default; - return true; - } - - if (!int.TryParse(text, NumberStyles.Number, culture ?? CultureInfo.CurrentCulture, out var converted)) - { - value = default; - return false; - } - - value = converted; - return true; - } - - private static BindConverter ConvertToLong = ConvertToLongCore; - private static BindConverter ConvertToNullableLong = ConvertToNullableLongCore; - - private static bool ConvertToLongCore(object obj, CultureInfo culture, out long value) - { - var text = (string)obj; - if (string.IsNullOrEmpty(text)) - { - value = default; - return false; - } - - if (!long.TryParse(text, NumberStyles.Number, culture ?? CultureInfo.CurrentCulture, out var converted)) - { - value = default; - return false; - } - - value = converted; - return true; - } - - private static bool ConvertToNullableLongCore(object obj, CultureInfo culture, out long? value) - { - var text = (string)obj; - if (string.IsNullOrEmpty(text)) - { - value = default; - return true; - } - - if (!long.TryParse(text, NumberStyles.Number, culture ?? CultureInfo.CurrentCulture, out var converted)) - { - value = default; - return false; - } - - value = converted; - return true; - } - - private static BindConverter ConvertToFloat = ConvertToFloatCore; - private static BindConverter ConvertToNullableFloat = ConvertToNullableFloatCore; - - private static bool ConvertToFloatCore(object obj, CultureInfo culture, out float value) - { - var text = (string)obj; - if (string.IsNullOrEmpty(text)) - { - value = default; - return false; - } - - if (!float.TryParse(text, NumberStyles.Number, culture ?? CultureInfo.CurrentCulture, out var converted)) - { - value = default; - return false; - } - - value = converted; - return true; - } - - private static bool ConvertToNullableFloatCore(object obj, CultureInfo culture, out float? value) - { - var text = (string)obj; - if (string.IsNullOrEmpty(text)) - { - value = default; - return true; - } - - if (!float.TryParse(text, NumberStyles.Number, culture ?? CultureInfo.CurrentCulture, out var converted)) - { - value = default; - return false; - } - - value = converted; - return true; - } - - private static BindConverter ConvertToDouble = ConvertToDoubleCore; - private static BindConverter ConvertToNullableDouble = ConvertToNullableDoubleCore; - - private static bool ConvertToDoubleCore(object obj, CultureInfo culture, out double value) - { - var text = (string)obj; - if (string.IsNullOrEmpty(text)) - { - value = default; - return false; - } - - if (!double.TryParse(text, NumberStyles.Number, culture ?? CultureInfo.CurrentCulture, out var converted)) - { - value = default; - return false; - } - - value = converted; - return true; - } - - private static bool ConvertToNullableDoubleCore(object obj, CultureInfo culture, out double? value) - { - var text = (string)obj; - if (string.IsNullOrEmpty(text)) - { - value = default; - return true; - } - - if (!double.TryParse(text, NumberStyles.Number, culture ?? CultureInfo.CurrentCulture, out var converted)) - { - value = default; - return false; - } - - value = converted; - return true; - } - - private static BindConverter ConvertToDecimal = ConvertToDecimalCore; - private static BindConverter ConvertToNullableDecimal = ConvertToNullableDecimalCore; - - private static bool ConvertToDecimalCore(object obj, CultureInfo culture, out decimal value) - { - var text = (string)obj; - if (string.IsNullOrEmpty(text)) - { - value = default; - return false; - } - - if (!decimal.TryParse(text, NumberStyles.Number, culture ?? CultureInfo.CurrentCulture, out var converted)) - { - value = default; - return false; - } - - value = converted; - return true; - } - - private static bool ConvertToNullableDecimalCore(object obj, CultureInfo culture, out decimal? value) - { - var text = (string)obj; - if (string.IsNullOrEmpty(text)) - { - value = default; - return true; - } - - if (!decimal.TryParse(text, NumberStyles.Number, culture ?? CultureInfo.CurrentCulture, out var converted)) - { - value = default; - return false; - } - - value = converted; - return true; - } - - private static BindConverter ConvertToDateTime = ConvertToDateTimeCore; - private static BindConverterWithFormat ConvertToDateTimeWithFormat = ConvertToDateTimeCore; - private static BindConverter ConvertToNullableDateTime = ConvertToNullableDateTimeCore; - private static BindConverterWithFormat ConvertToNullableDateTimeWithFormat = ConvertToNullableDateTimeCore; - - private static bool ConvertToDateTimeCore(object obj, CultureInfo culture, out DateTime value) - { - return ConvertToDateTimeCore(obj, culture, format: null, out value); - } - - private static bool ConvertToDateTimeCore(object obj, CultureInfo culture, string format, out DateTime value) - { - var text = (string)obj; - if (string.IsNullOrEmpty(text)) - { - value = default; - return false; - } - - if (format != null && DateTime.TryParseExact(text, format, culture ?? CultureInfo.CurrentCulture, DateTimeStyles.None, out var converted)) - { - value = converted; - return true; - } - else if (format == null && DateTime.TryParse(text, culture ?? CultureInfo.CurrentCulture, DateTimeStyles.None, out converted)) - { - value = converted; - return true; - } - - value = default; - return false; - } - - private static bool ConvertToNullableDateTimeCore(object obj, CultureInfo culture, out DateTime? value) - { - return ConvertToNullableDateTimeCore(obj, culture, format: null, out value); - } - - private static bool ConvertToNullableDateTimeCore(object obj, CultureInfo culture, string format, out DateTime? value) - { - var text = (string)obj; - if (string.IsNullOrEmpty(text)) - { - value = default; - return true; - } - - if (format != null && DateTime.TryParseExact(text, format, culture ?? CultureInfo.CurrentCulture, DateTimeStyles.None, out var converted)) - { - value = converted; - return true; - } - else if (format == null && DateTime.TryParse(text, culture ?? CultureInfo.CurrentCulture, DateTimeStyles.None, out converted)) - { - value = converted; - return true; - } - - value = default; - return false; - } - - private static BindConverter ConvertToDateTimeOffset = ConvertToDateTimeOffsetCore; - private static BindConverterWithFormat ConvertToDateTimeOffsetWithFormat = ConvertToDateTimeOffsetCore; - private static BindConverter ConvertToNullableDateTimeOffset = ConvertToNullableDateTimeOffsetCore; - private static BindConverterWithFormat ConvertToNullableDateTimeOffsetWithFormat = ConvertToNullableDateTimeOffsetCore; - - private static bool ConvertToDateTimeOffsetCore(object obj, CultureInfo culture, out DateTimeOffset value) - { - return ConvertToDateTimeOffsetCore(obj, culture, format: null, out value); - } - - private static bool ConvertToDateTimeOffsetCore(object obj, CultureInfo culture, string format, out DateTimeOffset value) - { - var text = (string)obj; - if (string.IsNullOrEmpty(text)) - { - value = default; - return false; - } - - if (format != null && DateTimeOffset.TryParseExact(text, format, culture ?? CultureInfo.CurrentCulture, DateTimeStyles.None, out var converted)) - { - value = converted; - return true; - } - else if (format == null && DateTimeOffset.TryParse(text, culture ?? CultureInfo.CurrentCulture, DateTimeStyles.None, out converted)) - { - value = converted; - return true; - } - - value = default; - return false; - } - - private static bool ConvertToNullableDateTimeOffsetCore(object obj, CultureInfo culture, out DateTimeOffset? value) - { - return ConvertToNullableDateTimeOffsetCore(obj, culture, format: null, out value); - } - - private static bool ConvertToNullableDateTimeOffsetCore(object obj, CultureInfo culture, string format, out DateTimeOffset? value) - { - var text = (string)obj; - if (string.IsNullOrEmpty(text)) - { - value = default; - return true; - } - - if (format != null && DateTimeOffset.TryParseExact(text, format, culture ?? CultureInfo.CurrentCulture, DateTimeStyles.None, out var converted)) - { - value = converted; - return true; - } - else if (format == null && DateTimeOffset.TryParse(text, culture ?? CultureInfo.CurrentCulture, DateTimeStyles.None, out converted)) - { - value = converted; - return true; - } - - value = default; - return false; - } - - private static bool ConvertToEnum(object obj, CultureInfo culture, out T value) where T : struct, Enum - { - var text = (string)obj; - if (string.IsNullOrEmpty(text)) - { - value = default; - return true; - } - - if (!Enum.TryParse(text, out var converted)) - { - value = default; - return false; - } - - value = converted; - return true; - } - - private static bool ConvertToNullableEnum(object obj, CultureInfo culture, out T? value) where T : struct, Enum - { - var text = (string)obj; - if (string.IsNullOrEmpty(text)) - { - value = default; - return true; - } - - if (!Enum.TryParse(text, out var converted)) - { - value = default; - return false; - } - - value = converted; - return true; - } - /// /// For internal use only. /// @@ -611,7 +209,7 @@ namespace Microsoft.AspNetCore.Components double existingValue, CultureInfo culture = null) { - return CreateBinderCore(factory, receiver, setter, culture, ConvertToDouble); + return CreateBinderCore(factory, receiver, setter, culture, ConvertToDoubleDelegate); } /// @@ -630,7 +228,7 @@ namespace Microsoft.AspNetCore.Components double? existingValue, CultureInfo culture = null) { - return CreateBinderCore(factory, receiver, setter, culture, ConvertToNullableDouble); + return CreateBinderCore(factory, receiver, setter, culture, ConvertToNullableDoubleDelegate); } /// @@ -687,7 +285,7 @@ namespace Microsoft.AspNetCore.Components DateTime existingValue, CultureInfo culture = null) { - return CreateBinderCore(factory, receiver, setter, culture, format: null, ConvertToDateTimeWithFormat); + return CreateBinderCore(factory, receiver, setter, culture, ConvertToDateTime); } /// @@ -727,7 +325,7 @@ namespace Microsoft.AspNetCore.Components DateTime? existingValue, CultureInfo culture = null) { - return CreateBinderCore(factory, receiver, setter, culture, format: null, ConvertToNullableDateTimeWithFormat); + return CreateBinderCore(factory, receiver, setter, culture, ConvertToNullableDateTime); } /// @@ -767,7 +365,7 @@ namespace Microsoft.AspNetCore.Components DateTimeOffset existingValue, CultureInfo culture = null) { - return CreateBinderCore(factory, receiver, setter, culture, format: null, ConvertToDateTimeOffsetWithFormat); + return CreateBinderCore(factory, receiver, setter, culture, ConvertToDateTimeOffset); } /// @@ -807,7 +405,7 @@ namespace Microsoft.AspNetCore.Components DateTimeOffset? existingValue, CultureInfo culture = null) { - return CreateBinderCore(factory, receiver, setter, culture, format: null, ConvertToNullableDateTimeOffsetWithFormat); + return CreateBinderCore(factory, receiver, setter, culture, ConvertToNullableDateTimeOffset); } /// @@ -848,7 +446,7 @@ namespace Microsoft.AspNetCore.Components T existingValue, CultureInfo culture = null) { - return CreateBinderCore(factory, receiver, setter, culture, BinderConverterCache.Get()); + return CreateBinderCore(factory, receiver, setter, culture, ParserDelegateCache.Get()); } private static EventCallback CreateBinderCore( @@ -856,7 +454,7 @@ namespace Microsoft.AspNetCore.Components object receiver, Action setter, CultureInfo culture, - BindConverter converter) + BindConverter.BindParser converter) { Action callback = e => { @@ -900,7 +498,7 @@ namespace Microsoft.AspNetCore.Components Action setter, CultureInfo culture, string format, - BindConverterWithFormat converter) + BindConverter.BindParserWithFormat converter) { Action callback = e => { @@ -937,147 +535,5 @@ namespace Microsoft.AspNetCore.Components }; return factory.Create(receiver, callback); } - - // We can't rely on generics + static to cache here unfortunately. That would require us to overload - // CreateBinder on T : struct AND T : class, which is not allowed. - private static class BinderConverterCache - { - private readonly static ConcurrentDictionary _cache = new ConcurrentDictionary(); - - private static MethodInfo _convertToEnum; - private static MethodInfo _convertToNullableEnum; - - public static BindConverter Get() - { - if (!_cache.TryGetValue(typeof(T), out var converter)) - { - // We need to replicate all of the primitive cases that we handle here so that they will behave the same way. - // The result will be cached. - if (typeof(T) == typeof(string)) - { - converter = ConvertToString; - } - else if (typeof(T) == typeof(bool)) - { - converter = ConvertToBool; - } - else if (typeof(T) == typeof(bool?)) - { - converter = ConvertToNullableBool; - } - else if (typeof(T) == typeof(int)) - { - converter = ConvertToInt; - } - else if (typeof(T) == typeof(int?)) - { - converter = ConvertToNullableInt; - } - else if (typeof(T) == typeof(long)) - { - converter = ConvertToLong; - } - else if (typeof(T) == typeof(long?)) - { - converter = ConvertToNullableLong; - } - else if (typeof(T) == typeof(float)) - { - converter = ConvertToFloat; - } - else if (typeof(T) == typeof(float?)) - { - converter = ConvertToNullableFloat; - } - else if (typeof(T) == typeof(double)) - { - converter = ConvertToDouble; - } - else if (typeof(T) == typeof(double?)) - { - converter = ConvertToNullableDouble; - } - else if (typeof(T) == typeof(decimal)) - { - converter = ConvertToDecimal; - } - else if (typeof(T) == typeof(decimal?)) - { - converter = ConvertToNullableDecimal; - } - else if (typeof(T) == typeof(DateTime)) - { - converter = ConvertToDateTime; - } - else if (typeof(T) == typeof(DateTime?)) - { - converter = ConvertToNullableDateTime; - } - else if (typeof(T) == typeof(DateTimeOffset)) - { - converter = ConvertToDateTimeOffset; - } - else if (typeof(T) == typeof(DateTimeOffset?)) - { - converter = ConvertToNullableDateTimeOffset; - } - else if (typeof(T).IsEnum) - { - // We have to deal invoke this dynamically to work around the type constraint on Enum.TryParse. - var method = _convertToEnum ??= typeof(EventCallbackFactoryBinderExtensions).GetMethod(nameof(ConvertToEnum), BindingFlags.NonPublic | BindingFlags.Static); - converter = method.MakeGenericMethod(typeof(T)).CreateDelegate(typeof(BindConverter), target: null); - } - else if (Nullable.GetUnderlyingType(typeof(T)) is Type innerType && innerType.IsEnum) - { - // We have to deal invoke this dynamically to work around the type constraint on Enum.TryParse. - var method = _convertToNullableEnum ??= typeof(EventCallbackFactoryBinderExtensions).GetMethod(nameof(ConvertToNullableEnum), BindingFlags.NonPublic | BindingFlags.Static); - converter = method.MakeGenericMethod(innerType).CreateDelegate(typeof(BindConverter), target: null); - } - else - { - converter = MakeTypeConverterConverter(); - } - - _cache.TryAdd(typeof(T), converter); - } - - return (BindConverter)converter; - } - - private static BindConverter MakeTypeConverterConverter() - { - var typeConverter = TypeDescriptor.GetConverter(typeof(T)); - if (typeConverter == null || !typeConverter.CanConvertFrom(typeof(string))) - { - throw new InvalidOperationException( - $"The type '{typeof(T).FullName}' does not have an associated {typeof(TypeConverter).Name} that supports " + - $"conversion from a string. " + - $"Apply '{typeof(TypeConverterAttribute).Name}' to the type to register a converter."); - } - - return ConvertWithTypeConverter; - - bool ConvertWithTypeConverter(object obj, CultureInfo culture, out T value) - { - var text = (string)obj; - if (string.IsNullOrEmpty(text)) - { - value = default; - return true; - } - - // We intentionally close-over the TypeConverter to cache it. The TypeDescriptor infrastructure is slow. - var converted = typeConverter.ConvertFromString(context: null, culture ?? CultureInfo.CurrentCulture, text); - if (converted == null) - { - value = default; - return false; - } - - value = (T)converted; - return true; - } - } - } } } diff --git a/src/Components/Components/src/Forms/InputComponents/InputCheckbox.cs b/src/Components/Components/src/Forms/InputComponents/InputCheckbox.cs index df84c8bdaa..981287ee8d 100644 --- a/src/Components/Components/src/Forms/InputComponents/InputCheckbox.cs +++ b/src/Components/Components/src/Forms/InputComponents/InputCheckbox.cs @@ -27,7 +27,7 @@ namespace Microsoft.AspNetCore.Components.Forms builder.AddMultipleAttributes(1, AdditionalAttributes); builder.AddAttribute(2, "type", "checkbox"); builder.AddAttribute(3, "class", CssClass); - builder.AddAttribute(4, "checked", BindMethods.GetValue(CurrentValue)); + builder.AddAttribute(4, "checked", BindConverter.FormatValue(CurrentValue)); builder.AddAttribute(5, "onchange", EventCallback.Factory.CreateBinder(this, __value => CurrentValue = __value, CurrentValue)); builder.CloseElement(); } diff --git a/src/Components/Components/src/Forms/InputComponents/InputDate.cs b/src/Components/Components/src/Forms/InputComponents/InputDate.cs index 0a79bbc15d..f080af908d 100644 --- a/src/Components/Components/src/Forms/InputComponents/InputDate.cs +++ b/src/Components/Components/src/Forms/InputComponents/InputDate.cs @@ -27,7 +27,7 @@ namespace Microsoft.AspNetCore.Components.Forms builder.AddMultipleAttributes(1, AdditionalAttributes); builder.AddAttribute(2, "type", "date"); builder.AddAttribute(3, "class", CssClass); - builder.AddAttribute(4, "value", BindMethods.GetValue(CurrentValueAsString)); + builder.AddAttribute(4, "value", BindConverter.FormatValue(CurrentValueAsString)); builder.AddAttribute(5, "onchange", EventCallback.Factory.CreateBinder(this, __value => CurrentValueAsString = __value, CurrentValueAsString)); builder.CloseElement(); } @@ -38,9 +38,9 @@ namespace Microsoft.AspNetCore.Components.Forms switch (value) { case DateTime dateTimeValue: - return dateTimeValue.ToString(DateFormat, CultureInfo.InvariantCulture); + return BindConverter.FormatValue(dateTimeValue, DateFormat, CultureInfo.InvariantCulture); case DateTimeOffset dateTimeOffsetValue: - return dateTimeOffsetValue.ToString(DateFormat, CultureInfo.InvariantCulture); + return BindConverter.FormatValue(dateTimeOffsetValue, DateFormat, CultureInfo.InvariantCulture); default: return string.Empty; // Handles null for Nullable, etc. } @@ -81,7 +81,7 @@ namespace Microsoft.AspNetCore.Components.Forms static bool TryParseDateTime(string value, out T result) { - var success = DateTime.TryParseExact(value, DateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out var parsedValue); + var success = BindConverter.TryConvertToDateTime(value, CultureInfo.InvariantCulture, DateFormat, out var parsedValue); if (success) { result = (T)(object)parsedValue; @@ -96,7 +96,7 @@ namespace Microsoft.AspNetCore.Components.Forms static bool TryParseDateTimeOffset(string value, out T result) { - var success = DateTimeOffset.TryParseExact(value, DateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out var parsedValue); + var success = BindConverter.TryConvertToDateTimeOffset(value, CultureInfo.InvariantCulture, DateFormat, out var parsedValue); if (success) { result = (T)(object)parsedValue; diff --git a/src/Components/Components/src/Forms/InputComponents/InputNumber.cs b/src/Components/Components/src/Forms/InputComponents/InputNumber.cs index 044f4aac3d..c5a222f6ff 100644 --- a/src/Components/Components/src/Forms/InputComponents/InputNumber.cs +++ b/src/Components/Components/src/Forms/InputComponents/InputNumber.cs @@ -13,38 +13,18 @@ namespace Microsoft.AspNetCore.Components.Forms /// public class InputNumber : InputBase { - delegate bool Parser(string value, out T result); - private static Parser _parser; private static string _stepAttributeValue; // Null by default, so only allows whole numbers as per HTML spec - // Determine the parsing logic once per T and cache it, so we don't have to consider all the possible types on each parse static InputNumber() { // Unwrap Nullable, because InputBase already deals with the Nullable aspect // of it for us. We will only get asked to parse the T for nonempty inputs. var targetType = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T); - - if (targetType == typeof(int)) + if (targetType == typeof(int) || + targetType == typeof(float) || + targetType == typeof(double) || + targetType == typeof(decimal)) { - _parser = TryParseInt; - } - else if (targetType == typeof(long)) - { - _parser = TryParseLong; - } - else if (targetType == typeof(float)) - { - _parser = TryParseFloat; - _stepAttributeValue = "any"; - } - else if (targetType == typeof(double)) - { - _parser = TryParseDouble; - _stepAttributeValue = "any"; - } - else if (targetType == typeof(decimal)) - { - _parser = TryParseDecimal; _stepAttributeValue = "any"; } else @@ -62,11 +42,11 @@ namespace Microsoft.AspNetCore.Components.Forms protected override void BuildRenderTree(RenderTreeBuilder builder) { builder.OpenElement(0, "input"); - builder.AddAttribute(1, "step", _stepAttributeValue); // Before the splat so the user can override + builder.AddAttribute(1, "step", _stepAttributeValue); builder.AddMultipleAttributes(2, AdditionalAttributes); builder.AddAttribute(3, "type", "number"); builder.AddAttribute(4, "class", CssClass); - builder.AddAttribute(5, "value", BindMethods.GetValue(CurrentValueAsString)); + builder.AddAttribute(5, "value", BindConverter.FormatValue(CurrentValueAsString)); builder.AddAttribute(6, "onchange", EventCallback.Factory.CreateBinder(this, __value => CurrentValueAsString = __value, CurrentValueAsString)); builder.CloseElement(); } @@ -74,7 +54,7 @@ namespace Microsoft.AspNetCore.Components.Forms /// protected override bool TryParseValueFromString(string value, out T result, out string validationErrorMessage) { - if (_parser(value, out result)) + if (BindConverter.TryConvertTo(value, CultureInfo.InvariantCulture, out result)) { validationErrorMessage = null; return true; @@ -100,98 +80,23 @@ namespace Microsoft.AspNetCore.Components.Forms return null; case int @int: - return @int.ToString(CultureInfo.InvariantCulture); + return BindConverter.FormatValue(@int, CultureInfo.InvariantCulture); case long @long: - return @long.ToString(CultureInfo.InvariantCulture); + return BindConverter.FormatValue(@long, CultureInfo.InvariantCulture); case float @float: - return @float.ToString(CultureInfo.InvariantCulture); + return BindConverter.FormatValue(@float, CultureInfo.InvariantCulture); case double @double: - return @double.ToString(CultureInfo.InvariantCulture); + return BindConverter.FormatValue(@double, CultureInfo.InvariantCulture); case decimal @decimal: - return @decimal.ToString(CultureInfo.InvariantCulture); + return BindConverter.FormatValue(@decimal, CultureInfo.InvariantCulture); default: throw new InvalidOperationException($"Unsupported type {value.GetType()}"); } } - - static bool TryParseInt(string value, out T result) - { - var success = int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedValue); - if (success) - { - result = (T)(object)parsedValue; - return true; - } - else - { - result = default; - return false; - } - } - - static bool TryParseLong(string value, out T result) - { - var success = long.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsedValue); - if (success) - { - result = (T)(object)parsedValue; - return true; - } - else - { - result = default; - return false; - } - } - - static bool TryParseFloat(string value, out T result) - { - var success = float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var parsedValue); - if (success && !float.IsInfinity(parsedValue)) - { - result = (T)(object)parsedValue; - return true; - } - else - { - result = default; - return false; - } - } - - static bool TryParseDouble(string value, out T result) - { - var success = double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var parsedValue); - if (success && !double.IsInfinity(parsedValue)) - { - result = (T)(object)parsedValue; - return true; - } - else - { - result = default; - return false; - } - } - - static bool TryParseDecimal(string value, out T result) - { - var success = decimal.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var parsedValue); - if (success) - { - result = (T)(object)parsedValue; - return true; - } - else - { - result = default; - return false; - } - } } } diff --git a/src/Components/Components/src/Forms/InputComponents/InputSelect.cs b/src/Components/Components/src/Forms/InputComponents/InputSelect.cs index 6705ce9320..fda1822afe 100644 --- a/src/Components/Components/src/Forms/InputComponents/InputSelect.cs +++ b/src/Components/Components/src/Forms/InputComponents/InputSelect.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.Globalization; using Microsoft.AspNetCore.Components.RenderTree; namespace Microsoft.AspNetCore.Components.Forms @@ -22,7 +23,7 @@ namespace Microsoft.AspNetCore.Components.Forms builder.OpenElement(0, "select"); builder.AddMultipleAttributes(1, AdditionalAttributes); builder.AddAttribute(2, "class", CssClass); - builder.AddAttribute(3, "value", BindMethods.GetValue(CurrentValueAsString)); + builder.AddAttribute(3, "value", BindConverter.FormatValue(CurrentValueAsString)); builder.AddAttribute(4, "onchange", EventCallback.Factory.CreateBinder(this, __value => CurrentValueAsString = __value, CurrentValueAsString)); builder.AddContent(5, ChildContent); builder.CloseElement(); @@ -39,14 +40,14 @@ namespace Microsoft.AspNetCore.Components.Forms } else if (typeof(T).IsEnum) { - // There's no non-generic Enum.TryParse (https://github.com/dotnet/corefx/issues/692) - try + var success = BindConverter.TryConvertTo(value, CultureInfo.CurrentCulture, out var parsedValue); + if (success) { - result = (T)Enum.Parse(typeof(T), value); + result = parsedValue; validationErrorMessage = null; return true; } - catch (ArgumentException) + else { result = default; validationErrorMessage = $"The {FieldIdentifier.FieldName} field is not valid."; diff --git a/src/Components/Components/src/Forms/InputComponents/InputText.cs b/src/Components/Components/src/Forms/InputComponents/InputText.cs index ffb7004601..94c09c4694 100644 --- a/src/Components/Components/src/Forms/InputComponents/InputText.cs +++ b/src/Components/Components/src/Forms/InputComponents/InputText.cs @@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Components.Forms builder.OpenElement(0, "input"); builder.AddMultipleAttributes(1, AdditionalAttributes); builder.AddAttribute(2, "class", CssClass); - builder.AddAttribute(3, "value", BindMethods.GetValue(CurrentValue)); + builder.AddAttribute(3, "value", BindConverter.FormatValue(CurrentValue)); builder.AddAttribute(4, "onchange", EventCallback.Factory.CreateBinder(this, __value => CurrentValueAsString = __value, CurrentValueAsString)); builder.CloseElement(); } diff --git a/src/Components/Components/src/Forms/InputComponents/InputTextArea.cs b/src/Components/Components/src/Forms/InputComponents/InputTextArea.cs index ed676d6404..ba61d95896 100644 --- a/src/Components/Components/src/Forms/InputComponents/InputTextArea.cs +++ b/src/Components/Components/src/Forms/InputComponents/InputTextArea.cs @@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Components.Forms builder.OpenElement(0, "textarea"); builder.AddMultipleAttributes(1, AdditionalAttributes); builder.AddAttribute(2, "class", CssClass); - builder.AddAttribute(3, "value", BindMethods.GetValue(CurrentValue)); + builder.AddAttribute(3, "value", BindConverter.FormatValue(CurrentValue)); builder.AddAttribute(4, "onchange", EventCallback.Factory.CreateBinder(this, __value => CurrentValueAsString = __value, CurrentValueAsString)); builder.CloseElement(); } diff --git a/src/Components/Components/src/IComponent.cs b/src/Components/Components/src/IComponent.cs index d775efdbd9..c06dbb193e 100644 --- a/src/Components/Components/src/IComponent.cs +++ b/src/Components/Components/src/IComponent.cs @@ -11,10 +11,10 @@ namespace Microsoft.AspNetCore.Components public interface IComponent { /// - /// Initializes the component. + /// Attaches the component to a . /// /// A that allows the component to be rendered. - void Configure(RenderHandle renderHandle); + void Attach(RenderHandle renderHandle); /// /// Sets parameters supplied by the component's parent in the render tree. diff --git a/src/Components/Components/src/LayoutComponentBase.cs b/src/Components/Components/src/LayoutComponentBase.cs index b9c8672d86..2480cd2e18 100644 --- a/src/Components/Components/src/LayoutComponentBase.cs +++ b/src/Components/Components/src/LayoutComponentBase.cs @@ -16,6 +16,6 @@ namespace Microsoft.AspNetCore.Components /// Gets the content to be rendered inside the layout. /// [Parameter] - protected RenderFragment Body { get; private set; } + public RenderFragment Body { get; private set; } } } diff --git a/src/Components/Components/src/Microsoft.AspNetCore.Components.csproj b/src/Components/Components/src/Microsoft.AspNetCore.Components.csproj index 4a6ddef33d..6c773e9d42 100644 --- a/src/Components/Components/src/Microsoft.AspNetCore.Components.csproj +++ b/src/Components/Components/src/Microsoft.AspNetCore.Components.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 @@ -9,7 +9,7 @@ 3.0 - + @@ -19,4 +19,29 @@ + + + + + + + + + + + + + Microsoft.AspNetCore.Components.nuspec + $(GenerateNuspecDependsOn);_GetNuspecDependencyPackageVersions + + + + + + + + + diff --git a/src/Components/Components/src/Microsoft.AspNetCore.Components.nuspec b/src/Components/Components/src/Microsoft.AspNetCore.Components.nuspec new file mode 100644 index 0000000000..415e911bfe --- /dev/null +++ b/src/Components/Components/src/Microsoft.AspNetCore.Components.nuspec @@ -0,0 +1,20 @@ + + + + $CommonMetadataElements$ + + + + + + + + + + + + + + + + diff --git a/src/Components/Components/src/PageDisplay.cs b/src/Components/Components/src/PageDisplay.cs index d109f51557..37e429915b 100644 --- a/src/Components/Components/src/PageDisplay.cs +++ b/src/Components/Components/src/PageDisplay.cs @@ -45,7 +45,7 @@ namespace Microsoft.AspNetCore.Components public RenderFragment AuthorizingContent { get; private set; } /// - public void Configure(RenderHandle renderHandle) + public void Attach(RenderHandle renderHandle) { _renderHandle = renderHandle; } diff --git a/src/Components/Components/src/RenderTree/ArrayBuilder.cs b/src/Components/Components/src/RenderTree/ArrayBuilder.cs index 6be35ded27..9d3e71993a 100644 --- a/src/Components/Components/src/RenderTree/ArrayBuilder.cs +++ b/src/Components/Components/src/RenderTree/ArrayBuilder.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Buffers; +using System.Diagnostics; using System.Runtime.CompilerServices; namespace Microsoft.AspNetCore.Components.RenderTree @@ -15,26 +17,25 @@ namespace Microsoft.AspNetCore.Components.RenderTree /// components can be long-lived and re-render frequently, with the rendered size /// varying dramatically depending on the user's navigation in the app. /// - internal class ArrayBuilder + internal class ArrayBuilder : IDisposable { - private const int MinCapacity = 10; + // The following fields are memory mapped to the WASM client. Do not re-order or use auto-properties. private T[] _items; private int _itemsInUse; - /// - /// Constructs a new instance of . - /// - public ArrayBuilder() : this(MinCapacity) - { - } + private static readonly T[] Empty = Array.Empty(); + private readonly ArrayPool _arrayPool; + private readonly int _minCapacity; + private bool _disposed; /// /// Constructs a new instance of . /// - public ArrayBuilder(int capacity) + public ArrayBuilder(int minCapacity = 32, ArrayPool arrayPool = null) { - _items = new T[capacity < MinCapacity ? MinCapacity : capacity]; - _itemsInUse = 0; + _arrayPool = arrayPool ?? ArrayPool.Shared; + _minCapacity = minCapacity; + _items = Empty; } /// @@ -57,7 +58,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree { if (_itemsInUse == _items.Length) { - SetCapacity(_items.Length * 2, preserveContents: true); + GrowBuffer(_items.Length * 2); } var indexOfAppendedItem = _itemsInUse++; @@ -72,13 +73,13 @@ namespace Microsoft.AspNetCore.Components.RenderTree var requiredCapacity = _itemsInUse + length; if (_items.Length < requiredCapacity) { - var candidateCapacity = _items.Length * 2; + var candidateCapacity = Math.Max(_items.Length * 2, _minCapacity); while (candidateCapacity < requiredCapacity) { candidateCapacity *= 2; } - SetCapacity(candidateCapacity, preserveContents: true); + GrowBuffer(candidateCapacity); } Array.Copy(source, startIndex, _items, _itemsInUse, length); @@ -95,34 +96,51 @@ namespace Microsoft.AspNetCore.Components.RenderTree /// The value. [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Overwrite(int index, in T value) - => _items[index] = value; + { + if (index > _itemsInUse) + { + ThrowIndexOutOfBoundsException(); + } + + _items[index] = value; + } /// /// Removes the last item. /// public void RemoveLast() { + if (_itemsInUse == 0) + { + ThrowIndexOutOfBoundsException(); + } + _itemsInUse--; - _items[_itemsInUse] = default(T); // Release to GC + _items[_itemsInUse] = default; // Release to GC } /// /// Inserts the item at the specified index, moving the contents of the subsequent entries along by one. /// - /// The index at which the value is to be inserted. + /// The index at which the value is to be inserted. /// The value to insert. - public void InsertExpensive(int insertAtIndex, T value) + public void InsertExpensive(int index, T value) { + if (index > _itemsInUse) + { + ThrowIndexOutOfBoundsException(); + } + // Same expansion logic as elsewhere if (_itemsInUse == _items.Length) { - SetCapacity(_items.Length * 2, preserveContents: true); + GrowBuffer(_items.Length * 2); } - Array.Copy(_items, insertAtIndex, _items, insertAtIndex + 1, _itemsInUse - insertAtIndex); + Array.Copy(_items, index, _items, index + 1, _itemsInUse - index); _itemsInUse++; - _items[insertAtIndex] = value; + _items[index] = value; } /// @@ -131,17 +149,9 @@ namespace Microsoft.AspNetCore.Components.RenderTree /// public void Clear() { - var previousItemsInUse = _itemsInUse; + ReturnBuffer(); + _items = Empty; _itemsInUse = 0; - - if (_items.Length > previousItemsInUse * 1.5) - { - SetCapacity((previousItemsInUse + _items.Length) / 2, preserveContents: false); - } - else if (previousItemsInUse > 0) - { - Array.Clear(_items, 0, previousItemsInUse); // Release to GC - } } /// @@ -160,29 +170,42 @@ namespace Microsoft.AspNetCore.Components.RenderTree public ArrayBuilderSegment ToSegment(int fromIndexInclusive, int toIndexExclusive) => new ArrayBuilderSegment(this, fromIndexInclusive, toIndexExclusive - fromIndexInclusive); - private void SetCapacity(int desiredCapacity, bool preserveContents) + private void GrowBuffer(int desiredCapacity) { - if (desiredCapacity < _itemsInUse) - { - throw new ArgumentOutOfRangeException(nameof(desiredCapacity), $"The value cannot be less than {nameof(Count)}"); - } + var newCapacity = Math.Max(desiredCapacity, _minCapacity); + Debug.Assert(newCapacity > _items.Length); - var newCapacity = desiredCapacity < MinCapacity ? MinCapacity : desiredCapacity; - if (newCapacity != _items.Length) - { - var newItems = new T[newCapacity]; + var newItems = _arrayPool.Rent(newCapacity); + Array.Copy(_items, newItems, _itemsInUse); - if (preserveContents) - { - Array.Copy(_items, newItems, _itemsInUse); - } + // Return the old buffer and start using the new buffer + ReturnBuffer(); + _items = newItems; + } - _items = newItems; - } - else if (!preserveContents) + private void ReturnBuffer() + { + if (!ReferenceEquals(_items, Empty)) { - Array.Clear(_items, 0, _items.Length); + // ArrayPool<>.Return with clearArray: true calls Array.Clear on the entire buffer. + // In the most common case, _itemsInUse would be much smaller than _items.Length so we'll specifically clear that subset. + Array.Clear(_items, 0, _itemsInUse); + _arrayPool.Return(_items); } } + + public void Dispose() + { + if (!_disposed) + { + _disposed = true; + ReturnBuffer(); + } + } + + private static void ThrowIndexOutOfBoundsException() + { + throw new ArgumentOutOfRangeException("index"); + } } } diff --git a/src/Components/Components/src/RenderTree/ArrayBuilderSegment.cs b/src/Components/Components/src/RenderTree/ArrayBuilderSegment.cs index a868b654cc..18317ece48 100644 --- a/src/Components/Components/src/RenderTree/ArrayBuilderSegment.cs +++ b/src/Components/Components/src/RenderTree/ArrayBuilderSegment.cs @@ -13,6 +13,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree /// The type of the elements in the array public readonly struct ArrayBuilderSegment : IEnumerable { + // The following fields are memory mapped to the WASM client. Do not re-order or use auto-properties. private readonly ArrayBuilder _builder; private readonly int _offset; private readonly int _count; diff --git a/src/Components/Components/src/RenderTree/RenderTreeBuilder.cs b/src/Components/Components/src/RenderTree/RenderTreeBuilder.cs index 3b48edd123..b0e0687617 100644 --- a/src/Components/Components/src/RenderTree/RenderTreeBuilder.cs +++ b/src/Components/Components/src/RenderTree/RenderTreeBuilder.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Threading.Tasks; -using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Rendering; namespace Microsoft.AspNetCore.Components.RenderTree @@ -18,14 +17,14 @@ namespace Microsoft.AspNetCore.Components.RenderTree /// /// Provides methods for building a collection of entries. /// - public class RenderTreeBuilder + public class RenderTreeBuilder : IDisposable { private readonly static object BoxedTrue = true; private readonly static object BoxedFalse = false; private readonly static string ComponentReferenceCaptureInvalidParentMessage = $"Component reference captures may only be added as children of frames of type {RenderTreeFrameType.Component}"; private readonly Renderer _renderer; - private readonly ArrayBuilder _entries = new ArrayBuilder(10); + private readonly ArrayBuilder _entries = new ArrayBuilder(); private readonly Stack _openElementIndices = new Stack(); private RenderTreeFrameType? _lastNonAttributeFrameType; private bool _hasSeenAddMultipleAttributes; @@ -524,7 +523,9 @@ namespace Microsoft.AspNetCore.Components.RenderTree { if (value == null) { - throw new ArgumentNullException(nameof(value)); + // Null is equivalent to not having set a key, which is valuable because Razor syntax doesn't have an + // easy way to have conditional directive attributes + return; } var parentFrameIndex = GetCurrentParentFrameIndex(); @@ -795,5 +796,10 @@ namespace Microsoft.AspNetCore.Components.RenderTree var seenAttributeNames = (_seenAttributeNames ??= new Dictionary(StringComparer.OrdinalIgnoreCase)); seenAttributeNames[name] = _entries.Count; // See comment in ProcessAttributes for why this is OK. } + + void IDisposable.Dispose() + { + _entries.Dispose(); + } } } diff --git a/src/Components/Components/src/RenderTree/RenderTreeDiffBuilder.cs b/src/Components/Components/src/RenderTree/RenderTreeDiffBuilder.cs index e11b12bb24..933e1f9ab5 100644 --- a/src/Components/Components/src/RenderTree/RenderTreeDiffBuilder.cs +++ b/src/Components/Components/src/RenderTree/RenderTreeDiffBuilder.cs @@ -50,6 +50,8 @@ namespace Microsoft.AspNetCore.Components.RenderTree // if you plan to refactor this, be sure to benchmark the old and new versions // on Mono WebAssembly. + var origOldStartIndex = oldStartIndex; + var origNewStartIndex = newStartIndex; var hasMoreOld = oldEndIndexExcl > oldStartIndex; var hasMoreNew = newEndIndexExcl > newStartIndex; var prevOldSeq = -1; @@ -108,13 +110,13 @@ namespace Microsoft.AspNetCore.Components.RenderTree // Keys don't match if (keyedItemInfos == null) { - keyedItemInfos = BuildKeyToInfoLookup(diffContext, oldStartIndex, oldEndIndexExcl, newStartIndex, newEndIndexExcl); + keyedItemInfos = BuildKeyToInfoLookup(diffContext, origOldStartIndex, oldEndIndexExcl, origNewStartIndex, newEndIndexExcl); } - var oldKeyItemInfo = oldKey != null ? keyedItemInfos[oldKey] : new KeyedItemInfo(-1, -1, false); - var newKeyItemInfo = newKey != null ? keyedItemInfos[newKey] : new KeyedItemInfo(-1, -1, false); - var oldKeyIsInNewTree = oldKeyItemInfo.NewIndex >= 0 && oldKeyItemInfo.IsUnique; - var newKeyIsInOldTree = newKeyItemInfo.OldIndex >= 0 && newKeyItemInfo.IsUnique; + var oldKeyItemInfo = oldKey != null ? keyedItemInfos[oldKey] : new KeyedItemInfo(-1, -1); + var newKeyItemInfo = newKey != null ? keyedItemInfos[newKey] : new KeyedItemInfo(-1, -1); + var oldKeyIsInNewTree = oldKeyItemInfo.NewIndex >= 0; + var newKeyIsInOldTree = newKeyItemInfo.OldIndex >= 0; // If either key is not in the other tree, we can handle it as an insert or a delete // on this iteration. We're only forced to use the move logic that's not the case @@ -304,7 +306,12 @@ namespace Microsoft.AspNetCore.Components.RenderTree var key = KeyValue(ref frame); if (key != null) { - result[key] = new KeyedItemInfo(oldStartIndex, -1, isUnique: !result.ContainsKey(key)); + if (result.ContainsKey(key)) + { + ThrowExceptionForDuplicateKey(key); + } + + result[key] = new KeyedItemInfo(oldStartIndex, -1); } oldStartIndex = NextSiblingIndex(frame, oldStartIndex); @@ -316,9 +323,19 @@ namespace Microsoft.AspNetCore.Components.RenderTree var key = KeyValue(ref frame); if (key != null) { - result[key] = result.TryGetValue(key, out var existingEntry) - ? new KeyedItemInfo(existingEntry.OldIndex, newStartIndex, isUnique: existingEntry.NewIndex < 0) - : new KeyedItemInfo(-1, newStartIndex, isUnique: true); + if (!result.TryGetValue(key, out var existingEntry)) + { + result[key] = new KeyedItemInfo(-1, newStartIndex); + } + else + { + if (existingEntry.NewIndex >= 0) + { + ThrowExceptionForDuplicateKey(key); + } + + result[key] = new KeyedItemInfo(existingEntry.OldIndex, newStartIndex); + } } newStartIndex = NextSiblingIndex(frame, newStartIndex); @@ -327,6 +344,11 @@ namespace Microsoft.AspNetCore.Components.RenderTree return result; } + private static void ThrowExceptionForDuplicateKey(object key) + { + throw new InvalidOperationException($"More than one sibling has the same key value, '{key}'. Key values must be unique."); + } + private static object KeyValue(ref RenderTreeFrame frame) { switch (frame.FrameType) diff --git a/src/Components/Components/src/RenderTree/RenderTreeFrame.cs b/src/Components/Components/src/RenderTree/RenderTreeFrame.cs index 0226cfd9d7..1c0105ad77 100644 --- a/src/Components/Components/src/RenderTree/RenderTreeFrame.cs +++ b/src/Components/Components/src/RenderTree/RenderTreeFrame.cs @@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree /// /// Represents an entry in a tree of user interface (UI) items. /// - [StructLayout(LayoutKind.Explicit)] + [StructLayout(LayoutKind.Explicit, Pack = 4)] public readonly struct RenderTreeFrame { // Note that the struct layout has to be valid in both 32-bit and 64-bit runtime platforms, @@ -24,8 +24,8 @@ namespace Microsoft.AspNetCore.Components.RenderTree // Offset Type // ------ ---- // 0-3 Int32 (sequence number) - // 4-7 Int32 (frame type) - // 8-15 Value types (usage varies by frame type) + // 4-5 Int16 (frame type) + // 6-15 Value types (usage varies by frame type) // 16-23 Reference type (usage varies by frame type) // 24-31 Reference type (usage varies by frame type) // 32-39 Reference type (usage varies by frame type) @@ -90,7 +90,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree /// If the property equals /// gets the ID of the corresponding event handler, if any. /// - [FieldOffset(8)] public readonly int AttributeEventHandlerId; + [FieldOffset(8)] public readonly ulong AttributeEventHandlerId; /// /// If the property equals , @@ -267,7 +267,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree } // Attribute constructor - private RenderTreeFrame(int sequence, string attributeName, object attributeValue, int attributeEventHandlerId, string attributeEventUpdatesAttributeName) + private RenderTreeFrame(int sequence, string attributeName, object attributeValue, ulong attributeEventHandlerId, string attributeEventUpdatesAttributeName) : this() { FrameType = RenderTreeFrameType.Attribute; @@ -337,7 +337,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree internal RenderTreeFrame WithComponent(ComponentState componentState) => new RenderTreeFrame(Sequence, componentSubtreeLength: ComponentSubtreeLength, ComponentType, componentState, ComponentKey); - internal RenderTreeFrame WithAttributeEventHandlerId(int eventHandlerId) + internal RenderTreeFrame WithAttributeEventHandlerId(ulong eventHandlerId) => new RenderTreeFrame(Sequence, attributeName: AttributeName, AttributeValue, eventHandlerId, AttributeEventUpdatesAttributeName); internal RenderTreeFrame WithAttributeValue(object attributeValue) diff --git a/src/Components/Components/src/RenderTree/RenderTreeFrameType.cs b/src/Components/Components/src/RenderTree/RenderTreeFrameType.cs index 09630e3fcf..61d2558305 100644 --- a/src/Components/Components/src/RenderTree/RenderTreeFrameType.cs +++ b/src/Components/Components/src/RenderTree/RenderTreeFrameType.cs @@ -6,7 +6,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree /// /// Describes the type of a . /// - public enum RenderTreeFrameType: int + public enum RenderTreeFrameType: short { /// /// Used only for unintialized frames. diff --git a/src/Components/Components/src/Rendering/ComponentState.cs b/src/Components/Components/src/Rendering/ComponentState.cs index bc9b7e3277..1d34ffff84 100644 --- a/src/Components/Components/src/Rendering/ComponentState.cs +++ b/src/Components/Components/src/Rendering/ComponentState.cs @@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Components.Rendering /// within the context of a . This is an internal implementation /// detail of . /// - internal class ComponentState + internal class ComponentState : IDisposable { private readonly Renderer _renderer; private readonly IReadOnlyList _cascadingParameters; @@ -91,6 +91,8 @@ namespace Microsoft.AspNetCore.Components.Rendering { RemoveCascadingParameterSubscriptions(); } + + DisposeBuffers(); } public Task NotifyRenderCompletedAsync() @@ -172,5 +174,22 @@ namespace Microsoft.AspNetCore.Components.Rendering } } } + + public void Dispose() + { + DisposeBuffers(); + + if (Component is IDisposable disposable) + { + disposable.Dispose(); + } + } + + private void DisposeBuffers() + { + ((IDisposable)_renderTreeBuilderPrevious).Dispose(); + ((IDisposable)CurrrentRenderTree).Dispose(); + _latestDirectParametersSnapshot?.Dispose(); + } } } diff --git a/src/Components/Components/src/Rendering/KeyedItemInfo.cs b/src/Components/Components/src/Rendering/KeyedItemInfo.cs index f2a56341e2..c8a7de5e1a 100644 --- a/src/Components/Components/src/Rendering/KeyedItemInfo.cs +++ b/src/Components/Components/src/Rendering/KeyedItemInfo.cs @@ -10,24 +10,13 @@ namespace Microsoft.AspNetCore.Components.Rendering public readonly int NewIndex; public readonly int OldSiblingIndex; public readonly int NewSiblingIndex; - public readonly bool IsUnique; - public KeyedItemInfo(int oldIndex, int newIndex, bool isUnique) + public KeyedItemInfo(int oldIndex, int newIndex) { OldIndex = oldIndex; NewIndex = newIndex; OldSiblingIndex = -1; NewSiblingIndex = -1; - - // Non-unique keys are problematic, because there's no way to know which instance - // should match with which other, plus they would force us to keep track of which - // usages have been consumed as we proceed through the diff. Since this is such - // an edge case, we "tolerate" it just by tracking which keys have duplicates, and - // for those ones, we never treat them as moved. Instead for those we fall back on - // insert+delete behavior, i.e., not preserving elements/components. - // - // Guidance for developers is therefore to use distinct keys. - IsUnique = isUnique; } private KeyedItemInfo(in KeyedItemInfo copyFrom, int oldSiblingIndex, int newSiblingIndex) diff --git a/src/Components/Components/src/Rendering/RenderBatch.cs b/src/Components/Components/src/Rendering/RenderBatch.cs index cb79c0ff0d..e4e0d6cb4f 100644 --- a/src/Components/Components/src/Rendering/RenderBatch.cs +++ b/src/Components/Components/src/Rendering/RenderBatch.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 Microsoft.AspNetCore.Components.RenderTree; @@ -30,13 +30,13 @@ namespace Microsoft.AspNetCore.Components.Rendering /// /// Gets the IDs of the event handlers that were disposed. /// - public ArrayRange DisposedEventHandlerIDs { get; } + public ArrayRange DisposedEventHandlerIDs { get; } internal RenderBatch( ArrayRange updatedComponents, ArrayRange referenceFrames, ArrayRange disposedComponentIDs, - ArrayRange disposedEventHandlerIDs) + ArrayRange disposedEventHandlerIDs) { UpdatedComponents = updatedComponents; ReferenceFrames = referenceFrames; diff --git a/src/Components/Components/src/Rendering/RenderBatchBuilder.cs b/src/Components/Components/src/Rendering/RenderBatchBuilder.cs index bb5daacec4..f26a248fd8 100644 --- a/src/Components/Components/src/Rendering/RenderBatchBuilder.cs +++ b/src/Components/Components/src/Rendering/RenderBatchBuilder.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.Collections.Generic; using Microsoft.AspNetCore.Components.RenderTree; @@ -12,16 +13,16 @@ namespace Microsoft.AspNetCore.Components.Rendering /// and the intermediate states (such as the queue of components still to /// be rendered). /// - internal class RenderBatchBuilder + internal class RenderBatchBuilder : IDisposable { // Primary result data public ArrayBuilder UpdatedComponentDiffs { get; } = new ArrayBuilder(); public ArrayBuilder DisposedComponentIds { get; } = new ArrayBuilder(); - public ArrayBuilder DisposedEventHandlerIds { get; } = new ArrayBuilder(); + public ArrayBuilder DisposedEventHandlerIds { get; } = new ArrayBuilder(); // Buffers referenced by UpdatedComponentDiffs - public ArrayBuilder EditsBuffer { get; } = new ArrayBuilder(); - public ArrayBuilder ReferenceFramesBuffer { get; } = new ArrayBuilder(); + public ArrayBuilder EditsBuffer { get; } = new ArrayBuilder(64); + public ArrayBuilder ReferenceFramesBuffer { get; } = new ArrayBuilder(64); // State of render pipeline public Queue ComponentRenderQueue { get; } = new Queue(); @@ -56,5 +57,14 @@ namespace Microsoft.AspNetCore.Components.Rendering ReferenceFramesBuffer.ToRange(), DisposedComponentIds.ToRange(), DisposedEventHandlerIds.ToRange()); + + public void Dispose() + { + EditsBuffer.Dispose(); + ReferenceFramesBuffer.Dispose(); + UpdatedComponentDiffs.Dispose(); + DisposedComponentIds.Dispose(); + DisposedEventHandlerIds.Dispose(); + } } } diff --git a/src/Components/Components/src/Rendering/RenderTreeUpdater.cs b/src/Components/Components/src/Rendering/RenderTreeUpdater.cs index 5168ba3936..139b3e09d7 100644 --- a/src/Components/Components/src/Rendering/RenderTreeUpdater.cs +++ b/src/Components/Components/src/Rendering/RenderTreeUpdater.cs @@ -7,7 +7,7 @@ namespace Microsoft.AspNetCore.Components.Rendering { internal class RenderTreeUpdater { - public static void UpdateToMatchClientState(RenderTreeBuilder renderTreeBuilder, int eventHandlerId, object newFieldValue) + public static void UpdateToMatchClientState(RenderTreeBuilder renderTreeBuilder, ulong eventHandlerId, object newFieldValue) { // We only allow the client to supply string or bool currently, since those are the only kinds of // values we output on attributes that go to the client diff --git a/src/Components/Components/src/Rendering/Renderer.Log.cs b/src/Components/Components/src/Rendering/Renderer.Log.cs index 1a737c21c4..68a28b62a1 100644 --- a/src/Components/Components/src/Rendering/Renderer.Log.cs +++ b/src/Components/Components/src/Rendering/Renderer.Log.cs @@ -22,8 +22,8 @@ namespace Microsoft.AspNetCore.Components.Rendering private static readonly Action _disposingComponent = LoggerMessage.Define(LogLevel.Debug, new EventId(4, "DisposingComponent"), "Disposing component {ComponentId} of type {ComponentType}"); - private static readonly Action _handlingEvent = - LoggerMessage.Define(LogLevel.Debug, new EventId(5, "HandlingEvent"), "Handling event {EventId} of type '{EventType}'"); + private static readonly Action _handlingEvent = + LoggerMessage.Define(LogLevel.Debug, new EventId(5, "HandlingEvent"), "Handling event {EventId} of type '{EventType}'"); public static void InitializingComponent(ILogger logger, ComponentState componentState, ComponentState parentComponentState) { @@ -56,7 +56,7 @@ namespace Microsoft.AspNetCore.Components.Rendering } } - internal static void HandlingEvent(ILogger logger, int eventHandlerId, UIEventArgs eventArgs) + internal static void HandlingEvent(ILogger logger, ulong eventHandlerId, UIEventArgs eventArgs) { _handlingEvent(logger, eventHandlerId, eventArgs?.Type ?? "null", null); } diff --git a/src/Components/Components/src/Rendering/Renderer.cs b/src/Components/Components/src/Rendering/Renderer.cs index cce99aca88..60fe23e423 100644 --- a/src/Components/Components/src/Rendering/Renderer.cs +++ b/src/Components/Components/src/Rendering/Renderer.cs @@ -17,16 +17,16 @@ namespace Microsoft.AspNetCore.Components.Rendering /// public abstract partial class Renderer : IDisposable { - private readonly ComponentFactory _componentFactory; + private readonly IServiceProvider _serviceProvider; private readonly Dictionary _componentStateById = new Dictionary(); private readonly RenderBatchBuilder _batchBuilder = new RenderBatchBuilder(); - private readonly Dictionary _eventBindings = new Dictionary(); - private readonly Dictionary _eventHandlerIdReplacements = new Dictionary(); + private readonly Dictionary _eventBindings = new Dictionary(); + private readonly Dictionary _eventHandlerIdReplacements = new Dictionary(); private readonly ILogger _logger; private int _nextComponentId = 0; // TODO: change to 'long' when Mono .NET->JS interop supports it private bool _isBatchInProgress; - private int _lastEventHandlerId = 0; + private ulong _lastEventHandlerId; private List _pendingTasks; /// @@ -61,9 +61,8 @@ namespace Microsoft.AspNetCore.Components.Rendering throw new ArgumentNullException(nameof(loggerFactory)); } - _componentFactory = new ComponentFactory(serviceProvider); + _serviceProvider = serviceProvider; _logger = loggerFactory.CreateLogger(); - _componentFactory = new ComponentFactory(serviceProvider); } /// @@ -77,7 +76,7 @@ namespace Microsoft.AspNetCore.Components.Rendering /// The type of the component to instantiate. /// The component instance. protected IComponent InstantiateComponent(Type componentType) - => _componentFactory.InstantiateComponent(componentType); + => ComponentFactory.Instance.InstantiateComponent(_serviceProvider, componentType); /// /// Associates the with the , assigning @@ -186,7 +185,7 @@ namespace Microsoft.AspNetCore.Components.Rendering var componentState = new ComponentState(this, componentId, component, parentComponentState); Log.InitializingComponent(_logger, componentState, parentComponentState); _componentStateById.Add(componentId, componentState); - component.Configure(new RenderHandle(this, componentId)); + component.Attach(new RenderHandle(this, componentId)); return componentState; } @@ -207,7 +206,7 @@ namespace Microsoft.AspNetCore.Components.Rendering /// A which will complete once all asynchronous processing related to the event /// has completed. /// - public virtual Task DispatchEventAsync(int eventHandlerId, EventFieldInfo fieldInfo, UIEventArgs eventArgs) + public virtual Task DispatchEventAsync(ulong eventHandlerId, EventFieldInfo fieldInfo, UIEventArgs eventArgs) { EnsureSynchronizationContext(); @@ -355,7 +354,7 @@ namespace Microsoft.AspNetCore.Components.Rendering } } - internal void TrackReplacedEventHandlerId(int oldEventHandlerId, int newEventHandlerId) + internal void TrackReplacedEventHandlerId(ulong oldEventHandlerId, ulong newEventHandlerId) { // Tracking the chain of old->new replacements allows us to interpret incoming EventFieldInfo // values even if they refer to an event handler ID that's since been superseded. This is essential @@ -363,7 +362,7 @@ namespace Microsoft.AspNetCore.Components.Rendering _eventHandlerIdReplacements.Add(oldEventHandlerId, newEventHandlerId); } - private int FindLatestEventHandlerIdInChain(int eventHandlerId) + private ulong FindLatestEventHandlerIdInChain(ulong eventHandlerId) { while (_eventHandlerIdReplacements.TryGetValue(eventHandlerId, out var replacementEventHandlerId)) { @@ -574,7 +573,7 @@ namespace Microsoft.AspNetCore.Components.Rendering } } - private void RemoveEventHandlerIds(ArrayRange eventHandlerIds, Task afterTaskIgnoreErrors) + private void RemoveEventHandlerIds(ArrayRange eventHandlerIds, Task afterTaskIgnoreErrors) { if (eventHandlerIds.Count == 0) { @@ -599,7 +598,7 @@ namespace Microsoft.AspNetCore.Components.Rendering // Factor out the async part into a separate local method purely so, in the // synchronous case, there's no state machine or task construction - async Task ContinueAfterTask(ArrayRange eventHandlerIds, Task afterTaskIgnoreErrors) + async Task ContinueAfterTask(ArrayRange eventHandlerIds, Task afterTaskIgnoreErrors) { // We need to delay the actual removal (e.g., until we've confirmed the client // has processed the batch and hence can be sure not to reuse the handler IDs @@ -638,7 +637,7 @@ namespace Microsoft.AspNetCore.Components.Rendering } } - private void UpdateRenderTreeToMatchClientState(int eventHandlerId, EventFieldInfo fieldInfo) + private void UpdateRenderTreeToMatchClientState(ulong eventHandlerId, EventFieldInfo fieldInfo) { var componentState = GetOptionalComponentState(fieldInfo.ComponentId); if (componentState != null) @@ -664,13 +663,15 @@ namespace Microsoft.AspNetCore.Components.Rendering { try { - disposable.Dispose(); + componentState.Dispose(); } catch (Exception exception) { HandleException(exception); } } + + _batchBuilder.Dispose(); } } diff --git a/src/Components/Components/src/Routing/RouteEntry.cs b/src/Components/Components/src/Routing/RouteEntry.cs index d96317151b..cff9420bd9 100644 --- a/src/Components/Components/src/Routing/RouteEntry.cs +++ b/src/Components/Components/src/Routing/RouteEntry.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; @@ -26,8 +26,8 @@ namespace Microsoft.AspNetCore.Components.Routing } // Parameters will be lazily initialized. - IDictionary parameters = null; - for (int i = 0; i < Template.Segments.Length; i++) + Dictionary parameters = null; + for (var i = 0; i < Template.Segments.Length; i++) { var segment = Template.Segments[i]; var pathSegment = context.Segments[i]; @@ -39,23 +39,14 @@ namespace Microsoft.AspNetCore.Components.Routing { if (segment.IsParameter) { - GetParameters()[segment.Value] = matchedParameterValue; + parameters ??= new Dictionary(StringComparer.Ordinal); + parameters[segment.Value] = matchedParameterValue; } } } context.Parameters = parameters; context.Handler = Handler; - - IDictionary GetParameters() - { - if (parameters == null) - { - parameters = new Dictionary(); - } - - return parameters; - } } } } diff --git a/src/Components/Components/src/Routing/RouteTable.cs b/src/Components/Components/src/Routing/RouteTable.cs index 4449c00f3c..029bc47657 100644 --- a/src/Components/Components/src/Routing/RouteTable.cs +++ b/src/Components/Components/src/Routing/RouteTable.cs @@ -1,12 +1,6 @@ // 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.Linq; -using System.Reflection; -using Microsoft.AspNetCore.Components; - namespace Microsoft.AspNetCore.Components.Routing { internal class RouteTable @@ -16,117 +10,13 @@ namespace Microsoft.AspNetCore.Components.Routing Routes = routes; } - public RouteEntry[] Routes { get; set; } - - public static RouteTable Create(IEnumerable types) - { - var routes = new List(); - foreach (var type in types) - { - // We're deliberately using inherit = false here. - // - // RouteAttribute is defined as non-inherited, because inheriting a route attribute always causes an - // ambiguity. You end up with two components (base class and derived class) with the same route. - var routeAttributes = type.GetCustomAttributes(inherit: false); - - foreach (var routeAttribute in routeAttributes) - { - var template = TemplateParser.ParseTemplate(routeAttribute.Template); - var entry = new RouteEntry(template, type); - routes.Add(entry); - } - } - - return new RouteTable(routes.OrderBy(id => id, RoutePrecedence).ToArray()); - } - - public static IComparer RoutePrecedence { get; } = Comparer.Create(RouteComparison); - - /// - /// Route precedence algorithm. - /// We collect all the routes and sort them from most specific to - /// less specific. The specificity of a route is given by the specificity - /// of its segments and the position of those segments in the route. - /// * A literal segment is more specific than a parameter segment. - /// * A parameter segment with more constraints is more specific than one with fewer constraints - /// * Segment earlier in the route are evaluated before segments later in the route. - /// For example: - /// /Literal is more specific than /Parameter - /// /Route/With/{parameter} is more specific than /{multiple}/With/{parameters} - /// /Product/{id:int} is more specific than /Product/{id} - /// - /// Routes can be ambiguous if: - /// They are composed of literals and those literals have the same values (case insensitive) - /// They are composed of a mix of literals and parameters, in the same relative order and the - /// literals have the same values. - /// For example: - /// * /literal and /Literal - /// /{parameter}/literal and /{something}/literal - /// /{parameter:constraint}/literal and /{something:constraint}/literal - /// - /// To calculate the precedence we sort the list of routes as follows: - /// * Shorter routes go first. - /// * A literal wins over a parameter in precedence. - /// * For literals with different values (case insensitive) we choose the lexical order - /// * For parameters with different numbers of constraints, the one with more wins - /// If we get to the end of the comparison routing we've detected an ambiguous pair of routes. - /// - internal static int RouteComparison(RouteEntry x, RouteEntry y) - { - var xTemplate = x.Template; - var yTemplate = y.Template; - if (xTemplate.Segments.Length != y.Template.Segments.Length) - { - return xTemplate.Segments.Length < y.Template.Segments.Length ? -1 : 1; - } - else - { - for (int i = 0; i < xTemplate.Segments.Length; i++) - { - var xSegment = xTemplate.Segments[i]; - var ySegment = yTemplate.Segments[i]; - if (!xSegment.IsParameter && ySegment.IsParameter) - { - return -1; - } - if (xSegment.IsParameter && !ySegment.IsParameter) - { - return 1; - } - - if (xSegment.IsParameter) - { - if (xSegment.Constraints.Length > ySegment.Constraints.Length) - { - return -1; - } - else if (xSegment.Constraints.Length < ySegment.Constraints.Length) - { - return 1; - } - } - else - { - var comparison = string.Compare(xSegment.Value, ySegment.Value, StringComparison.OrdinalIgnoreCase); - if (comparison != 0) - { - return comparison; - } - } - } - - throw new InvalidOperationException($@"The following routes are ambiguous: -'{x.Template.TemplateText}' in '{x.Handler.FullName}' -'{y.Template.TemplateText}' in '{y.Handler.FullName}' -"); - } - } + public RouteEntry[] Routes { get; } internal void Route(RouteContext routeContext) { - foreach (var route in Routes) + for (var i = 0; i < Routes.Length; i++) { - route.Match(routeContext); + Routes[i].Match(routeContext); if (routeContext.Handler != null) { return; diff --git a/src/Components/Components/src/Routing/RouteTableFactory.cs b/src/Components/Components/src/Routing/RouteTableFactory.cs new file mode 100644 index 0000000000..eae29a7530 --- /dev/null +++ b/src/Components/Components/src/Routing/RouteTableFactory.cs @@ -0,0 +1,137 @@ +// 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.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Microsoft.AspNetCore.Components.Routing; + +namespace Microsoft.AspNetCore.Components +{ + /// + /// Resolves components for an application. + /// + internal static class RouteTableFactory + { + private static readonly ConcurrentDictionary Cache = + new ConcurrentDictionary(); + public static readonly IComparer RoutePrecedence = Comparer.Create(RouteComparison); + + public static RouteTable Create(Assembly appAssembly) + { + if (Cache.TryGetValue(appAssembly, out var resolvedComponents)) + { + return resolvedComponents; + } + + var componentTypes = appAssembly.ExportedTypes.Where(t => typeof(IComponent).IsAssignableFrom(t)); + var routeTable = Create(componentTypes); + Cache.TryAdd(appAssembly, routeTable); + return routeTable; + } + + internal static RouteTable Create(IEnumerable componentTypes) + { + var routes = new List(); + foreach (var type in componentTypes) + { + // We're deliberately using inherit = false here. + // + // RouteAttribute is defined as non-inherited, because inheriting a route attribute always causes an + // ambiguity. You end up with two components (base class and derived class) with the same route. + var routeAttributes = type.GetCustomAttributes(inherit: false); + + foreach (var routeAttribute in routeAttributes) + { + var template = TemplateParser.ParseTemplate(routeAttribute.Template); + var entry = new RouteEntry(template, type); + routes.Add(entry); + } + } + + return new RouteTable(routes.OrderBy(id => id, RoutePrecedence).ToArray()); + } + + /// + /// Route precedence algorithm. + /// We collect all the routes and sort them from most specific to + /// less specific. The specificity of a route is given by the specificity + /// of its segments and the position of those segments in the route. + /// * A literal segment is more specific than a parameter segment. + /// * A parameter segment with more constraints is more specific than one with fewer constraints + /// * Segment earlier in the route are evaluated before segments later in the route. + /// For example: + /// /Literal is more specific than /Parameter + /// /Route/With/{parameter} is more specific than /{multiple}/With/{parameters} + /// /Product/{id:int} is more specific than /Product/{id} + /// + /// Routes can be ambiguous if: + /// They are composed of literals and those literals have the same values (case insensitive) + /// They are composed of a mix of literals and parameters, in the same relative order and the + /// literals have the same values. + /// For example: + /// * /literal and /Literal + /// /{parameter}/literal and /{something}/literal + /// /{parameter:constraint}/literal and /{something:constraint}/literal + /// + /// To calculate the precedence we sort the list of routes as follows: + /// * Shorter routes go first. + /// * A literal wins over a parameter in precedence. + /// * For literals with different values (case insensitive) we choose the lexical order + /// * For parameters with different numbers of constraints, the one with more wins + /// If we get to the end of the comparison routing we've detected an ambiguous pair of routes. + /// + internal static int RouteComparison(RouteEntry x, RouteEntry y) + { + var xTemplate = x.Template; + var yTemplate = y.Template; + if (xTemplate.Segments.Length != y.Template.Segments.Length) + { + return xTemplate.Segments.Length < y.Template.Segments.Length ? -1 : 1; + } + else + { + for (var i = 0; i < xTemplate.Segments.Length; i++) + { + var xSegment = xTemplate.Segments[i]; + var ySegment = yTemplate.Segments[i]; + if (!xSegment.IsParameter && ySegment.IsParameter) + { + return -1; + } + if (xSegment.IsParameter && !ySegment.IsParameter) + { + return 1; + } + + if (xSegment.IsParameter) + { + if (xSegment.Constraints.Length > ySegment.Constraints.Length) + { + return -1; + } + else if (xSegment.Constraints.Length < ySegment.Constraints.Length) + { + return 1; + } + } + else + { + var comparison = string.Compare(xSegment.Value, ySegment.Value, StringComparison.OrdinalIgnoreCase); + if (comparison != 0) + { + return comparison; + } + } + } + + throw new InvalidOperationException($@"The following routes are ambiguous: +'{x.Template.TemplateText}' in '{x.Handler.FullName}' +'{y.Template.TemplateText}' in '{y.Handler.FullName}' +"); + } + } + } +} diff --git a/src/Components/Components/src/Routing/RouteTemplate.cs b/src/Components/Components/src/Routing/RouteTemplate.cs index 1856ef20d2..bb59be07ee 100644 --- a/src/Components/Components/src/Routing/RouteTemplate.cs +++ b/src/Components/Components/src/Routing/RouteTemplate.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. @@ -6,11 +6,9 @@ namespace Microsoft.AspNetCore.Components.Routing { internal class RouteTemplate { - public static readonly char[] Separators = new[] { '/' }; - - public RouteTemplate(string TemplateText, TemplateSegment[] segments) + public RouteTemplate(string templateText, TemplateSegment[] segments) { - this.TemplateText = TemplateText; + TemplateText = templateText; Segments = segments; } diff --git a/src/Components/Components/src/Routing/Router.cs b/src/Components/Components/src/Routing/Router.cs index 87a66a692a..451de47bc5 100644 --- a/src/Components/Components/src/Routing/Router.cs +++ b/src/Components/Components/src/Routing/Router.cs @@ -7,7 +7,6 @@ using System.Reflection; using System.Threading.Tasks; using Microsoft.AspNetCore.Components.RenderTree; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Logging.Abstractions; namespace Microsoft.AspNetCore.Components.Routing { @@ -57,7 +56,7 @@ namespace Microsoft.AspNetCore.Components.Routing private RouteTable Routes { get; set; } /// - public void Configure(RenderHandle renderHandle) + public void Attach(RenderHandle renderHandle) { _logger = LoggerFactory.CreateLogger(); _renderHandle = renderHandle; @@ -70,8 +69,7 @@ namespace Microsoft.AspNetCore.Components.Routing public Task SetParametersAsync(ParameterCollection parameters) { parameters.SetParameterProperties(this); - var types = ComponentResolver.ResolveComponents(AppAssembly); - Routes = RouteTable.Create(types); + Routes = RouteTableFactory.Create(AppAssembly); Refresh(isNavigationIntercepted: false); return Task.CompletedTask; } diff --git a/src/Components/Components/test/Auth/AuthorizeViewTest.cs b/src/Components/Components/test/Auth/AuthorizeViewTest.cs index 36a83cd935..d87eb9eaeb 100644 --- a/src/Components/Components/test/Auth/AuthorizeViewTest.cs +++ b/src/Components/Components/test/Auth/AuthorizeViewTest.cs @@ -427,6 +427,24 @@ namespace Microsoft.AspNetCore.Components }); } + [Fact] + public void RejectsNonemptyScheme() + { + // Arrange + var authorizationService = new TestAuthorizationService(); + var renderer = CreateTestRenderer(authorizationService); + var rootComponent = new TestAuthStateProviderComponent(builder => + { + builder.OpenComponent(0); + builder.CloseComponent(); + }); + renderer.AssignRootComponentId(rootComponent); + + // Act/Assert + var ex = Assert.Throws(rootComponent.TriggerRender); + Assert.Equal("The authorization data specifies an authentication scheme with value 'test scheme'. Authentication schemes cannot be specified for components.", ex.Message); + } + private static TestAuthStateProviderComponent WrapInAuthorizeView( RenderFragment childContent = null, RenderFragment authorizedContent = null, @@ -481,7 +499,7 @@ namespace Microsoft.AspNetCore.Components // recurse into all descendants because we're passing ChildContent class NeverReRenderComponent : ComponentBase { - [Parameter] RenderFragment ChildContent { get; set; } + [Parameter] public RenderFragment ChildContent { get; set; } protected override bool ShouldRender() => false; @@ -557,5 +575,11 @@ namespace Microsoft.AspNetCore.Components { public string PolicyName { get; set; } } + + public class AuthorizeViewCoreWithScheme : AuthorizeViewCore + { + protected override IAuthorizeData[] GetAuthorizeData() + => new[] { new AuthorizeAttribute { AuthenticationSchemes = "test scheme" } }; + } } } diff --git a/src/Components/Components/test/BindConverterTest.cs b/src/Components/Components/test/BindConverterTest.cs new file mode 100644 index 0000000000..c6fe1275cc --- /dev/null +++ b/src/Components/Components/test/BindConverterTest.cs @@ -0,0 +1,307 @@ +// 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.ComponentModel; +using System.Diagnostics; +using System.Globalization; +using System.Text.Json; +using Xunit; + +namespace Microsoft.AspNetCore.Components +{ + // This is some basic coverage, it's not in depth because there are many many APIs here + // and they mostly call through to CoreFx. We don't want to test the globalization details + // of .NET in detail where we can avoid it. + // + // Instead there's a sampling of things that have somewhat unique behavior or semantics. + public class BindConverterTest + { + [Fact] + public void FormatValue_Bool() + { + // Arrange + var value = true; + var expected = true; + + // Act + var actual = BindConverter.FormatValue(value); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void FormatValue_Bool_Generic() + { + // Arrange + var value = true; + var expected = true; + + // Act + var actual = BindConverter.FormatValue(value); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void FormatValue_NullableBool() + { + // Arrange + var value = (bool?)true; + var expected = true; + + // Act + var actual = BindConverter.FormatValue(value); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void FormatValue_NullableBool_Generic() + { + // Arrange + var value = true; + var expected = true; + + // Act + var actual = BindConverter.FormatValue(value); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void FormatValue_NullableBoolNull() + { + // Arrange + var value = (bool?)null; + var expected = (bool?)null; + + // Act + var actual = BindConverter.FormatValue(value); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void FormatValue_NullableBoolNull_Generic() + { + // Arrange + var value = (bool?)null; + var expected = (bool?)null; + + // Act + var actual = BindConverter.FormatValue(value); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void FormatValue_Int() + { + // Arrange + var value = 17; + var expected = "17"; + + // Act + var actual = BindConverter.FormatValue(value); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void FormatValue_Int_Generic() + { + // Arrange + var value = 17; + var expected = "17"; + + // Act + var actual = BindConverter.FormatValue(value); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void FormatValue_NullableInt() + { + // Arrange + var value = (int?)17; + var expected = "17"; + + // Act + var actual = BindConverter.FormatValue(value); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void FormatValue_NullableInt_Generic() + { + // Arrange + var value = 17; + var expected = "17"; + + // Act + var actual = BindConverter.FormatValue(value); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void FormatValue_DateTime() + { + // Arrange + var value = DateTime.Now; + var expected = value.ToString(CultureInfo.CurrentCulture); + + // Act + var actual = BindConverter.FormatValue(value); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void FormatValue_DateTime_Format() + { + // Arrange + var value = DateTime.Now; + var expected = value.ToString("MM-yyyy", CultureInfo.InvariantCulture); + + // Act + var actual = BindConverter.FormatValue(value, "MM-yyyy", CultureInfo.InvariantCulture); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void FormatValue_Enum() + { + // Arrange + var value = SomeLetters.A; + var expected = value.ToString(); + + // Act + var actual = BindConverter.FormatValue(value); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void FormatValue_Enum_OutOfRange() + { + // Arrange + var value = SomeLetters.A + 3; + var expected = value.ToString(); + + // Act + var actual = BindConverter.FormatValue(value); + + // Assert + Assert.Equal(expected, actual); + } + + [Fact] + public void FormatValue_NullableEnum() + { + // Arrange + var value = (SomeLetters?)null; + + // Act + var actual = BindConverter.FormatValue(value); + + // Assert + Assert.Null(actual); + } + + [Fact] + public void FormatValue_TypeConverter() + { + // Arrange + var value = new Person() + { + Name = "Glenn", + Age = 47, + }; + + var expected = JsonSerializer.Serialize(value); + + // Act + var actual = BindConverter.FormatValue(value); + + // Assert + Assert.Equal(expected, actual); + } + + private enum SomeLetters + { + A, + B, + C, + Q, + } + + [TypeConverter(typeof(PersonConverter))] + private class Person + { + public string Name { get; set; } + + public int Age { get; set; } + } + + private class PersonConverter : TypeConverter + { + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + if (sourceType == typeof(string)) + { + return true; + } + + return base.CanConvertFrom(context, sourceType); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + if (value is string text) + { + return JsonSerializer.Deserialize(text); + } + + return base.ConvertFrom(context, culture, value); + } + + public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) + { + if (destinationType == typeof(string)) + { + return true; + } + + return base.CanConvertTo(context, destinationType); + } + + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + { + if (destinationType == typeof(string)) + { + return JsonSerializer.Serialize((Person)value); + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } + } +} diff --git a/src/Components/Components/test/CascadingParameterStateTest.cs b/src/Components/Components/test/CascadingParameterStateTest.cs index 95d42dc24f..12ed03a445 100644 --- a/src/Components/Components/test/CascadingParameterStateTest.cs +++ b/src/Components/Components/test/CascadingParameterStateTest.cs @@ -374,7 +374,7 @@ namespace Microsoft.AspNetCore.Components.Test { var supplier = new CascadingValue(); var renderer = new TestRenderer(); - supplier.Configure(new RenderHandle(renderer, 0)); + supplier.Attach(new RenderHandle(renderer, 0)); var supplierParams = new Dictionary { @@ -389,19 +389,19 @@ namespace Microsoft.AspNetCore.Components.Test renderer.Dispatcher.InvokeAsync((Action)(() => supplier.SetParametersAsync(ParameterCollection.FromDictionary(supplierParams)))); return supplier; } - + class ComponentWithNoParams : TestComponentBase { } class ComponentWithNoCascadingParams : TestComponentBase { - [Parameter] bool SomeRegularParameter { get; set; } + [Parameter] public bool SomeRegularParameter { get; set; } } class ComponentWithCascadingParams : TestComponentBase { - [Parameter] bool RegularParam { get; set; } + [Parameter] public bool RegularParam { get; set; } [CascadingParameter] internal ValueType1 CascadingParam1 { get; set; } [CascadingParameter] internal ValueType2 CascadingParam2 { get; set; } } @@ -424,7 +424,7 @@ namespace Microsoft.AspNetCore.Components.Test class TestComponentBase : IComponent { - public void Configure(RenderHandle renderHandle) + public void Attach(RenderHandle renderHandle) => throw new NotImplementedException(); public Task SetParametersAsync(ParameterCollection parameters) diff --git a/src/Components/Components/test/CascadingParameterTest.cs b/src/Components/Components/test/CascadingParameterTest.cs index 5f6a01e364..766a1bd3b4 100644 --- a/src/Components/Components/test/CascadingParameterTest.cs +++ b/src/Components/Components/test/CascadingParameterTest.cs @@ -381,7 +381,7 @@ namespace Microsoft.AspNetCore.Components.Test public int NumRenders { get; private set; } [CascadingParameter] T CascadingParameter { get; set; } - [Parameter] string RegularParameter { get; set; } + [Parameter] public string RegularParameter { get; set; } public override async Task SetParametersAsync(ParameterCollection parameters) { diff --git a/src/Components/Components/test/ComponentFactoryTest.cs b/src/Components/Components/test/ComponentFactoryTest.cs new file mode 100644 index 0000000000..0d77c0f24a --- /dev/null +++ b/src/Components/Components/test/ComponentFactoryTest.cs @@ -0,0 +1,168 @@ +// 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.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace Microsoft.AspNetCore.Components +{ + public class ComponentFactoryTest + { + [Fact] + public void InstantiateComponent_CreatesInstance() + { + // Arrange + var componentType = typeof(EmptyComponent); + var factory = new ComponentFactory(); + + // Act + var instance = factory.InstantiateComponent(GetServiceProvider(), componentType); + + // Assert + Assert.NotNull(instance); + Assert.IsType(instance); + } + + [Fact] + public void InstantiateComponent_AssignsPropertiesWithInjectAttribute() + { + // Arrange + var componentType = typeof(ComponentWithInjectProperties); + var factory = new ComponentFactory(); + + // Act + var instance = factory.InstantiateComponent(GetServiceProvider(), componentType); + + // Assert + Assert.NotNull(instance); + var component = Assert.IsType(instance); + // Public, and non-public properties, and properties with non-public setters should get assigned + Assert.NotNull(component.Property1); + Assert.NotNull(component.GetProperty2()); + Assert.NotNull(component.Property3); + Assert.NotNull(component.Property4); + } + + [Fact] + public void InstantiateComponent_AssignsPropertiesWithInjectAttributeOnBaseType() + { + // Arrange + var componentType = typeof(DerivedComponent); + var factory = new ComponentFactory(); + + // Act + var instance = factory.InstantiateComponent(GetServiceProvider(), componentType); + + // Assert + Assert.NotNull(instance); + var component = Assert.IsType(instance); + Assert.NotNull(component.Property1); + Assert.NotNull(component.GetProperty2()); + Assert.NotNull(component.Property3); + + // Property on derived type without [Inject] should not be assigned + Assert.Null(component.Property4); + // Property on the base type with the [Inject] attribute should + Assert.NotNull(((ComponentWithInjectProperties)component).Property4); + } + + [Fact] + public void InstantiateComponent_IgnoresPropertiesWithoutInjectAttribute() + { + // Arrange + var componentType = typeof(ComponentWithNonInjectableProperties); + var factory = new ComponentFactory(); + + // Act + var instance = factory.InstantiateComponent(GetServiceProvider(), componentType); + + // Assert + Assert.NotNull(instance); + var component = Assert.IsType(instance); + // Public, and non-public properties, and properties with non-public setters should get assigned + Assert.NotNull(component.Property1); + Assert.Null(component.Property2); + } + + private static IServiceProvider GetServiceProvider() + { + return new ServiceCollection() + .AddTransient() + .AddTransient() + .BuildServiceProvider(); + } + + private class EmptyComponent : IComponent + { + public void Attach(RenderHandle renderHandle) + { + throw new NotImplementedException(); + } + + public Task SetParametersAsync(ParameterCollection parameters) + { + throw new NotImplementedException(); + } + } + + private class ComponentWithInjectProperties : IComponent + { + [Inject] + public TestService1 Property1 { get; set; } + + [Inject] + private TestService2 Property2 { get; set; } + + [Inject] + public TestService1 Property3 { get; private set; } + + [Inject] + public TestService1 Property4 { get; set; } + + public TestService2 GetProperty2() => Property2; + + public void Attach(RenderHandle renderHandle) + { + throw new NotImplementedException(); + } + + public Task SetParametersAsync(ParameterCollection parameters) + { + throw new NotImplementedException(); + } + } + + private class ComponentWithNonInjectableProperties : IComponent + { + [Inject] + public TestService1 Property1 { get; set; } + + public TestService1 Property2 { get; set; } + + public void Attach(RenderHandle renderHandle) + { + throw new NotImplementedException(); + } + + public Task SetParametersAsync(ParameterCollection parameters) + { + throw new NotImplementedException(); + } + } + + private class DerivedComponent : ComponentWithInjectProperties + { + public new TestService2 Property4 { get; set; } + + [Inject] + public TestService2 Property5 { get; set; } + } + + public class TestService1 { } + public class TestService2 { } + } +} diff --git a/src/Components/Components/test/DependencyInjectionTest.cs b/src/Components/Components/test/DependencyInjectionTest.cs index b6ccabbe49..fedbcba993 100644 --- a/src/Components/Components/test/DependencyInjectionTest.cs +++ b/src/Components/Components/test/DependencyInjectionTest.cs @@ -194,7 +194,7 @@ namespace Microsoft.AspNetCore.Components.Test // not throw, then be sure also to add a test to verify that injection // occurs before lifecycle methods. - public void Configure(RenderHandle renderHandle) + public void Attach(RenderHandle renderHandle) => throw new NotImplementedException(); public Task SetParametersAsync(ParameterCollection parameters) diff --git a/src/Components/Components/test/ElementReferenceTest.cs b/src/Components/Components/test/ElementReferenceTest.cs new file mode 100644 index 0000000000..39c947aaf0 --- /dev/null +++ b/src/Components/Components/test/ElementReferenceTest.cs @@ -0,0 +1,82 @@ +// 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.Text.Json; +using Xunit; + +namespace Microsoft.AspNetCore.Components +{ + public class ElementReferenceTest + { + [Fact] + public void Serializing_Works() + { + // Arrange + var elementReference = ElementReference.CreateWithUniqueId(); + var expected = $"{{\"__internalId\":\"{elementReference.Id}\"}}"; + + // Act + var json = JsonSerializer.Serialize(elementReference, JsonSerializerOptionsProvider.Options); + + // Assert + Assert.Equal(expected, json); + } + + [Fact] + public void Deserializing_Works() + { + // Arrange + var id = ElementReference.CreateWithUniqueId().Id; + var json = $"{{\"__internalId\":\"{id}\"}}"; + + // Act + var elementReference = JsonSerializer.Deserialize(json, JsonSerializerOptionsProvider.Options); + + // Assert + Assert.Equal(id, elementReference.Id); + } + + [Fact] + public void Deserializing_WithFormatting_Works() + { + // Arrange + var id = ElementReference.CreateWithUniqueId().Id; + var json = +@$"{{ + ""__internalId"": ""{id}"" +}}"; + + // Act + var elementReference = JsonSerializer.Deserialize(json, JsonSerializerOptionsProvider.Options); + + // Assert + Assert.Equal(id, elementReference.Id); + } + + [Fact] + public void Deserializing_Throws_IfUnknownPropertyAppears() + { + // Arrange + var json = "{\"id\":\"some-value\"}"; + + // Act + var ex = Assert.Throws(() => JsonSerializer.Deserialize(json, JsonSerializerOptionsProvider.Options)); + + // Assert + Assert.Equal("Unexpected JSON property 'id'.", ex.Message); + } + + [Fact] + public void Deserializing_Throws_IfIdIsNotSpecified() + { + // Arrange + var json = "{}"; + + // Act + var ex = Assert.Throws(() => JsonSerializer.Deserialize(json, JsonSerializerOptionsProvider.Options)); + + // Assert + Assert.Equal("__internalId is required.", ex.Message); + } + } +} diff --git a/src/Components/Components/test/EventCallbackFactoryBinderExtensionsTest.cs b/src/Components/Components/test/EventCallbackFactoryBinderExtensionsTest.cs index 62402f2265..220e9285ab 100644 --- a/src/Components/Components/test/EventCallbackFactoryBinderExtensionsTest.cs +++ b/src/Components/Components/test/EventCallbackFactoryBinderExtensionsTest.cs @@ -662,7 +662,7 @@ namespace Microsoft.AspNetCore.Components return item.InvokeAsync(arg); } - public void Configure(RenderHandle renderHandle) + public void Attach(RenderHandle renderHandle) { throw new System.NotImplementedException(); } diff --git a/src/Components/Components/test/EventCallbackFactoryTest.cs b/src/Components/Components/test/EventCallbackFactoryTest.cs index 714c73c656..dc9a3bfd30 100644 --- a/src/Components/Components/test/EventCallbackFactoryTest.cs +++ b/src/Components/Components/test/EventCallbackFactoryTest.cs @@ -635,7 +635,7 @@ namespace Microsoft.AspNetCore.Components return Task.CompletedTask; } - public void Configure(RenderHandle renderHandle) + public void Attach(RenderHandle renderHandle) { throw new NotImplementedException(); } diff --git a/src/Components/Components/test/EventCallbackTest.cs b/src/Components/Components/test/EventCallbackTest.cs index 37d6b354ff..e390735fa8 100644 --- a/src/Components/Components/test/EventCallbackTest.cs +++ b/src/Components/Components/test/EventCallbackTest.cs @@ -449,7 +449,7 @@ namespace Microsoft.AspNetCore.Components return item.InvokeAsync(arg); } - public void Configure(RenderHandle renderHandle) => throw new NotImplementedException(); + public void Attach(RenderHandle renderHandle) => throw new NotImplementedException(); public Task SetParametersAsync(ParameterCollection parameters) => throw new NotImplementedException(); } diff --git a/src/Components/Components/test/PageDisplayTest.cs b/src/Components/Components/test/PageDisplayTest.cs index 8e834a7e4f..e73e7e7083 100644 --- a/src/Components/Components/test/PageDisplayTest.cs +++ b/src/Components/Components/test/PageDisplayTest.cs @@ -220,7 +220,7 @@ namespace Microsoft.AspNetCore.Components.Test private class RootLayout : AutoRenderComponent { [Parameter] - RenderFragment Body { get; set; } + public RenderFragment Body { get; set; } protected override void BuildRenderTree(RenderTreeBuilder builder) { @@ -234,7 +234,7 @@ namespace Microsoft.AspNetCore.Components.Test private class NestedLayout : AutoRenderComponent { [Parameter] - RenderFragment Body { get; set; } + public RenderFragment Body { get; set; } protected override void BuildRenderTree(RenderTreeBuilder builder) { diff --git a/src/Components/Components/test/ParameterCollectionAssignmentExtensionsTest.cs b/src/Components/Components/test/ParameterCollectionAssignmentExtensionsTest.cs index 7193081888..131697490f 100644 --- a/src/Components/Components/test/ParameterCollectionAssignmentExtensionsTest.cs +++ b/src/Components/Components/test/ParameterCollectionAssignmentExtensionsTest.cs @@ -364,11 +364,11 @@ namespace Microsoft.AspNetCore.Components.Test // "internal" to show we're not requiring public accessors, but also // to keep the assertions simple in the tests - [Parameter] internal int IntProp { get; set; } - [Parameter] internal string StringProp { get; set; } + [Parameter] public int IntProp { get; set; } + [Parameter] public string StringProp { get; set; } // Also a truly private one to show there's nothing special about 'internal' - [Parameter] private object ObjectProp { get; set; } + [Parameter] public object ObjectProp { get; set; } public static string ObjectPropName => nameof(ObjectProp); public object ObjectPropCurrentValue @@ -386,7 +386,7 @@ namespace Microsoft.AspNetCore.Components.Test class HasPropertyWhoseSetterThrows { [Parameter] - internal string StringProp + public string StringProp { get => string.Empty; set => throw new InvalidOperationException("This setter throws"); @@ -395,37 +395,37 @@ namespace Microsoft.AspNetCore.Components.Test class HasInheritedProperties : HasInstanceProperties { - [Parameter] internal int DerivedClassIntProp { get; set; } + [Parameter] public int DerivedClassIntProp { get; set; } } class HasParametersVaryingOnlyByCase { - [Parameter] internal object MyValue { get; set; } - [Parameter] internal object Myvalue { get; set; } + [Parameter] public object MyValue { get; set; } + [Parameter] public object Myvalue { get; set; } } class HasParameterClashingWithInherited : HasInstanceProperties { - [Parameter] new int IntProp { get; set; } + [Parameter] public new int IntProp { get; set; } } class HasCaptureUnmatchedValuesProperty { - [Parameter] internal int IntProp { get; set; } - [Parameter] internal string StringProp { get; set; } - [Parameter] internal object ObjectProp { get; set; } - [Parameter(CaptureUnmatchedValues = true)] internal IReadOnlyDictionary CaptureUnmatchedValues { get; set; } + [Parameter] public int IntProp { get; set; } + [Parameter] public string StringProp { get; set; } + [Parameter] public object ObjectProp { get; set; } + [Parameter(CaptureUnmatchedValues = true)] public IReadOnlyDictionary CaptureUnmatchedValues { get; set; } } class HasDupliateCaptureUnmatchedValuesProperty { - [Parameter(CaptureUnmatchedValues = true)] internal Dictionary CaptureUnmatchedValuesProp1 { get; set; } - [Parameter(CaptureUnmatchedValues = true)] internal IDictionary CaptureUnmatchedValuesProp2 { get; set; } + [Parameter(CaptureUnmatchedValues = true)] public Dictionary CaptureUnmatchedValuesProp1 { get; set; } + [Parameter(CaptureUnmatchedValues = true)] public IDictionary CaptureUnmatchedValuesProp2 { get; set; } } class HasWrongTypeCaptureUnmatchedValuesProperty { - [Parameter(CaptureUnmatchedValues = true)] internal KeyValuePair[] CaptureUnmatchedValuesProp { get; set; } + [Parameter(CaptureUnmatchedValues = true)] public KeyValuePair[] CaptureUnmatchedValuesProp { get; set; } } class ParameterCollectionBuilder : IEnumerable @@ -454,7 +454,7 @@ namespace Microsoft.AspNetCore.Components.Test class FakeComponent : IComponent { - public void Configure(RenderHandle renderHandle) + public void Attach(RenderHandle renderHandle) => throw new NotImplementedException(); public Task SetParametersAsync(ParameterCollection parameters) diff --git a/src/Components/Components/test/ParameterCollectionTest.cs b/src/Components/Components/test/ParameterCollectionTest.cs index 66620132d0..9e9b8da975 100644 --- a/src/Components/Components/test/ParameterCollectionTest.cs +++ b/src/Components/Components/test/ParameterCollectionTest.cs @@ -342,7 +342,7 @@ namespace Microsoft.AspNetCore.Components.Test private class FakeComponent : IComponent { - public void Configure(RenderHandle renderHandle) + public void Attach(RenderHandle renderHandle) => throw new NotImplementedException(); public Task SetParametersAsync(ParameterCollection parameters) diff --git a/src/Components/Components/test/RenderTreeBuilderTest.cs b/src/Components/Components/test/RenderTreeBuilderTest.cs index 6d03c1261c..3207c31e54 100644 --- a/src/Components/Components/test/RenderTreeBuilderTest.cs +++ b/src/Components/Components/test/RenderTreeBuilderTest.cs @@ -1558,22 +1558,45 @@ namespace Microsoft.AspNetCore.Components.Test } [Fact] - public void CannotAddNullKey() + public void IgnoresNullElementKey() { - // Although we could translate 'null' into either some default "null key" - // instance, or just no-op the call, it almost certainly indicates a programming - // error so it's better to fail. - // Arrange var builder = new RenderTreeBuilder(new TestRenderer()); - // Act/Assert - var ex = Assert.Throws(() => - { - builder.OpenElement(0, "elem"); - builder.SetKey(null); - }); - Assert.Equal("value", ex.ParamName); + // Act + builder.OpenElement(0, "elem"); + builder.SetKey(null); + builder.CloseElement(); + + // Assert + Assert.Collection( + builder.GetFrames().AsEnumerable(), + frame => + { + AssertFrame.Element(frame, "elem", 1, 0); + Assert.Null(frame.ElementKey); + }); + } + + [Fact] + public void IgnoresNullComponentKey() + { + // Arrange + var builder = new RenderTreeBuilder(new TestRenderer()); + + // Act + builder.OpenComponent(0); + builder.SetKey(null); + builder.CloseComponent(); + + // Assert + Assert.Collection( + builder.GetFrames().AsEnumerable(), + frame => + { + AssertFrame.Component(frame, 1, 0); + Assert.Null(frame.ComponentKey); + }); } [Fact] @@ -1787,7 +1810,7 @@ namespace Microsoft.AspNetCore.Components.Test private class TestComponent : IComponent { - public void Configure(RenderHandle renderHandle) { } + public void Attach(RenderHandle renderHandle) { } public Task SetParametersAsync(ParameterCollection parameters) => throw new NotImplementedException(); diff --git a/src/Components/Components/test/RenderTreeDiffBuilderTest.cs b/src/Components/Components/test/RenderTreeDiffBuilderTest.cs index 5054d574b4..28d9193ea0 100644 --- a/src/Components/Components/test/RenderTreeDiffBuilderTest.cs +++ b/src/Components/Components/test/RenderTreeDiffBuilderTest.cs @@ -13,11 +13,12 @@ using Xunit; namespace Microsoft.AspNetCore.Components.Test { - public class RenderTreeDiffBuilderTest + public class RenderTreeDiffBuilderTest : IDisposable { private readonly Renderer renderer; private readonly RenderTreeBuilder oldTree; private readonly RenderTreeBuilder newTree; + private RenderBatchBuilder batchBuilder; public RenderTreeDiffBuilderTest() { @@ -26,6 +27,14 @@ namespace Microsoft.AspNetCore.Components.Test newTree = new RenderTreeBuilder(renderer); } + void IDisposable.Dispose() + { + renderer.Dispose(); + ((IDisposable)oldTree).Dispose(); + ((IDisposable)newTree).Dispose(); + batchBuilder?.Dispose(); + } + [Theory] [MemberData(nameof(RecognizesEquivalentFramesAsSameCases))] public void RecognizesEquivalentFramesAsSame(RenderFragment appendFragment) @@ -208,7 +217,8 @@ namespace Microsoft.AspNetCore.Components.Test oldTree.SetKey("retained key"); oldTree.AddAttribute(1, "ParamName", "Param old value"); oldTree.CloseComponent(); - GetRenderedBatch(new RenderTreeBuilder(renderer), oldTree, false); // Assign initial IDs + using var initial = new RenderTreeBuilder(renderer); + GetRenderedBatch(initial, oldTree, false); // Assign initial IDs var oldComponent = GetComponents(oldTree).Single(); newTree.OpenComponent(0); @@ -227,12 +237,12 @@ namespace Microsoft.AspNetCore.Components.Test // param on the second component. // Act - var batch = GetRenderedBatch(initializeFromFrames: false); + var batchBuilder = GetRenderedBatch(initializeFromFrames: false); var newComponents = GetComponents(newTree); // Assert: Inserts new component at position 0 - Assert.Equal(1, batch.UpdatedComponents.Count); - Assert.Collection(batch.UpdatedComponents.Array[0].Edits, + Assert.Equal(1, batchBuilder.UpdatedComponents.Count); + Assert.Collection(batchBuilder.UpdatedComponents.Array[0].Edits, entry => AssertEdit(entry, RenderTreeEditType.PrependFrame, 0)); // Assert: Retains old component instance in position 1, and updates its params @@ -255,7 +265,8 @@ namespace Microsoft.AspNetCore.Components.Test oldTree.CloseComponent(); // Instantiate initial components - GetRenderedBatch(new RenderTreeBuilder(renderer), oldTree, false); + using var initial = new RenderTreeBuilder(renderer); + GetRenderedBatch(initial, oldTree, false); var oldComponents = GetComponents(oldTree); newTree.OpenComponent(0); @@ -286,7 +297,8 @@ namespace Microsoft.AspNetCore.Components.Test oldTree.CloseComponent(); // Instantiate initial component - GetRenderedBatch(new RenderTreeBuilder(renderer), oldTree, false); + using var renderTreeBuilder = new RenderTreeBuilder(renderer); + GetRenderedBatch(renderTreeBuilder, oldTree, false); var oldComponent = GetComponents(oldTree).Single(); Assert.NotNull(oldComponent); @@ -315,86 +327,37 @@ namespace Microsoft.AspNetCore.Components.Test } [Fact] - public void HandlesClashingKeys_FirstUsage() + public void RejectsClashingKeysInOldTree() { - // This scenario is problematic for the algorithm if it uses a "first key - // usage wins" policy for duplicate keys. It would not end up with attrib1b - // anywhere in the output, because whenever it sees key1 in oldTree, it tries - // to diff against the first usage of key1 in newTree, which has attrib1a. - - // However, because of the actual "duplicated keys are excluded from the - // dictionary match" policy, we don't preserve any of the key1 items, and - // the diff is valid. - // Arrange - AddWithKey(oldTree, "key3", "attrib3"); - AddWithKey(oldTree, "key1", "attrib1a"); AddWithKey(oldTree, "key1", "attrib1a"); AddWithKey(oldTree, "key2", "attrib2"); + AddWithKey(oldTree, "key1", "attrib3"); - AddWithKey(newTree, "key1", "attrib1a"); - AddWithKey(newTree, "key2", "attrib2"); AddWithKey(newTree, "key1", "attrib1b"); + AddWithKey(newTree, "key2", "attrib2"); + AddWithKey(newTree, "key3", "attrib3"); - // Act - var (result, referenceFrames) = GetSingleUpdatedComponent(); - - // Assert - Assert.Collection(result.Edits, - // Insert key1+attrib1a at the top - edit => - { - AssertEdit(edit, RenderTreeEditType.PrependFrame, 0); - Assert.Equal("attrib1a", referenceFrames[edit.ReferenceFrameIndex + 1].AttributeValue); - }, - // Delete key3+attrib3 - edit => AssertEdit(edit, RenderTreeEditType.RemoveFrame, 1), - // Delete key1+attrib1a - edit => AssertEdit(edit, RenderTreeEditType.RemoveFrame, 1), - // Delete the other key1+attrib1a - edit => AssertEdit(edit, RenderTreeEditType.RemoveFrame, 1), - // Insert key1+attrib1b at the bottom - edit => - { - AssertEdit(edit, RenderTreeEditType.PrependFrame, 2); - Assert.Equal("attrib1b", referenceFrames[edit.ReferenceFrameIndex + 1].AttributeValue); - }); + // Act/Assert + var ex = Assert.Throws(() => GetSingleUpdatedComponent()); + Assert.Equal("More than one sibling has the same key value, 'key1'. Key values must be unique.", ex.Message); } [Fact] - public void HandlesClashingKeys_LastUsage() + public void RejectsClashingKeysInNewTree() { - // This scenario is problematic for the algorithm if it uses a "last key - // usage wins" policy for duplicate keys. It would not end up with attrib1b - // anywhere in the output, because when it sees key1 in oldTree, it tries - // to diff against the last usage of key1 in newTree, which has attrib1a. - - // However, because of the actual "duplicated keys are excluded from the - // dictionary match" policy, we don't preserve any of the key1 items, and - // the diff is valid. - // Arrange AddWithKey(oldTree, "key1", "attrib1a"); AddWithKey(oldTree, "key2", "attrib2"); - AddWithKey(oldTree, "key1", "attrib1b"); + AddWithKey(oldTree, "key3", "attrib3"); - AddWithKey(newTree, "key2", "attrib2"); AddWithKey(newTree, "key1", "attrib1b"); - AddWithKey(newTree, "key1", "attrib1a"); + AddWithKey(newTree, "key2", "attrib2"); + AddWithKey(newTree, "key1", "attrib3"); - // Act - var (result, referenceFrames) = GetSingleUpdatedComponent(); - - // Assert - Assert.Collection(result.Edits, - // Delete key1+attrib1a - edit => AssertEdit(edit, RenderTreeEditType.RemoveFrame, 0), - // Insert a new key1+attrib1a at the bottom - edit => - { - AssertEdit(edit, RenderTreeEditType.PrependFrame, 2); - Assert.Equal("attrib1a", referenceFrames[edit.ReferenceFrameIndex + 1].AttributeValue); - }); + // Act/Assert + var ex = Assert.Throws(() => GetSingleUpdatedComponent()); + Assert.Equal("More than one sibling has the same key value, 'key1'. Key values must be unique.", ex.Message); } [Fact] @@ -773,10 +736,11 @@ namespace Microsoft.AspNetCore.Components.Test // Arrange oldTree.OpenComponent(123); oldTree.CloseComponent(); - GetRenderedBatch(new RenderTreeBuilder(renderer), oldTree, false); // Assign initial IDs + using var initial = new RenderTreeBuilder(renderer); + GetRenderedBatch(initial, oldTree, false); // Assign initial IDs newTree.OpenComponent(123); newTree.CloseComponent(); - var batchBuilder = new RenderBatchBuilder(); + using var batchBuilder = new RenderBatchBuilder(); // Act var diff = RenderTreeDiffBuilder.ComputeDiff(renderer, batchBuilder, 0, oldTree.GetFrames(), newTree.GetFrames()); @@ -884,7 +848,7 @@ namespace Microsoft.AspNetCore.Components.Test newTree.CloseElement(); // Act - var (result, referenceFrames, batch) = GetSingleUpdatedComponentWithBatch(initializeFromFrames: true); + var (result, referenceFrames, batchBuilder) = GetSingleUpdatedComponentWithBatch(initializeFromFrames: true); var removedEventHandlerFrame = oldTree.GetFrames().Array[2]; // Assert @@ -895,10 +859,10 @@ namespace Microsoft.AspNetCore.Components.Test Assert.Equal(0, entry.ReferenceFrameIndex); }); AssertFrame.Attribute(referenceFrames[0], "onbar", addedHandler); - Assert.NotEqual(0, removedEventHandlerFrame.AttributeEventHandlerId); + Assert.NotEqual(default, removedEventHandlerFrame.AttributeEventHandlerId); Assert.Equal( new[] { removedEventHandlerFrame.AttributeEventHandlerId }, - batch.DisposedEventHandlerIDs.AsEnumerable()); + batchBuilder.DisposedEventHandlerIDs.AsEnumerable()); } [Fact] @@ -1588,7 +1552,9 @@ namespace Microsoft.AspNetCore.Components.Test newTree.CloseComponent(); // newTree.CloseElement(); // - RenderTreeDiffBuilder.ComputeDiff(renderer, new RenderBatchBuilder(), 0, new RenderTreeBuilder(renderer).GetFrames(), oldTree.GetFrames()); + using var batchBuilder = new RenderBatchBuilder(); + using var renderTreeBuilder = new RenderTreeBuilder(renderer); + RenderTreeDiffBuilder.ComputeDiff(renderer, batchBuilder, 0, renderTreeBuilder.GetFrames(), oldTree.GetFrames()); var originalFakeComponentInstance = oldTree.GetFrames().Array[2].Component; var originalFakeComponent2Instance = oldTree.GetFrames().Array[3].Component; @@ -1618,7 +1584,7 @@ namespace Microsoft.AspNetCore.Components.Test newTree.CloseElement(); // Act - var (result, referenceFrames, batch) = GetSingleUpdatedComponentWithBatch(initializeFromFrames: true); + var (result, referenceFrames, batchBuilder) = GetSingleUpdatedComponentWithBatch(initializeFromFrames: true); var oldAttributeFrame = oldTree.GetFrames().Array[1]; var newAttributeFrame = newTree.GetFrames().Array[1]; @@ -1626,9 +1592,9 @@ namespace Microsoft.AspNetCore.Components.Test Assert.Empty(result.Edits); AssertFrame.Attribute(oldAttributeFrame, "ontest", retainedHandler); AssertFrame.Attribute(newAttributeFrame, "ontest", retainedHandler); - Assert.NotEqual(0, oldAttributeFrame.AttributeEventHandlerId); + Assert.NotEqual(default, oldAttributeFrame.AttributeEventHandlerId); Assert.Equal(oldAttributeFrame.AttributeEventHandlerId, newAttributeFrame.AttributeEventHandlerId); - Assert.Empty(batch.DisposedEventHandlerIDs.AsEnumerable()); + Assert.Empty(batchBuilder.DisposedEventHandlerIDs.AsEnumerable()); } [Fact] @@ -1645,7 +1611,7 @@ namespace Microsoft.AspNetCore.Components.Test newTree.CloseElement(); // Act - var (result, referenceFrames, batch) = GetSingleUpdatedComponentWithBatch(initializeFromFrames: true); + var (result, referenceFrames, batchBuilder) = GetSingleUpdatedComponentWithBatch(initializeFromFrames: true); var oldAttributeFrame = oldTree.GetFrames().Array[1]; var newAttributeFrame = newTree.GetFrames().Array[2]; @@ -1653,9 +1619,9 @@ namespace Microsoft.AspNetCore.Components.Test Assert.Single(result.Edits); AssertFrame.Attribute(oldAttributeFrame, "ontest", retainedHandler); AssertFrame.Attribute(newAttributeFrame, "ontest", retainedHandler); - Assert.NotEqual(0, oldAttributeFrame.AttributeEventHandlerId); + Assert.NotEqual(default, oldAttributeFrame.AttributeEventHandlerId); Assert.Equal(oldAttributeFrame.AttributeEventHandlerId, newAttributeFrame.AttributeEventHandlerId); - Assert.Empty(batch.DisposedEventHandlerIDs.AsEnumerable()); + Assert.Empty(batchBuilder.DisposedEventHandlerIDs.AsEnumerable()); } [Fact] @@ -1672,7 +1638,9 @@ namespace Microsoft.AspNetCore.Components.Test newTree.AddAttribute(14, nameof(FakeComponent.ObjectProperty), objectWillNotChange); newTree.CloseComponent(); - RenderTreeDiffBuilder.ComputeDiff(renderer, new RenderBatchBuilder(), 0, new RenderTreeBuilder(renderer).GetFrames(), oldTree.GetFrames()); + using var batchBuilder = new RenderBatchBuilder(); + using var renderTree = new RenderTreeBuilder(renderer); + RenderTreeDiffBuilder.ComputeDiff(renderer, batchBuilder, 0, renderTree.GetFrames(), oldTree.GetFrames()); var originalComponentInstance = (FakeComponent)oldTree.GetFrames().Array[0].Component; // Act @@ -1710,7 +1678,9 @@ namespace Microsoft.AspNetCore.Components.Test tree.CloseComponent(); } - RenderTreeDiffBuilder.ComputeDiff(renderer, new RenderBatchBuilder(), 0, new RenderTreeBuilder(renderer).GetFrames(), oldTree.GetFrames()); + using var batchBuilder = new RenderBatchBuilder(); + using var renderTreeBuilder = new RenderTreeBuilder(renderer); + RenderTreeDiffBuilder.ComputeDiff(renderer, batchBuilder, 0, renderTreeBuilder.GetFrames(), oldTree.GetFrames()); var originalComponentInstance = (CaptureSetParametersComponent)oldTree.GetFrames().Array[0].Component; Assert.Equal(1, originalComponentInstance.SetParametersCallCount); @@ -1738,7 +1708,9 @@ namespace Microsoft.AspNetCore.Components.Test tree.CloseComponent(); } - RenderTreeDiffBuilder.ComputeDiff(renderer, new RenderBatchBuilder(), 0, new RenderTreeBuilder(renderer).GetFrames(), oldTree.GetFrames()); + using var batchBuilder = new RenderBatchBuilder(); + using var renderTreeBuilder = new RenderTreeBuilder(renderer); + RenderTreeDiffBuilder.ComputeDiff(renderer, batchBuilder, 0, renderTreeBuilder.GetFrames(), oldTree.GetFrames()); var componentInstance = (CaptureSetParametersComponent)oldTree.GetFrames().Array[0].Component; Assert.Equal(1, componentInstance.SetParametersCallCount); @@ -1762,8 +1734,9 @@ namespace Microsoft.AspNetCore.Components.Test newTree.OpenComponent(30); // newTree.CloseComponent(); // - var batchBuilder = new RenderBatchBuilder(); - RenderTreeDiffBuilder.ComputeDiff(renderer, batchBuilder, 0, new RenderTreeBuilder(renderer).GetFrames(), oldTree.GetFrames()); + using var batchBuilder = new RenderBatchBuilder(); + using var renderTree = new RenderTreeBuilder(renderer); + RenderTreeDiffBuilder.ComputeDiff(renderer, batchBuilder, 0, renderTree.GetFrames(), oldTree.GetFrames()); // Act/Assert // Note that we track NonDisposableComponent was disposed even though it's not IDisposable, @@ -1972,7 +1945,8 @@ namespace Microsoft.AspNetCore.Components.Test oldTree.AddAttribute(1, nameof(FakeComponent.StringProperty), "Second param"); oldTree.CloseComponent(); - GetRenderedBatch(new RenderTreeBuilder(renderer), oldTree, false); // Assign initial IDs + using var renderTreeBuilder = new RenderTreeBuilder(renderer); + GetRenderedBatch(renderTreeBuilder, oldTree, false); // Assign initial IDs var oldComponents = GetComponents(oldTree); newTree.OpenComponent(0); @@ -2173,12 +2147,19 @@ namespace Microsoft.AspNetCore.Components.Test { if (initializeFromFrames) { - var emptyFrames = new RenderTreeBuilder(renderer).GetFrames(); + using var renderTreeBuilder = new RenderTreeBuilder(renderer); + using var initializeBatchBuilder = new RenderBatchBuilder(); + + var emptyFrames = renderTreeBuilder.GetFrames(); var oldFrames = from.GetFrames(); - RenderTreeDiffBuilder.ComputeDiff(renderer, new RenderBatchBuilder(), 0, emptyFrames, oldFrames); + + RenderTreeDiffBuilder.ComputeDiff(renderer, initializeBatchBuilder, 0, emptyFrames, oldFrames); } - var batchBuilder = new RenderBatchBuilder(); + batchBuilder?.Dispose(); + // This gets disposed as part of the test type's Dispose + batchBuilder = new RenderBatchBuilder(); + var diff = RenderTreeDiffBuilder.ComputeDiff(renderer, batchBuilder, 0, from.GetFrames(), to.GetFrames()); batchBuilder.UpdatedComponentDiffs.Append(diff); return batchBuilder.ToBatch(); @@ -2224,23 +2205,23 @@ namespace Microsoft.AspNetCore.Components.Test private class FakeComponent : IComponent { [Parameter] - internal int IntProperty { get; set; } + public int IntProperty { get; set; } [Parameter] - internal string StringProperty { get; set; } + public string StringProperty { get; set; } [Parameter] - internal object ObjectProperty { get; set; } + public object ObjectProperty { get; set; } [Parameter] - internal string ReadonlyProperty { get; private set; } + public string ReadonlyProperty { get; private set; } [Parameter] - string PrivateProperty { get; set; } + public string PrivateProperty { get; set; } public string NonParameterProperty { get; set; } - public void Configure(RenderHandle renderHandle) { } + public void Attach(RenderHandle renderHandle) { } public Task SetParametersAsync(ParameterCollection parameters) { parameters.SetParameterProperties(this); @@ -2250,7 +2231,7 @@ namespace Microsoft.AspNetCore.Components.Test private class FakeComponent2 : IComponent { - public void Configure(RenderHandle renderHandle) + public void Attach(RenderHandle renderHandle) { } @@ -2261,7 +2242,7 @@ namespace Microsoft.AspNetCore.Components.Test { public int SetParametersCallCount { get; private set; } - public void Configure(RenderHandle renderHandle) + public void Attach(RenderHandle renderHandle) { } @@ -2277,14 +2258,14 @@ namespace Microsoft.AspNetCore.Components.Test public int DisposalCount { get; private set; } public void Dispose() => DisposalCount++; - public void Configure(RenderHandle renderHandle) { } + public void Attach(RenderHandle renderHandle) { } public Task SetParametersAsync(ParameterCollection parameters) => Task.CompletedTask; } private class NonDisposableComponent : IComponent { - public void Configure(RenderHandle renderHandle) { } + public void Attach(RenderHandle renderHandle) { } public Task SetParametersAsync(ParameterCollection parameters) => Task.CompletedTask; } diff --git a/src/Components/Components/test/RendererTest.cs b/src/Components/Components/test/RendererTest.cs index 5b4baef88c..9cccfafefb 100644 --- a/src/Components/Components/test/RendererTest.cs +++ b/src/Components/Components/test/RendererTest.cs @@ -3313,7 +3313,7 @@ namespace Microsoft.AspNetCore.Components.Test Assert.Equal(RenderTreeEditType.SetAttribute, edit.Type); var attributeFrame = batch2.ReferenceFrames[edit.ReferenceFrameIndex]; AssertFrame.Attribute(attributeFrame, "ontestevent", typeof(Action)); - Assert.NotEqual(0, attributeFrame.AttributeEventHandlerId); + Assert.NotEqual(default, attributeFrame.AttributeEventHandlerId); Assert.NotEqual(eventHandlerId, attributeFrame.AttributeEventHandlerId); }); } @@ -3365,7 +3365,7 @@ namespace Microsoft.AspNetCore.Components.Test Assert.Equal(RenderTreeEditType.SetAttribute, edit.Type); var attributeFrame = latestBatch.ReferenceFrames[edit.ReferenceFrameIndex]; AssertFrame.Attribute(attributeFrame, "ontestevent", typeof(Action)); - Assert.NotEqual(0, attributeFrame.AttributeEventHandlerId); + Assert.NotEqual(default, attributeFrame.AttributeEventHandlerId); Assert.NotEqual(eventHandlerId, attributeFrame.AttributeEventHandlerId); }); } @@ -3399,7 +3399,7 @@ namespace Microsoft.AspNetCore.Components.Test _renderFragment = renderFragment; } - public void Configure(RenderHandle renderHandle) + public void Attach(RenderHandle renderHandle) { _renderHandle = renderHandle; } @@ -3434,7 +3434,7 @@ namespace Microsoft.AspNetCore.Components.Test private class MessageComponent : AutoRenderComponent { [Parameter] - internal string Message { get; set; } + public string Message { get; set; } protected override void BuildRenderTree(RenderTreeBuilder builder) { @@ -3444,9 +3444,9 @@ namespace Microsoft.AspNetCore.Components.Test private class MyStrongComponent : AutoRenderComponent { - [Parameter(CaptureUnmatchedValues = true)] internal IDictionary Attributes { get; set; } + [Parameter(CaptureUnmatchedValues = true)] public IDictionary Attributes { get; set; } - [Parameter] internal string Text { get; set; } + [Parameter] public string Text { get; set; } protected override void BuildRenderTree(RenderTreeBuilder builder) { @@ -3460,17 +3460,17 @@ namespace Microsoft.AspNetCore.Components.Test private class FakeComponent : IComponent { [Parameter] - internal int IntProperty { get; private set; } + public int IntProperty { get; private set; } [Parameter] - internal string StringProperty { get; private set; } + public string StringProperty { get; private set; } [Parameter] - internal object ObjectProperty { get; set; } + public object ObjectProperty { get; set; } public RenderHandle RenderHandle { get; private set; } - public void Configure(RenderHandle renderHandle) + public void Attach(RenderHandle renderHandle) => RenderHandle = renderHandle; public Task SetParametersAsync(ParameterCollection parameters) @@ -3483,28 +3483,28 @@ namespace Microsoft.AspNetCore.Components.Test private class EventComponent : AutoRenderComponent, IComponent, IHandleEvent { [Parameter] - internal Action OnTest { get; set; } + public Action OnTest { get; set; } [Parameter] - internal Func OnTestAsync { get; set; } + public Func OnTestAsync { get; set; } [Parameter] - internal Action OnClick { get; set; } + public Action OnClick { get; set; } [Parameter] - internal Func OnClickAsync { get; set; } + public Func OnClickAsync { get; set; } [Parameter] - internal Action OnClickAction { get; set; } + public Action OnClickAction { get; set; } [Parameter] - internal Func OnClickAsyncAction { get; set; } + public Func OnClickAsyncAction { get; set; } [Parameter] - internal EventCallback OnClickEventCallback { get; set; } + public EventCallback OnClickEventCallback { get; set; } [Parameter] - internal EventCallback OnClickEventCallbackOfT { get; set; } + public EventCallback OnClickEventCallbackOfT { get; set; } public bool SkipElement { get; set; } private int renderCount = 0; @@ -3568,10 +3568,10 @@ namespace Microsoft.AspNetCore.Components.Test private class ConditionalParentComponent : AutoRenderComponent where T : IComponent { [Parameter] - internal bool IncludeChild { get; set; } + public bool IncludeChild { get; set; } [Parameter] - internal IDictionary ChildParameters { get; set; } + public IDictionary ChildParameters { get; set; } protected override void BuildRenderTree(RenderTreeBuilder builder) { @@ -3596,7 +3596,7 @@ namespace Microsoft.AspNetCore.Components.Test private class ReRendersParentComponent : AutoRenderComponent { [Parameter] - internal TestComponent Parent { get; private set; } + public TestComponent Parent { get; private set; } private bool _isFirstTime = true; @@ -3615,11 +3615,11 @@ namespace Microsoft.AspNetCore.Components.Test private class RendersSelfAfterEventComponent : IComponent, IHandleEvent { [Parameter] - Action OnClick { get; set; } + public Action OnClick { get; set; } private RenderHandle _renderHandle; - public void Configure(RenderHandle renderHandle) + public void Attach(RenderHandle renderHandle) => _renderHandle = renderHandle; public Task SetParametersAsync(ParameterCollection parameters) @@ -3650,7 +3650,7 @@ namespace Microsoft.AspNetCore.Components.Test private readonly List _renderHandles = new List(); - public void Configure(RenderHandle renderHandle) + public void Attach(RenderHandle renderHandle) => _renderHandles.Add(renderHandle); public Task SetParametersAsync(ParameterCollection parameters) @@ -3764,7 +3764,7 @@ namespace Microsoft.AspNetCore.Components.Test public int Number { get; set; } - public void Configure(RenderHandle renderHandle) + public void Attach(RenderHandle renderHandle) { _renderHandler = renderHandle; } @@ -3806,7 +3806,7 @@ namespace Microsoft.AspNetCore.Components.Test SomeMethodCallCount++; } - public void Configure(RenderHandle renderHandle) + public void Attach(RenderHandle renderHandle) { _renderHandle = renderHandle; } diff --git a/src/Components/Components/test/Rendering/ArrayBuilderSegmentTest.cs b/src/Components/Components/test/Rendering/ArrayBuilderSegmentTest.cs index 35596f4b0e..3e6a22fb6f 100644 --- a/src/Components/Components/test/Rendering/ArrayBuilderSegmentTest.cs +++ b/src/Components/Components/test/Rendering/ArrayBuilderSegmentTest.cs @@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Components.Rendering public void BasicPropertiesWork() { // Arrange: builder containing 1..5 - var builder = new ArrayBuilder(); + using var builder = new ArrayBuilder(); builder.Append(new[] { 1, 2, 3, 4, 5 }, 0, 5); // Act: take segment containing 2..3 @@ -32,7 +32,7 @@ namespace Microsoft.AspNetCore.Components.Rendering public void StillWorksAfterUnderlyingCapacityChange() { // Arrange: builder containing 1..8 - var builder = new ArrayBuilder(capacity: 10); + using var builder = new ArrayBuilder(minCapacity: 10, new TestArrayPool()); builder.Append(new[] { 1, 2, 3, 4, 5, 6, 7, 8 }, 0, 8); var originalBuffer = builder.Buffer; diff --git a/src/Components/Components/test/Rendering/ArrayBuilderTest.cs b/src/Components/Components/test/Rendering/ArrayBuilderTest.cs new file mode 100644 index 0000000000..6213b6fdd3 --- /dev/null +++ b/src/Components/Components/test/Rendering/ArrayBuilderTest.cs @@ -0,0 +1,317 @@ +// 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.Buffers; +using System.Linq; +using Xunit; + +namespace Microsoft.AspNetCore.Components.RenderTree +{ + public class ArrayBuilderTest + { + private readonly TestArrayPool ArrayPool = new TestArrayPool(); + + [Fact] + public void Append_SingleItem() + { + // Arrange + var value = 7; + using var builder = CreateArrayBuilder(); + + // Act + builder.Append(value); + + // Assert + Assert.Equal(1, builder.Count); + Assert.Equal(value, builder.Buffer[0]); + } + + [Fact] + public void Append_ThreeItem() + { + // Arrange + var value1 = 7; + var value2 = 22; + var value3 = 3; + using var builder = CreateArrayBuilder(); + + // Act + builder.Append(value1); + builder.Append(value2); + builder.Append(value3); + + // Assert + Assert.Equal(3, builder.Count); + Assert.Equal(new[] { value1, value2, value3 }, builder.Buffer.Take(3)); + } + + [Fact] + public void Append_FillBuffer() + { + // Arrange + var capacity = 8; + using var builder = new ArrayBuilder(minCapacity: capacity); + + // Act + for (var i = 0; i < capacity; i++) + { + builder.Append(5); + } + + // Assert + Assert.Equal(capacity, builder.Count); + Assert.Equal(Enumerable.Repeat(5, capacity), builder.Buffer.Take(capacity)); + } + + [Fact] + public void AppendArray_CopySubset() + { + // Arrange + var array = Enumerable.Repeat(8, 5).ToArray(); + using var builder = CreateArrayBuilder(); + + // Act + builder.Append(array, 0, 2); + + // Assert + Assert.Equal(2, builder.Count); + Assert.Equal(new[] { 8, 8 }, builder.Buffer.Take(2)); + } + + [Fact] + public void AppendArray_CopyArray() + { + // Arrange + var array = Enumerable.Repeat(8, 5).ToArray(); + using var builder = CreateArrayBuilder(); + + // Act + builder.Append(array, 0, array.Length); + + // Assert + Assert.Equal(array.Length, builder.Count); + Assert.Equal(array, builder.Buffer.Take(array.Length)); + } + + [Fact] + public void AppendArray_AfterPriorInsertion() + { + // Arrange + var array = Enumerable.Repeat(8, 5).ToArray(); + using var builder = CreateArrayBuilder(); + + // Act + builder.Append(118); + builder.Append(array, 0, 2); + + // Assert + Assert.Equal(3, builder.Count); + Assert.Equal(new[] { 118, 8, 8 }, builder.Buffer.Take(3)); + } + + [Theory] + // These are at boundaries of our capacity increments. + [InlineData(1023)] + [InlineData(1024)] + [InlineData(1025)] + public void AppendArray_LargerThanBuffer(int size) + { + // Arrange + var array = Enumerable.Repeat(17, size).ToArray(); + using var builder = CreateArrayBuilder(); + + // Act + builder.Append(array, 0, array.Length); + + // Assert + Assert.Equal(array.Length, builder.Count); + Assert.Equal(array, builder.Buffer.Take(array.Length)); + } + + [Fact] + public void Overwrite_Works() + { + // Arrange + using var builder = CreateArrayBuilder(); + builder.Append(7); + builder.Append(3); + builder.Append(9); + + // Act + builder.Overwrite(1, 2); + + // Assert + Assert.Equal(3, builder.Count); + Assert.Equal(new[]{ 7, 2, 9}, builder.Buffer.Take(3)); + } + + [Fact] + public void Insert_Works() + { + // Arrange + using var builder = CreateArrayBuilder(); + builder.Append(7); + builder.Append(3); + builder.Append(9); + + // Act + builder.InsertExpensive(1, 2); + + // Assert + Assert.Equal(4, builder.Count); + Assert.Equal(new[] { 7, 2, 3, 9 }, builder.Buffer.Take(4)); + } + + [Fact] + public void Insert_WhenBufferIsAtCapacity() + { + // Arrange + using var builder = CreateArrayBuilder(2); + builder.Append(new[] { 1, 3 }, 0, 2); + + // Act + builder.InsertExpensive(1, 2); + + // Assert + Assert.Equal(3, builder.Count); + Assert.Equal(new[] { 1, 2, 3 }, builder.Buffer.Take(3)); + } + + [Fact] + public void RemoveLast_Works() + { + // Arrange + using var builder = CreateArrayBuilder(); + builder.Append(1); + builder.Append(2); + builder.Append(3); + + // Act + builder.RemoveLast(); + + // Assert + Assert.Equal(2, builder.Count); + Assert.Equal(new[] { 1, 2, }, builder.Buffer.Take(2)); + } + + [Fact] + public void RemoveLast_LastEntry() + { + // Arrange + int[] buffer; + using (var builder = CreateArrayBuilder()) + { + builder.Append(1); + buffer = builder.Buffer; + + // Act + builder.RemoveLast(); + + // Assert + Assert.Equal(0, builder.Count); + } + + // Also verify that the buffer is indeed returned in this case. + var returnedBuffer = Assert.Single(ArrayPool.ReturnedBuffers); + Assert.Same(buffer, returnedBuffer); + } + + [Fact] + public void Clear_ReturnsBuffer() + { + // Arrange + using var builder = CreateArrayBuilder(); + builder.Append(1); + var buffer = builder.Buffer; + + // Act + builder.Clear(); + + // Assert + Assert.Equal(0, builder.Count); + var returnedBuffer = Assert.Single(ArrayPool.ReturnedBuffers); + Assert.Same(buffer, returnedBuffer); + } + + [Fact] + public void Dispose_WithEmptyBuffer_DoesNotReturnIt() + { + // Arrange + var builder = CreateArrayBuilder(); + + // Act + builder.Dispose(); + + // Assert + Assert.Empty(ArrayPool.ReturnedBuffers); + } + + [Fact] + public void Dispose_NonEmptyBufferIsReturned() + { + // Arrange + var builder = CreateArrayBuilder(); + builder.Append(1); + var buffer = builder.Buffer; + + // Act + builder.Dispose(); + + // Assert + Assert.Single(ArrayPool.ReturnedBuffers); + var returnedBuffer = Assert.Single(ArrayPool.ReturnedBuffers); + Assert.Same(buffer, returnedBuffer); + } + + [Fact] + public void DoubleDispose_DoesNotReturnBufferTwice() + { + // Arrange + var builder = CreateArrayBuilder(); + builder.Append(1); + var buffer = builder.Buffer; + + // Act + builder.Dispose(); + builder.Dispose(); + + // Assert + Assert.Single(ArrayPool.ReturnedBuffers); + var returnedBuffer = Assert.Single(ArrayPool.ReturnedBuffers); + Assert.Same(buffer, returnedBuffer); + } + + [Fact] + public void UnusedBufferIsReturned_OnResize() + { + // Arrange + var builder = CreateArrayBuilder(2); + + // Act + for (var i = 0; i < 10; i++) + { + builder.Append(i); + } + + // Assert + Assert.Collection( + ArrayPool.ReturnedBuffers, + buffer => Assert.Equal(2, buffer.Length), + buffer => Assert.Equal(4, buffer.Length), + buffer => Assert.Equal(8, buffer.Length)); + + // Clear this because this is no longer interesting. + ArrayPool.ReturnedBuffers.Clear(); + + var buffer = builder.Buffer; + builder.Dispose(); + + Assert.Same(buffer, Assert.Single(ArrayPool.ReturnedBuffers)); + } + + private ArrayBuilder CreateArrayBuilder(int capacity = 32) + { + return new ArrayBuilder(capacity, ArrayPool); + } + } +} diff --git a/src/Components/Components/test/Rendering/HtmlRendererTestBase.cs b/src/Components/Components/test/Rendering/HtmlRendererTestBase.cs index 84f02e61b0..1650158ee7 100644 --- a/src/Components/Components/test/Rendering/HtmlRendererTestBase.cs +++ b/src/Components/Components/test/Rendering/HtmlRendererTestBase.cs @@ -439,7 +439,7 @@ namespace Microsoft.AspNetCore.Components.Rendering { public RenderHandle RenderHandle { get; private set; } - public void Configure(RenderHandle renderHandle) + public void Attach(RenderHandle renderHandle) { RenderHandle = renderHandle; } @@ -553,7 +553,7 @@ namespace Microsoft.AspNetCore.Components.Rendering { private RenderHandle _renderHandle; - public void Configure(RenderHandle renderHandle) + public void Attach(RenderHandle renderHandle) { _renderHandle = renderHandle; } @@ -584,7 +584,7 @@ namespace Microsoft.AspNetCore.Components.Rendering [Inject] public RenderFragment Fragment { get; set; } - public void Configure(RenderHandle renderHandle) + public void Attach(RenderHandle renderHandle) { _renderHandle = renderHandle; } diff --git a/src/Components/Components/test/Rendering/TestArrayPool.cs b/src/Components/Components/test/Rendering/TestArrayPool.cs new file mode 100644 index 0000000000..c5335a6b64 --- /dev/null +++ b/src/Components/Components/test/Rendering/TestArrayPool.cs @@ -0,0 +1,23 @@ +// 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.Buffers; +using System.Collections.Generic; + +namespace Microsoft.AspNetCore.Components.RenderTree +{ + internal class TestArrayPool : ArrayPool + { + public override T[] Rent(int minimumLength) + { + return new T[minimumLength]; + } + + public List ReturnedBuffers = new List(); + + public override void Return(T[] array, bool clearArray = false) + { + ReturnedBuffers.Add(array); + } + } +} diff --git a/src/Components/Components/test/Routing/RouteTableTests.cs b/src/Components/Components/test/Routing/RouteTableFactoryTests.cs similarity index 97% rename from src/Components/Components/test/Routing/RouteTableTests.cs rename to src/Components/Components/test/Routing/RouteTableFactoryTests.cs index c2e34d4298..f6733d8ceb 100644 --- a/src/Components/Components/test/Routing/RouteTableTests.cs +++ b/src/Components/Components/test/Routing/RouteTableFactoryTests.cs @@ -9,13 +9,13 @@ using Xunit; namespace Microsoft.AspNetCore.Components.Test.Routing { - public class RouteTableTests + public class RouteTableFactoryTests { [Fact] public void CanDiscoverRoute() { // Arrange & Act - var routes = RouteTable.Create(new[] { typeof(MyComponent), }); + var routes = RouteTableFactory.Create(new[] { typeof(MyComponent), }); // Assert Assert.Equal("Test1", Assert.Single(routes.Routes).Template.TemplateText); @@ -30,7 +30,7 @@ namespace Microsoft.AspNetCore.Components.Test.Routing public void CanDiscoverRoutes_WithInheritance() { // Arrange & Act - var routes = RouteTable.Create(new[] { typeof(MyComponent), typeof(MyInheritedComponent), }); + var routes = RouteTableFactory.Create(new[] { typeof(MyComponent), typeof(MyInheritedComponent), }); // Assert Assert.Collection( @@ -363,7 +363,7 @@ namespace Microsoft.AspNetCore.Components.Test.Routing { return new RouteTable(_routeTemplates .Select(rt => new RouteEntry(TemplateParser.ParseTemplate(rt.Item1), rt.Item2)) - .OrderBy(id => id, RouteTable.RoutePrecedence) + .OrderBy(id => id, RouteTableFactory.RoutePrecedence) .ToArray()); } catch (InvalidOperationException ex) when (ex.InnerException is InvalidOperationException) diff --git a/src/Components/Components/test/Routing/TemplateParserTests.cs b/src/Components/Components/test/Routing/TemplateParserTests.cs index cd5951bb44..752963f52d 100644 --- a/src/Components/Components/test/Routing/TemplateParserTests.cs +++ b/src/Components/Components/test/Routing/TemplateParserTests.cs @@ -159,22 +159,12 @@ namespace Microsoft.AspNetCore.Components.Routing public bool Equals(RouteTemplate x, RouteTemplate y) { - if (x == null && y == null) - { - return true; - } - - if ((x == null) != (y == null)) - { - return false; - } - if (x.Segments.Length != y.Segments.Length) { return false; } - for (int i = 0; i < x.Segments.Length; i++) + for (var i = 0; i < x.Segments.Length; i++) { var xSegment = x.Segments[i]; var ySegment = y.Segments[i]; diff --git a/src/Components/Server/ref/Microsoft.AspNetCore.Components.Server.netcoreapp3.0.cs b/src/Components/Server/ref/Microsoft.AspNetCore.Components.Server.netcoreapp3.0.cs index 4470d13745..e39c5d64ac 100644 --- a/src/Components/Server/ref/Microsoft.AspNetCore.Components.Server.netcoreapp3.0.cs +++ b/src/Components/Server/ref/Microsoft.AspNetCore.Components.Server.netcoreapp3.0.cs @@ -3,22 +3,27 @@ namespace Microsoft.AspNetCore.Builder { + public sealed partial class ComponentEndpointConventionBuilder : Microsoft.AspNetCore.Builder.IEndpointConventionBuilder, Microsoft.AspNetCore.Builder.IHubEndpointConventionBuilder + { + internal ComponentEndpointConventionBuilder() { } + public void Add(System.Action convention) { } + } public static partial class ComponentEndpointConventionBuilderExtensions { - public static Microsoft.AspNetCore.Components.Server.ComponentEndpointConventionBuilder AddComponent(this Microsoft.AspNetCore.Components.Server.ComponentEndpointConventionBuilder builder, System.Type componentType, string selector) { throw null; } + public static Microsoft.AspNetCore.Builder.ComponentEndpointConventionBuilder AddComponent(this Microsoft.AspNetCore.Builder.ComponentEndpointConventionBuilder builder, System.Type componentType, string selector) { throw null; } } public static partial class ComponentEndpointRouteBuilderExtensions { - public static Microsoft.AspNetCore.Components.Server.ComponentEndpointConventionBuilder MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints) { throw null; } - public static Microsoft.AspNetCore.Components.Server.ComponentEndpointConventionBuilder MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, System.Action configureOptions) { throw null; } - public static Microsoft.AspNetCore.Components.Server.ComponentEndpointConventionBuilder MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, System.Type type, string selector) { throw null; } - public static Microsoft.AspNetCore.Components.Server.ComponentEndpointConventionBuilder MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, System.Type type, string selector, System.Action configureOptions) { throw null; } - public static Microsoft.AspNetCore.Components.Server.ComponentEndpointConventionBuilder MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, System.Type componentType, string selector, string path) { throw null; } - public static Microsoft.AspNetCore.Components.Server.ComponentEndpointConventionBuilder MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, System.Type componentType, string selector, string path, System.Action configureOptions) { throw null; } - public static Microsoft.AspNetCore.Components.Server.ComponentEndpointConventionBuilder MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string selector) where TComponent : Microsoft.AspNetCore.Components.IComponent { throw null; } - public static Microsoft.AspNetCore.Components.Server.ComponentEndpointConventionBuilder MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string selector, System.Action configureOptions) where TComponent : Microsoft.AspNetCore.Components.IComponent { throw null; } - public static Microsoft.AspNetCore.Components.Server.ComponentEndpointConventionBuilder MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string selector, string path) where TComponent : Microsoft.AspNetCore.Components.IComponent { throw null; } - public static Microsoft.AspNetCore.Components.Server.ComponentEndpointConventionBuilder MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string selector, string path, System.Action configureOptions) where TComponent : Microsoft.AspNetCore.Components.IComponent { throw null; } + public static Microsoft.AspNetCore.Builder.ComponentEndpointConventionBuilder MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints) { throw null; } + public static Microsoft.AspNetCore.Builder.ComponentEndpointConventionBuilder MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, System.Action configureOptions) { throw null; } + public static Microsoft.AspNetCore.Builder.ComponentEndpointConventionBuilder MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, System.Type type, string selector) { throw null; } + public static Microsoft.AspNetCore.Builder.ComponentEndpointConventionBuilder MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, System.Type type, string selector, System.Action configureOptions) { throw null; } + public static Microsoft.AspNetCore.Builder.ComponentEndpointConventionBuilder MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, System.Type componentType, string selector, string path) { throw null; } + public static Microsoft.AspNetCore.Builder.ComponentEndpointConventionBuilder MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, System.Type componentType, string selector, string path, System.Action configureOptions) { throw null; } + public static Microsoft.AspNetCore.Builder.ComponentEndpointConventionBuilder MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string selector) where TComponent : Microsoft.AspNetCore.Components.IComponent { throw null; } + public static Microsoft.AspNetCore.Builder.ComponentEndpointConventionBuilder MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string selector, System.Action configureOptions) where TComponent : Microsoft.AspNetCore.Components.IComponent { throw null; } + public static Microsoft.AspNetCore.Builder.ComponentEndpointConventionBuilder MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string selector, string path) where TComponent : Microsoft.AspNetCore.Components.IComponent { throw null; } + public static Microsoft.AspNetCore.Builder.ComponentEndpointConventionBuilder MapBlazorHub(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string selector, string path, System.Action configureOptions) where TComponent : Microsoft.AspNetCore.Components.IComponent { throw null; } } } namespace Microsoft.AspNetCore.Components.Server @@ -31,11 +36,6 @@ namespace Microsoft.AspNetCore.Components.Server public bool JSInteropDetailedErrors { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public int MaxRetainedDisconnectedCircuits { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } } - public sealed partial class ComponentEndpointConventionBuilder : Microsoft.AspNetCore.Builder.IEndpointConventionBuilder, Microsoft.AspNetCore.SignalR.IHubEndpointConventionBuilder - { - internal ComponentEndpointConventionBuilder() { } - public void Add(System.Action convention) { } - } public partial class ComponentPrerenderingContext { public ComponentPrerenderingContext() { } @@ -52,13 +52,6 @@ namespace Microsoft.AspNetCore.Components.Server { System.Threading.Tasks.Task PrerenderComponentAsync(Microsoft.AspNetCore.Components.Server.ComponentPrerenderingContext context); } - public static partial class WasmMediaTypeNames - { - public static partial class Application - { - public const string Wasm = "application/wasm"; - } - } } namespace Microsoft.AspNetCore.Components.Server.Circuits { @@ -76,15 +69,6 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits public virtual System.Threading.Tasks.Task OnConnectionDownAsync(Microsoft.AspNetCore.Components.Server.Circuits.Circuit circuit, System.Threading.CancellationToken cancellationToken) { throw null; } public virtual System.Threading.Tasks.Task OnConnectionUpAsync(Microsoft.AspNetCore.Components.Server.Circuits.Circuit circuit, System.Threading.CancellationToken cancellationToken) { throw null; } } - public partial class RemoteUriHelper : Microsoft.AspNetCore.Components.UriHelperBase - { - public RemoteUriHelper(Microsoft.Extensions.Logging.ILogger logger) { } - public bool HasAttachedJSRuntime { get { throw null; } } - public override void InitializeState(string uriAbsolute, string baseUriAbsolute) { } - protected override void NavigateToCore(string uri, bool forceLoad) { } - [Microsoft.JSInterop.JSInvokableAttribute("NotifyLocationChanged")] - public static void NotifyLocationChanged(string uriAbsolute, bool isInterceptedLink) { } - } } namespace Microsoft.AspNetCore.Components.Web.Rendering { diff --git a/src/Components/Server/src/Builder/ComponentEndpointConventionBuilder.cs b/src/Components/Server/src/Builder/ComponentEndpointConventionBuilder.cs index 284d9fd004..021272c7d6 100644 --- a/src/Components/Server/src/Builder/ComponentEndpointConventionBuilder.cs +++ b/src/Components/Server/src/Builder/ComponentEndpointConventionBuilder.cs @@ -2,10 +2,8 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.SignalR; -namespace Microsoft.AspNetCore.Components.Server +namespace Microsoft.AspNetCore.Builder { /// /// Builds conventions that will be used for customization of ComponentHub instances. diff --git a/src/Components/Server/src/Builder/ComponentEndpointConventionBuilderExtensions.cs b/src/Components/Server/src/Builder/ComponentEndpointConventionBuilderExtensions.cs index 0a29e1790d..b7d260d25e 100644 --- a/src/Components/Server/src/Builder/ComponentEndpointConventionBuilderExtensions.cs +++ b/src/Components/Server/src/Builder/ComponentEndpointConventionBuilderExtensions.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using Microsoft.AspNetCore.Components.Server; -using Microsoft.AspNetCore.SignalR; namespace Microsoft.AspNetCore.Builder { diff --git a/src/Components/Server/src/Circuits/RemoteUriHelper.cs b/src/Components/Server/src/Circuits/RemoteUriHelper.cs index f89e917db0..e9a1b28494 100644 --- a/src/Components/Server/src/Circuits/RemoteUriHelper.cs +++ b/src/Components/Server/src/Circuits/RemoteUriHelper.cs @@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits /// /// A Server-Side Components implementation of . /// - public class RemoteUriHelper : UriHelperBase + internal class RemoteUriHelper : UriHelperBase { private readonly ILogger _logger; private IJSRuntime _jsRuntime; diff --git a/src/Components/Server/src/Circuits/RenderBatchWriter.cs b/src/Components/Server/src/Circuits/RenderBatchWriter.cs index d66c5de2a5..ef035de4ba 100644 --- a/src/Components/Server/src/Circuits/RenderBatchWriter.cs +++ b/src/Components/Server/src/Circuits/RenderBatchWriter.cs @@ -130,16 +130,15 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits void Write(in RenderTreeFrame frame) { + // TODO: Change this to write as a short, saving 2 bytes per frame _binaryWriter.Write((int)frame.FrameType); // We want each frame to take up the same number of bytes, so that the // recipient can index into the array directly instead of having to // walk through it. - // Since we can fit every frame type into 3 ints, use that as the + // Since we can fit every frame type into 16 bytes, use that as the // common size. For smaller frames, we add padding to expand it to - // 12 bytes (i.e., 3 x 4-byte ints). - // The total size then for each frame is 16 bytes (frame type, then - // 3 other ints). + // 16 bytes. switch (frame.FrameType) { case RenderTreeFrameType.Attribute: @@ -160,41 +159,41 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits var attributeValueString = frame.AttributeValue as string; WriteString(attributeValueString, allowDeduplication: string.IsNullOrEmpty(attributeValueString)); } - _binaryWriter.Write(frame.AttributeEventHandlerId); + _binaryWriter.Write(frame.AttributeEventHandlerId); // 8 bytes break; case RenderTreeFrameType.Component: _binaryWriter.Write(frame.ComponentSubtreeLength); _binaryWriter.Write(frame.ComponentId); - WritePadding(_binaryWriter, 4); + WritePadding(_binaryWriter, 8); break; case RenderTreeFrameType.ComponentReferenceCapture: // The client doesn't need to know about these. But we still have // to include them in the array otherwise the ReferenceFrameIndex // values in the edits data would be wrong. - WritePadding(_binaryWriter, 12); + WritePadding(_binaryWriter, 16); break; case RenderTreeFrameType.Element: _binaryWriter.Write(frame.ElementSubtreeLength); WriteString(frame.ElementName, allowDeduplication: true); - WritePadding(_binaryWriter, 4); + WritePadding(_binaryWriter, 8); break; case RenderTreeFrameType.ElementReferenceCapture: WriteString(frame.ElementReferenceCaptureId, allowDeduplication: false); - WritePadding(_binaryWriter, 8); + WritePadding(_binaryWriter, 12); break; case RenderTreeFrameType.Region: _binaryWriter.Write(frame.RegionSubtreeLength); - WritePadding(_binaryWriter, 8); + WritePadding(_binaryWriter, 12); break; case RenderTreeFrameType.Text: WriteString( frame.TextContent, allowDeduplication: string.IsNullOrWhiteSpace(frame.TextContent)); - WritePadding(_binaryWriter, 8); + WritePadding(_binaryWriter, 12); break; case RenderTreeFrameType.Markup: WriteString(frame.MarkupContent, allowDeduplication: false); - WritePadding(_binaryWriter, 8); + WritePadding(_binaryWriter, 12); break; default: throw new ArgumentException($"Unsupported frame type: {frame.FrameType}"); @@ -216,6 +215,21 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits return startPos; } + int Write(in ArrayRange numbers) + { + var startPos = (int)_binaryWriter.BaseStream.Position; + _binaryWriter.Write(numbers.Count); + + var array = numbers.Array; + var count = numbers.Count; + for (var index = 0; index < count; index++) + { + _binaryWriter.Write(array[index]); + } + + return startPos; + } + void WriteString(string value, bool allowDeduplication) { if (value == null) diff --git a/src/Components/Server/src/WasmMediaTypeNames.cs b/src/Components/Server/src/WasmMediaTypeNames.cs deleted file mode 100644 index f632d62fd5..0000000000 --- a/src/Components/Server/src/WasmMediaTypeNames.cs +++ /dev/null @@ -1,22 +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. - -namespace Microsoft.AspNetCore.Components.Server -{ - /// - /// Contains values for WASM-related media types. - /// - public static class WasmMediaTypeNames - { - /// - /// Contains values for WASM-related media types within the "application." namespace - /// - public static class Application - { - /// - /// The standard media type name for WebAssembly binary files. - /// - public const string Wasm = "application/wasm"; - } - } -} diff --git a/src/Components/Server/test/Circuits/CircuitHostTest.cs b/src/Components/Server/test/Circuits/CircuitHostTest.cs index e5606f179a..8004c5cd08 100644 --- a/src/Components/Server/test/Circuits/CircuitHostTest.cs +++ b/src/Components/Server/test/Circuits/CircuitHostTest.cs @@ -273,7 +273,7 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits private class ThrowOnDisposeComponent : IComponent, IDisposable { public bool DidCallDispose { get; private set; } - public void Configure(RenderHandle renderHandle) { } + public void Attach(RenderHandle renderHandle) { } public Task SetParametersAsync(ParameterCollection parameters) => Task.CompletedTask; diff --git a/src/Components/Server/test/Circuits/CircuitPrerendererTest.cs b/src/Components/Server/test/Circuits/CircuitPrerendererTest.cs index 2633cf8069..2ecf8a7fab 100644 --- a/src/Components/Server/test/Circuits/CircuitPrerendererTest.cs +++ b/src/Components/Server/test/Circuits/CircuitPrerendererTest.cs @@ -221,7 +221,7 @@ namespace Microsoft.AspNetCore.Components.Server.Tests.Circuits [Inject] IUriHelper UriHelper { get; set; } - public void Configure(RenderHandle renderHandle) + public void Attach(RenderHandle renderHandle) { _renderHandle = renderHandle; } @@ -242,7 +242,7 @@ namespace Microsoft.AspNetCore.Components.Server.Tests.Circuits class ThrowExceptionComponent : IComponent { - public void Configure(RenderHandle renderHandle) + public void Attach(RenderHandle renderHandle) => throw new InvalidTimeZoneException(); public Task SetParametersAsync(ParameterCollection parameters) diff --git a/src/Components/Server/test/Circuits/RemoteRendererTest.cs b/src/Components/Server/test/Circuits/RemoteRendererTest.cs index a940902735..bfdd02fd1c 100644 --- a/src/Components/Server/test/Circuits/RemoteRendererTest.cs +++ b/src/Components/Server/test/Circuits/RemoteRendererTest.cs @@ -293,7 +293,7 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering public Action OnAfterRenderComplete { get; set; } - public void Configure(RenderHandle renderHandle) + public void Attach(RenderHandle renderHandle) { _renderHandle = renderHandle; } @@ -325,7 +325,7 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering [Parameter] public Trigger Trigger { get; set; } - public void Configure(RenderHandle renderHandle) + public void Attach(RenderHandle renderHandle) { _renderHandle = renderHandle; } diff --git a/src/Components/Server/test/Circuits/RenderBatchWriterTest.cs b/src/Components/Server/test/Circuits/RenderBatchWriterTest.cs index fba9b4de0d..3ccba9ff5a 100644 --- a/src/Components/Server/test/Circuits/RenderBatchWriterTest.cs +++ b/src/Components/Server/test/Circuits/RenderBatchWriterTest.cs @@ -76,7 +76,7 @@ namespace Microsoft.AspNetCore.Components.Server new ArrayRange(), new ArrayRange(), new ArrayRange(), - new ArrayRange(new[] { 123, int.MaxValue, int.MinValue, 456 }, 3) // Only use first 3 to show that param is respected + new ArrayRange(new ulong[] { 123, ulong.MaxValue, ulong.MinValue, 456 }, 3) // Only use first 3 to show that param is respected )); // Assert @@ -84,15 +84,15 @@ namespace Microsoft.AspNetCore.Components.Server 0, // Length of UpdatedComponents 0, // Length of ReferenceFrames 0, // Length of DisposedComponentIds - 3, 123, int.MaxValue, int.MinValue, // DisposedEventHandlerIds as length-prefixed array + 3, (ulong)123, ulong.MaxValue, ulong.MinValue, // DisposedEventHandlerIds as length-prefixed array 0, // Index of UpdatedComponents 4, // Index of ReferenceFrames 8, // Index of DisposedComponentIds 12, // Index of DisposedEventHandlerIds - 28 // Index of strings + 40 // Index of strings ); - Assert.Equal(48, bytes.Length); // No other data + Assert.Equal(60, bytes.Length); // No other data } [Fact] @@ -200,7 +200,7 @@ namespace Microsoft.AspNetCore.Components.Server RenderTreeFrame.Attribute(123, "Attribute with string value", "String value"), RenderTreeFrame.Attribute(124, "Attribute with nonstring value", 1), RenderTreeFrame.Attribute(125, "Attribute with delegate value", new Action(() => { })) - .WithAttributeEventHandlerId(789), + .WithAttributeEventHandlerId(((ulong)uint.MaxValue) + 1), RenderTreeFrame.ChildComponent(126, typeof(object)) .WithComponentSubtreeLength(5678) .WithComponent(new ComponentState(renderer, 2000, new FakeComponent(), null)), @@ -230,22 +230,22 @@ namespace Microsoft.AspNetCore.Components.Server var referenceFramesStartIndex = ReadInt(bytes, bytes.Length - 16); AssertBinaryContents(bytes, referenceFramesStartIndex, 16, // Number of frames - RenderTreeFrameType.Attribute, "Attribute with string value", "String value", 0, - RenderTreeFrameType.Attribute, "Attribute with nonstring value", NullStringMarker, 0, - RenderTreeFrameType.Attribute, "Attribute with delegate value", NullStringMarker, 789, - RenderTreeFrameType.Component, 5678, 2000, 0, - RenderTreeFrameType.ComponentReferenceCapture, 0, 0, 0, - RenderTreeFrameType.Element, 1234, "Some element", 0, - RenderTreeFrameType.ElementReferenceCapture, "my unique ID", 0, 0, - RenderTreeFrameType.Region, 1234, 0, 0, - RenderTreeFrameType.Text, "Some text", 0, 0, - RenderTreeFrameType.Markup, "Some markup", 0, 0, - RenderTreeFrameType.Text, "\n\t ", 0, 0, - RenderTreeFrameType.Attribute, "Attribute with string value", "String value", 0, - RenderTreeFrameType.Element, 999, "Some element", 0, - RenderTreeFrameType.Text, "Some text", 0, 0, - RenderTreeFrameType.Markup, "Some markup", 0, 0, - RenderTreeFrameType.Text, "\n\t ", 0, 0 + RenderTreeFrameType.Attribute, "Attribute with string value", "String value", 0, 0, + RenderTreeFrameType.Attribute, "Attribute with nonstring value", NullStringMarker, 0, 0, + RenderTreeFrameType.Attribute, "Attribute with delegate value", NullStringMarker, ((ulong)uint.MaxValue) + 1, + RenderTreeFrameType.Component, 5678, 2000, 0, 0, + RenderTreeFrameType.ComponentReferenceCapture, 0, 0, 0, 0, + RenderTreeFrameType.Element, 1234, "Some element", 0, 0, + RenderTreeFrameType.ElementReferenceCapture, "my unique ID", 0, 0, 0, + RenderTreeFrameType.Region, 1234, 0, 0, 0, + RenderTreeFrameType.Text, "Some text", 0, 0, 0, + RenderTreeFrameType.Markup, "Some markup", 0, 0, 0, + RenderTreeFrameType.Text, "\n\t ", 0, 0, 0, + RenderTreeFrameType.Attribute, "Attribute with string value", "String value", 0, 0, + RenderTreeFrameType.Element, 999, "Some element", 0, 0, + RenderTreeFrameType.Text, "Some text", 0, 0, 0, + RenderTreeFrameType.Markup, "Some markup", 0, 0, 0, + RenderTreeFrameType.Text, "\n\t ", 0, 0, 0 ); Assert.Equal(new[] @@ -313,13 +313,17 @@ namespace Microsoft.AspNetCore.Components.Server { // Assume enums are represented as ints var expectedEntry = expectedEntryIterationVar.GetType().IsEnum - ? (int)expectedEntryIterationVar + ? Convert.ToInt32(expectedEntryIterationVar) : expectedEntryIterationVar; if (expectedEntry is int expectedInt) { Assert.Equal(expectedInt, reader.ReadInt32()); } + else if (expectedEntry is ulong expectedUlong) + { + Assert.Equal(expectedUlong, reader.ReadUInt64()); + } else if (expectedEntry is string || expectedEntry == NullStringMarker) { // For strings, we have to look up the value in the table of strings @@ -366,7 +370,7 @@ namespace Microsoft.AspNetCore.Components.Server class FakeComponent : IComponent { - public void Configure(RenderHandle renderHandle) + public void Attach(RenderHandle renderHandle) => throw new NotImplementedException(); public Task SetParametersAsync(ParameterCollection parameters) diff --git a/src/Components/Shared/test/AutoRenderComponent.cs b/src/Components/Shared/test/AutoRenderComponent.cs index 4c426d0c2a..8ebe6034bb 100644 --- a/src/Components/Shared/test/AutoRenderComponent.cs +++ b/src/Components/Shared/test/AutoRenderComponent.cs @@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Components.Test.Helpers { private RenderHandle _renderHandle; - public void Configure(RenderHandle renderHandle) + public void Attach(RenderHandle renderHandle) { _renderHandle = renderHandle; } diff --git a/src/Components/Shared/test/IComponentExtensions.cs b/src/Components/Shared/test/IComponentExtensions.cs index d70d3017c8..34b7528548 100644 --- a/src/Components/Shared/test/IComponentExtensions.cs +++ b/src/Components/Shared/test/IComponentExtensions.cs @@ -33,7 +33,7 @@ namespace Microsoft.AspNetCore.Components.Test.Helpers private abstract class AbstractComponent : IComponent { - public abstract void Configure(RenderHandle renderHandle); + public abstract void Attach(RenderHandle renderHandle); public abstract Task SetParametersAsync(ParameterCollection parameters); } } diff --git a/src/Components/Shared/test/TestRenderer.cs b/src/Components/Shared/test/TestRenderer.cs index 85828b0b23..944be7ec87 100644 --- a/src/Components/Shared/test/TestRenderer.cs +++ b/src/Components/Shared/test/TestRenderer.cs @@ -60,10 +60,10 @@ namespace Microsoft.AspNetCore.Components.Test.Helpers public new Task RenderRootComponentAsync(int componentId, ParameterCollection parameters) => Dispatcher.InvokeAsync(() => base.RenderRootComponentAsync(componentId, parameters)); - public Task DispatchEventAsync(int eventHandlerId, UIEventArgs args) + public Task DispatchEventAsync(ulong eventHandlerId, UIEventArgs args) => Dispatcher.InvokeAsync(() => base.DispatchEventAsync(eventHandlerId, null, args)); - public new Task DispatchEventAsync(int eventHandlerId, EventFieldInfo eventFieldInfo, UIEventArgs args) + public new Task DispatchEventAsync(ulong eventHandlerId, EventFieldInfo eventFieldInfo, UIEventArgs args) => Dispatcher.InvokeAsync(() => base.DispatchEventAsync(eventHandlerId, eventFieldInfo, args)); private static Task UnwrapTask(Task task) diff --git a/src/Components/Web.JS/dist/Release/blazor.server.js b/src/Components/Web.JS/dist/Release/blazor.server.js index e4ff73a3e3..215af6db47 100644 --- a/src/Components/Web.JS/dist/Release/blazor.server.js +++ b/src/Components/Web.JS/dist/Release/blazor.server.js @@ -1,15 +1,15 @@ -!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=48)}([function(e,t,n){"use strict";var r;n.d(t,"a",function(){return r}),function(e){e[e.Trace=0]="Trace",e[e.Debug=1]="Debug",e[e.Information=2]="Information",e[e.Warning=3]="Warning",e[e.Error=4]="Error",e[e.Critical=5]="Critical",e[e.None=6]="None"}(r||(r={}))},function(e,t,n){"use strict";n.d(t,"a",function(){return s}),n.d(t,"c",function(){return c}),n.d(t,"f",function(){return u}),n.d(t,"g",function(){return l}),n.d(t,"h",function(){return f}),n.d(t,"e",function(){return h}),n.d(t,"d",function(){return p}),n.d(t,"b",function(){return d});var r=n(0),o=n(6),i=function(e,t,n,r){return new(n||(n=Promise))(function(o,i){function a(e){try{c(r.next(e))}catch(e){i(e)}}function s(e){try{c(r.throw(e))}catch(e){i(e)}}function c(e){e.done?o(e.value):new n(function(t){t(e.value)}).then(a,s)}c((r=r.apply(e,t||[])).next())})},a=function(e,t){var n,r,o,i,a={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function s(i){return function(s){return function(i){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,r&&(o=2&i[0]?r.return:i[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,i[1])).done)return o;switch(r=0,o&&(i=[2&i[0],o.value]),i[0]){case 0:case 1:o=i;break;case 4:return a.label++,{value:i[1],done:!1};case 5:a.label++,r=i[1],i=[0];continue;case 7:i=a.ops.pop(),a.trys.pop();continue;default:if(!(o=(o=a.trys).length>0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]-1&&this.subject.observers.splice(e,1),0===this.subject.observers.length&&this.subject.cancelCallback&&this.subject.cancelCallback().catch(function(e){})},e}(),d=function(){function e(e){this.minimumLogLevel=e,this.outputConsole=console}return e.prototype.log=function(e,t){if(e>=this.minimumLogLevel)switch(e){case r.a.Critical:case r.a.Error:this.outputConsole.error("["+(new Date).toISOString()+"] "+r.a[e]+": "+t);break;case r.a.Warning:this.outputConsole.warn("["+(new Date).toISOString()+"] "+r.a[e]+": "+t);break;case r.a.Information:this.outputConsole.info("["+(new Date).toISOString()+"] "+r.a[e]+": "+t);break;default:this.outputConsole.log("["+(new Date).toISOString()+"] "+r.a[e]+": "+t)}},e}()},function(e,t,n){"use strict";n.r(t);var r,o,i=n(3),a=n(4),s=n(42),c=n(0),u=(r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])},function(e,t){function n(){this.constructor=e}r(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),l=function(e){function t(t){var n=e.call(this)||this;return n.logger=t,n}return u(t,e),t.prototype.send=function(e){var t=this;return e.abortSignal&&e.abortSignal.aborted?Promise.reject(new i.a):e.method?e.url?new Promise(function(n,r){var o=new XMLHttpRequest;o.open(e.method,e.url,!0),o.withCredentials=!0,o.setRequestHeader("X-Requested-With","XMLHttpRequest"),o.setRequestHeader("Content-Type","text/plain;charset=UTF-8");var s=e.headers;s&&Object.keys(s).forEach(function(e){o.setRequestHeader(e,s[e])}),e.responseType&&(o.responseType=e.responseType),e.abortSignal&&(e.abortSignal.onabort=function(){o.abort(),r(new i.a)}),e.timeout&&(o.timeout=e.timeout),o.onload=function(){e.abortSignal&&(e.abortSignal.onabort=null),o.status>=200&&o.status<300?n(new a.b(o.status,o.statusText,o.response||o.responseText)):r(new i.b(o.statusText,o.status))},o.onerror=function(){t.logger.log(c.a.Warning,"Error from HTTP request. "+o.status+": "+o.statusText+"."),r(new i.b(o.statusText,o.status))},o.ontimeout=function(){t.logger.log(c.a.Warning,"Timeout from HTTP request."),r(new i.c)},o.send(e.content||"")}):Promise.reject(new Error("No url defined.")):Promise.reject(new Error("No method defined."))},t}(a.a),f=function(){var e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])};return function(t,n){function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}(),h=function(e){function t(t){var n=e.call(this)||this;return"undefined"!=typeof XMLHttpRequest?n.httpClient=new l(t):n.httpClient=new s.a(t),n}return f(t,e),t.prototype.send=function(e){return e.abortSignal&&e.abortSignal.aborted?Promise.reject(new i.a):e.method?e.url?this.httpClient.send(e):Promise.reject(new Error("No url defined.")):Promise.reject(new Error("No method defined."))},t.prototype.getCookieString=function(e){return this.httpClient.getCookieString(e)},t}(a.a),p=n(43);!function(e){e[e.Invocation=1]="Invocation",e[e.StreamItem=2]="StreamItem",e[e.Completion=3]="Completion",e[e.StreamInvocation=4]="StreamInvocation",e[e.CancelInvocation=5]="CancelInvocation",e[e.Ping=6]="Ping",e[e.Close=7]="Close"}(o||(o={}));var d,g=n(1),y=function(){function e(){this.observers=[]}return e.prototype.next=function(e){for(var t=0,n=this.observers;t0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]0?[2,Promise.reject(new Error("Unable to connect to the server with any of the available transports. "+i.join(" ")))]:[2,Promise.reject(new Error("None of the transports supported by the client are supported by the server."))]}})})},e.prototype.constructTransport=function(e){switch(e){case E.WebSockets:if(!this.options.WebSocket)throw new Error("'WebSocket' is not supported in your environment.");return new A(this.httpClient,this.accessTokenFactory,this.logger,this.options.logMessageContent||!1,this.options.WebSocket);case E.ServerSentEvents:if(!this.options.EventSource)throw new Error("'EventSource' is not supported in your environment.");return new O(this.httpClient,this.accessTokenFactory,this.logger,this.options.logMessageContent||!1,this.options.EventSource);case E.LongPolling:return new P(this.httpClient,this.accessTokenFactory,this.logger,this.options.logMessageContent||!1);default:throw new Error("Unknown transport: "+e+".")}},e.prototype.resolveTransportOrError=function(e,t,n){var r=E[e.transport];if(null==r)return this.logger.log(c.a.Debug,"Skipping transport '"+e.transport+"' because it is not supported by this client."),new Error("Skipping transport '"+e.transport+"' because it is not supported by this client.");if(!function(e,t){return!e||0!=(t&e)}(t,r))return this.logger.log(c.a.Debug,"Skipping transport '"+E[r]+"' because it was disabled by the client."),new Error("'"+E[r]+"' is disabled by the client.");if(!(e.transferFormats.map(function(e){return S[e]}).indexOf(n)>=0))return this.logger.log(c.a.Debug,"Skipping transport '"+E[r]+"' because it does not support the requested transfer format '"+S[n]+"'."),new Error("'"+E[r]+"' does not support "+S[n]+".");if(r===E.WebSockets&&!this.options.WebSocket||r===E.ServerSentEvents&&!this.options.EventSource)return this.logger.log(c.a.Debug,"Skipping transport '"+E[r]+"' because it is not supported in your environment.'"),new Error("'"+E[r]+"' is not supported in your environment.");this.logger.log(c.a.Debug,"Selecting transport '"+E[r]+"'.");try{return this.constructTransport(r)}catch(e){return e}},e.prototype.isITransport=function(e){return e&&"object"==typeof e&&"connect"in e},e.prototype.stopConnection=function(e){if(this.logger.log(c.a.Debug,"HttpConnection.stopConnection("+e+") called while in state "+this.connectionState+"."),this.transport=void 0,e=this.stopError||e,this.stopError=void 0,"Disconnected"!==this.connectionState)if("Connecting "!==this.connectionState){if("Disconnecting"===this.connectionState&&this.stopPromiseResolver(),e?this.logger.log(c.a.Error,"Connection disconnected with error '"+e+"'."):this.logger.log(c.a.Information,"Connection disconnected."),this.connectionId=void 0,this.connectionState="Disconnected",this.onclose&&this.connectionStarted){this.connectionStarted=!1;try{this.onclose(e)}catch(t){this.logger.log(c.a.Error,"HttpConnection.onclose("+e+") threw error '"+t+"'.")}}}else this.logger.log(c.a.Warning,"Call to HttpConnection.stopConnection("+e+") was ignored because the connection hasn't yet left the in the connecting state.");else this.logger.log(c.a.Debug,"Call to HttpConnection.stopConnection("+e+") was ignored because the connection is already in the disconnected state.")},e.prototype.resolveUrl=function(e){if(0===e.lastIndexOf("https://",0)||0===e.lastIndexOf("http://",0))return e;if(!g.c.isBrowser||!window.document)throw new Error("Cannot resolve '"+e+"'.");var t=window.document.createElement("a");return t.href=e,this.logger.log(c.a.Information,"Normalizing '"+e+"' to '"+t.href+"'."),t.href},e.prototype.resolveNegotiateUrl=function(e){var t=e.indexOf("?"),n=e.substring(0,-1===t?e.length:t);return"/"!==n[n.length-1]&&(n+="/"),n+="negotiate",n+=-1===t?"":e.substring(t)},e}();var W=function(){function e(e){this.transport=e,this.buffer=[],this.executing=!0,this.sendBufferedData=new q,this.transportResult=new q,this.sendLoopPromise=this.sendLoop()}return e.prototype.send=function(e){return this.bufferData(e),this.transportResult||(this.transportResult=new q),this.transportResult.promise},e.prototype.stop=function(){return this.executing=!1,this.sendBufferedData.resolve(),this.sendLoopPromise},e.prototype.bufferData=function(e){if(this.buffer.length&&typeof this.buffer[0]!=typeof e)throw new Error("Expected data to be of type "+typeof this.buffer+" but was of type "+typeof e);this.buffer.push(e),this.sendBufferedData.resolve()},e.prototype.sendLoop=function(){return B(this,void 0,void 0,function(){var t,n,r;return j(this,function(o){switch(o.label){case 0:return[4,this.sendBufferedData.promise];case 1:if(o.sent(),!this.executing)return this.transportResult&&this.transportResult.reject("Connection stopped."),[3,6];this.sendBufferedData=new q,t=this.transportResult,this.transportResult=void 0,n="string"==typeof this.buffer[0]?this.buffer.join(""):e.concatBuffers(this.buffer),this.buffer.length=0,o.label=2;case 2:return o.trys.push([2,4,,5]),[4,this.transport.send(n)];case 3:return o.sent(),t.resolve(),[3,5];case 4:return r=o.sent(),t.reject(r),[3,5];case 5:return[3,0];case 6:return[2]}})})},e.concatBuffers=function(e){for(var t=e.map(function(e){return e.byteLength}).reduce(function(e,t){return e+t}),n=new Uint8Array(t),r=0,o=0,i=e;o0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]-1&&this.subject.observers.splice(e,1),0===this.subject.observers.length&&this.subject.cancelCallback&&this.subject.cancelCallback().catch(function(e){})},e}(),d=function(){function e(e){this.minimumLogLevel=e,this.outputConsole=console}return e.prototype.log=function(e,t){if(e>=this.minimumLogLevel)switch(e){case r.a.Critical:case r.a.Error:this.outputConsole.error("["+(new Date).toISOString()+"] "+r.a[e]+": "+t);break;case r.a.Warning:this.outputConsole.warn("["+(new Date).toISOString()+"] "+r.a[e]+": "+t);break;case r.a.Information:this.outputConsole.info("["+(new Date).toISOString()+"] "+r.a[e]+": "+t);break;default:this.outputConsole.log("["+(new Date).toISOString()+"] "+r.a[e]+": "+t)}},e}()},function(e,t,n){"use strict";n.r(t);var r,o,i=n(3),a=n(4),s=n(42),c=n(0),u=(r=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])},function(e,t){function n(){this.constructor=e}r(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),l=function(e){function t(t){var n=e.call(this)||this;return n.logger=t,n}return u(t,e),t.prototype.send=function(e){var t=this;return e.abortSignal&&e.abortSignal.aborted?Promise.reject(new i.a):e.method?e.url?new Promise(function(n,r){var o=new XMLHttpRequest;o.open(e.method,e.url,!0),o.withCredentials=!0,o.setRequestHeader("X-Requested-With","XMLHttpRequest"),o.setRequestHeader("Content-Type","text/plain;charset=UTF-8");var s=e.headers;s&&Object.keys(s).forEach(function(e){o.setRequestHeader(e,s[e])}),e.responseType&&(o.responseType=e.responseType),e.abortSignal&&(e.abortSignal.onabort=function(){o.abort(),r(new i.a)}),e.timeout&&(o.timeout=e.timeout),o.onload=function(){e.abortSignal&&(e.abortSignal.onabort=null),o.status>=200&&o.status<300?n(new a.b(o.status,o.statusText,o.response||o.responseText)):r(new i.b(o.statusText,o.status))},o.onerror=function(){t.logger.log(c.a.Warning,"Error from HTTP request. "+o.status+": "+o.statusText+"."),r(new i.b(o.statusText,o.status))},o.ontimeout=function(){t.logger.log(c.a.Warning,"Timeout from HTTP request."),r(new i.c)},o.send(e.content||"")}):Promise.reject(new Error("No url defined.")):Promise.reject(new Error("No method defined."))},t}(a.a),f=function(){var e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])};return function(t,n){function r(){this.constructor=t}e(t,n),t.prototype=null===n?Object.create(n):(r.prototype=n.prototype,new r)}}(),h=function(e){function t(t){var n=e.call(this)||this;return"undefined"!=typeof XMLHttpRequest?n.httpClient=new l(t):n.httpClient=new s.a(t),n}return f(t,e),t.prototype.send=function(e){return e.abortSignal&&e.abortSignal.aborted?Promise.reject(new i.a):e.method?e.url?this.httpClient.send(e):Promise.reject(new Error("No url defined.")):Promise.reject(new Error("No method defined."))},t.prototype.getCookieString=function(e){return this.httpClient.getCookieString(e)},t}(a.a),p=n(43);!function(e){e[e.Invocation=1]="Invocation",e[e.StreamItem=2]="StreamItem",e[e.Completion=3]="Completion",e[e.StreamInvocation=4]="StreamInvocation",e[e.CancelInvocation=5]="CancelInvocation",e[e.Ping=6]="Ping",e[e.Close=7]="Close"}(o||(o={}));var d,g=n(1),y=function(){function e(){this.observers=[]}return e.prototype.next=function(e){for(var t=0,n=this.observers;t0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]0?[2,Promise.reject(new Error("Unable to connect to the server with any of the available transports. "+i.join(" ")))]:[2,Promise.reject(new Error("None of the transports supported by the client are supported by the server."))]}})})},e.prototype.constructTransport=function(e){switch(e){case E.WebSockets:if(!this.options.WebSocket)throw new Error("'WebSocket' is not supported in your environment.");return new A(this.httpClient,this.accessTokenFactory,this.logger,this.options.logMessageContent||!1,this.options.WebSocket);case E.ServerSentEvents:if(!this.options.EventSource)throw new Error("'EventSource' is not supported in your environment.");return new O(this.httpClient,this.accessTokenFactory,this.logger,this.options.logMessageContent||!1,this.options.EventSource);case E.LongPolling:return new P(this.httpClient,this.accessTokenFactory,this.logger,this.options.logMessageContent||!1);default:throw new Error("Unknown transport: "+e+".")}},e.prototype.resolveTransportOrError=function(e,t,n){var r=E[e.transport];if(null==r)return this.logger.log(c.a.Debug,"Skipping transport '"+e.transport+"' because it is not supported by this client."),new Error("Skipping transport '"+e.transport+"' because it is not supported by this client.");if(!function(e,t){return!e||0!=(t&e)}(t,r))return this.logger.log(c.a.Debug,"Skipping transport '"+E[r]+"' because it was disabled by the client."),new Error("'"+E[r]+"' is disabled by the client.");if(!(e.transferFormats.map(function(e){return S[e]}).indexOf(n)>=0))return this.logger.log(c.a.Debug,"Skipping transport '"+E[r]+"' because it does not support the requested transfer format '"+S[n]+"'."),new Error("'"+E[r]+"' does not support "+S[n]+".");if(r===E.WebSockets&&!this.options.WebSocket||r===E.ServerSentEvents&&!this.options.EventSource)return this.logger.log(c.a.Debug,"Skipping transport '"+E[r]+"' because it is not supported in your environment.'"),new Error("'"+E[r]+"' is not supported in your environment.");this.logger.log(c.a.Debug,"Selecting transport '"+E[r]+"'.");try{return this.constructTransport(r)}catch(e){return e}},e.prototype.isITransport=function(e){return e&&"object"==typeof e&&"connect"in e},e.prototype.stopConnection=function(e){if(this.logger.log(c.a.Debug,"HttpConnection.stopConnection("+e+") called while in state "+this.connectionState+"."),this.transport=void 0,e=this.stopError||e,this.stopError=void 0,"Disconnected"!==this.connectionState)if("Connecting "!==this.connectionState){if("Disconnecting"===this.connectionState&&this.stopPromiseResolver(),e?this.logger.log(c.a.Error,"Connection disconnected with error '"+e+"'."):this.logger.log(c.a.Information,"Connection disconnected."),this.connectionId=void 0,this.connectionState="Disconnected",this.onclose&&this.connectionStarted){this.connectionStarted=!1;try{this.onclose(e)}catch(t){this.logger.log(c.a.Error,"HttpConnection.onclose("+e+") threw error '"+t+"'.")}}}else this.logger.log(c.a.Warning,"Call to HttpConnection.stopConnection("+e+") was ignored because the connection hasn't yet left the in the connecting state.");else this.logger.log(c.a.Debug,"Call to HttpConnection.stopConnection("+e+") was ignored because the connection is already in the disconnected state.")},e.prototype.resolveUrl=function(e){if(0===e.lastIndexOf("https://",0)||0===e.lastIndexOf("http://",0))return e;if(!g.c.isBrowser||!window.document)throw new Error("Cannot resolve '"+e+"'.");var t=window.document.createElement("a");return t.href=e,this.logger.log(c.a.Information,"Normalizing '"+e+"' to '"+t.href+"'."),t.href},e.prototype.resolveNegotiateUrl=function(e){var t=e.indexOf("?"),n=e.substring(0,-1===t?e.length:t);return"/"!==n[n.length-1]&&(n+="/"),n+="negotiate",n+=-1===t?"":e.substring(t)},e}();var W=function(){function e(e){this.transport=e,this.buffer=[],this.executing=!0,this.sendBufferedData=new q,this.transportResult=new q,this.sendLoopPromise=this.sendLoop()}return e.prototype.send=function(e){return this.bufferData(e),this.transportResult||(this.transportResult=new q),this.transportResult.promise},e.prototype.stop=function(){return this.executing=!1,this.sendBufferedData.resolve(),this.sendLoopPromise},e.prototype.bufferData=function(e){if(this.buffer.length&&typeof this.buffer[0]!=typeof e)throw new Error("Expected data to be of type "+typeof this.buffer+" but was of type "+typeof e);this.buffer.push(e),this.sendBufferedData.resolve()},e.prototype.sendLoop=function(){return B(this,void 0,void 0,function(){var t,n,r;return j(this,function(o){switch(o.label){case 0:return[4,this.sendBufferedData.promise];case 1:if(o.sent(),!this.executing)return this.transportResult&&this.transportResult.reject("Connection stopped."),[3,6];this.sendBufferedData=new q,t=this.transportResult,this.transportResult=void 0,n="string"==typeof this.buffer[0]?this.buffer.join(""):e.concatBuffers(this.buffer),this.buffer.length=0,o.label=2;case 2:return o.trys.push([2,4,,5]),[4,this.transport.send(n)];case 3:return o.sent(),t.resolve(),[3,5];case 4:return r=o.sent(),t.reject(r),[3,5];case 5:return[3,0];case 6:return[2]}})})},e.concatBuffers=function(e){for(var t=e.map(function(e){return e.byteLength}).reduce(function(e,t){return e+t}),n=new Uint8Array(t),r=0,o=0,i=e;o * @license MIT */ -var r=n(49),o=n(50),i=n(51);function a(){return c.TYPED_ARRAY_SUPPORT?2147483647:1073741823}function s(e,t){if(a()=a())throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+a().toString(16)+" bytes");return 0|e}function d(e,t){if(c.isBuffer(e))return e.length;if("undefined"!=typeof ArrayBuffer&&"function"==typeof ArrayBuffer.isView&&(ArrayBuffer.isView(e)||e instanceof ArrayBuffer))return e.byteLength;"string"!=typeof e&&(e=""+e);var n=e.length;if(0===n)return 0;for(var r=!1;;)switch(t){case"ascii":case"latin1":case"binary":return n;case"utf8":case"utf-8":case void 0:return F(e).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*n;case"hex":return n>>>1;case"base64":return H(e).length;default:if(r)return F(e).length;t=(""+t).toLowerCase(),r=!0}}function g(e,t,n){var r=e[t];e[t]=e[n],e[n]=r}function y(e,t,n,r,o){if(0===e.length)return-1;if("string"==typeof n?(r=n,n=0):n>2147483647?n=2147483647:n<-2147483648&&(n=-2147483648),n=+n,isNaN(n)&&(n=o?0:e.length-1),n<0&&(n=e.length+n),n>=e.length){if(o)return-1;n=e.length-1}else if(n<0){if(!o)return-1;n=0}if("string"==typeof t&&(t=c.from(t,r)),c.isBuffer(t))return 0===t.length?-1:v(e,t,n,r,o);if("number"==typeof t)return t&=255,c.TYPED_ARRAY_SUPPORT&&"function"==typeof Uint8Array.prototype.indexOf?o?Uint8Array.prototype.indexOf.call(e,t,n):Uint8Array.prototype.lastIndexOf.call(e,t,n):v(e,[t],n,r,o);throw new TypeError("val must be string, number or Buffer")}function v(e,t,n,r,o){var i,a=1,s=e.length,c=t.length;if(void 0!==r&&("ucs2"===(r=String(r).toLowerCase())||"ucs-2"===r||"utf16le"===r||"utf-16le"===r)){if(e.length<2||t.length<2)return-1;a=2,s/=2,c/=2,n/=2}function u(e,t){return 1===a?e[t]:e.readUInt16BE(t*a)}if(o){var l=-1;for(i=n;is&&(n=s-c),i=n;i>=0;i--){for(var f=!0,h=0;ho&&(r=o):r=o;var i=t.length;if(i%2!=0)throw new TypeError("Invalid hex string");r>i/2&&(r=i/2);for(var a=0;a>8,o=n%256,i.push(o),i.push(r);return i}(t,e.length-n),e,n,r)}function I(e,t,n){return 0===t&&n===e.length?r.fromByteArray(e):r.fromByteArray(e.slice(t,n))}function _(e,t,n){n=Math.min(e.length,n);for(var r=[],o=t;o239?4:u>223?3:u>191?2:1;if(o+f<=n)switch(f){case 1:u<128&&(l=u);break;case 2:128==(192&(i=e[o+1]))&&(c=(31&u)<<6|63&i)>127&&(l=c);break;case 3:i=e[o+1],a=e[o+2],128==(192&i)&&128==(192&a)&&(c=(15&u)<<12|(63&i)<<6|63&a)>2047&&(c<55296||c>57343)&&(l=c);break;case 4:i=e[o+1],a=e[o+2],s=e[o+3],128==(192&i)&&128==(192&a)&&128==(192&s)&&(c=(15&u)<<18|(63&i)<<12|(63&a)<<6|63&s)>65535&&c<1114112&&(l=c)}null===l?(l=65533,f=1):l>65535&&(l-=65536,r.push(l>>>10&1023|55296),l=56320|1023&l),r.push(l),o+=f}return function(e){var t=e.length;if(t<=T)return String.fromCharCode.apply(String,e);var n="",r=0;for(;rthis.length)return"";if((void 0===n||n>this.length)&&(n=this.length),n<=0)return"";if((n>>>=0)<=(t>>>=0))return"";for(e||(e="utf8");;)switch(e){case"hex":return P(this,t,n);case"utf8":case"utf-8":return _(this,t,n);case"ascii":return k(this,t,n);case"latin1":case"binary":return x(this,t,n);case"base64":return I(this,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return R(this,t,n);default:if(r)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),r=!0}}.apply(this,arguments)},c.prototype.equals=function(e){if(!c.isBuffer(e))throw new TypeError("Argument must be a Buffer");return this===e||0===c.compare(this,e)},c.prototype.inspect=function(){var e="",n=t.INSPECT_MAX_BYTES;return this.length>0&&(e=this.toString("hex",0,n).match(/.{2}/g).join(" "),this.length>n&&(e+=" ... ")),""},c.prototype.compare=function(e,t,n,r,o){if(!c.isBuffer(e))throw new TypeError("Argument must be a Buffer");if(void 0===t&&(t=0),void 0===n&&(n=e?e.length:0),void 0===r&&(r=0),void 0===o&&(o=this.length),t<0||n>e.length||r<0||o>this.length)throw new RangeError("out of range index");if(r>=o&&t>=n)return 0;if(r>=o)return-1;if(t>=n)return 1;if(this===e)return 0;for(var i=(o>>>=0)-(r>>>=0),a=(n>>>=0)-(t>>>=0),s=Math.min(i,a),u=this.slice(r,o),l=e.slice(t,n),f=0;fo)&&(n=o),e.length>0&&(n<0||t<0)||t>this.length)throw new RangeError("Attempt to write outside buffer bounds");r||(r="utf8");for(var i=!1;;)switch(r){case"hex":return b(this,e,t,n);case"utf8":case"utf-8":return m(this,e,t,n);case"ascii":return w(this,e,t,n);case"latin1":case"binary":return E(this,e,t,n);case"base64":return S(this,e,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return C(this,e,t,n);default:if(i)throw new TypeError("Unknown encoding: "+r);r=(""+r).toLowerCase(),i=!0}},c.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};var T=4096;function k(e,t,n){var r="";n=Math.min(e.length,n);for(var o=t;or)&&(n=r);for(var o="",i=t;in)throw new RangeError("Trying to access beyond buffer length")}function O(e,t,n,r,o,i){if(!c.isBuffer(e))throw new TypeError('"buffer" argument must be a Buffer instance');if(t>o||te.length)throw new RangeError("Index out of range")}function M(e,t,n,r){t<0&&(t=65535+t+1);for(var o=0,i=Math.min(e.length-n,2);o>>8*(r?o:1-o)}function L(e,t,n,r){t<0&&(t=4294967295+t+1);for(var o=0,i=Math.min(e.length-n,4);o>>8*(r?o:3-o)&255}function A(e,t,n,r,o,i){if(n+r>e.length)throw new RangeError("Index out of range");if(n<0)throw new RangeError("Index out of range")}function B(e,t,n,r,i){return i||A(e,0,n,4),o.write(e,t,n,r,23,4),n+4}function j(e,t,n,r,i){return i||A(e,0,n,8),o.write(e,t,n,r,52,8),n+8}c.prototype.slice=function(e,t){var n,r=this.length;if((e=~~e)<0?(e+=r)<0&&(e=0):e>r&&(e=r),(t=void 0===t?r:~~t)<0?(t+=r)<0&&(t=0):t>r&&(t=r),t0&&(o*=256);)r+=this[e+--t]*o;return r},c.prototype.readUInt8=function(e,t){return t||D(e,1,this.length),this[e]},c.prototype.readUInt16LE=function(e,t){return t||D(e,2,this.length),this[e]|this[e+1]<<8},c.prototype.readUInt16BE=function(e,t){return t||D(e,2,this.length),this[e]<<8|this[e+1]},c.prototype.readUInt32LE=function(e,t){return t||D(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},c.prototype.readUInt32BE=function(e,t){return t||D(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},c.prototype.readIntLE=function(e,t,n){e|=0,t|=0,n||D(e,t,this.length);for(var r=this[e],o=1,i=0;++i=(o*=128)&&(r-=Math.pow(2,8*t)),r},c.prototype.readIntBE=function(e,t,n){e|=0,t|=0,n||D(e,t,this.length);for(var r=t,o=1,i=this[e+--r];r>0&&(o*=256);)i+=this[e+--r]*o;return i>=(o*=128)&&(i-=Math.pow(2,8*t)),i},c.prototype.readInt8=function(e,t){return t||D(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},c.prototype.readInt16LE=function(e,t){t||D(e,2,this.length);var n=this[e]|this[e+1]<<8;return 32768&n?4294901760|n:n},c.prototype.readInt16BE=function(e,t){t||D(e,2,this.length);var n=this[e+1]|this[e]<<8;return 32768&n?4294901760|n:n},c.prototype.readInt32LE=function(e,t){return t||D(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},c.prototype.readInt32BE=function(e,t){return t||D(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},c.prototype.readFloatLE=function(e,t){return t||D(e,4,this.length),o.read(this,e,!0,23,4)},c.prototype.readFloatBE=function(e,t){return t||D(e,4,this.length),o.read(this,e,!1,23,4)},c.prototype.readDoubleLE=function(e,t){return t||D(e,8,this.length),o.read(this,e,!0,52,8)},c.prototype.readDoubleBE=function(e,t){return t||D(e,8,this.length),o.read(this,e,!1,52,8)},c.prototype.writeUIntLE=function(e,t,n,r){(e=+e,t|=0,n|=0,r)||O(this,e,t,n,Math.pow(2,8*n)-1,0);var o=1,i=0;for(this[t]=255&e;++i=0&&(i*=256);)this[t+o]=e/i&255;return t+n},c.prototype.writeUInt8=function(e,t,n){return e=+e,t|=0,n||O(this,e,t,1,255,0),c.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),this[t]=255&e,t+1},c.prototype.writeUInt16LE=function(e,t,n){return e=+e,t|=0,n||O(this,e,t,2,65535,0),c.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):M(this,e,t,!0),t+2},c.prototype.writeUInt16BE=function(e,t,n){return e=+e,t|=0,n||O(this,e,t,2,65535,0),c.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):M(this,e,t,!1),t+2},c.prototype.writeUInt32LE=function(e,t,n){return e=+e,t|=0,n||O(this,e,t,4,4294967295,0),c.TYPED_ARRAY_SUPPORT?(this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=255&e):L(this,e,t,!0),t+4},c.prototype.writeUInt32BE=function(e,t,n){return e=+e,t|=0,n||O(this,e,t,4,4294967295,0),c.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):L(this,e,t,!1),t+4},c.prototype.writeIntLE=function(e,t,n,r){if(e=+e,t|=0,!r){var o=Math.pow(2,8*n-1);O(this,e,t,n,o-1,-o)}var i=0,a=1,s=0;for(this[t]=255&e;++i>0)-s&255;return t+n},c.prototype.writeIntBE=function(e,t,n,r){if(e=+e,t|=0,!r){var o=Math.pow(2,8*n-1);O(this,e,t,n,o-1,-o)}var i=n-1,a=1,s=0;for(this[t+i]=255&e;--i>=0&&(a*=256);)e<0&&0===s&&0!==this[t+i+1]&&(s=1),this[t+i]=(e/a>>0)-s&255;return t+n},c.prototype.writeInt8=function(e,t,n){return e=+e,t|=0,n||O(this,e,t,1,127,-128),c.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),e<0&&(e=255+e+1),this[t]=255&e,t+1},c.prototype.writeInt16LE=function(e,t,n){return e=+e,t|=0,n||O(this,e,t,2,32767,-32768),c.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):M(this,e,t,!0),t+2},c.prototype.writeInt16BE=function(e,t,n){return e=+e,t|=0,n||O(this,e,t,2,32767,-32768),c.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):M(this,e,t,!1),t+2},c.prototype.writeInt32LE=function(e,t,n){return e=+e,t|=0,n||O(this,e,t,4,2147483647,-2147483648),c.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24):L(this,e,t,!0),t+4},c.prototype.writeInt32BE=function(e,t,n){return e=+e,t|=0,n||O(this,e,t,4,2147483647,-2147483648),e<0&&(e=4294967295+e+1),c.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):L(this,e,t,!1),t+4},c.prototype.writeFloatLE=function(e,t,n){return B(this,e,t,!0,n)},c.prototype.writeFloatBE=function(e,t,n){return B(this,e,t,!1,n)},c.prototype.writeDoubleLE=function(e,t,n){return j(this,e,t,!0,n)},c.prototype.writeDoubleBE=function(e,t,n){return j(this,e,t,!1,n)},c.prototype.copy=function(e,t,n,r){if(n||(n=0),r||0===r||(r=this.length),t>=e.length&&(t=e.length),t||(t=0),r>0&&r=this.length)throw new RangeError("sourceStart out of bounds");if(r<0)throw new RangeError("sourceEnd out of bounds");r>this.length&&(r=this.length),e.length-t=0;--o)e[o+t]=this[o+n];else if(i<1e3||!c.TYPED_ARRAY_SUPPORT)for(o=0;o>>=0,n=void 0===n?this.length:n>>>0,e||(e=0),"number"==typeof e)for(i=t;i55295&&n<57344){if(!o){if(n>56319){(t-=3)>-1&&i.push(239,191,189);continue}if(a+1===r){(t-=3)>-1&&i.push(239,191,189);continue}o=n;continue}if(n<56320){(t-=3)>-1&&i.push(239,191,189),o=n;continue}n=65536+(o-55296<<10|n-56320)}else o&&(t-=3)>-1&&i.push(239,191,189);if(o=null,n<128){if((t-=1)<0)break;i.push(n)}else if(n<2048){if((t-=2)<0)break;i.push(n>>6|192,63&n|128)}else if(n<65536){if((t-=3)<0)break;i.push(n>>12|224,n>>6&63|128,63&n|128)}else{if(!(n<1114112))throw new Error("Invalid code point");if((t-=4)<0)break;i.push(n>>18|240,n>>12&63|128,n>>6&63|128,63&n|128)}}return i}function H(e){return r.toByteArray(function(e){if((e=function(e){return e.trim?e.trim():e.replace(/^\s+|\s+$/g,"")}(e).replace(U,"")).length<2)return"";for(;e.length%4!=0;)e+="=";return e}(e))}function W(e,t,n,r){for(var o=0;o=t.length||o>=e.length);++o)t[o+n]=e[o];return o}}).call(this,n(8))},function(e,t,n){"use strict";n.d(t,"a",function(){return r});var r=function(){function e(){}return e.prototype.log=function(e,t){},e.instance=new e,e}()},function(e,t,n){"use strict";n.d(t,"a",function(){return r});var r=function(){function e(){}return e.write=function(t){return""+t+e.RecordSeparator},e.parse=function(t){if(t[t.length-1]!==e.RecordSeparator)throw new Error("Message is incomplete.");var n=t.split(e.RecordSeparator);return n.pop(),n},e.RecordSeparatorCode=30,e.RecordSeparator=String.fromCharCode(e.RecordSeparatorCode),e}()},function(e,t){var n;n=function(){return this}();try{n=n||new Function("return this")()}catch(e){"object"==typeof window&&(n=window)}e.exports=n},function(e,t,n){"use strict";var r=n(21),o=Object.keys||function(e){var t=[];for(var n in e)t.push(n);return t};e.exports=f;var i=n(18);i.inherits=n(14);var a=n(34),s=n(39);i.inherits(f,a);for(var c=o(s.prototype),u=0;u=0,"must have a non-negative type"),o(a,"must have a decode function"),this.registerEncoder(function(e){return e instanceof t},function(t){var o=i(),a=r.allocUnsafe(1);return a.writeInt8(e,0),o.append(a),o.append(n(t)),o}),this.registerDecoder(e,a),this},registerEncoder:function(e,n){return o(e,"must have an encode function"),o(n,"must have an encode function"),t.push({check:e,encode:n}),this},registerDecoder:function(e,t){return o(e>=0,"must have a non-negative type"),o(t,"must have a decode function"),n.push({type:e,decode:t}),this},encoder:a.encoder,decoder:a.decoder,buffer:!0,type:"msgpack5",IncompleteBufferError:s.IncompleteBufferError}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(25),o=n(12),i={};function a(e,t,n){var o=i[e];o||(o=i[e]=new r.BrowserRenderer(e)),o.attachRootComponentToLogicalElement(n,t)}t.attachRootComponentToLogicalElement=a,t.attachRootComponentToElement=function(e,t,n){var r=document.querySelector(t);if(!r)throw new Error("Could not find any element matching selector '"+t+"'.");a(e,o.toLogicalElement(r,!0),n)},t.renderBatch=function(e,t){var n=i[e];if(!n)throw new Error("There is no browser renderer with ID "+e+".");for(var r=t.arrayRangeReader,o=t.updatedComponents(),a=r.values(o),s=r.count(o),c=t.referenceFrames(),u=r.values(c),l=t.diffReader,f=0;f0&&!t)throw new Error("New logical elements must start empty, or allowExistingContents must be true");return e[r]=[],e}function s(e,t,n){var i=e;if(e instanceof Comment&&(u(i)&&u(i).length>0))throw new Error("Not implemented: inserting non-empty logical container");if(c(i))throw new Error("Not implemented: moving existing logical children");var a=u(t);if(n0;)e(r,0);var i=r;i.parentNode.removeChild(i)},t.getLogicalParent=c,t.getLogicalSiblingEnd=function(e){return e[i]||null},t.getLogicalChild=function(e,t){return u(e)[t]},t.isSvgElement=function(e){return"http://www.w3.org/2000/svg"===l(e).namespaceURI},t.getLogicalChildrenArray=u,t.permuteLogicalChildren=function(e,t){var n=u(e);t.forEach(function(e){e.moveRangeStart=n[e.fromSiblingIndex],e.moveRangeEnd=function e(t){if(t instanceof Element)return t;var n=f(t);if(n)return n.previousSibling;var r=c(t);return r instanceof Element?r.lastChild:e(r)}(e.moveRangeStart)}),t.forEach(function(t){var r=t.moveToBeforeMarker=document.createComment("marker"),o=n[t.toSiblingIndex+1];o?o.parentNode.insertBefore(r,o):h(r,e)}),t.forEach(function(e){for(var t=e.moveToBeforeMarker,n=t.parentNode,r=e.moveRangeStart,o=e.moveRangeEnd,i=r;i;){var a=i.nextSibling;if(n.insertBefore(i,t),i===o)break;i=a}n.removeChild(t)}),t.forEach(function(e){n[e.toSiblingIndex]=e.moveRangeStart})},t.getClosestDomElement=l},function(e,t,n){var r=n(5),o=r.Buffer;function i(e,t){for(var n in e)t[n]=e[n]}function a(e,t,n){return o(e,t,n)}o.from&&o.alloc&&o.allocUnsafe&&o.allocUnsafeSlow?e.exports=r:(i(r,t),t.Buffer=a),i(o,a),a.from=function(e,t,n){if("number"==typeof e)throw new TypeError("Argument must not be a number");return o(e,t,n)},a.alloc=function(e,t,n){if("number"!=typeof e)throw new TypeError("Argument must be a number");var r=o(e);return void 0!==t?"string"==typeof n?r.fill(t,n):r.fill(t):r.fill(0),r},a.allocUnsafe=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return o(e)},a.allocUnsafeSlow=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return r.SlowBuffer(e)}},function(e,t){"function"==typeof Object.create?e.exports=function(e,t){e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})}:e.exports=function(e,t){e.super_=t;var n=function(){};n.prototype=t.prototype,e.prototype=new n,e.prototype.constructor=e}},function(e,t,n){"use strict";var r;!function(e){window.DotNet=e;var t=[],n={},r={},o=1,i=null;function a(e){t.push(e)}function s(e,t){for(var n=[],r=2;r1)for(var n=1;n0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]this.length)&&(r=this.length),n>=this.length)return e||i.alloc(0);if(r<=0)return e||i.alloc(0);var o,a,s=!!e,c=this._offset(n),u=r-n,l=u,f=s&&t||0,h=c[1];if(0===n&&r==this.length){if(!s)return 1===this._bufs.length?this._bufs[0]:i.concat(this._bufs,this.length);for(a=0;a(o=this._bufs[a].length-h))){this._bufs[a].copy(e,f,h,h+l);break}this._bufs[a].copy(e,f,h),f+=o,l-=o,h&&(h=0)}return e},a.prototype.shallowSlice=function(e,t){e=e||0,t=t||this.length,e<0&&(e+=this.length),t<0&&(t+=this.length);var n=this._offset(e),r=this._offset(t),o=this._bufs.slice(n[0],r[0]+1);return 0==r[1]?o.pop():o[o.length-1]=o[o.length-1].slice(0,r[1]),0!=n[1]&&(o[0]=o[0].slice(n[1])),new a(o)},a.prototype.toString=function(e,t,n){return this.slice(t,n).toString(e)},a.prototype.consume=function(e){for(;this._bufs.length;){if(!(e>=this._bufs[0].length)){this._bufs[0]=this._bufs[0].slice(e),this.length-=e;break}e-=this._bufs[0].length,this.length-=this._bufs[0].length,this._bufs.shift()}return this},a.prototype.duplicate=function(){for(var e=0,t=new a;e0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]=i)return e;switch(e){case"%s":return String(r[n++]);case"%d":return Number(r[n++]);case"%j":try{return JSON.stringify(r[n++])}catch(e){return"[Circular]"}default:return e}}),c=r[n];n=3&&(r.depth=arguments[2]),arguments.length>=4&&(r.colors=arguments[3]),d(n)?r.showHidden=n:n&&t._extend(r,n),b(r.showHidden)&&(r.showHidden=!1),b(r.depth)&&(r.depth=2),b(r.colors)&&(r.colors=!1),b(r.customInspect)&&(r.customInspect=!0),r.colors&&(r.stylize=c),l(r,e,r.depth)}function c(e,t){var n=s.styles[t];return n?"["+s.colors[n][0]+"m"+e+"["+s.colors[n][1]+"m":e}function u(e,t){return e}function l(e,n,r){if(e.customInspect&&n&&C(n.inspect)&&n.inspect!==t.inspect&&(!n.constructor||n.constructor.prototype!==n)){var o=n.inspect(r,e);return v(o)||(o=l(e,o,r)),o}var i=function(e,t){if(b(t))return e.stylize("undefined","undefined");if(v(t)){var n="'"+JSON.stringify(t).replace(/^"|"$/g,"").replace(/'/g,"\\'").replace(/\\"/g,'"')+"'";return e.stylize(n,"string")}if(y(t))return e.stylize(""+t,"number");if(d(t))return e.stylize(""+t,"boolean");if(g(t))return e.stylize("null","null")}(e,n);if(i)return i;var a=Object.keys(n),s=function(e){var t={};return e.forEach(function(e,n){t[e]=!0}),t}(a);if(e.showHidden&&(a=Object.getOwnPropertyNames(n)),S(n)&&(a.indexOf("message")>=0||a.indexOf("description")>=0))return f(n);if(0===a.length){if(C(n)){var c=n.name?": "+n.name:"";return e.stylize("[Function"+c+"]","special")}if(m(n))return e.stylize(RegExp.prototype.toString.call(n),"regexp");if(E(n))return e.stylize(Date.prototype.toString.call(n),"date");if(S(n))return f(n)}var u,w="",I=!1,_=["{","}"];(p(n)&&(I=!0,_=["[","]"]),C(n))&&(w=" [Function"+(n.name?": "+n.name:"")+"]");return m(n)&&(w=" "+RegExp.prototype.toString.call(n)),E(n)&&(w=" "+Date.prototype.toUTCString.call(n)),S(n)&&(w=" "+f(n)),0!==a.length||I&&0!=n.length?r<0?m(n)?e.stylize(RegExp.prototype.toString.call(n),"regexp"):e.stylize("[Object]","special"):(e.seen.push(n),u=I?function(e,t,n,r,o){for(var i=[],a=0,s=t.length;a=0&&0,e+t.replace(/\u001b\[\d\d?m/g,"").length+1},0)>60)return n[0]+(""===t?"":t+"\n ")+" "+e.join(",\n ")+" "+n[1];return n[0]+t+" "+e.join(", ")+" "+n[1]}(u,w,_)):_[0]+w+_[1]}function f(e){return"["+Error.prototype.toString.call(e)+"]"}function h(e,t,n,r,o,i){var a,s,c;if((c=Object.getOwnPropertyDescriptor(t,o)||{value:t[o]}).get?s=c.set?e.stylize("[Getter/Setter]","special"):e.stylize("[Getter]","special"):c.set&&(s=e.stylize("[Setter]","special")),k(r,o)||(a="["+o+"]"),s||(e.seen.indexOf(c.value)<0?(s=g(n)?l(e,c.value,null):l(e,c.value,n-1)).indexOf("\n")>-1&&(s=i?s.split("\n").map(function(e){return" "+e}).join("\n").substr(2):"\n"+s.split("\n").map(function(e){return" "+e}).join("\n")):s=e.stylize("[Circular]","special")),b(a)){if(i&&o.match(/^\d+$/))return s;(a=JSON.stringify(""+o)).match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)?(a=a.substr(1,a.length-2),a=e.stylize(a,"name")):(a=a.replace(/'/g,"\\'").replace(/\\"/g,'"').replace(/(^"|"$)/g,"'"),a=e.stylize(a,"string"))}return a+": "+s}function p(e){return Array.isArray(e)}function d(e){return"boolean"==typeof e}function g(e){return null===e}function y(e){return"number"==typeof e}function v(e){return"string"==typeof e}function b(e){return void 0===e}function m(e){return w(e)&&"[object RegExp]"===I(e)}function w(e){return"object"==typeof e&&null!==e}function E(e){return w(e)&&"[object Date]"===I(e)}function S(e){return w(e)&&("[object Error]"===I(e)||e instanceof Error)}function C(e){return"function"==typeof e}function I(e){return Object.prototype.toString.call(e)}function _(e){return e<10?"0"+e.toString(10):e.toString(10)}t.debuglog=function(n){if(b(i)&&(i=e.env.NODE_DEBUG||""),n=n.toUpperCase(),!a[n])if(new RegExp("\\b"+n+"\\b","i").test(i)){var r=e.pid;a[n]=function(){var e=t.format.apply(t,arguments);console.error("%s %d: %s",n,r,e)}}else a[n]=function(){};return a[n]},t.inspect=s,s.colors={bold:[1,22],italic:[3,23],underline:[4,24],inverse:[7,27],white:[37,39],grey:[90,39],black:[30,39],blue:[34,39],cyan:[36,39],green:[32,39],magenta:[35,39],red:[31,39],yellow:[33,39]},s.styles={special:"cyan",number:"yellow",boolean:"yellow",undefined:"grey",null:"bold",string:"green",date:"magenta",regexp:"red"},t.isArray=p,t.isBoolean=d,t.isNull=g,t.isNullOrUndefined=function(e){return null==e},t.isNumber=y,t.isString=v,t.isSymbol=function(e){return"symbol"==typeof e},t.isUndefined=b,t.isRegExp=m,t.isObject=w,t.isDate=E,t.isError=S,t.isFunction=C,t.isPrimitive=function(e){return null===e||"boolean"==typeof e||"number"==typeof e||"string"==typeof e||"symbol"==typeof e||void 0===e},t.isBuffer=n(53);var T=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function k(e,t){return Object.prototype.hasOwnProperty.call(e,t)}t.log=function(){var e,n;console.log("%s - %s",(e=new Date,n=[_(e.getHours()),_(e.getMinutes()),_(e.getSeconds())].join(":"),[e.getDate(),T[e.getMonth()],n].join(" ")),t.format.apply(t,arguments))},t.inherits=n(54),t._extend=function(e,t){if(!t||!w(t))return e;for(var n=Object.keys(t),r=n.length;r--;)e[n[r]]=t[n[r]];return e};var x="undefined"!=typeof Symbol?Symbol("util.promisify.custom"):void 0;function P(e,t){if(!e){var n=new Error("Promise was rejected with a falsy value");n.reason=e,e=n}return t(e)}t.promisify=function(e){if("function"!=typeof e)throw new TypeError('The "original" argument must be of type Function');if(x&&e[x]){var t;if("function"!=typeof(t=e[x]))throw new TypeError('The "util.promisify.custom" argument must be of type Function');return Object.defineProperty(t,x,{value:t,enumerable:!1,writable:!1,configurable:!0}),t}function t(){for(var t,n,r=new Promise(function(e,r){t=e,n=r}),o=[],i=0;i0?("string"==typeof t||a.objectMode||Object.getPrototypeOf(t)===u.prototype||(t=function(e){return u.from(e)}(t)),r?a.endEmitted?e.emit("error",new Error("stream.unshift() after end event")):E(e,a,t,!0):a.ended?e.emit("error",new Error("stream.push() after EOF")):(a.reading=!1,a.decoder&&!n?(t=a.decoder.write(t),a.objectMode||0!==t.length?E(e,a,t,!1):T(e,a)):E(e,a,t,!1))):r||(a.reading=!1));return function(e){return!e.ended&&(e.needReadable||e.lengtht.highWaterMark&&(t.highWaterMark=function(e){return e>=S?e=S:(e--,e|=e>>>1,e|=e>>>2,e|=e>>>4,e|=e>>>8,e|=e>>>16,e++),e}(e)),e<=t.length?e:t.ended?t.length:(t.needReadable=!0,0))}function I(e){var t=e._readableState;t.needReadable=!1,t.emittedReadable||(p("emitReadable",t.flowing),t.emittedReadable=!0,t.sync?o.nextTick(_,e):_(e))}function _(e){p("emit readable"),e.emit("readable"),R(e)}function T(e,t){t.readingMore||(t.readingMore=!0,o.nextTick(k,e,t))}function k(e,t){for(var n=t.length;!t.reading&&!t.flowing&&!t.ended&&t.length=t.length?(n=t.decoder?t.buffer.join(""):1===t.buffer.length?t.buffer.head.data:t.buffer.concat(t.length),t.buffer.clear()):n=function(e,t,n){var r;ei.length?i.length:e;if(a===i.length?o+=i:o+=i.slice(0,e),0===(e-=a)){a===i.length?(++r,n.next?t.head=n.next:t.head=t.tail=null):(t.head=n,n.data=i.slice(a));break}++r}return t.length-=r,o}(e,t):function(e,t){var n=u.allocUnsafe(e),r=t.head,o=1;r.data.copy(n),e-=r.data.length;for(;r=r.next;){var i=r.data,a=e>i.length?i.length:e;if(i.copy(n,n.length-e,0,a),0===(e-=a)){a===i.length?(++o,r.next?t.head=r.next:t.head=t.tail=null):(t.head=r,r.data=i.slice(a));break}++o}return t.length-=o,n}(e,t);return r}(e,t.buffer,t.decoder),n);var n}function O(e){var t=e._readableState;if(t.length>0)throw new Error('"endReadable()" called on non-empty stream');t.endEmitted||(t.ended=!0,o.nextTick(M,t,e))}function M(e,t){e.endEmitted||0!==e.length||(e.endEmitted=!0,t.readable=!1,t.emit("end"))}function L(e,t){for(var n=0,r=e.length;n=t.highWaterMark||t.ended))return p("read: emitReadable",t.length,t.ended),0===t.length&&t.ended?O(this):I(this),null;if(0===(e=C(e,t))&&t.ended)return 0===t.length&&O(this),null;var r,o=t.needReadable;return p("need readable",o),(0===t.length||t.length-e0?D(e,t):null)?(t.needReadable=!0,e=0):t.length-=e,0===t.length&&(t.ended||(t.needReadable=!0),n!==e&&t.ended&&O(this)),null!==r&&this.emit("data",r),r},m.prototype._read=function(e){this.emit("error",new Error("_read() is not implemented"))},m.prototype.pipe=function(e,t){var n=this,i=this._readableState;switch(i.pipesCount){case 0:i.pipes=e;break;case 1:i.pipes=[i.pipes,e];break;default:i.pipes.push(e)}i.pipesCount+=1,p("pipe count=%d opts=%j",i.pipesCount,t);var c=(!t||!1!==t.end)&&e!==r.stdout&&e!==r.stderr?l:m;function u(t,r){p("onunpipe"),t===n&&r&&!1===r.hasUnpiped&&(r.hasUnpiped=!0,p("cleanup"),e.removeListener("close",v),e.removeListener("finish",b),e.removeListener("drain",f),e.removeListener("error",y),e.removeListener("unpipe",u),n.removeListener("end",l),n.removeListener("end",m),n.removeListener("data",g),h=!0,!i.awaitDrain||e._writableState&&!e._writableState.needDrain||f())}function l(){p("onend"),e.end()}i.endEmitted?o.nextTick(c):n.once("end",c),e.on("unpipe",u);var f=function(e){return function(){var t=e._readableState;p("pipeOnDrain",t.awaitDrain),t.awaitDrain&&t.awaitDrain--,0===t.awaitDrain&&s(e,"data")&&(t.flowing=!0,R(e))}}(n);e.on("drain",f);var h=!1;var d=!1;function g(t){p("ondata"),d=!1,!1!==e.write(t)||d||((1===i.pipesCount&&i.pipes===e||i.pipesCount>1&&-1!==L(i.pipes,e))&&!h&&(p("false write response, pause",n._readableState.awaitDrain),n._readableState.awaitDrain++,d=!0),n.pause())}function y(t){p("onerror",t),m(),e.removeListener("error",y),0===s(e,"error")&&e.emit("error",t)}function v(){e.removeListener("finish",b),m()}function b(){p("onfinish"),e.removeListener("close",v),m()}function m(){p("unpipe"),n.unpipe(e)}return n.on("data",g),function(e,t,n){if("function"==typeof e.prependListener)return e.prependListener(t,n);e._events&&e._events[t]?a(e._events[t])?e._events[t].unshift(n):e._events[t]=[n,e._events[t]]:e.on(t,n)}(e,"error",y),e.once("close",v),e.once("finish",b),e.emit("pipe",n),i.flowing||(p("pipe resume"),n.resume()),e},m.prototype.unpipe=function(e){var t=this._readableState,n={hasUnpiped:!1};if(0===t.pipesCount)return this;if(1===t.pipesCount)return e&&e!==t.pipes?this:(e||(e=t.pipes),t.pipes=null,t.pipesCount=0,t.flowing=!1,e&&e.emit("unpipe",this,n),this);if(!e){var r=t.pipes,o=t.pipesCount;t.pipes=null,t.pipesCount=0,t.flowing=!1;for(var i=0;i0&&a.length>o&&!a.warned){a.warned=!0;var c=new Error("Possible EventEmitter memory leak detected. "+a.length+" "+String(t)+" listeners added. Use emitter.setMaxListeners() to increase limit");c.name="MaxListenersExceededWarning",c.emitter=e,c.type=t,c.count=a.length,s=c,console&&console.warn&&console.warn(s)}return e}function f(e,t,n){var r={fired:!1,wrapFn:void 0,target:e,type:t,listener:n},o=function(){for(var e=[],t=0;t0&&(a=t[0]),a instanceof Error)throw a;var s=new Error("Unhandled error."+(a?" ("+a.message+")":""));throw s.context=a,s}var c=o[e];if(void 0===c)return!1;if("function"==typeof c)i(c,this,t);else{var u=c.length,l=d(c,u);for(n=0;n=0;i--)if(n[i]===t||n[i].listener===t){a=n[i].listener,o=i;break}if(o<0)return this;0===o?n.shift():function(e,t){for(;t+1=0;r--)this.removeListener(e,t[r]);return this},s.prototype.listeners=function(e){return h(this,e,!0)},s.prototype.rawListeners=function(e){return h(this,e,!1)},s.listenerCount=function(e,t){return"function"==typeof e.listenerCount?e.listenerCount(t):p.call(e,t)},s.prototype.listenerCount=p,s.prototype.eventNames=function(){return this._eventsCount>0?r(this._events):[]}},function(e,t,n){e.exports=n(35).EventEmitter},function(e,t,n){"use strict";var r=n(21);function o(e,t){e.emit("error",t)}e.exports={destroy:function(e,t){var n=this,i=this._readableState&&this._readableState.destroyed,a=this._writableState&&this._writableState.destroyed;return i||a?(t?t(e):!e||this._writableState&&this._writableState.errorEmitted||r.nextTick(o,this,e),this):(this._readableState&&(this._readableState.destroyed=!0),this._writableState&&(this._writableState.destroyed=!0),this._destroy(e||null,function(e){!t&&e?(r.nextTick(o,n,e),n._writableState&&(n._writableState.errorEmitted=!0)):t&&t(e)}),this)},undestroy:function(){this._readableState&&(this._readableState.destroyed=!1,this._readableState.reading=!1,this._readableState.ended=!1,this._readableState.endEmitted=!1),this._writableState&&(this._writableState.destroyed=!1,this._writableState.ended=!1,this._writableState.ending=!1,this._writableState.finished=!1,this._writableState.errorEmitted=!1)}}},function(e,t,n){"use strict";var r=n(60).Buffer,o=r.isEncoding||function(e){switch((e=""+e)&&e.toLowerCase()){case"hex":case"utf8":case"utf-8":case"ascii":case"binary":case"base64":case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":case"raw":return!0;default:return!1}};function i(e){var t;switch(this.encoding=function(e){var t=function(e){if(!e)return"utf8";for(var t;;)switch(e){case"utf8":case"utf-8":return"utf8";case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return"utf16le";case"latin1":case"binary":return"latin1";case"base64":case"ascii":case"hex":return e;default:if(t)return;e=(""+e).toLowerCase(),t=!0}}(e);if("string"!=typeof t&&(r.isEncoding===o||!o(e)))throw new Error("Unknown encoding: "+e);return t||e}(e),this.encoding){case"utf16le":this.text=c,this.end=u,t=4;break;case"utf8":this.fillLast=s,t=4;break;case"base64":this.text=l,this.end=f,t=3;break;default:return this.write=h,void(this.end=p)}this.lastNeed=0,this.lastTotal=0,this.lastChar=r.allocUnsafe(t)}function a(e){return e<=127?0:e>>5==6?2:e>>4==14?3:e>>3==30?4:e>>6==2?-1:-2}function s(e){var t=this.lastTotal-this.lastNeed,n=function(e,t,n){if(128!=(192&t[0]))return e.lastNeed=0,"�";if(e.lastNeed>1&&t.length>1){if(128!=(192&t[1]))return e.lastNeed=1,"�";if(e.lastNeed>2&&t.length>2&&128!=(192&t[2]))return e.lastNeed=2,"�"}}(this,e);return void 0!==n?n:this.lastNeed<=e.length?(e.copy(this.lastChar,t,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal)):(e.copy(this.lastChar,t,0,e.length),void(this.lastNeed-=e.length))}function c(e,t){if((e.length-t)%2==0){var n=e.toString("utf16le",t);if(n){var r=n.charCodeAt(n.length-1);if(r>=55296&&r<=56319)return this.lastNeed=2,this.lastTotal=4,this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1],n.slice(0,-1)}return n}return this.lastNeed=1,this.lastTotal=2,this.lastChar[0]=e[e.length-1],e.toString("utf16le",t,e.length-1)}function u(e){var t=e&&e.length?this.write(e):"";if(this.lastNeed){var n=this.lastTotal-this.lastNeed;return t+this.lastChar.toString("utf16le",0,n)}return t}function l(e,t){var n=(e.length-t)%3;return 0===n?e.toString("base64",t):(this.lastNeed=3-n,this.lastTotal=3,1===n?this.lastChar[0]=e[e.length-1]:(this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1]),e.toString("base64",t,e.length-n))}function f(e){var t=e&&e.length?this.write(e):"";return this.lastNeed?t+this.lastChar.toString("base64",0,3-this.lastNeed):t}function h(e){return e.toString(this.encoding)}function p(e){return e&&e.length?this.write(e):""}t.StringDecoder=i,i.prototype.write=function(e){if(0===e.length)return"";var t,n;if(this.lastNeed){if(void 0===(t=this.fillLast(e)))return"";n=this.lastNeed,this.lastNeed=0}else n=0;return n=0)return o>0&&(e.lastNeed=o-1),o;if(--r=0)return o>0&&(e.lastNeed=o-2),o;if(--r=0)return o>0&&(2===o?o=0:e.lastNeed=o-3),o;return 0}(this,e,t);if(!this.lastNeed)return e.toString("utf8",t);this.lastTotal=n;var r=e.length-(n-this.lastNeed);return e.copy(this.lastChar,0,r),e.toString("utf8",t,r)},i.prototype.fillLast=function(e){if(this.lastNeed<=e.length)return e.copy(this.lastChar,this.lastTotal-this.lastNeed,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal);e.copy(this.lastChar,this.lastTotal-this.lastNeed,0,e.length),this.lastNeed-=e.length}},function(e,t,n){"use strict";(function(t,r,o){var i=n(21);function a(e){var t=this;this.next=null,this.entry=null,this.finish=function(){!function(e,t,n){var r=e.entry;e.entry=null;for(;r;){var o=r.callback;t.pendingcb--,o(n),r=r.next}t.corkedRequestsFree?t.corkedRequestsFree.next=e:t.corkedRequestsFree=e}(t,e)}}e.exports=b;var s,c=!t.browser&&["v0.10","v0.9."].indexOf(t.version.slice(0,5))>-1?r:i.nextTick;b.WritableState=v;var u=n(18);u.inherits=n(14);var l={deprecate:n(63)},f=n(36),h=n(13).Buffer,p=o.Uint8Array||function(){};var d,g=n(37);function y(){}function v(e,t){s=s||n(9),e=e||{};var r=t instanceof s;this.objectMode=!!e.objectMode,r&&(this.objectMode=this.objectMode||!!e.writableObjectMode);var o=e.highWaterMark,u=e.writableHighWaterMark,l=this.objectMode?16:16384;this.highWaterMark=o||0===o?o:r&&(u||0===u)?u:l,this.highWaterMark=Math.floor(this.highWaterMark),this.finalCalled=!1,this.needDrain=!1,this.ending=!1,this.ended=!1,this.finished=!1,this.destroyed=!1;var f=!1===e.decodeStrings;this.decodeStrings=!f,this.defaultEncoding=e.defaultEncoding||"utf8",this.length=0,this.writing=!1,this.corked=0,this.sync=!0,this.bufferProcessing=!1,this.onwrite=function(e){!function(e,t){var n=e._writableState,r=n.sync,o=n.writecb;if(function(e){e.writing=!1,e.writecb=null,e.length-=e.writelen,e.writelen=0}(n),t)!function(e,t,n,r,o){--t.pendingcb,n?(i.nextTick(o,r),i.nextTick(I,e,t),e._writableState.errorEmitted=!0,e.emit("error",r)):(o(r),e._writableState.errorEmitted=!0,e.emit("error",r),I(e,t))}(e,n,r,t,o);else{var a=S(n);a||n.corked||n.bufferProcessing||!n.bufferedRequest||E(e,n),r?c(w,e,n,a,o):w(e,n,a,o)}}(t,e)},this.writecb=null,this.writelen=0,this.bufferedRequest=null,this.lastBufferedRequest=null,this.pendingcb=0,this.prefinished=!1,this.errorEmitted=!1,this.bufferedRequestCount=0,this.corkedRequestsFree=new a(this)}function b(e){if(s=s||n(9),!(d.call(b,this)||this instanceof s))return new b(e);this._writableState=new v(e,this),this.writable=!0,e&&("function"==typeof e.write&&(this._write=e.write),"function"==typeof e.writev&&(this._writev=e.writev),"function"==typeof e.destroy&&(this._destroy=e.destroy),"function"==typeof e.final&&(this._final=e.final)),f.call(this)}function m(e,t,n,r,o,i,a){t.writelen=r,t.writecb=a,t.writing=!0,t.sync=!0,n?e._writev(o,t.onwrite):e._write(o,i,t.onwrite),t.sync=!1}function w(e,t,n,r){n||function(e,t){0===t.length&&t.needDrain&&(t.needDrain=!1,e.emit("drain"))}(e,t),t.pendingcb--,r(),I(e,t)}function E(e,t){t.bufferProcessing=!0;var n=t.bufferedRequest;if(e._writev&&n&&n.next){var r=t.bufferedRequestCount,o=new Array(r),i=t.corkedRequestsFree;i.entry=n;for(var s=0,c=!0;n;)o[s]=n,n.isBuf||(c=!1),n=n.next,s+=1;o.allBuffers=c,m(e,t,!0,t.length,o,"",i.finish),t.pendingcb++,t.lastBufferedRequest=null,i.next?(t.corkedRequestsFree=i.next,i.next=null):t.corkedRequestsFree=new a(t),t.bufferedRequestCount=0}else{for(;n;){var u=n.chunk,l=n.encoding,f=n.callback;if(m(e,t,!1,t.objectMode?1:u.length,u,l,f),n=n.next,t.bufferedRequestCount--,t.writing)break}null===n&&(t.lastBufferedRequest=null)}t.bufferedRequest=n,t.bufferProcessing=!1}function S(e){return e.ending&&0===e.length&&null===e.bufferedRequest&&!e.finished&&!e.writing}function C(e,t){e._final(function(n){t.pendingcb--,n&&e.emit("error",n),t.prefinished=!0,e.emit("prefinish"),I(e,t)})}function I(e,t){var n=S(t);return n&&(!function(e,t){t.prefinished||t.finalCalled||("function"==typeof e._final?(t.pendingcb++,t.finalCalled=!0,i.nextTick(C,e,t)):(t.prefinished=!0,e.emit("prefinish")))}(e,t),0===t.pendingcb&&(t.finished=!0,e.emit("finish"))),n}u.inherits(b,f),v.prototype.getBuffer=function(){for(var e=this.bufferedRequest,t=[];e;)t.push(e),e=e.next;return t},function(){try{Object.defineProperty(v.prototype,"buffer",{get:l.deprecate(function(){return this.getBuffer()},"_writableState.buffer is deprecated. Use _writableState.getBuffer instead.","DEP0003")})}catch(e){}}(),"function"==typeof Symbol&&Symbol.hasInstance&&"function"==typeof Function.prototype[Symbol.hasInstance]?(d=Function.prototype[Symbol.hasInstance],Object.defineProperty(b,Symbol.hasInstance,{value:function(e){return!!d.call(this,e)||this===b&&(e&&e._writableState instanceof v)}})):d=function(e){return e instanceof this},b.prototype.pipe=function(){this.emit("error",new Error("Cannot pipe, not readable"))},b.prototype.write=function(e,t,n){var r,o=this._writableState,a=!1,s=!o.objectMode&&(r=e,h.isBuffer(r)||r instanceof p);return s&&!h.isBuffer(e)&&(e=function(e){return h.from(e)}(e)),"function"==typeof t&&(n=t,t=null),s?t="buffer":t||(t=o.defaultEncoding),"function"!=typeof n&&(n=y),o.ended?function(e,t){var n=new Error("write after end");e.emit("error",n),i.nextTick(t,n)}(this,n):(s||function(e,t,n,r){var o=!0,a=!1;return null===n?a=new TypeError("May not write null values to stream"):"string"==typeof n||void 0===n||t.objectMode||(a=new TypeError("Invalid non-string/buffer chunk")),a&&(e.emit("error",a),i.nextTick(r,a),o=!1),o}(this,o,e,n))&&(o.pendingcb++,a=function(e,t,n,r,o,i){if(!n){var a=function(e,t,n){e.objectMode||!1===e.decodeStrings||"string"!=typeof t||(t=h.from(t,n));return t}(t,r,o);r!==a&&(n=!0,o="buffer",r=a)}var s=t.objectMode?1:r.length;t.length+=s;var c=t.length-1))throw new TypeError("Unknown encoding: "+e);return this._writableState.defaultEncoding=e,this},Object.defineProperty(b.prototype,"writableHighWaterMark",{enumerable:!1,get:function(){return this._writableState.highWaterMark}}),b.prototype._write=function(e,t,n){n(new Error("_write() is not implemented"))},b.prototype._writev=null,b.prototype.end=function(e,t,n){var r=this._writableState;"function"==typeof e?(n=e,e=null,t=null):"function"==typeof t&&(n=t,t=null),null!=e&&this.write(e,t),r.corked&&(r.corked=1,this.uncork()),r.ending||r.finished||function(e,t,n){t.ending=!0,I(e,t),n&&(t.finished?i.nextTick(n):e.once("finish",n));t.ended=!0,e.writable=!1}(this,r,n)},Object.defineProperty(b.prototype,"destroyed",{get:function(){return void 0!==this._writableState&&this._writableState.destroyed},set:function(e){this._writableState&&(this._writableState.destroyed=e)}}),b.prototype.destroy=g.destroy,b.prototype._undestroy=g.undestroy,b.prototype._destroy=function(e,t){this.end(),t(e)}}).call(this,n(17),n(61).setImmediate,n(8))},function(e,t,n){"use strict";e.exports=a;var r=n(9),o=n(18);function i(e,t){var n=this._transformState;n.transforming=!1;var r=n.writecb;if(!r)return this.emit("error",new Error("write callback called multiple times"));n.writechunk=null,n.writecb=null,null!=t&&this.push(t),r(e);var o=this._readableState;o.reading=!1,(o.needReadable||o.length0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]=200&&c.statusCode<300?r(new a.b(c.statusCode,c.statusMessage||"",u)):o(new i.b(c.statusMessage||"",c.statusCode||0))});t.abortSignal&&(t.abortSignal.onabort=function(){f.abort(),o(new i.a)})})},n.prototype.getCookieString=function(e){return this.cookieJar.getCookieString(e)},n}(a.a)}).call(this,n(5).Buffer)},function(e,t,n){"use strict";(function(e){n.d(t,"a",function(){return i});var r=n(7),o=n(1),i=function(){function t(){}return t.prototype.writeHandshakeRequest=function(e){return r.a.write(JSON.stringify(e))},t.prototype.parseHandshakeResponse=function(t){var n,i;if(Object(o.g)(t)||void 0!==e&&t instanceof e){var a=new Uint8Array(t);if(-1===(c=a.indexOf(r.a.RecordSeparatorCode)))throw new Error("Message is incomplete.");var s=c+1;n=String.fromCharCode.apply(null,a.slice(0,s)),i=a.byteLength>s?a.slice(s).buffer:null}else{var c,u=t;if(-1===(c=u.indexOf(r.a.RecordSeparator)))throw new Error("Message is incomplete.");s=c+1;n=u.substring(0,s),i=u.length>s?u.substring(s):null}var l=r.a.parse(n),f=JSON.parse(l[0]);if(f.type)throw new Error("Expected a handshake response from the server.");return[i,f]},t}()}).call(this,n(5).Buffer)},,,,,function(e,t,n){"use strict";var r=this&&this.__assign||function(){return(r=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]0?r-4:r,f=0;f>16&255,s[c++]=t>>8&255,s[c++]=255&t;2===a&&(t=o[e.charCodeAt(f)]<<2|o[e.charCodeAt(f+1)]>>4,s[c++]=255&t);1===a&&(t=o[e.charCodeAt(f)]<<10|o[e.charCodeAt(f+1)]<<4|o[e.charCodeAt(f+2)]>>2,s[c++]=t>>8&255,s[c++]=255&t);return s},t.fromByteArray=function(e){for(var t,n=e.length,o=n%3,i=[],a=0,s=n-o;as?s:a+16383));1===o?(t=e[n-1],i.push(r[t>>2]+r[t<<4&63]+"==")):2===o&&(t=(e[n-2]<<8)+e[n-1],i.push(r[t>>10]+r[t>>4&63]+r[t<<2&63]+"="));return i.join("")};for(var r=[],o=[],i="undefined"!=typeof Uint8Array?Uint8Array:Array,a="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",s=0,c=a.length;s0)throw new Error("Invalid string. Length must be a multiple of 4");var n=e.indexOf("=");return-1===n&&(n=t),[n,n===t?0:4-n%4]}function l(e,t,n){for(var o,i,a=[],s=t;s>18&63]+r[i>>12&63]+r[i>>6&63]+r[63&i]);return a.join("")}o["-".charCodeAt(0)]=62,o["_".charCodeAt(0)]=63},function(e,t){t.read=function(e,t,n,r,o){var i,a,s=8*o-r-1,c=(1<>1,l=-7,f=n?o-1:0,h=n?-1:1,p=e[t+f];for(f+=h,i=p&(1<<-l)-1,p>>=-l,l+=s;l>0;i=256*i+e[t+f],f+=h,l-=8);for(a=i&(1<<-l)-1,i>>=-l,l+=r;l>0;a=256*a+e[t+f],f+=h,l-=8);if(0===i)i=1-u;else{if(i===c)return a?NaN:1/0*(p?-1:1);a+=Math.pow(2,r),i-=u}return(p?-1:1)*a*Math.pow(2,i-r)},t.write=function(e,t,n,r,o,i){var a,s,c,u=8*i-o-1,l=(1<>1,h=23===o?Math.pow(2,-24)-Math.pow(2,-77):0,p=r?0:i-1,d=r?1:-1,g=t<0||0===t&&1/t<0?1:0;for(t=Math.abs(t),isNaN(t)||t===1/0?(s=isNaN(t)?1:0,a=l):(a=Math.floor(Math.log(t)/Math.LN2),t*(c=Math.pow(2,-a))<1&&(a--,c*=2),(t+=a+f>=1?h/c:h*Math.pow(2,1-f))*c>=2&&(a++,c/=2),a+f>=l?(s=0,a=l):a+f>=1?(s=(t*c-1)*Math.pow(2,o),a+=f):(s=t*Math.pow(2,f-1)*Math.pow(2,o),a=0));o>=8;e[n+p]=255&s,p+=d,s/=256,o-=8);for(a=a<0;e[n+p]=255&a,p+=d,a/=256,u-=8);e[n+p-d]|=128*g}},function(e,t){var n={}.toString;e.exports=Array.isArray||function(e){return"[object Array]"==n.call(e)}},function(e,t,n){"use strict";(function(t){ +var r=n(49),o=n(50),i=n(51);function a(){return c.TYPED_ARRAY_SUPPORT?2147483647:1073741823}function s(e,t){if(a()=a())throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+a().toString(16)+" bytes");return 0|e}function d(e,t){if(c.isBuffer(e))return e.length;if("undefined"!=typeof ArrayBuffer&&"function"==typeof ArrayBuffer.isView&&(ArrayBuffer.isView(e)||e instanceof ArrayBuffer))return e.byteLength;"string"!=typeof e&&(e=""+e);var n=e.length;if(0===n)return 0;for(var r=!1;;)switch(t){case"ascii":case"latin1":case"binary":return n;case"utf8":case"utf-8":case void 0:return F(e).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*n;case"hex":return n>>>1;case"base64":return H(e).length;default:if(r)return F(e).length;t=(""+t).toLowerCase(),r=!0}}function g(e,t,n){var r=e[t];e[t]=e[n],e[n]=r}function y(e,t,n,r,o){if(0===e.length)return-1;if("string"==typeof n?(r=n,n=0):n>2147483647?n=2147483647:n<-2147483648&&(n=-2147483648),n=+n,isNaN(n)&&(n=o?0:e.length-1),n<0&&(n=e.length+n),n>=e.length){if(o)return-1;n=e.length-1}else if(n<0){if(!o)return-1;n=0}if("string"==typeof t&&(t=c.from(t,r)),c.isBuffer(t))return 0===t.length?-1:v(e,t,n,r,o);if("number"==typeof t)return t&=255,c.TYPED_ARRAY_SUPPORT&&"function"==typeof Uint8Array.prototype.indexOf?o?Uint8Array.prototype.indexOf.call(e,t,n):Uint8Array.prototype.lastIndexOf.call(e,t,n):v(e,[t],n,r,o);throw new TypeError("val must be string, number or Buffer")}function v(e,t,n,r,o){var i,a=1,s=e.length,c=t.length;if(void 0!==r&&("ucs2"===(r=String(r).toLowerCase())||"ucs-2"===r||"utf16le"===r||"utf-16le"===r)){if(e.length<2||t.length<2)return-1;a=2,s/=2,c/=2,n/=2}function u(e,t){return 1===a?e[t]:e.readUInt16BE(t*a)}if(o){var l=-1;for(i=n;is&&(n=s-c),i=n;i>=0;i--){for(var f=!0,h=0;ho&&(r=o):r=o;var i=t.length;if(i%2!=0)throw new TypeError("Invalid hex string");r>i/2&&(r=i/2);for(var a=0;a>8,o=n%256,i.push(o),i.push(r);return i}(t,e.length-n),e,n,r)}function _(e,t,n){return 0===t&&n===e.length?r.fromByteArray(e):r.fromByteArray(e.slice(t,n))}function I(e,t,n){n=Math.min(e.length,n);for(var r=[],o=t;o239?4:u>223?3:u>191?2:1;if(o+f<=n)switch(f){case 1:u<128&&(l=u);break;case 2:128==(192&(i=e[o+1]))&&(c=(31&u)<<6|63&i)>127&&(l=c);break;case 3:i=e[o+1],a=e[o+2],128==(192&i)&&128==(192&a)&&(c=(15&u)<<12|(63&i)<<6|63&a)>2047&&(c<55296||c>57343)&&(l=c);break;case 4:i=e[o+1],a=e[o+2],s=e[o+3],128==(192&i)&&128==(192&a)&&128==(192&s)&&(c=(15&u)<<18|(63&i)<<12|(63&a)<<6|63&s)>65535&&c<1114112&&(l=c)}null===l?(l=65533,f=1):l>65535&&(l-=65536,r.push(l>>>10&1023|55296),l=56320|1023&l),r.push(l),o+=f}return function(e){var t=e.length;if(t<=T)return String.fromCharCode.apply(String,e);var n="",r=0;for(;rthis.length)return"";if((void 0===n||n>this.length)&&(n=this.length),n<=0)return"";if((n>>>=0)<=(t>>>=0))return"";for(e||(e="utf8");;)switch(e){case"hex":return P(this,t,n);case"utf8":case"utf-8":return I(this,t,n);case"ascii":return k(this,t,n);case"latin1":case"binary":return x(this,t,n);case"base64":return _(this,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return R(this,t,n);default:if(r)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),r=!0}}.apply(this,arguments)},c.prototype.equals=function(e){if(!c.isBuffer(e))throw new TypeError("Argument must be a Buffer");return this===e||0===c.compare(this,e)},c.prototype.inspect=function(){var e="",n=t.INSPECT_MAX_BYTES;return this.length>0&&(e=this.toString("hex",0,n).match(/.{2}/g).join(" "),this.length>n&&(e+=" ... ")),""},c.prototype.compare=function(e,t,n,r,o){if(!c.isBuffer(e))throw new TypeError("Argument must be a Buffer");if(void 0===t&&(t=0),void 0===n&&(n=e?e.length:0),void 0===r&&(r=0),void 0===o&&(o=this.length),t<0||n>e.length||r<0||o>this.length)throw new RangeError("out of range index");if(r>=o&&t>=n)return 0;if(r>=o)return-1;if(t>=n)return 1;if(this===e)return 0;for(var i=(o>>>=0)-(r>>>=0),a=(n>>>=0)-(t>>>=0),s=Math.min(i,a),u=this.slice(r,o),l=e.slice(t,n),f=0;fo)&&(n=o),e.length>0&&(n<0||t<0)||t>this.length)throw new RangeError("Attempt to write outside buffer bounds");r||(r="utf8");for(var i=!1;;)switch(r){case"hex":return b(this,e,t,n);case"utf8":case"utf-8":return m(this,e,t,n);case"ascii":return w(this,e,t,n);case"latin1":case"binary":return E(this,e,t,n);case"base64":return S(this,e,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return C(this,e,t,n);default:if(i)throw new TypeError("Unknown encoding: "+r);r=(""+r).toLowerCase(),i=!0}},c.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};var T=4096;function k(e,t,n){var r="";n=Math.min(e.length,n);for(var o=t;or)&&(n=r);for(var o="",i=t;in)throw new RangeError("Trying to access beyond buffer length")}function O(e,t,n,r,o,i){if(!c.isBuffer(e))throw new TypeError('"buffer" argument must be a Buffer instance');if(t>o||te.length)throw new RangeError("Index out of range")}function M(e,t,n,r){t<0&&(t=65535+t+1);for(var o=0,i=Math.min(e.length-n,2);o>>8*(r?o:1-o)}function L(e,t,n,r){t<0&&(t=4294967295+t+1);for(var o=0,i=Math.min(e.length-n,4);o>>8*(r?o:3-o)&255}function A(e,t,n,r,o,i){if(n+r>e.length)throw new RangeError("Index out of range");if(n<0)throw new RangeError("Index out of range")}function B(e,t,n,r,i){return i||A(e,0,n,4),o.write(e,t,n,r,23,4),n+4}function j(e,t,n,r,i){return i||A(e,0,n,8),o.write(e,t,n,r,52,8),n+8}c.prototype.slice=function(e,t){var n,r=this.length;if((e=~~e)<0?(e+=r)<0&&(e=0):e>r&&(e=r),(t=void 0===t?r:~~t)<0?(t+=r)<0&&(t=0):t>r&&(t=r),t0&&(o*=256);)r+=this[e+--t]*o;return r},c.prototype.readUInt8=function(e,t){return t||D(e,1,this.length),this[e]},c.prototype.readUInt16LE=function(e,t){return t||D(e,2,this.length),this[e]|this[e+1]<<8},c.prototype.readUInt16BE=function(e,t){return t||D(e,2,this.length),this[e]<<8|this[e+1]},c.prototype.readUInt32LE=function(e,t){return t||D(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},c.prototype.readUInt32BE=function(e,t){return t||D(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},c.prototype.readIntLE=function(e,t,n){e|=0,t|=0,n||D(e,t,this.length);for(var r=this[e],o=1,i=0;++i=(o*=128)&&(r-=Math.pow(2,8*t)),r},c.prototype.readIntBE=function(e,t,n){e|=0,t|=0,n||D(e,t,this.length);for(var r=t,o=1,i=this[e+--r];r>0&&(o*=256);)i+=this[e+--r]*o;return i>=(o*=128)&&(i-=Math.pow(2,8*t)),i},c.prototype.readInt8=function(e,t){return t||D(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},c.prototype.readInt16LE=function(e,t){t||D(e,2,this.length);var n=this[e]|this[e+1]<<8;return 32768&n?4294901760|n:n},c.prototype.readInt16BE=function(e,t){t||D(e,2,this.length);var n=this[e+1]|this[e]<<8;return 32768&n?4294901760|n:n},c.prototype.readInt32LE=function(e,t){return t||D(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},c.prototype.readInt32BE=function(e,t){return t||D(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},c.prototype.readFloatLE=function(e,t){return t||D(e,4,this.length),o.read(this,e,!0,23,4)},c.prototype.readFloatBE=function(e,t){return t||D(e,4,this.length),o.read(this,e,!1,23,4)},c.prototype.readDoubleLE=function(e,t){return t||D(e,8,this.length),o.read(this,e,!0,52,8)},c.prototype.readDoubleBE=function(e,t){return t||D(e,8,this.length),o.read(this,e,!1,52,8)},c.prototype.writeUIntLE=function(e,t,n,r){(e=+e,t|=0,n|=0,r)||O(this,e,t,n,Math.pow(2,8*n)-1,0);var o=1,i=0;for(this[t]=255&e;++i=0&&(i*=256);)this[t+o]=e/i&255;return t+n},c.prototype.writeUInt8=function(e,t,n){return e=+e,t|=0,n||O(this,e,t,1,255,0),c.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),this[t]=255&e,t+1},c.prototype.writeUInt16LE=function(e,t,n){return e=+e,t|=0,n||O(this,e,t,2,65535,0),c.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):M(this,e,t,!0),t+2},c.prototype.writeUInt16BE=function(e,t,n){return e=+e,t|=0,n||O(this,e,t,2,65535,0),c.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):M(this,e,t,!1),t+2},c.prototype.writeUInt32LE=function(e,t,n){return e=+e,t|=0,n||O(this,e,t,4,4294967295,0),c.TYPED_ARRAY_SUPPORT?(this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=255&e):L(this,e,t,!0),t+4},c.prototype.writeUInt32BE=function(e,t,n){return e=+e,t|=0,n||O(this,e,t,4,4294967295,0),c.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):L(this,e,t,!1),t+4},c.prototype.writeIntLE=function(e,t,n,r){if(e=+e,t|=0,!r){var o=Math.pow(2,8*n-1);O(this,e,t,n,o-1,-o)}var i=0,a=1,s=0;for(this[t]=255&e;++i>0)-s&255;return t+n},c.prototype.writeIntBE=function(e,t,n,r){if(e=+e,t|=0,!r){var o=Math.pow(2,8*n-1);O(this,e,t,n,o-1,-o)}var i=n-1,a=1,s=0;for(this[t+i]=255&e;--i>=0&&(a*=256);)e<0&&0===s&&0!==this[t+i+1]&&(s=1),this[t+i]=(e/a>>0)-s&255;return t+n},c.prototype.writeInt8=function(e,t,n){return e=+e,t|=0,n||O(this,e,t,1,127,-128),c.TYPED_ARRAY_SUPPORT||(e=Math.floor(e)),e<0&&(e=255+e+1),this[t]=255&e,t+1},c.prototype.writeInt16LE=function(e,t,n){return e=+e,t|=0,n||O(this,e,t,2,32767,-32768),c.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):M(this,e,t,!0),t+2},c.prototype.writeInt16BE=function(e,t,n){return e=+e,t|=0,n||O(this,e,t,2,32767,-32768),c.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):M(this,e,t,!1),t+2},c.prototype.writeInt32LE=function(e,t,n){return e=+e,t|=0,n||O(this,e,t,4,2147483647,-2147483648),c.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24):L(this,e,t,!0),t+4},c.prototype.writeInt32BE=function(e,t,n){return e=+e,t|=0,n||O(this,e,t,4,2147483647,-2147483648),e<0&&(e=4294967295+e+1),c.TYPED_ARRAY_SUPPORT?(this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e):L(this,e,t,!1),t+4},c.prototype.writeFloatLE=function(e,t,n){return B(this,e,t,!0,n)},c.prototype.writeFloatBE=function(e,t,n){return B(this,e,t,!1,n)},c.prototype.writeDoubleLE=function(e,t,n){return j(this,e,t,!0,n)},c.prototype.writeDoubleBE=function(e,t,n){return j(this,e,t,!1,n)},c.prototype.copy=function(e,t,n,r){if(n||(n=0),r||0===r||(r=this.length),t>=e.length&&(t=e.length),t||(t=0),r>0&&r=this.length)throw new RangeError("sourceStart out of bounds");if(r<0)throw new RangeError("sourceEnd out of bounds");r>this.length&&(r=this.length),e.length-t=0;--o)e[o+t]=this[o+n];else if(i<1e3||!c.TYPED_ARRAY_SUPPORT)for(o=0;o>>=0,n=void 0===n?this.length:n>>>0,e||(e=0),"number"==typeof e)for(i=t;i55295&&n<57344){if(!o){if(n>56319){(t-=3)>-1&&i.push(239,191,189);continue}if(a+1===r){(t-=3)>-1&&i.push(239,191,189);continue}o=n;continue}if(n<56320){(t-=3)>-1&&i.push(239,191,189),o=n;continue}n=65536+(o-55296<<10|n-56320)}else o&&(t-=3)>-1&&i.push(239,191,189);if(o=null,n<128){if((t-=1)<0)break;i.push(n)}else if(n<2048){if((t-=2)<0)break;i.push(n>>6|192,63&n|128)}else if(n<65536){if((t-=3)<0)break;i.push(n>>12|224,n>>6&63|128,63&n|128)}else{if(!(n<1114112))throw new Error("Invalid code point");if((t-=4)<0)break;i.push(n>>18|240,n>>12&63|128,n>>6&63|128,63&n|128)}}return i}function H(e){return r.toByteArray(function(e){if((e=function(e){return e.trim?e.trim():e.replace(/^\s+|\s+$/g,"")}(e).replace(U,"")).length<2)return"";for(;e.length%4!=0;)e+="=";return e}(e))}function W(e,t,n,r){for(var o=0;o=t.length||o>=e.length);++o)t[o+n]=e[o];return o}}).call(this,n(8))},function(e,t,n){"use strict";n.d(t,"a",function(){return r});var r=function(){function e(){}return e.prototype.log=function(e,t){},e.instance=new e,e}()},function(e,t,n){"use strict";n.d(t,"a",function(){return r});var r=function(){function e(){}return e.write=function(t){return""+t+e.RecordSeparator},e.parse=function(t){if(t[t.length-1]!==e.RecordSeparator)throw new Error("Message is incomplete.");var n=t.split(e.RecordSeparator);return n.pop(),n},e.RecordSeparatorCode=30,e.RecordSeparator=String.fromCharCode(e.RecordSeparatorCode),e}()},function(e,t){var n;n=function(){return this}();try{n=n||new Function("return this")()}catch(e){"object"==typeof window&&(n=window)}e.exports=n},function(e,t,n){"use strict";var r=n(21),o=Object.keys||function(e){var t=[];for(var n in e)t.push(n);return t};e.exports=f;var i=n(18);i.inherits=n(14);var a=n(34),s=n(39);i.inherits(f,a);for(var c=o(s.prototype),u=0;u=0,"must have a non-negative type"),o(a,"must have a decode function"),this.registerEncoder(function(e){return e instanceof t},function(t){var o=i(),a=r.allocUnsafe(1);return a.writeInt8(e,0),o.append(a),o.append(n(t)),o}),this.registerDecoder(e,a),this},registerEncoder:function(e,n){return o(e,"must have an encode function"),o(n,"must have an encode function"),t.push({check:e,encode:n}),this},registerDecoder:function(e,t){return o(e>=0,"must have a non-negative type"),o(t,"must have a decode function"),n.push({type:e,decode:t}),this},encoder:a.encoder,decoder:a.decoder,buffer:!0,type:"msgpack5",IncompleteBufferError:s.IncompleteBufferError}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(25),o=n(12),i={};function a(e,t,n){var o=i[e];o||(o=i[e]=new r.BrowserRenderer(e)),o.attachRootComponentToLogicalElement(n,t)}t.attachRootComponentToLogicalElement=a,t.attachRootComponentToElement=function(e,t,n){var r=document.querySelector(t);if(!r)throw new Error("Could not find any element matching selector '"+t+"'.");a(e,o.toLogicalElement(r,!0),n)},t.renderBatch=function(e,t){var n=i[e];if(!n)throw new Error("There is no browser renderer with ID "+e+".");for(var r=t.arrayRangeReader,o=t.updatedComponents(),a=r.values(o),s=r.count(o),c=t.referenceFrames(),u=r.values(c),l=t.diffReader,f=0;f0&&!t)throw new Error("New logical elements must start empty, or allowExistingContents must be true");return e[r]=[],e}function s(e,t,n){var i=e;if(e instanceof Comment&&(u(i)&&u(i).length>0))throw new Error("Not implemented: inserting non-empty logical container");if(c(i))throw new Error("Not implemented: moving existing logical children");var a=u(t);if(n0;)e(r,0);var i=r;i.parentNode.removeChild(i)},t.getLogicalParent=c,t.getLogicalSiblingEnd=function(e){return e[i]||null},t.getLogicalChild=function(e,t){return u(e)[t]},t.isSvgElement=function(e){return"http://www.w3.org/2000/svg"===l(e).namespaceURI},t.getLogicalChildrenArray=u,t.permuteLogicalChildren=function(e,t){var n=u(e);t.forEach(function(e){e.moveRangeStart=n[e.fromSiblingIndex],e.moveRangeEnd=function e(t){if(t instanceof Element)return t;var n=f(t);if(n)return n.previousSibling;var r=c(t);return r instanceof Element?r.lastChild:e(r)}(e.moveRangeStart)}),t.forEach(function(t){var r=t.moveToBeforeMarker=document.createComment("marker"),o=n[t.toSiblingIndex+1];o?o.parentNode.insertBefore(r,o):h(r,e)}),t.forEach(function(e){for(var t=e.moveToBeforeMarker,n=t.parentNode,r=e.moveRangeStart,o=e.moveRangeEnd,i=r;i;){var a=i.nextSibling;if(n.insertBefore(i,t),i===o)break;i=a}n.removeChild(t)}),t.forEach(function(e){n[e.toSiblingIndex]=e.moveRangeStart})},t.getClosestDomElement=l},function(e,t,n){var r=n(5),o=r.Buffer;function i(e,t){for(var n in e)t[n]=e[n]}function a(e,t,n){return o(e,t,n)}o.from&&o.alloc&&o.allocUnsafe&&o.allocUnsafeSlow?e.exports=r:(i(r,t),t.Buffer=a),i(o,a),a.from=function(e,t,n){if("number"==typeof e)throw new TypeError("Argument must not be a number");return o(e,t,n)},a.alloc=function(e,t,n){if("number"!=typeof e)throw new TypeError("Argument must be a number");var r=o(e);return void 0!==t?"string"==typeof n?r.fill(t,n):r.fill(t):r.fill(0),r},a.allocUnsafe=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return o(e)},a.allocUnsafeSlow=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return r.SlowBuffer(e)}},function(e,t){"function"==typeof Object.create?e.exports=function(e,t){e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})}:e.exports=function(e,t){e.super_=t;var n=function(){};n.prototype=t.prototype,e.prototype=new n,e.prototype.constructor=e}},function(e,t,n){"use strict";var r;!function(e){window.DotNet=e;var t=[],n={},r={},o=1,i=null;function a(e){t.push(e)}function s(e,t){for(var n=[],r=2;r1)for(var n=1;n0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]this.length)&&(r=this.length),n>=this.length)return e||i.alloc(0);if(r<=0)return e||i.alloc(0);var o,a,s=!!e,c=this._offset(n),u=r-n,l=u,f=s&&t||0,h=c[1];if(0===n&&r==this.length){if(!s)return 1===this._bufs.length?this._bufs[0]:i.concat(this._bufs,this.length);for(a=0;a(o=this._bufs[a].length-h))){this._bufs[a].copy(e,f,h,h+l);break}this._bufs[a].copy(e,f,h),f+=o,l-=o,h&&(h=0)}return e},a.prototype.shallowSlice=function(e,t){e=e||0,t=t||this.length,e<0&&(e+=this.length),t<0&&(t+=this.length);var n=this._offset(e),r=this._offset(t),o=this._bufs.slice(n[0],r[0]+1);return 0==r[1]?o.pop():o[o.length-1]=o[o.length-1].slice(0,r[1]),0!=n[1]&&(o[0]=o[0].slice(n[1])),new a(o)},a.prototype.toString=function(e,t,n){return this.slice(t,n).toString(e)},a.prototype.consume=function(e){for(;this._bufs.length;){if(!(e>=this._bufs[0].length)){this._bufs[0]=this._bufs[0].slice(e),this.length-=e;break}e-=this._bufs[0].length,this.length-=this._bufs[0].length,this._bufs.shift()}return this},a.prototype.duplicate=function(){for(var e=0,t=new a;e0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]=i)return e;switch(e){case"%s":return String(r[n++]);case"%d":return Number(r[n++]);case"%j":try{return JSON.stringify(r[n++])}catch(e){return"[Circular]"}default:return e}}),c=r[n];n=3&&(r.depth=arguments[2]),arguments.length>=4&&(r.colors=arguments[3]),d(n)?r.showHidden=n:n&&t._extend(r,n),b(r.showHidden)&&(r.showHidden=!1),b(r.depth)&&(r.depth=2),b(r.colors)&&(r.colors=!1),b(r.customInspect)&&(r.customInspect=!0),r.colors&&(r.stylize=c),l(r,e,r.depth)}function c(e,t){var n=s.styles[t];return n?"["+s.colors[n][0]+"m"+e+"["+s.colors[n][1]+"m":e}function u(e,t){return e}function l(e,n,r){if(e.customInspect&&n&&C(n.inspect)&&n.inspect!==t.inspect&&(!n.constructor||n.constructor.prototype!==n)){var o=n.inspect(r,e);return v(o)||(o=l(e,o,r)),o}var i=function(e,t){if(b(t))return e.stylize("undefined","undefined");if(v(t)){var n="'"+JSON.stringify(t).replace(/^"|"$/g,"").replace(/'/g,"\\'").replace(/\\"/g,'"')+"'";return e.stylize(n,"string")}if(y(t))return e.stylize(""+t,"number");if(d(t))return e.stylize(""+t,"boolean");if(g(t))return e.stylize("null","null")}(e,n);if(i)return i;var a=Object.keys(n),s=function(e){var t={};return e.forEach(function(e,n){t[e]=!0}),t}(a);if(e.showHidden&&(a=Object.getOwnPropertyNames(n)),S(n)&&(a.indexOf("message")>=0||a.indexOf("description")>=0))return f(n);if(0===a.length){if(C(n)){var c=n.name?": "+n.name:"";return e.stylize("[Function"+c+"]","special")}if(m(n))return e.stylize(RegExp.prototype.toString.call(n),"regexp");if(E(n))return e.stylize(Date.prototype.toString.call(n),"date");if(S(n))return f(n)}var u,w="",_=!1,I=["{","}"];(p(n)&&(_=!0,I=["[","]"]),C(n))&&(w=" [Function"+(n.name?": "+n.name:"")+"]");return m(n)&&(w=" "+RegExp.prototype.toString.call(n)),E(n)&&(w=" "+Date.prototype.toUTCString.call(n)),S(n)&&(w=" "+f(n)),0!==a.length||_&&0!=n.length?r<0?m(n)?e.stylize(RegExp.prototype.toString.call(n),"regexp"):e.stylize("[Object]","special"):(e.seen.push(n),u=_?function(e,t,n,r,o){for(var i=[],a=0,s=t.length;a=0&&0,e+t.replace(/\u001b\[\d\d?m/g,"").length+1},0)>60)return n[0]+(""===t?"":t+"\n ")+" "+e.join(",\n ")+" "+n[1];return n[0]+t+" "+e.join(", ")+" "+n[1]}(u,w,I)):I[0]+w+I[1]}function f(e){return"["+Error.prototype.toString.call(e)+"]"}function h(e,t,n,r,o,i){var a,s,c;if((c=Object.getOwnPropertyDescriptor(t,o)||{value:t[o]}).get?s=c.set?e.stylize("[Getter/Setter]","special"):e.stylize("[Getter]","special"):c.set&&(s=e.stylize("[Setter]","special")),k(r,o)||(a="["+o+"]"),s||(e.seen.indexOf(c.value)<0?(s=g(n)?l(e,c.value,null):l(e,c.value,n-1)).indexOf("\n")>-1&&(s=i?s.split("\n").map(function(e){return" "+e}).join("\n").substr(2):"\n"+s.split("\n").map(function(e){return" "+e}).join("\n")):s=e.stylize("[Circular]","special")),b(a)){if(i&&o.match(/^\d+$/))return s;(a=JSON.stringify(""+o)).match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)?(a=a.substr(1,a.length-2),a=e.stylize(a,"name")):(a=a.replace(/'/g,"\\'").replace(/\\"/g,'"').replace(/(^"|"$)/g,"'"),a=e.stylize(a,"string"))}return a+": "+s}function p(e){return Array.isArray(e)}function d(e){return"boolean"==typeof e}function g(e){return null===e}function y(e){return"number"==typeof e}function v(e){return"string"==typeof e}function b(e){return void 0===e}function m(e){return w(e)&&"[object RegExp]"===_(e)}function w(e){return"object"==typeof e&&null!==e}function E(e){return w(e)&&"[object Date]"===_(e)}function S(e){return w(e)&&("[object Error]"===_(e)||e instanceof Error)}function C(e){return"function"==typeof e}function _(e){return Object.prototype.toString.call(e)}function I(e){return e<10?"0"+e.toString(10):e.toString(10)}t.debuglog=function(n){if(b(i)&&(i=e.env.NODE_DEBUG||""),n=n.toUpperCase(),!a[n])if(new RegExp("\\b"+n+"\\b","i").test(i)){var r=e.pid;a[n]=function(){var e=t.format.apply(t,arguments);console.error("%s %d: %s",n,r,e)}}else a[n]=function(){};return a[n]},t.inspect=s,s.colors={bold:[1,22],italic:[3,23],underline:[4,24],inverse:[7,27],white:[37,39],grey:[90,39],black:[30,39],blue:[34,39],cyan:[36,39],green:[32,39],magenta:[35,39],red:[31,39],yellow:[33,39]},s.styles={special:"cyan",number:"yellow",boolean:"yellow",undefined:"grey",null:"bold",string:"green",date:"magenta",regexp:"red"},t.isArray=p,t.isBoolean=d,t.isNull=g,t.isNullOrUndefined=function(e){return null==e},t.isNumber=y,t.isString=v,t.isSymbol=function(e){return"symbol"==typeof e},t.isUndefined=b,t.isRegExp=m,t.isObject=w,t.isDate=E,t.isError=S,t.isFunction=C,t.isPrimitive=function(e){return null===e||"boolean"==typeof e||"number"==typeof e||"string"==typeof e||"symbol"==typeof e||void 0===e},t.isBuffer=n(53);var T=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function k(e,t){return Object.prototype.hasOwnProperty.call(e,t)}t.log=function(){var e,n;console.log("%s - %s",(e=new Date,n=[I(e.getHours()),I(e.getMinutes()),I(e.getSeconds())].join(":"),[e.getDate(),T[e.getMonth()],n].join(" ")),t.format.apply(t,arguments))},t.inherits=n(54),t._extend=function(e,t){if(!t||!w(t))return e;for(var n=Object.keys(t),r=n.length;r--;)e[n[r]]=t[n[r]];return e};var x="undefined"!=typeof Symbol?Symbol("util.promisify.custom"):void 0;function P(e,t){if(!e){var n=new Error("Promise was rejected with a falsy value");n.reason=e,e=n}return t(e)}t.promisify=function(e){if("function"!=typeof e)throw new TypeError('The "original" argument must be of type Function');if(x&&e[x]){var t;if("function"!=typeof(t=e[x]))throw new TypeError('The "util.promisify.custom" argument must be of type Function');return Object.defineProperty(t,x,{value:t,enumerable:!1,writable:!1,configurable:!0}),t}function t(){for(var t,n,r=new Promise(function(e,r){t=e,n=r}),o=[],i=0;i0?("string"==typeof t||a.objectMode||Object.getPrototypeOf(t)===u.prototype||(t=function(e){return u.from(e)}(t)),r?a.endEmitted?e.emit("error",new Error("stream.unshift() after end event")):E(e,a,t,!0):a.ended?e.emit("error",new Error("stream.push() after EOF")):(a.reading=!1,a.decoder&&!n?(t=a.decoder.write(t),a.objectMode||0!==t.length?E(e,a,t,!1):T(e,a)):E(e,a,t,!1))):r||(a.reading=!1));return function(e){return!e.ended&&(e.needReadable||e.lengtht.highWaterMark&&(t.highWaterMark=function(e){return e>=S?e=S:(e--,e|=e>>>1,e|=e>>>2,e|=e>>>4,e|=e>>>8,e|=e>>>16,e++),e}(e)),e<=t.length?e:t.ended?t.length:(t.needReadable=!0,0))}function _(e){var t=e._readableState;t.needReadable=!1,t.emittedReadable||(p("emitReadable",t.flowing),t.emittedReadable=!0,t.sync?o.nextTick(I,e):I(e))}function I(e){p("emit readable"),e.emit("readable"),R(e)}function T(e,t){t.readingMore||(t.readingMore=!0,o.nextTick(k,e,t))}function k(e,t){for(var n=t.length;!t.reading&&!t.flowing&&!t.ended&&t.length=t.length?(n=t.decoder?t.buffer.join(""):1===t.buffer.length?t.buffer.head.data:t.buffer.concat(t.length),t.buffer.clear()):n=function(e,t,n){var r;ei.length?i.length:e;if(a===i.length?o+=i:o+=i.slice(0,e),0===(e-=a)){a===i.length?(++r,n.next?t.head=n.next:t.head=t.tail=null):(t.head=n,n.data=i.slice(a));break}++r}return t.length-=r,o}(e,t):function(e,t){var n=u.allocUnsafe(e),r=t.head,o=1;r.data.copy(n),e-=r.data.length;for(;r=r.next;){var i=r.data,a=e>i.length?i.length:e;if(i.copy(n,n.length-e,0,a),0===(e-=a)){a===i.length?(++o,r.next?t.head=r.next:t.head=t.tail=null):(t.head=r,r.data=i.slice(a));break}++o}return t.length-=o,n}(e,t);return r}(e,t.buffer,t.decoder),n);var n}function O(e){var t=e._readableState;if(t.length>0)throw new Error('"endReadable()" called on non-empty stream');t.endEmitted||(t.ended=!0,o.nextTick(M,t,e))}function M(e,t){e.endEmitted||0!==e.length||(e.endEmitted=!0,t.readable=!1,t.emit("end"))}function L(e,t){for(var n=0,r=e.length;n=t.highWaterMark||t.ended))return p("read: emitReadable",t.length,t.ended),0===t.length&&t.ended?O(this):_(this),null;if(0===(e=C(e,t))&&t.ended)return 0===t.length&&O(this),null;var r,o=t.needReadable;return p("need readable",o),(0===t.length||t.length-e0?D(e,t):null)?(t.needReadable=!0,e=0):t.length-=e,0===t.length&&(t.ended||(t.needReadable=!0),n!==e&&t.ended&&O(this)),null!==r&&this.emit("data",r),r},m.prototype._read=function(e){this.emit("error",new Error("_read() is not implemented"))},m.prototype.pipe=function(e,t){var n=this,i=this._readableState;switch(i.pipesCount){case 0:i.pipes=e;break;case 1:i.pipes=[i.pipes,e];break;default:i.pipes.push(e)}i.pipesCount+=1,p("pipe count=%d opts=%j",i.pipesCount,t);var c=(!t||!1!==t.end)&&e!==r.stdout&&e!==r.stderr?l:m;function u(t,r){p("onunpipe"),t===n&&r&&!1===r.hasUnpiped&&(r.hasUnpiped=!0,p("cleanup"),e.removeListener("close",v),e.removeListener("finish",b),e.removeListener("drain",f),e.removeListener("error",y),e.removeListener("unpipe",u),n.removeListener("end",l),n.removeListener("end",m),n.removeListener("data",g),h=!0,!i.awaitDrain||e._writableState&&!e._writableState.needDrain||f())}function l(){p("onend"),e.end()}i.endEmitted?o.nextTick(c):n.once("end",c),e.on("unpipe",u);var f=function(e){return function(){var t=e._readableState;p("pipeOnDrain",t.awaitDrain),t.awaitDrain&&t.awaitDrain--,0===t.awaitDrain&&s(e,"data")&&(t.flowing=!0,R(e))}}(n);e.on("drain",f);var h=!1;var d=!1;function g(t){p("ondata"),d=!1,!1!==e.write(t)||d||((1===i.pipesCount&&i.pipes===e||i.pipesCount>1&&-1!==L(i.pipes,e))&&!h&&(p("false write response, pause",n._readableState.awaitDrain),n._readableState.awaitDrain++,d=!0),n.pause())}function y(t){p("onerror",t),m(),e.removeListener("error",y),0===s(e,"error")&&e.emit("error",t)}function v(){e.removeListener("finish",b),m()}function b(){p("onfinish"),e.removeListener("close",v),m()}function m(){p("unpipe"),n.unpipe(e)}return n.on("data",g),function(e,t,n){if("function"==typeof e.prependListener)return e.prependListener(t,n);e._events&&e._events[t]?a(e._events[t])?e._events[t].unshift(n):e._events[t]=[n,e._events[t]]:e.on(t,n)}(e,"error",y),e.once("close",v),e.once("finish",b),e.emit("pipe",n),i.flowing||(p("pipe resume"),n.resume()),e},m.prototype.unpipe=function(e){var t=this._readableState,n={hasUnpiped:!1};if(0===t.pipesCount)return this;if(1===t.pipesCount)return e&&e!==t.pipes?this:(e||(e=t.pipes),t.pipes=null,t.pipesCount=0,t.flowing=!1,e&&e.emit("unpipe",this,n),this);if(!e){var r=t.pipes,o=t.pipesCount;t.pipes=null,t.pipesCount=0,t.flowing=!1;for(var i=0;i0&&a.length>o&&!a.warned){a.warned=!0;var c=new Error("Possible EventEmitter memory leak detected. "+a.length+" "+String(t)+" listeners added. Use emitter.setMaxListeners() to increase limit");c.name="MaxListenersExceededWarning",c.emitter=e,c.type=t,c.count=a.length,s=c,console&&console.warn&&console.warn(s)}return e}function f(e,t,n){var r={fired:!1,wrapFn:void 0,target:e,type:t,listener:n},o=function(){for(var e=[],t=0;t0&&(a=t[0]),a instanceof Error)throw a;var s=new Error("Unhandled error."+(a?" ("+a.message+")":""));throw s.context=a,s}var c=o[e];if(void 0===c)return!1;if("function"==typeof c)i(c,this,t);else{var u=c.length,l=d(c,u);for(n=0;n=0;i--)if(n[i]===t||n[i].listener===t){a=n[i].listener,o=i;break}if(o<0)return this;0===o?n.shift():function(e,t){for(;t+1=0;r--)this.removeListener(e,t[r]);return this},s.prototype.listeners=function(e){return h(this,e,!0)},s.prototype.rawListeners=function(e){return h(this,e,!1)},s.listenerCount=function(e,t){return"function"==typeof e.listenerCount?e.listenerCount(t):p.call(e,t)},s.prototype.listenerCount=p,s.prototype.eventNames=function(){return this._eventsCount>0?r(this._events):[]}},function(e,t,n){e.exports=n(35).EventEmitter},function(e,t,n){"use strict";var r=n(21);function o(e,t){e.emit("error",t)}e.exports={destroy:function(e,t){var n=this,i=this._readableState&&this._readableState.destroyed,a=this._writableState&&this._writableState.destroyed;return i||a?(t?t(e):!e||this._writableState&&this._writableState.errorEmitted||r.nextTick(o,this,e),this):(this._readableState&&(this._readableState.destroyed=!0),this._writableState&&(this._writableState.destroyed=!0),this._destroy(e||null,function(e){!t&&e?(r.nextTick(o,n,e),n._writableState&&(n._writableState.errorEmitted=!0)):t&&t(e)}),this)},undestroy:function(){this._readableState&&(this._readableState.destroyed=!1,this._readableState.reading=!1,this._readableState.ended=!1,this._readableState.endEmitted=!1),this._writableState&&(this._writableState.destroyed=!1,this._writableState.ended=!1,this._writableState.ending=!1,this._writableState.finished=!1,this._writableState.errorEmitted=!1)}}},function(e,t,n){"use strict";var r=n(60).Buffer,o=r.isEncoding||function(e){switch((e=""+e)&&e.toLowerCase()){case"hex":case"utf8":case"utf-8":case"ascii":case"binary":case"base64":case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":case"raw":return!0;default:return!1}};function i(e){var t;switch(this.encoding=function(e){var t=function(e){if(!e)return"utf8";for(var t;;)switch(e){case"utf8":case"utf-8":return"utf8";case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return"utf16le";case"latin1":case"binary":return"latin1";case"base64":case"ascii":case"hex":return e;default:if(t)return;e=(""+e).toLowerCase(),t=!0}}(e);if("string"!=typeof t&&(r.isEncoding===o||!o(e)))throw new Error("Unknown encoding: "+e);return t||e}(e),this.encoding){case"utf16le":this.text=c,this.end=u,t=4;break;case"utf8":this.fillLast=s,t=4;break;case"base64":this.text=l,this.end=f,t=3;break;default:return this.write=h,void(this.end=p)}this.lastNeed=0,this.lastTotal=0,this.lastChar=r.allocUnsafe(t)}function a(e){return e<=127?0:e>>5==6?2:e>>4==14?3:e>>3==30?4:e>>6==2?-1:-2}function s(e){var t=this.lastTotal-this.lastNeed,n=function(e,t,n){if(128!=(192&t[0]))return e.lastNeed=0,"�";if(e.lastNeed>1&&t.length>1){if(128!=(192&t[1]))return e.lastNeed=1,"�";if(e.lastNeed>2&&t.length>2&&128!=(192&t[2]))return e.lastNeed=2,"�"}}(this,e);return void 0!==n?n:this.lastNeed<=e.length?(e.copy(this.lastChar,t,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal)):(e.copy(this.lastChar,t,0,e.length),void(this.lastNeed-=e.length))}function c(e,t){if((e.length-t)%2==0){var n=e.toString("utf16le",t);if(n){var r=n.charCodeAt(n.length-1);if(r>=55296&&r<=56319)return this.lastNeed=2,this.lastTotal=4,this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1],n.slice(0,-1)}return n}return this.lastNeed=1,this.lastTotal=2,this.lastChar[0]=e[e.length-1],e.toString("utf16le",t,e.length-1)}function u(e){var t=e&&e.length?this.write(e):"";if(this.lastNeed){var n=this.lastTotal-this.lastNeed;return t+this.lastChar.toString("utf16le",0,n)}return t}function l(e,t){var n=(e.length-t)%3;return 0===n?e.toString("base64",t):(this.lastNeed=3-n,this.lastTotal=3,1===n?this.lastChar[0]=e[e.length-1]:(this.lastChar[0]=e[e.length-2],this.lastChar[1]=e[e.length-1]),e.toString("base64",t,e.length-n))}function f(e){var t=e&&e.length?this.write(e):"";return this.lastNeed?t+this.lastChar.toString("base64",0,3-this.lastNeed):t}function h(e){return e.toString(this.encoding)}function p(e){return e&&e.length?this.write(e):""}t.StringDecoder=i,i.prototype.write=function(e){if(0===e.length)return"";var t,n;if(this.lastNeed){if(void 0===(t=this.fillLast(e)))return"";n=this.lastNeed,this.lastNeed=0}else n=0;return n=0)return o>0&&(e.lastNeed=o-1),o;if(--r=0)return o>0&&(e.lastNeed=o-2),o;if(--r=0)return o>0&&(2===o?o=0:e.lastNeed=o-3),o;return 0}(this,e,t);if(!this.lastNeed)return e.toString("utf8",t);this.lastTotal=n;var r=e.length-(n-this.lastNeed);return e.copy(this.lastChar,0,r),e.toString("utf8",t,r)},i.prototype.fillLast=function(e){if(this.lastNeed<=e.length)return e.copy(this.lastChar,this.lastTotal-this.lastNeed,0,this.lastNeed),this.lastChar.toString(this.encoding,0,this.lastTotal);e.copy(this.lastChar,this.lastTotal-this.lastNeed,0,e.length),this.lastNeed-=e.length}},function(e,t,n){"use strict";(function(t,r,o){var i=n(21);function a(e){var t=this;this.next=null,this.entry=null,this.finish=function(){!function(e,t,n){var r=e.entry;e.entry=null;for(;r;){var o=r.callback;t.pendingcb--,o(n),r=r.next}t.corkedRequestsFree?t.corkedRequestsFree.next=e:t.corkedRequestsFree=e}(t,e)}}e.exports=b;var s,c=!t.browser&&["v0.10","v0.9."].indexOf(t.version.slice(0,5))>-1?r:i.nextTick;b.WritableState=v;var u=n(18);u.inherits=n(14);var l={deprecate:n(63)},f=n(36),h=n(13).Buffer,p=o.Uint8Array||function(){};var d,g=n(37);function y(){}function v(e,t){s=s||n(9),e=e||{};var r=t instanceof s;this.objectMode=!!e.objectMode,r&&(this.objectMode=this.objectMode||!!e.writableObjectMode);var o=e.highWaterMark,u=e.writableHighWaterMark,l=this.objectMode?16:16384;this.highWaterMark=o||0===o?o:r&&(u||0===u)?u:l,this.highWaterMark=Math.floor(this.highWaterMark),this.finalCalled=!1,this.needDrain=!1,this.ending=!1,this.ended=!1,this.finished=!1,this.destroyed=!1;var f=!1===e.decodeStrings;this.decodeStrings=!f,this.defaultEncoding=e.defaultEncoding||"utf8",this.length=0,this.writing=!1,this.corked=0,this.sync=!0,this.bufferProcessing=!1,this.onwrite=function(e){!function(e,t){var n=e._writableState,r=n.sync,o=n.writecb;if(function(e){e.writing=!1,e.writecb=null,e.length-=e.writelen,e.writelen=0}(n),t)!function(e,t,n,r,o){--t.pendingcb,n?(i.nextTick(o,r),i.nextTick(_,e,t),e._writableState.errorEmitted=!0,e.emit("error",r)):(o(r),e._writableState.errorEmitted=!0,e.emit("error",r),_(e,t))}(e,n,r,t,o);else{var a=S(n);a||n.corked||n.bufferProcessing||!n.bufferedRequest||E(e,n),r?c(w,e,n,a,o):w(e,n,a,o)}}(t,e)},this.writecb=null,this.writelen=0,this.bufferedRequest=null,this.lastBufferedRequest=null,this.pendingcb=0,this.prefinished=!1,this.errorEmitted=!1,this.bufferedRequestCount=0,this.corkedRequestsFree=new a(this)}function b(e){if(s=s||n(9),!(d.call(b,this)||this instanceof s))return new b(e);this._writableState=new v(e,this),this.writable=!0,e&&("function"==typeof e.write&&(this._write=e.write),"function"==typeof e.writev&&(this._writev=e.writev),"function"==typeof e.destroy&&(this._destroy=e.destroy),"function"==typeof e.final&&(this._final=e.final)),f.call(this)}function m(e,t,n,r,o,i,a){t.writelen=r,t.writecb=a,t.writing=!0,t.sync=!0,n?e._writev(o,t.onwrite):e._write(o,i,t.onwrite),t.sync=!1}function w(e,t,n,r){n||function(e,t){0===t.length&&t.needDrain&&(t.needDrain=!1,e.emit("drain"))}(e,t),t.pendingcb--,r(),_(e,t)}function E(e,t){t.bufferProcessing=!0;var n=t.bufferedRequest;if(e._writev&&n&&n.next){var r=t.bufferedRequestCount,o=new Array(r),i=t.corkedRequestsFree;i.entry=n;for(var s=0,c=!0;n;)o[s]=n,n.isBuf||(c=!1),n=n.next,s+=1;o.allBuffers=c,m(e,t,!0,t.length,o,"",i.finish),t.pendingcb++,t.lastBufferedRequest=null,i.next?(t.corkedRequestsFree=i.next,i.next=null):t.corkedRequestsFree=new a(t),t.bufferedRequestCount=0}else{for(;n;){var u=n.chunk,l=n.encoding,f=n.callback;if(m(e,t,!1,t.objectMode?1:u.length,u,l,f),n=n.next,t.bufferedRequestCount--,t.writing)break}null===n&&(t.lastBufferedRequest=null)}t.bufferedRequest=n,t.bufferProcessing=!1}function S(e){return e.ending&&0===e.length&&null===e.bufferedRequest&&!e.finished&&!e.writing}function C(e,t){e._final(function(n){t.pendingcb--,n&&e.emit("error",n),t.prefinished=!0,e.emit("prefinish"),_(e,t)})}function _(e,t){var n=S(t);return n&&(!function(e,t){t.prefinished||t.finalCalled||("function"==typeof e._final?(t.pendingcb++,t.finalCalled=!0,i.nextTick(C,e,t)):(t.prefinished=!0,e.emit("prefinish")))}(e,t),0===t.pendingcb&&(t.finished=!0,e.emit("finish"))),n}u.inherits(b,f),v.prototype.getBuffer=function(){for(var e=this.bufferedRequest,t=[];e;)t.push(e),e=e.next;return t},function(){try{Object.defineProperty(v.prototype,"buffer",{get:l.deprecate(function(){return this.getBuffer()},"_writableState.buffer is deprecated. Use _writableState.getBuffer instead.","DEP0003")})}catch(e){}}(),"function"==typeof Symbol&&Symbol.hasInstance&&"function"==typeof Function.prototype[Symbol.hasInstance]?(d=Function.prototype[Symbol.hasInstance],Object.defineProperty(b,Symbol.hasInstance,{value:function(e){return!!d.call(this,e)||this===b&&(e&&e._writableState instanceof v)}})):d=function(e){return e instanceof this},b.prototype.pipe=function(){this.emit("error",new Error("Cannot pipe, not readable"))},b.prototype.write=function(e,t,n){var r,o=this._writableState,a=!1,s=!o.objectMode&&(r=e,h.isBuffer(r)||r instanceof p);return s&&!h.isBuffer(e)&&(e=function(e){return h.from(e)}(e)),"function"==typeof t&&(n=t,t=null),s?t="buffer":t||(t=o.defaultEncoding),"function"!=typeof n&&(n=y),o.ended?function(e,t){var n=new Error("write after end");e.emit("error",n),i.nextTick(t,n)}(this,n):(s||function(e,t,n,r){var o=!0,a=!1;return null===n?a=new TypeError("May not write null values to stream"):"string"==typeof n||void 0===n||t.objectMode||(a=new TypeError("Invalid non-string/buffer chunk")),a&&(e.emit("error",a),i.nextTick(r,a),o=!1),o}(this,o,e,n))&&(o.pendingcb++,a=function(e,t,n,r,o,i){if(!n){var a=function(e,t,n){e.objectMode||!1===e.decodeStrings||"string"!=typeof t||(t=h.from(t,n));return t}(t,r,o);r!==a&&(n=!0,o="buffer",r=a)}var s=t.objectMode?1:r.length;t.length+=s;var c=t.length-1))throw new TypeError("Unknown encoding: "+e);return this._writableState.defaultEncoding=e,this},Object.defineProperty(b.prototype,"writableHighWaterMark",{enumerable:!1,get:function(){return this._writableState.highWaterMark}}),b.prototype._write=function(e,t,n){n(new Error("_write() is not implemented"))},b.prototype._writev=null,b.prototype.end=function(e,t,n){var r=this._writableState;"function"==typeof e?(n=e,e=null,t=null):"function"==typeof t&&(n=t,t=null),null!=e&&this.write(e,t),r.corked&&(r.corked=1,this.uncork()),r.ending||r.finished||function(e,t,n){t.ending=!0,_(e,t),n&&(t.finished?i.nextTick(n):e.once("finish",n));t.ended=!0,e.writable=!1}(this,r,n)},Object.defineProperty(b.prototype,"destroyed",{get:function(){return void 0!==this._writableState&&this._writableState.destroyed},set:function(e){this._writableState&&(this._writableState.destroyed=e)}}),b.prototype.destroy=g.destroy,b.prototype._undestroy=g.undestroy,b.prototype._destroy=function(e,t){this.end(),t(e)}}).call(this,n(17),n(61).setImmediate,n(8))},function(e,t,n){"use strict";e.exports=a;var r=n(9),o=n(18);function i(e,t){var n=this._transformState;n.transforming=!1;var r=n.writecb;if(!r)return this.emit("error",new Error("write callback called multiple times"));n.writechunk=null,n.writecb=null,null!=t&&this.push(t),r(e);var o=this._readableState;o.reading=!1,(o.needReadable||o.length0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]=200&&c.statusCode<300?r(new a.b(c.statusCode,c.statusMessage||"",u)):o(new i.b(c.statusMessage||"",c.statusCode||0))});t.abortSignal&&(t.abortSignal.onabort=function(){f.abort(),o(new i.a)})})},n.prototype.getCookieString=function(e){return this.cookieJar.getCookieString(e)},n}(a.a)}).call(this,n(5).Buffer)},function(e,t,n){"use strict";(function(e){n.d(t,"a",function(){return i});var r=n(7),o=n(1),i=function(){function t(){}return t.prototype.writeHandshakeRequest=function(e){return r.a.write(JSON.stringify(e))},t.prototype.parseHandshakeResponse=function(t){var n,i;if(Object(o.g)(t)||void 0!==e&&t instanceof e){var a=new Uint8Array(t);if(-1===(c=a.indexOf(r.a.RecordSeparatorCode)))throw new Error("Message is incomplete.");var s=c+1;n=String.fromCharCode.apply(null,a.slice(0,s)),i=a.byteLength>s?a.slice(s).buffer:null}else{var c,u=t;if(-1===(c=u.indexOf(r.a.RecordSeparator)))throw new Error("Message is incomplete.");s=c+1;n=u.substring(0,s),i=u.length>s?u.substring(s):null}var l=r.a.parse(n),f=JSON.parse(l[0]);if(f.type)throw new Error("Expected a handshake response from the server.");return[i,f]},t}()}).call(this,n(5).Buffer)},,,,,function(e,t,n){"use strict";var r=this&&this.__assign||function(){return(r=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]0?r-4:r,f=0;f>16&255,s[c++]=t>>8&255,s[c++]=255&t;2===a&&(t=o[e.charCodeAt(f)]<<2|o[e.charCodeAt(f+1)]>>4,s[c++]=255&t);1===a&&(t=o[e.charCodeAt(f)]<<10|o[e.charCodeAt(f+1)]<<4|o[e.charCodeAt(f+2)]>>2,s[c++]=t>>8&255,s[c++]=255&t);return s},t.fromByteArray=function(e){for(var t,n=e.length,o=n%3,i=[],a=0,s=n-o;as?s:a+16383));1===o?(t=e[n-1],i.push(r[t>>2]+r[t<<4&63]+"==")):2===o&&(t=(e[n-2]<<8)+e[n-1],i.push(r[t>>10]+r[t>>4&63]+r[t<<2&63]+"="));return i.join("")};for(var r=[],o=[],i="undefined"!=typeof Uint8Array?Uint8Array:Array,a="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",s=0,c=a.length;s0)throw new Error("Invalid string. Length must be a multiple of 4");var n=e.indexOf("=");return-1===n&&(n=t),[n,n===t?0:4-n%4]}function l(e,t,n){for(var o,i,a=[],s=t;s>18&63]+r[i>>12&63]+r[i>>6&63]+r[63&i]);return a.join("")}o["-".charCodeAt(0)]=62,o["_".charCodeAt(0)]=63},function(e,t){t.read=function(e,t,n,r,o){var i,a,s=8*o-r-1,c=(1<>1,l=-7,f=n?o-1:0,h=n?-1:1,p=e[t+f];for(f+=h,i=p&(1<<-l)-1,p>>=-l,l+=s;l>0;i=256*i+e[t+f],f+=h,l-=8);for(a=i&(1<<-l)-1,i>>=-l,l+=r;l>0;a=256*a+e[t+f],f+=h,l-=8);if(0===i)i=1-u;else{if(i===c)return a?NaN:1/0*(p?-1:1);a+=Math.pow(2,r),i-=u}return(p?-1:1)*a*Math.pow(2,i-r)},t.write=function(e,t,n,r,o,i){var a,s,c,u=8*i-o-1,l=(1<>1,h=23===o?Math.pow(2,-24)-Math.pow(2,-77):0,p=r?0:i-1,d=r?1:-1,g=t<0||0===t&&1/t<0?1:0;for(t=Math.abs(t),isNaN(t)||t===1/0?(s=isNaN(t)?1:0,a=l):(a=Math.floor(Math.log(t)/Math.LN2),t*(c=Math.pow(2,-a))<1&&(a--,c*=2),(t+=a+f>=1?h/c:h*Math.pow(2,1-f))*c>=2&&(a++,c/=2),a+f>=l?(s=0,a=l):a+f>=1?(s=(t*c-1)*Math.pow(2,o),a+=f):(s=t*Math.pow(2,f-1)*Math.pow(2,o),a=0));o>=8;e[n+p]=255&s,p+=d,s/=256,o-=8);for(a=a<0;e[n+p]=255&a,p+=d,a/=256,u-=8);e[n+p-d]|=128*g}},function(e,t){var n={}.toString;e.exports=Array.isArray||function(e){return"[object Array]"==n.call(e)}},function(e,t,n){"use strict";(function(t){ /*! * The buffer module from node.js, for the browser. * * @author Feross Aboukhadijeh * @license MIT */ -function r(e,t){if(e===t)return 0;for(var n=e.length,r=t.length,o=0,i=Math.min(n,r);o=0;u--)if(l[u]!==f[u])return!1;for(u=l.length-1;u>=0;u--)if(c=l[u],!b(e[c],t[c],n,r))return!1;return!0}(e,t,n,a))}return n?e===t:e==t}function m(e){return"[object Arguments]"==Object.prototype.toString.call(e)}function w(e,t){if(!e||!t)return!1;if("[object RegExp]"==Object.prototype.toString.call(t))return t.test(e);try{if(e instanceof t)return!0}catch(e){}return!Error.isPrototypeOf(t)&&!0===t.call({},e)}function E(e,t,n,r){var o;if("function"!=typeof t)throw new TypeError('"block" argument must be a function');"string"==typeof n&&(r=n,n=null),o=function(e){var t;try{e()}catch(e){t=e}return t}(t),r=(n&&n.name?" ("+n.name+").":".")+(r?" "+r:"."),e&&!o&&y(o,n,"Missing expected exception"+r);var a="string"==typeof r,s=!e&&o&&!n;if((!e&&i.isError(o)&&a&&w(o,n)||s)&&y(o,n,"Got unwanted exception"+r),e&&o&&n&&!w(o,n)||!e&&o)throw o}f.AssertionError=function(e){var t;this.name="AssertionError",this.actual=e.actual,this.expected=e.expected,this.operator=e.operator,e.message?(this.message=e.message,this.generatedMessage=!1):(this.message=d(g((t=this).actual),128)+" "+t.operator+" "+d(g(t.expected),128),this.generatedMessage=!0);var n=e.stackStartFunction||y;if(Error.captureStackTrace)Error.captureStackTrace(this,n);else{var r=new Error;if(r.stack){var o=r.stack,i=p(n),a=o.indexOf("\n"+i);if(a>=0){var s=o.indexOf("\n",a+1);o=o.substring(s+1)}this.stack=o}}},i.inherits(f.AssertionError,Error),f.fail=y,f.ok=v,f.equal=function(e,t,n){e!=t&&y(e,t,n,"==",f.equal)},f.notEqual=function(e,t,n){e==t&&y(e,t,n,"!=",f.notEqual)},f.deepEqual=function(e,t,n){b(e,t,!1)||y(e,t,n,"deepEqual",f.deepEqual)},f.deepStrictEqual=function(e,t,n){b(e,t,!0)||y(e,t,n,"deepStrictEqual",f.deepStrictEqual)},f.notDeepEqual=function(e,t,n){b(e,t,!1)&&y(e,t,n,"notDeepEqual",f.notDeepEqual)},f.notDeepStrictEqual=function e(t,n,r){b(t,n,!0)&&y(t,n,r,"notDeepStrictEqual",e)},f.strictEqual=function(e,t,n){e!==t&&y(e,t,n,"===",f.strictEqual)},f.notStrictEqual=function(e,t,n){e===t&&y(e,t,n,"!==",f.notStrictEqual)},f.throws=function(e,t,n){E(!0,e,t,n)},f.doesNotThrow=function(e,t,n){E(!1,e,t,n)},f.ifError=function(e){if(e)throw e};var S=Object.keys||function(e){var t=[];for(var n in e)a.call(e,n)&&t.push(n);return t}}).call(this,n(8))},function(e,t){e.exports=function(e){return e&&"object"==typeof e&&"function"==typeof e.copy&&"function"==typeof e.fill&&"function"==typeof e.readUInt8}},function(e,t){"function"==typeof Object.create?e.exports=function(e,t){e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})}:e.exports=function(e,t){e.super_=t;var n=function(){};n.prototype=t.prototype,e.prototype=new n,e.prototype.constructor=e}},function(e,t,n){e.exports=n(9)},function(e,t){var n={}.toString;e.exports=Array.isArray||function(e){return"[object Array]"==n.call(e)}},function(e,t){},function(e,t,n){"use strict";var r=n(13).Buffer,o=n(59);e.exports=function(){function e(){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.head=null,this.tail=null,this.length=0}return e.prototype.push=function(e){var t={data:e,next:null};this.length>0?this.tail.next=t:this.head=t,this.tail=t,++this.length},e.prototype.unshift=function(e){var t={data:e,next:this.head};0===this.length&&(this.tail=t),this.head=t,++this.length},e.prototype.shift=function(){if(0!==this.length){var e=this.head.data;return 1===this.length?this.head=this.tail=null:this.head=this.head.next,--this.length,e}},e.prototype.clear=function(){this.head=this.tail=null,this.length=0},e.prototype.join=function(e){if(0===this.length)return"";for(var t=this.head,n=""+t.data;t=t.next;)n+=e+t.data;return n},e.prototype.concat=function(e){if(0===this.length)return r.alloc(0);if(1===this.length)return this.head.data;for(var t,n,o,i=r.allocUnsafe(e>>>0),a=this.head,s=0;a;)t=a.data,n=i,o=s,t.copy(n,o),s+=a.data.length,a=a.next;return i},e}(),o&&o.inspect&&o.inspect.custom&&(e.exports.prototype[o.inspect.custom]=function(){var e=o.inspect({length:this.length});return this.constructor.name+" "+e})},function(e,t){},function(e,t,n){var r=n(5),o=r.Buffer;function i(e,t){for(var n in e)t[n]=e[n]}function a(e,t,n){return o(e,t,n)}o.from&&o.alloc&&o.allocUnsafe&&o.allocUnsafeSlow?e.exports=r:(i(r,t),t.Buffer=a),i(o,a),a.from=function(e,t,n){if("number"==typeof e)throw new TypeError("Argument must not be a number");return o(e,t,n)},a.alloc=function(e,t,n){if("number"!=typeof e)throw new TypeError("Argument must be a number");var r=o(e);return void 0!==t?"string"==typeof n?r.fill(t,n):r.fill(t):r.fill(0),r},a.allocUnsafe=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return o(e)},a.allocUnsafeSlow=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return r.SlowBuffer(e)}},function(e,t,n){(function(e){var r=void 0!==e&&e||"undefined"!=typeof self&&self||window,o=Function.prototype.apply;function i(e,t){this._id=e,this._clearFn=t}t.setTimeout=function(){return new i(o.call(setTimeout,r,arguments),clearTimeout)},t.setInterval=function(){return new i(o.call(setInterval,r,arguments),clearInterval)},t.clearTimeout=t.clearInterval=function(e){e&&e.close()},i.prototype.unref=i.prototype.ref=function(){},i.prototype.close=function(){this._clearFn.call(r,this._id)},t.enroll=function(e,t){clearTimeout(e._idleTimeoutId),e._idleTimeout=t},t.unenroll=function(e){clearTimeout(e._idleTimeoutId),e._idleTimeout=-1},t._unrefActive=t.active=function(e){clearTimeout(e._idleTimeoutId);var t=e._idleTimeout;t>=0&&(e._idleTimeoutId=setTimeout(function(){e._onTimeout&&e._onTimeout()},t))},n(62),t.setImmediate="undefined"!=typeof self&&self.setImmediate||void 0!==e&&e.setImmediate||this&&this.setImmediate,t.clearImmediate="undefined"!=typeof self&&self.clearImmediate||void 0!==e&&e.clearImmediate||this&&this.clearImmediate}).call(this,n(8))},function(e,t,n){(function(e,t){!function(e,n){"use strict";if(!e.setImmediate){var r,o,i,a,s,c=1,u={},l=!1,f=e.document,h=Object.getPrototypeOf&&Object.getPrototypeOf(e);h=h&&h.setTimeout?h:e,"[object process]"==={}.toString.call(e.process)?r=function(e){t.nextTick(function(){d(e)})}:!function(){if(e.postMessage&&!e.importScripts){var t=!0,n=e.onmessage;return e.onmessage=function(){t=!1},e.postMessage("","*"),e.onmessage=n,t}}()?e.MessageChannel?((i=new MessageChannel).port1.onmessage=function(e){d(e.data)},r=function(e){i.port2.postMessage(e)}):f&&"onreadystatechange"in f.createElement("script")?(o=f.documentElement,r=function(e){var t=f.createElement("script");t.onreadystatechange=function(){d(e),t.onreadystatechange=null,o.removeChild(t),t=null},o.appendChild(t)}):r=function(e){setTimeout(d,0,e)}:(a="setImmediate$"+Math.random()+"$",s=function(t){t.source===e&&"string"==typeof t.data&&0===t.data.indexOf(a)&&d(+t.data.slice(a.length))},e.addEventListener?e.addEventListener("message",s,!1):e.attachEvent("onmessage",s),r=function(t){e.postMessage(a+t,"*")}),h.setImmediate=function(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),n=0;n0?this._transform(null,t,n):n()},e.exports.decoder=c,e.exports.encoder=s},function(e,t,n){(t=e.exports=n(34)).Stream=t,t.Readable=t,t.Writable=n(39),t.Duplex=n(9),t.Transform=n(40),t.PassThrough=n(66)},function(e,t,n){"use strict";e.exports=i;var r=n(40),o=n(18);function i(e){if(!(this instanceof i))return new i(e);r.call(this,e)}o.inherits=n(14),o.inherits(i,r),i.prototype._transform=function(e,t,n){n(null,e)}},function(e,t,n){var r=n(20);function o(e){Error.call(this),Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor),this.name=this.constructor.name,this.message=e||"unable to decode"}n(33).inherits(o,Error),e.exports=function(e){return function(e){e instanceof r||(e=r().append(e));var t=i(e);if(t)return e.consume(t.bytesConsumed),t.value;throw new o};function t(e,t,n){return t>=n+e}function n(e,t){return{value:e,bytesConsumed:t}}function i(e,r){r=void 0===r?0:r;var o=e.length-r;if(o<=0)return null;var i,l,f,h=e.readUInt8(r),p=0;if(!function(e,t){var n=function(e){switch(e){case 196:return 2;case 197:return 3;case 198:return 5;case 199:return 3;case 200:return 4;case 201:return 6;case 202:return 5;case 203:return 9;case 204:return 2;case 205:return 3;case 206:return 5;case 207:return 9;case 208:return 2;case 209:return 3;case 210:return 5;case 211:return 9;case 212:return 3;case 213:return 4;case 214:return 6;case 215:return 10;case 216:return 18;case 217:return 2;case 218:return 3;case 219:return 5;case 222:return 3;default:return-1}}(e);return!(-1!==n&&t=0;f--)p+=e.readUInt8(r+f+1)*Math.pow(2,8*(7-f));return n(p,9);case 208:return n(p=e.readInt8(r+1),2);case 209:return n(p=e.readInt16BE(r+1),3);case 210:return n(p=e.readInt32BE(r+1),5);case 211:return n(p=function(e,t){var n=128==(128&e[t]);if(n)for(var r=1,o=t+7;o>=t;o--){var i=(255^e[o])+r;e[o]=255&i,r=i>>8}var a=e.readUInt32BE(t+0),s=e.readUInt32BE(t+4);return(4294967296*a+s)*(n?-1:1)}(e.slice(r+1,r+9),0),9);case 202:return n(p=e.readFloatBE(r+1),5);case 203:return n(p=e.readDoubleBE(r+1),9);case 217:return t(i=e.readUInt8(r+1),o,2)?n(p=e.toString("utf8",r+2,r+2+i),2+i):null;case 218:return t(i=e.readUInt16BE(r+1),o,3)?n(p=e.toString("utf8",r+3,r+3+i),3+i):null;case 219:return t(i=e.readUInt32BE(r+1),o,5)?n(p=e.toString("utf8",r+5,r+5+i),5+i):null;case 196:return t(i=e.readUInt8(r+1),o,2)?n(p=e.slice(r+2,r+2+i),2+i):null;case 197:return t(i=e.readUInt16BE(r+1),o,3)?n(p=e.slice(r+3,r+3+i),3+i):null;case 198:return t(i=e.readUInt32BE(r+1),o,5)?n(p=e.slice(r+5,r+5+i),5+i):null;case 220:return o<3?null:(i=e.readUInt16BE(r+1),a(e,r,i,3));case 221:return o<5?null:(i=e.readUInt32BE(r+1),a(e,r,i,5));case 222:return i=e.readUInt16BE(r+1),s(e,r,i,3);case 223:throw new Error("map too big to decode in JS");case 212:return c(e,r,1);case 213:return c(e,r,2);case 214:return c(e,r,4);case 215:return c(e,r,8);case 216:return c(e,r,16);case 199:return i=e.readUInt8(r+1),l=e.readUInt8(r+2),t(i,o,3)?u(e,r,l,i,3):null;case 200:return i=e.readUInt16BE(r+1),l=e.readUInt8(r+3),t(i,o,4)?u(e,r,l,i,4):null;case 201:return i=e.readUInt32BE(r+1),l=e.readUInt8(r+5),t(i,o,6)?u(e,r,l,i,6):null}if(144==(240&h))return a(e,r,i=15&h,1);if(128==(240&h))return s(e,r,i=15&h,1);if(160==(224&h))return t(i=31&h,o,1)?n(p=e.toString("utf8",r+1,r+i+1),i+1):null;if(h>=224)return n(p=h-256,1);if(h<128)return n(h,1);throw new Error("not implemented yet")}function a(e,t,r,o){var a,s=[],c=0;for(t+=o,a=0;ai)&&((n=r.allocUnsafe(9))[0]=203,n.writeDoubleBE(e,1)),n}e.exports=function(e,t,n,i){function s(c,u){var l,f,h;if(void 0===c)throw new Error("undefined is not encodable in msgpack!");if(null===c)(l=r.allocUnsafe(1))[0]=192;else if(!0===c)(l=r.allocUnsafe(1))[0]=195;else if(!1===c)(l=r.allocUnsafe(1))[0]=194;else if("string"==typeof c)(f=r.byteLength(c))<32?((l=r.allocUnsafe(1+f))[0]=160|f,f>0&&l.write(c,1)):f<=255&&!n?((l=r.allocUnsafe(2+f))[0]=217,l[1]=f,l.write(c,2)):f<=65535?((l=r.allocUnsafe(3+f))[0]=218,l.writeUInt16BE(f,1),l.write(c,3)):((l=r.allocUnsafe(5+f))[0]=219,l.writeUInt32BE(f,1),l.write(c,5));else if(c&&(c.readUInt32LE||c instanceof Uint8Array))c instanceof Uint8Array&&(c=r.from(c)),c.length<=255?((l=r.allocUnsafe(2))[0]=196,l[1]=c.length):c.length<=65535?((l=r.allocUnsafe(3))[0]=197,l.writeUInt16BE(c.length,1)):((l=r.allocUnsafe(5))[0]=198,l.writeUInt32BE(c.length,1)),l=o([l,c]);else if(Array.isArray(c))c.length<16?(l=r.allocUnsafe(1))[0]=144|c.length:c.length<65536?((l=r.allocUnsafe(3))[0]=220,l.writeUInt16BE(c.length,1)):((l=r.allocUnsafe(5))[0]=221,l.writeUInt32BE(c.length,1)),l=c.reduce(function(e,t){return e.append(s(t,!0)),e},o().append(l));else{if(!i&&"function"==typeof c.getDate)return function(e){var t,n=1*e,i=Math.floor(n/1e3),a=1e6*(n-1e3*i);if(a||i>4294967295){(t=new r(10))[0]=215,t[1]=-1;var s=4*a,c=i/Math.pow(2,32),u=s+c&4294967295,l=4294967295&i;t.writeInt32BE(u,2),t.writeInt32BE(l,6)}else(t=new r(6))[0]=214,t[1]=-1,t.writeUInt32BE(Math.floor(n/1e3),2);return o().append(t)}(c);if("object"==typeof c)l=function(t){var n,i,a=-1,s=[];for(n=0;n>8),s.push(255&a)):(s.push(201),s.push(a>>24),s.push(a>>16&255),s.push(a>>8&255),s.push(255&a));return o().append(r.from(s)).append(i)}(c)||function(e){var t,n,i=[],a=0;for(t in e)e.hasOwnProperty(t)&&void 0!==e[t]&&"function"!=typeof e[t]&&(++a,i.push(s(t,!0)),i.push(s(e[t],!0)));a<16?(n=r.allocUnsafe(1))[0]=128|a:((n=r.allocUnsafe(3))[0]=222,n.writeUInt16BE(a,1));return i.unshift(n),i.reduce(function(e,t){return e.append(t)},o())}(c);else if("number"==typeof c){if((h=c)!==Math.floor(h))return a(c,t);if(c>=0)if(c<128)(l=r.allocUnsafe(1))[0]=c;else if(c<256)(l=r.allocUnsafe(2))[0]=204,l[1]=c;else if(c<65536)(l=r.allocUnsafe(3))[0]=205,l.writeUInt16BE(c,1);else if(c<=4294967295)(l=r.allocUnsafe(5))[0]=206,l.writeUInt32BE(c,1);else{if(!(c<=9007199254740991))return a(c,!0);(l=r.allocUnsafe(9))[0]=207,function(e,t){for(var n=7;n>=0;n--)e[n+1]=255&t,t/=256}(l,c)}else if(c>=-32)(l=r.allocUnsafe(1))[0]=256+c;else if(c>=-128)(l=r.allocUnsafe(2))[0]=208,l.writeInt8(c,1);else if(c>=-32768)(l=r.allocUnsafe(3))[0]=209,l.writeInt16BE(c,1);else if(c>-214748365)(l=r.allocUnsafe(5))[0]=210,l.writeInt32BE(c,1);else{if(!(c>=-9007199254740991))return a(c,!0);(l=r.allocUnsafe(9))[0]=211,function(e,t,n){var r=n<0;r&&(n=Math.abs(n));var o=n%4294967296,i=n/4294967296;if(e.writeUInt32BE(Math.floor(i),t+0),e.writeUInt32BE(o,t+4),r)for(var a=1,s=t+7;s>=t;s--){var c=(255^e[s])+a;e[s]=255&c,a=c>>8}}(l,1,c)}}}if(!l)throw new Error("not implemented yet");return u?l:l.slice()}return s}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(e){this.dialog=e}return e.prototype.show=function(){this.removeClasses(),this.dialog.classList.add(e.ShowClassName)},e.prototype.hide=function(){this.removeClasses(),this.dialog.classList.add(e.HideClassName)},e.prototype.failed=function(){this.removeClasses(),this.dialog.classList.add(e.FailedClassName)},e.prototype.removeClasses=function(){this.dialog.classList.remove(e.ShowClassName,e.HideClassName,e.FailedClassName)},e.ShowClassName="components-reconnect-show",e.HideClassName="components-reconnect-hide",e.FailedClassName="components-reconnect-failed",e}();t.UserSpecifiedDisplay=r},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(41),o=function(){function e(e){this.document=e,this.addedToDom=!1,this.modal=this.document.createElement("div"),this.modal.id=r.AutoReconnectCircuitHandler.DialogId;this.modal.style.cssText=["position: fixed","top: 0","right: 0","bottom: 0","left: 0","z-index: 1000","display: none","overflow: hidden","background-color: #fff","opacity: 0.8","text-align: center","font-weight: bold"].join(";"),this.modal.innerHTML='
',this.message=this.modal.querySelector("h5"),this.button=this.modal.querySelector("button"),this.button.addEventListener("click",function(){return window.Blazor.reconnect()})}return e.prototype.show=function(){this.addedToDom||(this.addedToDom=!0,this.document.body.appendChild(this.modal)),this.modal.style.display="block",this.button.style.display="none",this.message.textContent="Attempting to reconnect to the server..."},e.prototype.hide=function(){this.modal.style.display="none"},e.prototype.failed=function(){this.button.style.display="block",this.message.textContent="Failed to reconnect to the server."},e}();t.DefaultReconnectDisplay=o},function(e,t,n){"use strict";var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))(function(o,i){function a(e){try{c(r.next(e))}catch(e){i(e)}}function s(e){try{c(r.throw(e))}catch(e){i(e)}}function c(e){e.done?o(e.value):new n(function(t){t(e.value)}).then(a,s)}c((r=r.apply(e,t||[])).next())})},o=this&&this.__generator||function(e,t){var n,r,o,i,a={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function s(i){return function(s){return function(i){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,r&&(o=2&i[0]?r.return:i[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,i[1])).done)return o;switch(r=0,o&&(i=[2&i[0],o.value]),i[0]){case 0:case 1:o=i;break;case 4:return a.label++,{value:i[1],done:!1};case 5:a.label++,r=i[1],i=[0];continue;case 7:i=a.ops.pop(),a.trys.pop();continue;default:if(!(o=(o=a.trys).length>0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]this.nextBatchId)this.logger.log(s.LogLevel.Debug,"Waiting for batch "+this.nextBatchId+". Batch "+e+" not processed.");else try{this.nextBatchId++,this.logger.log(s.LogLevel.Debug,"Applying batch "+e+"."),i.renderBatch(this.browserRendererId,new a.OutOfProcessRenderBatch(t)),this.completeBatch(n,e)}catch(t){throw this.logger.log(s.LogLevel.Error,"There was an error applying batch "+e+"."),n.send("OnRenderCompleted",e,t.toString()),t}},e.prototype.getLastBatchid=function(){return this.nextBatchId-1},e.prototype.completeBatch=function(e,t){return r(this,void 0,void 0,function(){return o(this,function(n){switch(n.label){case 0:return n.trys.push([0,2,,3]),[4,e.send("OnRenderCompleted",t,null)];case 1:return n.sent(),[3,3];case 2:return n.sent(),this.logger.log(s.LogLevel.Warning,"Failed to deliver completion notification for render '"+t+"'."),[3,3];case 3:return[2]}})})},e.renderQueues=new Map,e}();t.default=c},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(73),o=function(){function e(e){this.batchData=e;var t=new c(e);this.arrayRangeReader=new u(e),this.arrayBuilderSegmentReader=new l(e),this.diffReader=new i(e),this.editReader=new a(e,t),this.frameReader=new s(e,t)}return e.prototype.updatedComponents=function(){return f(this.batchData,this.batchData.length-20)},e.prototype.referenceFrames=function(){return f(this.batchData,this.batchData.length-16)},e.prototype.disposedComponentIds=function(){return f(this.batchData,this.batchData.length-12)},e.prototype.disposedEventHandlerIds=function(){return f(this.batchData,this.batchData.length-8)},e.prototype.updatedComponentsEntry=function(e,t){var n=e+4*t;return f(this.batchData,n)},e.prototype.referenceFramesEntry=function(e,t){return e+16*t},e.prototype.disposedComponentIdsEntry=function(e,t){var n=e+4*t;return f(this.batchData,n)},e.prototype.disposedEventHandlerIdsEntry=function(e,t){var n=e+4*t;return f(this.batchData,n)},e}();t.OutOfProcessRenderBatch=o;var i=function(){function e(e){this.batchDataUint8=e}return e.prototype.componentId=function(e){return f(this.batchDataUint8,e)},e.prototype.edits=function(e){return e+4},e.prototype.editsEntry=function(e,t){return e+16*t},e}(),a=function(){function e(e,t){this.batchDataUint8=e,this.stringReader=t}return e.prototype.editType=function(e){return f(this.batchDataUint8,e)},e.prototype.siblingIndex=function(e){return f(this.batchDataUint8,e+4)},e.prototype.newTreeIndex=function(e){return f(this.batchDataUint8,e+8)},e.prototype.moveToSiblingIndex=function(e){return f(this.batchDataUint8,e+8)},e.prototype.removedAttributeName=function(e){var t=f(this.batchDataUint8,e+12);return this.stringReader.readString(t)},e}(),s=function(){function e(e,t){this.batchDataUint8=e,this.stringReader=t}return e.prototype.frameType=function(e){return f(this.batchDataUint8,e)},e.prototype.subtreeLength=function(e){return f(this.batchDataUint8,e+4)},e.prototype.elementReferenceCaptureId=function(e){var t=f(this.batchDataUint8,e+4);return this.stringReader.readString(t)},e.prototype.componentId=function(e){return f(this.batchDataUint8,e+8)},e.prototype.elementName=function(e){var t=f(this.batchDataUint8,e+8);return this.stringReader.readString(t)},e.prototype.textContent=function(e){var t=f(this.batchDataUint8,e+4);return this.stringReader.readString(t)},e.prototype.markupContent=function(e){var t=f(this.batchDataUint8,e+4);return this.stringReader.readString(t)},e.prototype.attributeName=function(e){var t=f(this.batchDataUint8,e+4);return this.stringReader.readString(t)},e.prototype.attributeValue=function(e){var t=f(this.batchDataUint8,e+8);return this.stringReader.readString(t)},e.prototype.attributeEventHandlerId=function(e){return f(this.batchDataUint8,e+12)},e}(),c=function(){function e(e){this.batchDataUint8=e,this.stringTableStartIndex=f(e,e.length-4)}return e.prototype.readString=function(e){if(-1===e)return null;var t,n=f(this.batchDataUint8,this.stringTableStartIndex+4*e),o=function(e,t){for(var n=0,r=0,o=0;o<4;o++){var i=e[t+o];if(n|=(127&i)<65535&&(u-=65536,r.push(u>>>10&1023|55296),u=56320|1023&u),r.push(u)}r.length>1024&&(o.push(String.fromCharCode.apply(null,r)),r.length=0)}return o.push(String.fromCharCode.apply(null,r)),o.join("")}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(22),o=function(){function e(){}return e.prototype.log=function(e,t){},e.instance=new e,e}();t.NullLogger=o;var i=function(){function e(e){this.minimumLogLevel=e}return e.prototype.log=function(e,t){if(e>=this.minimumLogLevel)switch(e){case r.LogLevel.Critical:case r.LogLevel.Error:console.error("["+(new Date).toISOString()+"] "+r.LogLevel[e]+": "+t);break;case r.LogLevel.Warning:console.warn("["+(new Date).toISOString()+"] "+r.LogLevel[e]+": "+t);break;case r.LogLevel.Information:console.info("["+(new Date).toISOString()+"] "+r.LogLevel[e]+": "+t);break;default:console.log("["+(new Date).toISOString()+"] "+r.LogLevel[e]+": "+t)}},e}();t.ConsoleLogger=i},function(e,t,n){"use strict";var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))(function(o,i){function a(e){try{c(r.next(e))}catch(e){i(e)}}function s(e){try{c(r.throw(e))}catch(e){i(e)}}function c(e){e.done?o(e.value):new n(function(t){t(e.value)}).then(a,s)}c((r=r.apply(e,t||[])).next())})},o=this&&this.__generator||function(e,t){var n,r,o,i,a={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function s(i){return function(s){return function(i){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,r&&(o=2&i[0]?r.return:i[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,i[1])).done)return o;switch(r=0,o&&(i=[2&i[0],o.value]),i[0]){case 0:case 1:o=i;break;case 4:return a.label++,{value:i[1],done:!1};case 5:a.label++,r=i[1],i=[0];continue;case 7:i=a.ops.pop(),a.trys.pop();continue;default:if(!(o=(o=a.trys).length>0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}}},a=this&&this.__read||function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,o,i=n.call(e),a=[];try{for(;(void 0===t||t-- >0)&&!(r=i.next()).done;)a.push(r.value)}catch(e){o={error:e}}finally{try{r&&!r.done&&(n=i.return)&&n.call(i)}finally{if(o)throw o.error}}return a};Object.defineProperty(t,"__esModule",{value:!0});var s=n(19),c=n(76),u=function(){function e(e,t){this.circuitId=e,this.components=t}return e.prototype.reconnect=function(e){return e.invoke("ConnectCircuit",this.circuitId)},e}();function l(e){if(e.nodeType===Node.COMMENT_NODE&&e.textContent){var t=/\W+M.A.C.Component:[^{]*(.*)$/.exec(e.textContent),n=t&&t[1];if(n)try{var r=JSON.parse(n),o=r.componentId,i=r.rendererId,a=r.circuitId;if(void 0!==o&&void 0!==i&&!!a)return{node:e,circuitId:a,rendererId:i,componentId:o};throw new Error("Found malformed start component comment at "+e.textContent)}catch(t){throw new Error("Found malformed start component comment at "+e.textContent)}}}function f(e,t,n,r){for(var o=n;o>=7)>0&&(r|=128),n.push(r)}while(t>0);t=e.byteLength||e.length;var o=new Uint8Array(n.length+t);return o.set(n,0),o.set(e,n.length),o.buffer},e.parse=function(e){for(var t=[],n=new Uint8Array(e),r=[0,7,14,21,28],o=0;o7)throw new Error("Messages bigger than 2GB are not supported.");if(!(n.byteLength>=o+i+a))throw new Error("Incomplete message.");t.push(n.slice?n.slice(o+i,o+i+a):n.subarray(o+i,o+i+a)),o=o+i+a}return t},e}();var s=new Uint8Array([145,i.MessageType.Ping]),c=function(){function e(){this.name="messagepack",this.version=1,this.transferFormat=i.TransferFormat.Binary,this.errorResult=1,this.voidResult=2,this.nonVoidResult=3}return e.prototype.parseMessages=function(e,t){if(!(e instanceof r.Buffer||(n=e,n&&"undefined"!=typeof ArrayBuffer&&(n instanceof ArrayBuffer||n.constructor&&"ArrayBuffer"===n.constructor.name))))throw new Error("Invalid input for MessagePack hub protocol. Expected an ArrayBuffer or Buffer.");var n;null===t&&(t=i.NullLogger.instance);for(var o=[],s=0,c=a.parse(e);s=0;u--)if(l[u]!==f[u])return!1;for(u=l.length-1;u>=0;u--)if(c=l[u],!b(e[c],t[c],n,r))return!1;return!0}(e,t,n,a))}return n?e===t:e==t}function m(e){return"[object Arguments]"==Object.prototype.toString.call(e)}function w(e,t){if(!e||!t)return!1;if("[object RegExp]"==Object.prototype.toString.call(t))return t.test(e);try{if(e instanceof t)return!0}catch(e){}return!Error.isPrototypeOf(t)&&!0===t.call({},e)}function E(e,t,n,r){var o;if("function"!=typeof t)throw new TypeError('"block" argument must be a function');"string"==typeof n&&(r=n,n=null),o=function(e){var t;try{e()}catch(e){t=e}return t}(t),r=(n&&n.name?" ("+n.name+").":".")+(r?" "+r:"."),e&&!o&&y(o,n,"Missing expected exception"+r);var a="string"==typeof r,s=!e&&o&&!n;if((!e&&i.isError(o)&&a&&w(o,n)||s)&&y(o,n,"Got unwanted exception"+r),e&&o&&n&&!w(o,n)||!e&&o)throw o}f.AssertionError=function(e){var t;this.name="AssertionError",this.actual=e.actual,this.expected=e.expected,this.operator=e.operator,e.message?(this.message=e.message,this.generatedMessage=!1):(this.message=d(g((t=this).actual),128)+" "+t.operator+" "+d(g(t.expected),128),this.generatedMessage=!0);var n=e.stackStartFunction||y;if(Error.captureStackTrace)Error.captureStackTrace(this,n);else{var r=new Error;if(r.stack){var o=r.stack,i=p(n),a=o.indexOf("\n"+i);if(a>=0){var s=o.indexOf("\n",a+1);o=o.substring(s+1)}this.stack=o}}},i.inherits(f.AssertionError,Error),f.fail=y,f.ok=v,f.equal=function(e,t,n){e!=t&&y(e,t,n,"==",f.equal)},f.notEqual=function(e,t,n){e==t&&y(e,t,n,"!=",f.notEqual)},f.deepEqual=function(e,t,n){b(e,t,!1)||y(e,t,n,"deepEqual",f.deepEqual)},f.deepStrictEqual=function(e,t,n){b(e,t,!0)||y(e,t,n,"deepStrictEqual",f.deepStrictEqual)},f.notDeepEqual=function(e,t,n){b(e,t,!1)&&y(e,t,n,"notDeepEqual",f.notDeepEqual)},f.notDeepStrictEqual=function e(t,n,r){b(t,n,!0)&&y(t,n,r,"notDeepStrictEqual",e)},f.strictEqual=function(e,t,n){e!==t&&y(e,t,n,"===",f.strictEqual)},f.notStrictEqual=function(e,t,n){e===t&&y(e,t,n,"!==",f.notStrictEqual)},f.throws=function(e,t,n){E(!0,e,t,n)},f.doesNotThrow=function(e,t,n){E(!1,e,t,n)},f.ifError=function(e){if(e)throw e};var S=Object.keys||function(e){var t=[];for(var n in e)a.call(e,n)&&t.push(n);return t}}).call(this,n(8))},function(e,t){e.exports=function(e){return e&&"object"==typeof e&&"function"==typeof e.copy&&"function"==typeof e.fill&&"function"==typeof e.readUInt8}},function(e,t){"function"==typeof Object.create?e.exports=function(e,t){e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})}:e.exports=function(e,t){e.super_=t;var n=function(){};n.prototype=t.prototype,e.prototype=new n,e.prototype.constructor=e}},function(e,t,n){e.exports=n(9)},function(e,t){var n={}.toString;e.exports=Array.isArray||function(e){return"[object Array]"==n.call(e)}},function(e,t){},function(e,t,n){"use strict";var r=n(13).Buffer,o=n(59);e.exports=function(){function e(){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.head=null,this.tail=null,this.length=0}return e.prototype.push=function(e){var t={data:e,next:null};this.length>0?this.tail.next=t:this.head=t,this.tail=t,++this.length},e.prototype.unshift=function(e){var t={data:e,next:this.head};0===this.length&&(this.tail=t),this.head=t,++this.length},e.prototype.shift=function(){if(0!==this.length){var e=this.head.data;return 1===this.length?this.head=this.tail=null:this.head=this.head.next,--this.length,e}},e.prototype.clear=function(){this.head=this.tail=null,this.length=0},e.prototype.join=function(e){if(0===this.length)return"";for(var t=this.head,n=""+t.data;t=t.next;)n+=e+t.data;return n},e.prototype.concat=function(e){if(0===this.length)return r.alloc(0);if(1===this.length)return this.head.data;for(var t,n,o,i=r.allocUnsafe(e>>>0),a=this.head,s=0;a;)t=a.data,n=i,o=s,t.copy(n,o),s+=a.data.length,a=a.next;return i},e}(),o&&o.inspect&&o.inspect.custom&&(e.exports.prototype[o.inspect.custom]=function(){var e=o.inspect({length:this.length});return this.constructor.name+" "+e})},function(e,t){},function(e,t,n){var r=n(5),o=r.Buffer;function i(e,t){for(var n in e)t[n]=e[n]}function a(e,t,n){return o(e,t,n)}o.from&&o.alloc&&o.allocUnsafe&&o.allocUnsafeSlow?e.exports=r:(i(r,t),t.Buffer=a),i(o,a),a.from=function(e,t,n){if("number"==typeof e)throw new TypeError("Argument must not be a number");return o(e,t,n)},a.alloc=function(e,t,n){if("number"!=typeof e)throw new TypeError("Argument must be a number");var r=o(e);return void 0!==t?"string"==typeof n?r.fill(t,n):r.fill(t):r.fill(0),r},a.allocUnsafe=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return o(e)},a.allocUnsafeSlow=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return r.SlowBuffer(e)}},function(e,t,n){(function(e){var r=void 0!==e&&e||"undefined"!=typeof self&&self||window,o=Function.prototype.apply;function i(e,t){this._id=e,this._clearFn=t}t.setTimeout=function(){return new i(o.call(setTimeout,r,arguments),clearTimeout)},t.setInterval=function(){return new i(o.call(setInterval,r,arguments),clearInterval)},t.clearTimeout=t.clearInterval=function(e){e&&e.close()},i.prototype.unref=i.prototype.ref=function(){},i.prototype.close=function(){this._clearFn.call(r,this._id)},t.enroll=function(e,t){clearTimeout(e._idleTimeoutId),e._idleTimeout=t},t.unenroll=function(e){clearTimeout(e._idleTimeoutId),e._idleTimeout=-1},t._unrefActive=t.active=function(e){clearTimeout(e._idleTimeoutId);var t=e._idleTimeout;t>=0&&(e._idleTimeoutId=setTimeout(function(){e._onTimeout&&e._onTimeout()},t))},n(62),t.setImmediate="undefined"!=typeof self&&self.setImmediate||void 0!==e&&e.setImmediate||this&&this.setImmediate,t.clearImmediate="undefined"!=typeof self&&self.clearImmediate||void 0!==e&&e.clearImmediate||this&&this.clearImmediate}).call(this,n(8))},function(e,t,n){(function(e,t){!function(e,n){"use strict";if(!e.setImmediate){var r,o,i,a,s,c=1,u={},l=!1,f=e.document,h=Object.getPrototypeOf&&Object.getPrototypeOf(e);h=h&&h.setTimeout?h:e,"[object process]"==={}.toString.call(e.process)?r=function(e){t.nextTick(function(){d(e)})}:!function(){if(e.postMessage&&!e.importScripts){var t=!0,n=e.onmessage;return e.onmessage=function(){t=!1},e.postMessage("","*"),e.onmessage=n,t}}()?e.MessageChannel?((i=new MessageChannel).port1.onmessage=function(e){d(e.data)},r=function(e){i.port2.postMessage(e)}):f&&"onreadystatechange"in f.createElement("script")?(o=f.documentElement,r=function(e){var t=f.createElement("script");t.onreadystatechange=function(){d(e),t.onreadystatechange=null,o.removeChild(t),t=null},o.appendChild(t)}):r=function(e){setTimeout(d,0,e)}:(a="setImmediate$"+Math.random()+"$",s=function(t){t.source===e&&"string"==typeof t.data&&0===t.data.indexOf(a)&&d(+t.data.slice(a.length))},e.addEventListener?e.addEventListener("message",s,!1):e.attachEvent("onmessage",s),r=function(t){e.postMessage(a+t,"*")}),h.setImmediate=function(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),n=0;n0?this._transform(null,t,n):n()},e.exports.decoder=c,e.exports.encoder=s},function(e,t,n){(t=e.exports=n(34)).Stream=t,t.Readable=t,t.Writable=n(39),t.Duplex=n(9),t.Transform=n(40),t.PassThrough=n(66)},function(e,t,n){"use strict";e.exports=i;var r=n(40),o=n(18);function i(e){if(!(this instanceof i))return new i(e);r.call(this,e)}o.inherits=n(14),o.inherits(i,r),i.prototype._transform=function(e,t,n){n(null,e)}},function(e,t,n){var r=n(20);function o(e){Error.call(this),Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor),this.name=this.constructor.name,this.message=e||"unable to decode"}n(33).inherits(o,Error),e.exports=function(e){return function(e){e instanceof r||(e=r().append(e));var t=i(e);if(t)return e.consume(t.bytesConsumed),t.value;throw new o};function t(e,t,n){return t>=n+e}function n(e,t){return{value:e,bytesConsumed:t}}function i(e,r){r=void 0===r?0:r;var o=e.length-r;if(o<=0)return null;var i,l,f,h=e.readUInt8(r),p=0;if(!function(e,t){var n=function(e){switch(e){case 196:return 2;case 197:return 3;case 198:return 5;case 199:return 3;case 200:return 4;case 201:return 6;case 202:return 5;case 203:return 9;case 204:return 2;case 205:return 3;case 206:return 5;case 207:return 9;case 208:return 2;case 209:return 3;case 210:return 5;case 211:return 9;case 212:return 3;case 213:return 4;case 214:return 6;case 215:return 10;case 216:return 18;case 217:return 2;case 218:return 3;case 219:return 5;case 222:return 3;default:return-1}}(e);return!(-1!==n&&t=0;f--)p+=e.readUInt8(r+f+1)*Math.pow(2,8*(7-f));return n(p,9);case 208:return n(p=e.readInt8(r+1),2);case 209:return n(p=e.readInt16BE(r+1),3);case 210:return n(p=e.readInt32BE(r+1),5);case 211:return n(p=function(e,t){var n=128==(128&e[t]);if(n)for(var r=1,o=t+7;o>=t;o--){var i=(255^e[o])+r;e[o]=255&i,r=i>>8}var a=e.readUInt32BE(t+0),s=e.readUInt32BE(t+4);return(4294967296*a+s)*(n?-1:1)}(e.slice(r+1,r+9),0),9);case 202:return n(p=e.readFloatBE(r+1),5);case 203:return n(p=e.readDoubleBE(r+1),9);case 217:return t(i=e.readUInt8(r+1),o,2)?n(p=e.toString("utf8",r+2,r+2+i),2+i):null;case 218:return t(i=e.readUInt16BE(r+1),o,3)?n(p=e.toString("utf8",r+3,r+3+i),3+i):null;case 219:return t(i=e.readUInt32BE(r+1),o,5)?n(p=e.toString("utf8",r+5,r+5+i),5+i):null;case 196:return t(i=e.readUInt8(r+1),o,2)?n(p=e.slice(r+2,r+2+i),2+i):null;case 197:return t(i=e.readUInt16BE(r+1),o,3)?n(p=e.slice(r+3,r+3+i),3+i):null;case 198:return t(i=e.readUInt32BE(r+1),o,5)?n(p=e.slice(r+5,r+5+i),5+i):null;case 220:return o<3?null:(i=e.readUInt16BE(r+1),a(e,r,i,3));case 221:return o<5?null:(i=e.readUInt32BE(r+1),a(e,r,i,5));case 222:return i=e.readUInt16BE(r+1),s(e,r,i,3);case 223:throw new Error("map too big to decode in JS");case 212:return c(e,r,1);case 213:return c(e,r,2);case 214:return c(e,r,4);case 215:return c(e,r,8);case 216:return c(e,r,16);case 199:return i=e.readUInt8(r+1),l=e.readUInt8(r+2),t(i,o,3)?u(e,r,l,i,3):null;case 200:return i=e.readUInt16BE(r+1),l=e.readUInt8(r+3),t(i,o,4)?u(e,r,l,i,4):null;case 201:return i=e.readUInt32BE(r+1),l=e.readUInt8(r+5),t(i,o,6)?u(e,r,l,i,6):null}if(144==(240&h))return a(e,r,i=15&h,1);if(128==(240&h))return s(e,r,i=15&h,1);if(160==(224&h))return t(i=31&h,o,1)?n(p=e.toString("utf8",r+1,r+i+1),i+1):null;if(h>=224)return n(p=h-256,1);if(h<128)return n(h,1);throw new Error("not implemented yet")}function a(e,t,r,o){var a,s=[],c=0;for(t+=o,a=0;ai)&&((n=r.allocUnsafe(9))[0]=203,n.writeDoubleBE(e,1)),n}e.exports=function(e,t,n,i){function s(c,u){var l,f,h;if(void 0===c)throw new Error("undefined is not encodable in msgpack!");if(null===c)(l=r.allocUnsafe(1))[0]=192;else if(!0===c)(l=r.allocUnsafe(1))[0]=195;else if(!1===c)(l=r.allocUnsafe(1))[0]=194;else if("string"==typeof c)(f=r.byteLength(c))<32?((l=r.allocUnsafe(1+f))[0]=160|f,f>0&&l.write(c,1)):f<=255&&!n?((l=r.allocUnsafe(2+f))[0]=217,l[1]=f,l.write(c,2)):f<=65535?((l=r.allocUnsafe(3+f))[0]=218,l.writeUInt16BE(f,1),l.write(c,3)):((l=r.allocUnsafe(5+f))[0]=219,l.writeUInt32BE(f,1),l.write(c,5));else if(c&&(c.readUInt32LE||c instanceof Uint8Array))c instanceof Uint8Array&&(c=r.from(c)),c.length<=255?((l=r.allocUnsafe(2))[0]=196,l[1]=c.length):c.length<=65535?((l=r.allocUnsafe(3))[0]=197,l.writeUInt16BE(c.length,1)):((l=r.allocUnsafe(5))[0]=198,l.writeUInt32BE(c.length,1)),l=o([l,c]);else if(Array.isArray(c))c.length<16?(l=r.allocUnsafe(1))[0]=144|c.length:c.length<65536?((l=r.allocUnsafe(3))[0]=220,l.writeUInt16BE(c.length,1)):((l=r.allocUnsafe(5))[0]=221,l.writeUInt32BE(c.length,1)),l=c.reduce(function(e,t){return e.append(s(t,!0)),e},o().append(l));else{if(!i&&"function"==typeof c.getDate)return function(e){var t,n=1*e,i=Math.floor(n/1e3),a=1e6*(n-1e3*i);if(a||i>4294967295){(t=new r(10))[0]=215,t[1]=-1;var s=4*a,c=i/Math.pow(2,32),u=s+c&4294967295,l=4294967295&i;t.writeInt32BE(u,2),t.writeInt32BE(l,6)}else(t=new r(6))[0]=214,t[1]=-1,t.writeUInt32BE(Math.floor(n/1e3),2);return o().append(t)}(c);if("object"==typeof c)l=function(t){var n,i,a=-1,s=[];for(n=0;n>8),s.push(255&a)):(s.push(201),s.push(a>>24),s.push(a>>16&255),s.push(a>>8&255),s.push(255&a));return o().append(r.from(s)).append(i)}(c)||function(e){var t,n,i=[],a=0;for(t in e)e.hasOwnProperty(t)&&void 0!==e[t]&&"function"!=typeof e[t]&&(++a,i.push(s(t,!0)),i.push(s(e[t],!0)));a<16?(n=r.allocUnsafe(1))[0]=128|a:((n=r.allocUnsafe(3))[0]=222,n.writeUInt16BE(a,1));return i.unshift(n),i.reduce(function(e,t){return e.append(t)},o())}(c);else if("number"==typeof c){if((h=c)!==Math.floor(h))return a(c,t);if(c>=0)if(c<128)(l=r.allocUnsafe(1))[0]=c;else if(c<256)(l=r.allocUnsafe(2))[0]=204,l[1]=c;else if(c<65536)(l=r.allocUnsafe(3))[0]=205,l.writeUInt16BE(c,1);else if(c<=4294967295)(l=r.allocUnsafe(5))[0]=206,l.writeUInt32BE(c,1);else{if(!(c<=9007199254740991))return a(c,!0);(l=r.allocUnsafe(9))[0]=207,function(e,t){for(var n=7;n>=0;n--)e[n+1]=255&t,t/=256}(l,c)}else if(c>=-32)(l=r.allocUnsafe(1))[0]=256+c;else if(c>=-128)(l=r.allocUnsafe(2))[0]=208,l.writeInt8(c,1);else if(c>=-32768)(l=r.allocUnsafe(3))[0]=209,l.writeInt16BE(c,1);else if(c>-214748365)(l=r.allocUnsafe(5))[0]=210,l.writeInt32BE(c,1);else{if(!(c>=-9007199254740991))return a(c,!0);(l=r.allocUnsafe(9))[0]=211,function(e,t,n){var r=n<0;r&&(n=Math.abs(n));var o=n%4294967296,i=n/4294967296;if(e.writeUInt32BE(Math.floor(i),t+0),e.writeUInt32BE(o,t+4),r)for(var a=1,s=t+7;s>=t;s--){var c=(255^e[s])+a;e[s]=255&c,a=c>>8}}(l,1,c)}}}if(!l)throw new Error("not implemented yet");return u?l:l.slice()}return s}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(e){this.dialog=e}return e.prototype.show=function(){this.removeClasses(),this.dialog.classList.add(e.ShowClassName)},e.prototype.hide=function(){this.removeClasses(),this.dialog.classList.add(e.HideClassName)},e.prototype.failed=function(){this.removeClasses(),this.dialog.classList.add(e.FailedClassName)},e.prototype.removeClasses=function(){this.dialog.classList.remove(e.ShowClassName,e.HideClassName,e.FailedClassName)},e.ShowClassName="components-reconnect-show",e.HideClassName="components-reconnect-hide",e.FailedClassName="components-reconnect-failed",e}();t.UserSpecifiedDisplay=r},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(41),o=function(){function e(e){this.document=e,this.addedToDom=!1,this.modal=this.document.createElement("div"),this.modal.id=r.AutoReconnectCircuitHandler.DialogId;this.modal.style.cssText=["position: fixed","top: 0","right: 0","bottom: 0","left: 0","z-index: 1000","display: none","overflow: hidden","background-color: #fff","opacity: 0.8","text-align: center","font-weight: bold"].join(";"),this.modal.innerHTML='
',this.message=this.modal.querySelector("h5"),this.button=this.modal.querySelector("button"),this.button.addEventListener("click",function(){return window.Blazor.reconnect()})}return e.prototype.show=function(){this.addedToDom||(this.addedToDom=!0,this.document.body.appendChild(this.modal)),this.modal.style.display="block",this.button.style.display="none",this.message.textContent="Attempting to reconnect to the server..."},e.prototype.hide=function(){this.modal.style.display="none"},e.prototype.failed=function(){this.button.style.display="block",this.message.textContent="Failed to reconnect to the server."},e}();t.DefaultReconnectDisplay=o},function(e,t,n){"use strict";var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))(function(o,i){function a(e){try{c(r.next(e))}catch(e){i(e)}}function s(e){try{c(r.throw(e))}catch(e){i(e)}}function c(e){e.done?o(e.value):new n(function(t){t(e.value)}).then(a,s)}c((r=r.apply(e,t||[])).next())})},o=this&&this.__generator||function(e,t){var n,r,o,i,a={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function s(i){return function(s){return function(i){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,r&&(o=2&i[0]?r.return:i[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,i[1])).done)return o;switch(r=0,o&&(i=[2&i[0],o.value]),i[0]){case 0:case 1:o=i;break;case 4:return a.label++,{value:i[1],done:!1};case 5:a.label++,r=i[1],i=[0];continue;case 7:i=a.ops.pop(),a.trys.pop();continue;default:if(!(o=(o=a.trys).length>0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]this.nextBatchId)this.logger.log(s.LogLevel.Debug,"Waiting for batch "+this.nextBatchId+". Batch "+e+" not processed.");else try{this.nextBatchId++,this.logger.log(s.LogLevel.Debug,"Applying batch "+e+"."),i.renderBatch(this.browserRendererId,new a.OutOfProcessRenderBatch(t)),this.completeBatch(n,e)}catch(t){throw this.logger.log(s.LogLevel.Error,"There was an error applying batch "+e+"."),n.send("OnRenderCompleted",e,t.toString()),t}},e.prototype.getLastBatchid=function(){return this.nextBatchId-1},e.prototype.completeBatch=function(e,t){return r(this,void 0,void 0,function(){return o(this,function(n){switch(n.label){case 0:return n.trys.push([0,2,,3]),[4,e.send("OnRenderCompleted",t,null)];case 1:return n.sent(),[3,3];case 2:return n.sent(),this.logger.log(s.LogLevel.Warning,"Failed to deliver completion notification for render '"+t+"'."),[3,3];case 3:return[2]}})})},e.renderQueues=new Map,e}();t.default=c},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(73),o=Math.pow(2,32),i=Math.pow(2,21)-1,a=function(){function e(e){this.batchData=e;var t=new l(e);this.arrayRangeReader=new f(e),this.arrayBuilderSegmentReader=new h(e),this.diffReader=new s(e),this.editReader=new c(e,t),this.frameReader=new u(e,t)}return e.prototype.updatedComponents=function(){return p(this.batchData,this.batchData.length-20)},e.prototype.referenceFrames=function(){return p(this.batchData,this.batchData.length-16)},e.prototype.disposedComponentIds=function(){return p(this.batchData,this.batchData.length-12)},e.prototype.disposedEventHandlerIds=function(){return p(this.batchData,this.batchData.length-8)},e.prototype.updatedComponentsEntry=function(e,t){var n=e+4*t;return p(this.batchData,n)},e.prototype.referenceFramesEntry=function(e,t){return e+20*t},e.prototype.disposedComponentIdsEntry=function(e,t){var n=e+4*t;return p(this.batchData,n)},e.prototype.disposedEventHandlerIdsEntry=function(e,t){var n=e+8*t;return g(this.batchData,n)},e}();t.OutOfProcessRenderBatch=a;var s=function(){function e(e){this.batchDataUint8=e}return e.prototype.componentId=function(e){return p(this.batchDataUint8,e)},e.prototype.edits=function(e){return e+4},e.prototype.editsEntry=function(e,t){return e+16*t},e}(),c=function(){function e(e,t){this.batchDataUint8=e,this.stringReader=t}return e.prototype.editType=function(e){return p(this.batchDataUint8,e)},e.prototype.siblingIndex=function(e){return p(this.batchDataUint8,e+4)},e.prototype.newTreeIndex=function(e){return p(this.batchDataUint8,e+8)},e.prototype.moveToSiblingIndex=function(e){return p(this.batchDataUint8,e+8)},e.prototype.removedAttributeName=function(e){var t=p(this.batchDataUint8,e+12);return this.stringReader.readString(t)},e}(),u=function(){function e(e,t){this.batchDataUint8=e,this.stringReader=t}return e.prototype.frameType=function(e){return p(this.batchDataUint8,e)},e.prototype.subtreeLength=function(e){return p(this.batchDataUint8,e+4)},e.prototype.elementReferenceCaptureId=function(e){var t=p(this.batchDataUint8,e+4);return this.stringReader.readString(t)},e.prototype.componentId=function(e){return p(this.batchDataUint8,e+8)},e.prototype.elementName=function(e){var t=p(this.batchDataUint8,e+8);return this.stringReader.readString(t)},e.prototype.textContent=function(e){var t=p(this.batchDataUint8,e+4);return this.stringReader.readString(t)},e.prototype.markupContent=function(e){var t=p(this.batchDataUint8,e+4);return this.stringReader.readString(t)},e.prototype.attributeName=function(e){var t=p(this.batchDataUint8,e+4);return this.stringReader.readString(t)},e.prototype.attributeValue=function(e){var t=p(this.batchDataUint8,e+8);return this.stringReader.readString(t)},e.prototype.attributeEventHandlerId=function(e){return g(this.batchDataUint8,e+12)},e}(),l=function(){function e(e){this.batchDataUint8=e,this.stringTableStartIndex=p(e,e.length-4)}return e.prototype.readString=function(e){if(-1===e)return null;var t,n=p(this.batchDataUint8,this.stringTableStartIndex+4*e),o=function(e,t){for(var n=0,r=0,o=0;o<4;o++){var i=e[t+o];if(n|=(127&i)<>>0)}function g(e,t){var n=d(e,t+4);if(n>i)throw new Error("Cannot read uint64 with high order part "+n+", because the result would exceed Number.MAX_SAFE_INTEGER.");return n*o+d(e,t)}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r="function"==typeof TextDecoder?new TextDecoder("utf-8"):null;t.decodeUtf8=r?r.decode.bind(r):function(e){var t=0,n=e.length,r=[],o=[];for(;t65535&&(u-=65536,r.push(u>>>10&1023|55296),u=56320|1023&u),r.push(u)}r.length>1024&&(o.push(String.fromCharCode.apply(null,r)),r.length=0)}return o.push(String.fromCharCode.apply(null,r)),o.join("")}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(22),o=function(){function e(){}return e.prototype.log=function(e,t){},e.instance=new e,e}();t.NullLogger=o;var i=function(){function e(e){this.minimumLogLevel=e}return e.prototype.log=function(e,t){if(e>=this.minimumLogLevel)switch(e){case r.LogLevel.Critical:case r.LogLevel.Error:console.error("["+(new Date).toISOString()+"] "+r.LogLevel[e]+": "+t);break;case r.LogLevel.Warning:console.warn("["+(new Date).toISOString()+"] "+r.LogLevel[e]+": "+t);break;case r.LogLevel.Information:console.info("["+(new Date).toISOString()+"] "+r.LogLevel[e]+": "+t);break;default:console.log("["+(new Date).toISOString()+"] "+r.LogLevel[e]+": "+t)}},e}();t.ConsoleLogger=i},function(e,t,n){"use strict";var r=this&&this.__awaiter||function(e,t,n,r){return new(n||(n=Promise))(function(o,i){function a(e){try{c(r.next(e))}catch(e){i(e)}}function s(e){try{c(r.throw(e))}catch(e){i(e)}}function c(e){e.done?o(e.value):new n(function(t){t(e.value)}).then(a,s)}c((r=r.apply(e,t||[])).next())})},o=this&&this.__generator||function(e,t){var n,r,o,i,a={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function s(i){return function(s){return function(i){if(n)throw new TypeError("Generator is already executing.");for(;a;)try{if(n=1,r&&(o=2&i[0]?r.return:i[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,i[1])).done)return o;switch(r=0,o&&(i=[2&i[0],o.value]),i[0]){case 0:case 1:o=i;break;case 4:return a.label++,{value:i[1],done:!1};case 5:a.label++,r=i[1],i=[0];continue;case 7:i=a.ops.pop(),a.trys.pop();continue;default:if(!(o=(o=a.trys).length>0&&o[o.length-1])&&(6===i[0]||2===i[0])){a=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}}},a=this&&this.__read||function(e,t){var n="function"==typeof Symbol&&e[Symbol.iterator];if(!n)return e;var r,o,i=n.call(e),a=[];try{for(;(void 0===t||t-- >0)&&!(r=i.next()).done;)a.push(r.value)}catch(e){o={error:e}}finally{try{r&&!r.done&&(n=i.return)&&n.call(i)}finally{if(o)throw o.error}}return a};Object.defineProperty(t,"__esModule",{value:!0});var s=n(19),c=n(76),u=function(){function e(e,t){this.circuitId=e,this.components=t}return e.prototype.reconnect=function(e){return e.invoke("ConnectCircuit",this.circuitId)},e}();function l(e){if(e.nodeType===Node.COMMENT_NODE&&e.textContent){var t=/\W+M.A.C.Component:[^{]*(.*)$/.exec(e.textContent),n=t&&t[1];if(n){try{var r=JSON.parse(n),o=r.componentId,i=r.rendererId,a=r.circuitId;if(void 0!==o&&void 0!==i&&!!a)return{node:e,circuitId:a,rendererId:i,componentId:o}}catch(e){}throw new Error("Found malformed start component comment at "+e.textContent)}}}function f(e,t,n,r){for(var o=n;o>=7)>0&&(r|=128),n.push(r)}while(t>0);t=e.byteLength||e.length;var o=new Uint8Array(n.length+t);return o.set(n,0),o.set(e,n.length),o.buffer},e.parse=function(e){for(var t=[],n=new Uint8Array(e),r=[0,7,14,21,28],o=0;o7)throw new Error("Messages bigger than 2GB are not supported.");if(!(n.byteLength>=o+i+a))throw new Error("Incomplete message.");t.push(n.slice?n.slice(o+i,o+i+a):n.subarray(o+i,o+i+a)),o=o+i+a}return t},e}();var s=new Uint8Array([145,i.MessageType.Ping]),c=function(){function e(){this.name="messagepack",this.version=1,this.transferFormat=i.TransferFormat.Binary,this.errorResult=1,this.voidResult=2,this.nonVoidResult=3}return e.prototype.parseMessages=function(e,t){if(!(e instanceof r.Buffer||(n=e,n&&"undefined"!=typeof ArrayBuffer&&(n instanceof ArrayBuffer||n.constructor&&"ArrayBuffer"===n.constructor.name))))throw new Error("Invalid input for MessagePack hub protocol. Expected an ArrayBuffer or Buffer.");var n;null===t&&(t=i.NullLogger.instance);for(var o=[],s=0,c=a.parse(e);s0&&!t)throw new Error("New logical elements must start empty, or allowExistingContents must be true");return e[r]=[],e}function u(e,t,n){var a=e;if(e instanceof Comment&&(s(a)&&s(a).length>0))throw new Error("Not implemented: inserting non-empty logical container");if(l(a))throw new Error("Not implemented: moving existing logical children");var i=s(t);if(n0;)e(r,0);var a=r;a.parentNode.removeChild(a)},t.getLogicalParent=l,t.getLogicalSiblingEnd=function(e){return e[a]||null},t.getLogicalChild=function(e,t){return s(e)[t]},t.isSvgElement=function(e){return"http://www.w3.org/2000/svg"===c(e).namespaceURI},t.getLogicalChildrenArray=s,t.permuteLogicalChildren=function(e,t){var n=s(e);t.forEach(function(e){e.moveRangeStart=n[e.fromSiblingIndex],e.moveRangeEnd=function e(t){if(t instanceof Element)return t;var n=f(t);if(n)return n.previousSibling;var r=l(t);return r instanceof Element?r.lastChild:e(r)}(e.moveRangeStart)}),t.forEach(function(t){var r=t.moveToBeforeMarker=document.createComment("marker"),o=n[t.toSiblingIndex+1];o?o.parentNode.insertBefore(r,o):d(r,e)}),t.forEach(function(e){for(var t=e.moveToBeforeMarker,n=t.parentNode,r=e.moveRangeStart,o=e.moveRangeEnd,a=r;a;){var i=a.nextSibling;if(n.insertBefore(a,t),a===o)break;a=i}n.removeChild(t)}),t.forEach(function(e){n[e.toSiblingIndex]=e.moveRangeStart})},t.getClosestDomElement=c},,,function(e,t,n){"use strict";var r;!function(e){window.DotNet=e;var t=[],n={},r={},o=1,a=null;function i(e){t.push(e)}function u(e,t){for(var n=[],r=2;r0&&o[o.length-1])&&(6===a[0]||2===a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]0&&o[o.length-1])&&(6===a[0]||2===a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]0&&o[o.length-1])&&(6===a[0]||2===a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]0&&o[o.length-1])&&(6===a[0]||2===a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]-1?a.substring(0,u):"",s=u>-1?a.substring(u+1):a,c=t.monoPlatform.findMethod(e,l,s,i);t.monoPlatform.callMethod(c,null,r)},callMethod:function(e,n,r){if(r.length>4)throw new Error("Currently, MonoPlatform supports passing a maximum of 4 arguments from JS to .NET. You tried to pass "+r.length+".");var o=Module.stackSave();try{for(var a=Module.stackAlloc(r.length),u=Module.stackAlloc(4),l=0;l0&&!t)throw new Error("New logical elements must start empty, or allowExistingContents must be true");return e[r]=[],e}function u(e,t,n){var a=e;if(e instanceof Comment&&(s(a)&&s(a).length>0))throw new Error("Not implemented: inserting non-empty logical container");if(l(a))throw new Error("Not implemented: moving existing logical children");var i=s(t);if(n0;)e(r,0);var a=r;a.parentNode.removeChild(a)},t.getLogicalParent=l,t.getLogicalSiblingEnd=function(e){return e[a]||null},t.getLogicalChild=function(e,t){return s(e)[t]},t.isSvgElement=function(e){return"http://www.w3.org/2000/svg"===c(e).namespaceURI},t.getLogicalChildrenArray=s,t.permuteLogicalChildren=function(e,t){var n=s(e);t.forEach(function(e){e.moveRangeStart=n[e.fromSiblingIndex],e.moveRangeEnd=function e(t){if(t instanceof Element)return t;var n=f(t);if(n)return n.previousSibling;var r=l(t);return r instanceof Element?r.lastChild:e(r)}(e.moveRangeStart)}),t.forEach(function(t){var r=t.moveToBeforeMarker=document.createComment("marker"),o=n[t.toSiblingIndex+1];o?o.parentNode.insertBefore(r,o):d(r,e)}),t.forEach(function(e){for(var t=e.moveToBeforeMarker,n=t.parentNode,r=e.moveRangeStart,o=e.moveRangeEnd,a=r;a;){var i=a.nextSibling;if(n.insertBefore(a,t),a===o)break;a=i}n.removeChild(t)}),t.forEach(function(e){n[e.toSiblingIndex]=e.moveRangeStart})},t.getClosestDomElement=c},,,function(e,t,n){"use strict";var r;!function(e){window.DotNet=e;var t=[],n={},r={},o=1,a=null;function i(e){t.push(e)}function u(e,t){for(var n=[],r=2;r0&&o[o.length-1])&&(6===a[0]||2===a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]0&&o[o.length-1])&&(6===a[0]||2===a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]0&&o[o.length-1])&&(6===a[0]||2===a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]0&&o[o.length-1])&&(6===a[0]||2===a[0])){i=0;continue}if(3===a[0]&&(!o||a[1]>o[0]&&a[1]-1?a.substring(0,u):"",s=u>-1?a.substring(u+1):a,c=t.monoPlatform.findMethod(e,l,s,i);t.monoPlatform.callMethod(c,null,r)},callMethod:function(e,n,r){if(r.length>4)throw new Error("Currently, MonoPlatform supports passing a maximum of 4 arguments from JS to .NET. You tried to pass "+r.length+".");var o=Module.stackSave();try{for(var a=Module.stackAlloc(r.length),u=Module.stackAlloc(4),l=0;l>2,r=Module.HEAPU32[n+1];if(r>v)throw new Error("Cannot read uint64 with high order part "+r+", because the result would exceed Number.MAX_SAFE_INTEGER.");return r*h+Module.HEAPU32[n]},readFloatField:function(e,t){return Module.getValue(e+(t||0),"float")},readObjectField:function(e,t){return Module.getValue(e+(t||0),"i32")},readStringField:function(e,n){var r=Module.getValue(e+(n||0),"i32");return 0===r?null:t.monoPlatform.toJavaScriptString(r)},readStructField:function(e,t){return e+(t||0)}};var b=document.createElement("a");function w(e){return e+12}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(31),o=window.chrome&&navigator.userAgent.indexOf("Edge")<0,a=!1;function i(){return a&&o}t.hasDebuggingEnabled=i,t.attachDebuggerHotkey=function(e){a=e.some(function(e){return/\.pdb$/.test(r.getFileNameFromUrl(e))});var t=navigator.platform.match(/^Mac/i)?"Cmd":"Alt";i()&&console.info("Debugging hotkey: Shift+"+t+"+D (when application has focus)"),document.addEventListener("keydown",function(e){var t;e.shiftKey&&(e.metaKey||e.altKey)&&"KeyD"===e.code&&(a?o?((t=document.createElement("a")).href="_framework/debug?url="+encodeURIComponent(location.href),t.target="_blank",t.rel="noopener noreferrer",t.click()):console.error("Currently, only Chrome is supported for debugging."):console.error("Cannot start debugging, because the application was not compiled with debugging enabled."))})}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(16),o=function(){function e(e){this.batchAddress=e,this.arrayRangeReader=a,this.arrayBuilderSegmentReader=i,this.diffReader=u,this.editReader=l,this.frameReader=s}return e.prototype.updatedComponents=function(){return r.platform.readStructField(this.batchAddress,0)},e.prototype.referenceFrames=function(){return r.platform.readStructField(this.batchAddress,a.structLength)},e.prototype.disposedComponentIds=function(){return r.platform.readStructField(this.batchAddress,2*a.structLength)},e.prototype.disposedEventHandlerIds=function(){return r.platform.readStructField(this.batchAddress,3*a.structLength)},e.prototype.updatedComponentsEntry=function(e,t){return c(e,t,u.structLength)},e.prototype.referenceFramesEntry=function(e,t){return c(e,t,s.structLength)},e.prototype.disposedComponentIdsEntry=function(e,t){var n=c(e,t,4);return r.platform.readInt32Field(n)},e.prototype.disposedEventHandlerIdsEntry=function(e,t){var n=c(e,t,8);return r.platform.readUint64Field(n)},e}();t.SharedMemoryRenderBatch=o;var a={structLength:8,values:function(e){return r.platform.readObjectField(e,0)},count:function(e){return r.platform.readInt32Field(e,4)}},i={structLength:12,values:function(e){var t=r.platform.readObjectField(e,0),n=r.platform.getObjectFieldsBaseAddress(t);return r.platform.readObjectField(n,0)},offset:function(e){return r.platform.readInt32Field(e,4)},count:function(e){return r.platform.readInt32Field(e,8)}},u={structLength:4+i.structLength,componentId:function(e){return r.platform.readInt32Field(e,0)},edits:function(e){return r.platform.readStructField(e,4)},editsEntry:function(e,t){return c(e,t,l.structLength)}},l={structLength:20,editType:function(e){return r.platform.readInt32Field(e,0)},siblingIndex:function(e){return r.platform.readInt32Field(e,4)},newTreeIndex:function(e){return r.platform.readInt32Field(e,8)},moveToSiblingIndex:function(e){return r.platform.readInt32Field(e,8)},removedAttributeName:function(e){return r.platform.readStringField(e,16)}},s={structLength:36,frameType:function(e){return r.platform.readInt16Field(e,4)},subtreeLength:function(e){return r.platform.readInt32Field(e,8)},elementReferenceCaptureId:function(e){return r.platform.readStringField(e,16)},componentId:function(e){return r.platform.readInt32Field(e,12)},elementName:function(e){return r.platform.readStringField(e,16)},textContent:function(e){return r.platform.readStringField(e,16)},markupContent:function(e){return r.platform.readStringField(e,16)},attributeName:function(e){return r.platform.readStringField(e,16)},attributeValue:function(e){return r.platform.readStringField(e,24)},attributeEventHandlerId:function(e){return r.platform.readUint64Field(e,8)}};function c(e,t,n){return r.platform.getArrayEntryPtr(e,t,n)}}]); \ No newline at end of file diff --git a/src/Components/Web.JS/src/Platform/Circuits/CircuitManager.ts b/src/Components/Web.JS/src/Platform/Circuits/CircuitManager.ts index 6d048a5565..44e351a8f1 100644 --- a/src/Components/Web.JS/src/Platform/Circuits/CircuitManager.ts +++ b/src/Components/Web.JS/src/Platform/Circuits/CircuitManager.ts @@ -94,12 +94,10 @@ function getComponentStartComment(node: Node): StartComponentComment | undefined rendererId: rendererId, componentId: componentId, }; - } else { - throw new Error(`Found malformed start component comment at ${node.textContent}`); } } catch (error) { - throw new Error(`Found malformed start component comment at ${node.textContent}`); } + throw new Error(`Found malformed start component comment at ${node.textContent}`); } } } @@ -114,20 +112,19 @@ function getComponentEndComment(component: StartComponentComment, children: Node } const componentEndComment = /\W+M.A.C.Component:\W+(\d+)\W+$/; const definition = componentEndComment.exec(node.textContent); - const rawComponentId = definition && definition[1]; - if (!rawComponentId) { + const json = definition && definition[1]; + if (!json) { continue; } try { - const componentId = Number.parseInt(rawComponentId); + // The value is expected to be a JSON encoded number + const componentId = JSON.parse(json); if (componentId === component.componentId) { return { componentId, node: node as Comment, index: i }; - } else { - throw new Error(`Found malformed end component comment at ${node.textContent}`); } } catch (error) { - throw new Error(`Found malformed end component comment at ${node.textContent}`); } + throw new Error(`Found malformed end component comment at ${node.textContent}`); } throw new Error(`End component comment not found for ${component.node}`); } diff --git a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts index 067d26e5b8..78930a7d52 100644 --- a/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts +++ b/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts @@ -13,6 +13,8 @@ let invoke_method: (method: MethodHandle, target: System_Object, argsArrayPtr: n let mono_string_get_utf8: (managedString: System_String) => Mono.Utf8Ptr; let mono_string: (jsString: string) => System_String; const appBinDirName = 'appBinDir'; +const uint64HighOrderShift = Math.pow(2, 32); +const maxSafeNumberHighPart = Math.pow(2, 21) - 1; // The high-order int32 from Number.MAX_SAFE_INTEGER export const monoPlatform: Platform = { start: function start(loadAssemblyUrls: string[]) { @@ -114,10 +116,27 @@ export const monoPlatform: Platform = { return (referenceTypedObject as any as number + 8) as any as Pointer; }, + readInt16Field: function readHeapInt16(baseAddress: Pointer, fieldOffset?: number): number { + return Module.getValue((baseAddress as any as number) + (fieldOffset || 0), 'i16'); + }, + readInt32Field: function readHeapInt32(baseAddress: Pointer, fieldOffset?: number): number { return Module.getValue((baseAddress as any as number) + (fieldOffset || 0), 'i32'); }, + readUint64Field: function readHeapUint64(baseAddress: Pointer, fieldOffset?: number): number { + // Module.getValue(..., 'i64') doesn't work because the implementation treats 'i64' as + // being the same as 'i32'. Also we must take care to read both halves as unsigned. + const address = (baseAddress as any as number) + (fieldOffset || 0); + const heapU32Index = address >> 2; + const highPart = Module.HEAPU32[heapU32Index + 1]; + if (highPart > maxSafeNumberHighPart) { + throw new Error(`Cannot read uint64 with high order part ${highPart}, because the result would exceed Number.MAX_SAFE_INTEGER.`); + } + + return (highPart * uint64HighOrderShift) + Module.HEAPU32[heapU32Index]; + }, + readFloatField: function readHeapFloat(baseAddress: Pointer, fieldOffset?: number): number { return Module.getValue((baseAddress as any as number) + (fieldOffset || 0), 'float'); }, diff --git a/src/Components/Web.JS/src/Platform/Platform.ts b/src/Components/Web.JS/src/Platform/Platform.ts index de183f900a..bb2f52113b 100644 --- a/src/Components/Web.JS/src/Platform/Platform.ts +++ b/src/Components/Web.JS/src/Platform/Platform.ts @@ -14,7 +14,9 @@ export interface Platform { getArrayEntryPtr(array: System_Array, index: number, itemSize: number): TPtr; getObjectFieldsBaseAddress(referenceTypedObject: System_Object): Pointer; + readInt16Field(baseAddress: Pointer, fieldOffset?: number): number; readInt32Field(baseAddress: Pointer, fieldOffset?: number): number; + readUint64Field(baseAddress: Pointer, fieldOffset?: number): number; readFloatField(baseAddress: Pointer, fieldOffset?: number): number; readObjectField(baseAddress: Pointer, fieldOffset?: number): T; readStringField(baseAddress: Pointer, fieldOffset?: number): string | null; diff --git a/src/Components/Web.JS/src/Rendering/RenderBatch/OutOfProcessRenderBatch.ts b/src/Components/Web.JS/src/Rendering/RenderBatch/OutOfProcessRenderBatch.ts index 6a528d27a7..e9fc57edf4 100644 --- a/src/Components/Web.JS/src/Rendering/RenderBatch/OutOfProcessRenderBatch.ts +++ b/src/Components/Web.JS/src/Rendering/RenderBatch/OutOfProcessRenderBatch.ts @@ -2,11 +2,13 @@ import { RenderBatch, ArrayRange, RenderTreeDiff, ArrayValues, RenderTreeEdit, E import { decodeUtf8 } from './Utf8Decoder'; const updatedComponentsEntryLength = 4; // Each is a single int32 giving the location of the data -const referenceFramesEntryLength = 16; // 1 byte for frame type, then 3 bytes for type-specific data +const referenceFramesEntryLength = 20; // 1 int for frame type, then 16 bytes for type-specific data const disposedComponentIdsEntryLength = 4; // Each is an int32 giving the ID -const disposedEventHandlerIdsEntryLength = 4; // Each is an int32 giving the ID +const disposedEventHandlerIdsEntryLength = 8; // Each is an int64 giving the ID const editsEntryLength = 16; // 4 ints const stringTableEntryLength = 4; // Each is an int32 giving the string data location, or -1 for null +const uint64HighPartShift = Math.pow(2, 32); +const maxSafeNumberHighPart = Math.pow(2, 21) - 1; // The high-order int32 from Number.MAX_SAFE_INTEGER export class OutOfProcessRenderBatch implements RenderBatch { constructor(private batchData: Uint8Array) { @@ -51,7 +53,7 @@ export class OutOfProcessRenderBatch implements RenderBatch { disposedEventHandlerIdsEntry(values: ArrayValues, index: number): number { const entryPos = (values as any) + index * disposedEventHandlerIdsEntryLength; - return readInt32LE(this.batchData, entryPos); + return readUint64LE(this.batchData, entryPos); } diffReader: RenderTreeDiffReader; @@ -160,7 +162,7 @@ class OutOfProcessRenderTreeFrameReader implements RenderTreeFrameReader { } attributeEventHandlerId(frame: RenderTreeFrame) { - return readInt32LE(this.batchDataUint8, frame as any + 12); // 4th int + return readUint64LE(this.batchDataUint8, frame as any + 12); // Bytes 12-19 } } @@ -235,6 +237,24 @@ function readInt32LE(buffer: Uint8Array, position: number): any { | (buffer[position + 3] << 24); } +function readUint32LE(buffer: Uint8Array, position: number): any { + return (buffer[position]) + + (buffer[position + 1] << 8) + + (buffer[position + 2] << 16) + + ((buffer[position + 3] << 24) >>> 0); // The >>> 0 coerces the value to unsigned +} + +function readUint64LE(buffer: Uint8Array, position: number): any { + // This cannot be done using bit-shift operators in JavaScript, because + // those all implicitly convert to int32 + const highPart = readUint32LE(buffer, position + 4); + if (highPart > maxSafeNumberHighPart) { + throw new Error(`Cannot read uint64 with high order part ${highPart}, because the result would exceed Number.MAX_SAFE_INTEGER.`); + } + + return (highPart * uint64HighPartShift) + readUint32LE(buffer, position); +} + function readLEB128(buffer: Uint8Array, position: number) { let result = 0; let shift = 0; diff --git a/src/Components/Web.JS/src/Rendering/RenderBatch/SharedMemoryRenderBatch.ts b/src/Components/Web.JS/src/Rendering/RenderBatch/SharedMemoryRenderBatch.ts index 3a62f80ac0..853900594d 100644 --- a/src/Components/Web.JS/src/Rendering/RenderBatch/SharedMemoryRenderBatch.ts +++ b/src/Components/Web.JS/src/Rendering/RenderBatch/SharedMemoryRenderBatch.ts @@ -43,8 +43,8 @@ export class SharedMemoryRenderBatch implements RenderBatch { } disposedEventHandlerIdsEntry(values: ArrayValues, index: number) { - const pointer = arrayValuesEntry(values, index, /* int length */ 4); - return platform.readInt32Field(pointer as any as Pointer); + const pointer = arrayValuesEntry(values, index, /* long length */ 8); + return platform.readUint64Field(pointer as any as Pointer); } arrayRangeReader = arrayRangeReader; @@ -99,7 +99,7 @@ const editReader = { // Keep in sync with memory layout in RenderTreeFrame.cs const frameReader = { structLength: 36, - frameType: (frame: RenderTreeFrame) => platform.readInt32Field(frame as any, 4) as FrameType, + frameType: (frame: RenderTreeFrame) => platform.readInt16Field(frame as any, 4) as FrameType, subtreeLength: (frame: RenderTreeFrame) => platform.readInt32Field(frame as any, 8), elementReferenceCaptureId: (frame: RenderTreeFrame) => platform.readStringField(frame as any, 16), componentId: (frame: RenderTreeFrame) => platform.readInt32Field(frame as any, 12), @@ -108,7 +108,7 @@ const frameReader = { markupContent: (frame: RenderTreeFrame) => platform.readStringField(frame as any, 16)!, attributeName: (frame: RenderTreeFrame) => platform.readStringField(frame as any, 16), attributeValue: (frame: RenderTreeFrame) => platform.readStringField(frame as any, 24), - attributeEventHandlerId: (frame: RenderTreeFrame) => platform.readInt32Field(frame as any, 8), + attributeEventHandlerId: (frame: RenderTreeFrame) => platform.readUint64Field(frame as any, 8), }; function arrayValuesEntry(arrayValues: ArrayValues, index: number, itemSize: number): T { diff --git a/src/Components/Web.JS/tests/CircuitManager.test.ts b/src/Components/Web.JS/tests/CircuitManager.test.ts index e70c03ca98..16017f19c4 100644 --- a/src/Components/Web.JS/tests/CircuitManager.test.ts +++ b/src/Components/Web.JS/tests/CircuitManager.test.ts @@ -1,7 +1,6 @@ (global as any).DotNet = { attachReviver: jest.fn() }; import { discoverPrerenderedCircuits } from '../src/Platform/Circuits/CircuitManager'; -import { NullLogger } from '../src/Platform/Logging/Loggers'; import { JSDOM } from 'jsdom'; describe('CircuitManager', () => { @@ -14,7 +13,7 @@ describe('CircuitManager', () => {
Preamble
- +

Prerendered content

@@ -40,11 +39,11 @@ describe('CircuitManager', () => {
Preamble
- +

Prerendered content

- +

Prerendered content

@@ -66,7 +65,31 @@ describe('CircuitManager', () => { expect(second.componentId).toEqual(2); }); - it('discoverPrerenderedCircuits throws for malformed circuits', () => { + it('discoverPrerenderedCircuits throws for malformed circuits - improper nesting', () => { + const dom = new JSDOM(` + + + Page + + +
Preamble
+ +

Prerendered content

+ +
+ +

Prerendered content

+ +
+ + `); + + expect(() => discoverPrerenderedCircuits(dom.window.document)) + .toThrow(); + }); + + + it('discoverPrerenderedCircuits throws for malformed circuits - mixed string and int', () => { const dom = new JSDOM(` @@ -76,11 +99,11 @@ describe('CircuitManager', () => {
Preamble

Prerendered content

- -