diff --git a/.azure/pipelines/quarantined-pr.yml b/.azure/pipelines/quarantined-pr.yml index d6e4b61ed2fe..adcff0e3054a 100644 --- a/.azure/pipelines/quarantined-pr.yml +++ b/.azure/pipelines/quarantined-pr.yml @@ -28,7 +28,7 @@ jobs: jobName: Helix_quarantined_x64 jobDisplayName: 'Tests: Helix' agentOs: Windows - timeoutInMinutes: 240 + timeoutInMinutes: 120 steps: # Build the shared framework - script: ./build.cmd -ci -nobl -all -pack -arch x64 /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log @@ -53,7 +53,7 @@ jobs: jobName: Windows_Quarantined_x64 jobDisplayName: 'Tests: Windows x64' agentOs: Windows - timeoutInMinutes: 240 + timeoutInMinutes: 90 isTestingJob: true steps: - powershell: "& ./build.ps1 -CI -nobl -all -pack -NoBuildJava" @@ -86,7 +86,7 @@ jobs: jobName: MacOS_Quarantined_Test jobDisplayName: "Tests: macOS 10.14" agentOs: macOS - timeoutInMinutes: 240 + timeoutInMinutes: 60 isTestingJob: true steps: - bash: ./build.sh --all --pack --ci --nobl --no-build-java @@ -119,7 +119,7 @@ jobs: jobName: Linux_Quarantined_Test jobDisplayName: "Tests: Ubuntu 16.04 x64" agentOs: Linux - timeoutInMinutes: 240 + timeoutInMinutes: 60 isTestingJob: true useHostedUbuntu: false steps: diff --git a/AspNetCore.sln b/AspNetCore.sln index 978e53e91529..968ff850f8df 100644 --- a/AspNetCore.sln +++ b/AspNetCore.sln @@ -1507,6 +1507,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagno EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Diagnostics.HealthChecks.Tests", "src\HealthChecks\HealthChecks\test\Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj", "{7509AA1E-3093-4BEE-984F-E11579E98A11}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.JSInterop.Tests", "src\JSInterop\Microsoft.JSInterop\test\Microsoft.JSInterop.Tests.csproj", "{DAAB6B35-CBD2-4573-B633-CDD42F583A0E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -7179,6 +7181,18 @@ Global {7509AA1E-3093-4BEE-984F-E11579E98A11}.Release|x64.Build.0 = Release|Any CPU {7509AA1E-3093-4BEE-984F-E11579E98A11}.Release|x86.ActiveCfg = Release|Any CPU {7509AA1E-3093-4BEE-984F-E11579E98A11}.Release|x86.Build.0 = Release|Any CPU + {DAAB6B35-CBD2-4573-B633-CDD42F583A0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DAAB6B35-CBD2-4573-B633-CDD42F583A0E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DAAB6B35-CBD2-4573-B633-CDD42F583A0E}.Debug|x64.ActiveCfg = Debug|Any CPU + {DAAB6B35-CBD2-4573-B633-CDD42F583A0E}.Debug|x64.Build.0 = Debug|Any CPU + {DAAB6B35-CBD2-4573-B633-CDD42F583A0E}.Debug|x86.ActiveCfg = Debug|Any CPU + {DAAB6B35-CBD2-4573-B633-CDD42F583A0E}.Debug|x86.Build.0 = Debug|Any CPU + {DAAB6B35-CBD2-4573-B633-CDD42F583A0E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DAAB6B35-CBD2-4573-B633-CDD42F583A0E}.Release|Any CPU.Build.0 = Release|Any CPU + {DAAB6B35-CBD2-4573-B633-CDD42F583A0E}.Release|x64.ActiveCfg = Release|Any CPU + {DAAB6B35-CBD2-4573-B633-CDD42F583A0E}.Release|x64.Build.0 = Release|Any CPU + {DAAB6B35-CBD2-4573-B633-CDD42F583A0E}.Release|x86.ActiveCfg = Release|Any CPU + {DAAB6B35-CBD2-4573-B633-CDD42F583A0E}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -7934,6 +7948,7 @@ Global {B06040BC-DA28-4923-8CAC-20EB517D471B} = {22D7D74B-565D-4047-97B4-F149B1A13350} {55CACC1F-FE96-47C8-8073-91F4CAA55C75} = {2A91479A-4ABE-4BB7-9A5E-CA3B9CCFC69E} {7509AA1E-3093-4BEE-984F-E11579E98A11} = {7CB09412-C9B0-47E8-A8C3-311AA4CFDE04} + {DAAB6B35-CBD2-4573-B633-CDD42F583A0E} = {16898702-3E33-41C1-B8D8-4CE3F1D46BD9} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F} diff --git a/eng/Dependencies.props b/eng/Dependencies.props index 8da95c63148c..68e1f6149cd7 100644 --- a/eng/Dependencies.props +++ b/eng/Dependencies.props @@ -64,6 +64,7 @@ and are generated based on the last package release. + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index feef8a227779..f2dce7e4a609 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -205,6 +205,10 @@ https://github.com/dotnet/runtime 907f7da59b40c80941b02ac2a46650adf3f606bc + + https://github.com/dotnet/runtime + 907f7da59b40c80941b02ac2a46650adf3f606bc + https://github.com/dotnet/runtime 907f7da59b40c80941b02ac2a46650adf3f606bc diff --git a/eng/Versions.props b/eng/Versions.props index 7dff1b4b2778..9df1d8b152df 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -108,6 +108,7 @@ 5.0.0-rc.1.20417.14 5.0.0-rc.1.20417.14 5.0.0-rc.1.20417.14 + 5.0.0-rc.1.20417.14 5.0.0-rc.1.20417.14 5.0.0-rc.1.20417.14 5.0.0-rc.1.20417.14 diff --git a/src/Antiforgery/src/Internal/DefaultAntiforgeryTokenStore.cs b/src/Antiforgery/src/Internal/DefaultAntiforgeryTokenStore.cs index b35358fea233..513f92b1c6f6 100644 --- a/src/Antiforgery/src/Internal/DefaultAntiforgeryTokenStore.cs +++ b/src/Antiforgery/src/Internal/DefaultAntiforgeryTokenStore.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.IO; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Options; @@ -57,7 +58,24 @@ public async Task GetRequestTokensAsync(HttpContext httpCon { // Check the content-type before accessing the form collection to make sure // we report errors gracefully. - var form = await httpContext.Request.ReadFormAsync(); + IFormCollection form; + try + { + form = await httpContext.Request.ReadFormAsync(); + } + catch (InvalidDataException ex) + { + // ReadFormAsync can throw InvalidDataException if the form content is malformed. + // Wrap it in an AntiforgeryValidationException and allow the caller to handle it as just another antiforgery failure. + throw new AntiforgeryValidationException(Resources.AntiforgeryToken_UnableToReadRequest, ex); + } + catch (IOException ex) + { + // Reading the request body (which happens as part of ReadFromAsync) may throw an exception if a client disconnects. + // Wrap it in an AntiforgeryValidationException and allow the caller to handle it as just another antiforgery failure. + throw new AntiforgeryValidationException(Resources.AntiforgeryToken_UnableToReadRequest, ex); + } + requestToken = form[_options.FormFieldName]; } diff --git a/src/Antiforgery/src/Resources.resx b/src/Antiforgery/src/Resources.resx index eeda70bc6388..1bf0528d9e35 100644 --- a/src/Antiforgery/src/Resources.resx +++ b/src/Antiforgery/src/Resources.resx @@ -136,6 +136,9 @@ Validation of the provided antiforgery token failed. The cookie token and the request token were swapped. + + Unable to read the antiforgery request token from the posted form. + The provided antiforgery token was meant for user "{0}", but the current user is "{1}". diff --git a/src/Antiforgery/test/DefaultAntiforgeryTokenStoreTest.cs b/src/Antiforgery/test/DefaultAntiforgeryTokenStoreTest.cs index e4af2032f52f..8456ee318ad1 100644 --- a/src/Antiforgery/test/DefaultAntiforgeryTokenStoreTest.cs +++ b/src/Antiforgery/test/DefaultAntiforgeryTokenStoreTest.cs @@ -3,6 +3,8 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Primitives; @@ -235,6 +237,56 @@ public async Task GetRequestTokens_BothHeaderValueAndFormFieldsEmpty_ReturnsNull Assert.Null(tokenSet.RequestToken); } + [Fact] + public async Task GetRequestTokens_ReadFormAsyncThrowsIOException_ThrowsAntiforgeryValidationException() + { + // Arrange + var ioException = new IOException(); + var httpContext = new Mock(); + + httpContext.Setup(r => r.Request.Cookies).Returns(Mock.Of()); + httpContext.SetupGet(r => r.Request.HasFormContentType).Returns(true); + httpContext.Setup(r => r.Request.ReadFormAsync(It.IsAny())).Throws(ioException); + + var options = new AntiforgeryOptions + { + Cookie = { Name = "cookie-name" }, + FormFieldName = "form-field-name", + HeaderName = null, + }; + + var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options)); + + // Act & Assert + var ex = await Assert.ThrowsAsync(() => tokenStore.GetRequestTokensAsync(httpContext.Object)); + Assert.Same(ioException, ex.InnerException); + } + + [Fact] + public async Task GetRequestTokens_ReadFormAsyncThrowsInvalidDataException_ThrowsAntiforgeryValidationException() + { + // Arrange + var exception = new InvalidDataException(); + var httpContext = new Mock(); + + httpContext.Setup(r => r.Request.Cookies).Returns(Mock.Of()); + httpContext.SetupGet(r => r.Request.HasFormContentType).Returns(true); + httpContext.Setup(r => r.Request.ReadFormAsync(It.IsAny())).Throws(exception); + + var options = new AntiforgeryOptions + { + Cookie = { Name = "cookie-name" }, + FormFieldName = "form-field-name", + HeaderName = null, + }; + + var tokenStore = new DefaultAntiforgeryTokenStore(new TestOptionsManager(options)); + + // Act & Assert + var ex = await Assert.ThrowsAsync(() => tokenStore.GetRequestTokensAsync(httpContext.Object)); + Assert.Same(exception, ex.InnerException); + } + [Theory] [InlineData(false, CookieSecurePolicy.SameAsRequest, null)] [InlineData(true, CookieSecurePolicy.SameAsRequest, true)] diff --git a/src/Components/Components/src/ComponentFactory.cs b/src/Components/Components/src/ComponentFactory.cs index eddb39a937bf..10c9b1aa06b6 100644 --- a/src/Components/Components/src/ComponentFactory.cs +++ b/src/Components/Components/src/ComponentFactory.cs @@ -63,7 +63,7 @@ private Action CreateInitializer(Type type) ( propertyName: property.Name, propertyType: property.PropertyType, - setter: MemberAssignment.CreatePropertySetter(type, property, cascading: false) + setter: new PropertySetter(type, property) )).ToArray(); return Initialize; diff --git a/src/Components/Components/src/Reflection/ComponentProperties.cs b/src/Components/Components/src/Reflection/ComponentProperties.cs index 1b6e43d7ba4e..dd6aafab26e2 100644 --- a/src/Components/Components/src/Reflection/ComponentProperties.cs +++ b/src/Components/Components/src/Reflection/ComponentProperties.cs @@ -144,7 +144,7 @@ public static void SetProperties(in ParameterView parameters, object target) } } - static void SetProperty(object target, IPropertySetter writer, string parameterName, object value) + static void SetProperty(object target, PropertySetter writer, string parameterName, object value) { try { @@ -246,13 +246,13 @@ private static void ThrowForInvalidCaptureUnmatchedValuesParameterType(Type targ private class WritersForType { private const int MaxCachedWriterLookups = 100; - private readonly Dictionary _underlyingWriters; - private readonly ConcurrentDictionary _referenceEqualityWritersCache; + private readonly Dictionary _underlyingWriters; + private readonly ConcurrentDictionary _referenceEqualityWritersCache; public WritersForType(Type targetType) { - _underlyingWriters = new Dictionary(StringComparer.OrdinalIgnoreCase); - _referenceEqualityWritersCache = new ConcurrentDictionary(ReferenceEqualityComparer.Instance); + _underlyingWriters = new Dictionary(StringComparer.OrdinalIgnoreCase); + _referenceEqualityWritersCache = new ConcurrentDictionary(ReferenceEqualityComparer.Instance); foreach (var propertyInfo in GetCandidateBindableProperties(targetType)) { @@ -271,7 +271,10 @@ public WritersForType(Type targetType) $"The type '{targetType.FullName}' declares a parameter matching the name '{propertyName}' that is not public. Parameters must be public."); } - var propertySetter = MemberAssignment.CreatePropertySetter(targetType, propertyInfo, cascading: cascadingParameterAttribute != null); + var propertySetter = new PropertySetter(targetType, propertyInfo) + { + Cascading = cascadingParameterAttribute != null, + }; if (_underlyingWriters.ContainsKey(propertyName)) { @@ -298,17 +301,17 @@ public WritersForType(Type targetType) ThrowForInvalidCaptureUnmatchedValuesParameterType(targetType, propertyInfo); } - CaptureUnmatchedValuesWriter = MemberAssignment.CreatePropertySetter(targetType, propertyInfo, cascading: false); + CaptureUnmatchedValuesWriter = new PropertySetter(targetType, propertyInfo); CaptureUnmatchedValuesPropertyName = propertyInfo.Name; } } } - public IPropertySetter? CaptureUnmatchedValuesWriter { get; } + public PropertySetter? CaptureUnmatchedValuesWriter { get; } public string? CaptureUnmatchedValuesPropertyName { get; } - public bool TryGetValue(string parameterName, [MaybeNullWhen(false)] out IPropertySetter writer) + public bool TryGetValue(string parameterName, [MaybeNullWhen(false)] out PropertySetter writer) { // In intensive parameter-passing scenarios, one of the most expensive things we do is the // lookup from parameterName to writer. Pre-5.0 that was because of the string hashing. diff --git a/src/Components/Components/src/Reflection/IPropertySetter.cs b/src/Components/Components/src/Reflection/IPropertySetter.cs index d6a60e2395ae..5cd1cb0494d5 100644 --- a/src/Components/Components/src/Reflection/IPropertySetter.cs +++ b/src/Components/Components/src/Reflection/IPropertySetter.cs @@ -1,12 +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; +using System.Reflection; + namespace Microsoft.AspNetCore.Components.Reflection { - internal interface IPropertySetter + internal sealed class PropertySetter { - bool Cascading { get; } + private static readonly MethodInfo CallPropertySetterOpenGenericMethod = + typeof(PropertySetter).GetMethod(nameof(CallPropertySetter), BindingFlags.NonPublic | BindingFlags.Static)!; + + private readonly Action _setterDelegate; + + public PropertySetter(Type targetType, PropertyInfo property) + { + if (property.SetMethod == null) + { + throw new InvalidOperationException($"Cannot provide a value for property " + + $"'{property.Name}' on type '{targetType.FullName}' because the property " + + $"has no setter."); + } + + var setMethod = property.SetMethod; + + var propertySetterAsAction = + setMethod.CreateDelegate(typeof(Action<,>).MakeGenericType(targetType, property.PropertyType)); + var callPropertySetterClosedGenericMethod = + CallPropertySetterOpenGenericMethod.MakeGenericMethod(targetType, property.PropertyType); + _setterDelegate = (Action) + callPropertySetterClosedGenericMethod.CreateDelegate(typeof(Action), propertySetterAsAction); + } + + public bool Cascading { get; init; } + + public void SetValue(object target, object value) => _setterDelegate(target, value); - void SetValue(object target, object value); + private static void CallPropertySetter( + Action setter, + object target, + object value) + where TTarget : notnull + { + if (value == null) + { + setter((TTarget)target, default!); + } + else + { + setter((TTarget)target, (TValue)value); + } + } } } diff --git a/src/Components/Components/src/Reflection/MemberAssignment.cs b/src/Components/Components/src/Reflection/MemberAssignment.cs index 4510d4e81c36..1d0afbe49add 100644 --- a/src/Components/Components/src/Reflection/MemberAssignment.cs +++ b/src/Components/Components/src/Reflection/MemberAssignment.cs @@ -44,46 +44,5 @@ public static IEnumerable GetPropertiesIncludingInherited( return dictionary.Values.SelectMany(p => p); } - - public static IPropertySetter CreatePropertySetter(Type targetType, PropertyInfo property, bool cascading) - { - if (property.SetMethod == null) - { - throw new InvalidOperationException($"Cannot provide a value for property " + - $"'{property.Name}' on type '{targetType.FullName}' because the property " + - $"has no setter."); - } - - return (IPropertySetter)Activator.CreateInstance( - typeof(PropertySetter<,>).MakeGenericType(targetType, property.PropertyType), - property.SetMethod, - cascading)!; - } - - class PropertySetter : IPropertySetter where TTarget : notnull - { - private readonly Action _setterDelegate; - - public PropertySetter(MethodInfo setMethod, bool cascading) - { - _setterDelegate = (Action)Delegate.CreateDelegate( - typeof(Action), setMethod); - Cascading = cascading; - } - - public bool Cascading { get; } - - public void SetValue(object target, object value) - { - if (value == null) - { - _setterDelegate((TTarget)target, default!); - } - else - { - _setterDelegate((TTarget)target, (TValue)value); - } - } - } } } diff --git a/src/Components/Forms/src/EditContext.cs b/src/Components/Forms/src/EditContext.cs index aed3b69dedaf..223f892de774 100644 --- a/src/Components/Forms/src/EditContext.cs +++ b/src/Components/Forms/src/EditContext.cs @@ -31,6 +31,7 @@ public EditContext(object model) // really don't, you can pass an empty object then ignore it. Ensuring it's nonnull // simplifies things for all consumers of EditContext. Model = model ?? throw new ArgumentNullException(nameof(model)); + Properties = new EditContextProperties(); } /// @@ -62,6 +63,11 @@ public FieldIdentifier Field(string fieldName) /// public object Model { get; } + /// + /// Gets a collection of arbitrary properties associated with this instance. + /// + public EditContextProperties Properties { get; } + /// /// Signals that the value for the specified field has changed. /// diff --git a/src/Components/Forms/src/EditContextProperties.cs b/src/Components/Forms/src/EditContextProperties.cs new file mode 100644 index 000000000000..9337d44871a1 --- /dev/null +++ b/src/Components/Forms/src/EditContextProperties.cs @@ -0,0 +1,63 @@ +// 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.Diagnostics.CodeAnalysis; + +namespace Microsoft.AspNetCore.Components.Forms +{ + /// + /// Holds arbitrary key/value pairs associated with an . + /// This can be used to track additional metadata for application-specific purposes. + /// + public sealed class EditContextProperties + { + // We don't want to expose any way of enumerating the underlying dictionary, because that would + // prevent its usage to store private information. So we only expose an indexer and TryGetValue. + private Dictionary? _contents; + + /// + /// Gets or sets a value in the collection. + /// + /// The key under which the value is stored. + /// The stored value. + public object this[object key] + { + get => _contents is null ? throw new KeyNotFoundException() : _contents[key]; + set + { + _contents ??= new Dictionary(); + _contents[key] = value; + } + } + + /// + /// Gets the value associated with the specified key, if any. + /// + /// The key under which the value is stored. + /// The value, if present. + /// True if the value was present, otherwise false. + public bool TryGetValue(object key, [NotNullWhen(true)] out object? value) + { + if (_contents is null) + { + value = default; + return false; + } + else + { + return _contents.TryGetValue(key, out value); + } + } + + /// + /// Removes the specified entry from the collection. + /// + /// The key of the entry to be removed. + /// True if the value was present, otherwise false. + public bool Remove(object key) + { + return _contents?.Remove(key) ?? false; + } + } +} diff --git a/src/Components/Forms/test/EditContextTest.cs b/src/Components/Forms/test/EditContextTest.cs index e26bda2c7f0e..78198c4114b8 100644 --- a/src/Components/Forms/test/EditContextTest.cs +++ b/src/Components/Forms/test/EditContextTest.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.Linq; using Xunit; @@ -252,6 +253,76 @@ public void LookingUpModel_ThatOverridesGetHashCodeAndEquals_Works() Assert.True(editContext.IsModified(editContext.Field(nameof(EquatableModel.Property)))); } + [Fact] + public void Properties_CanRetrieveViaIndexer() + { + // Arrange + var editContext = new EditContext(new object()); + var key1 = new object(); + var key2 = new object(); + var key3 = new object(); + var value1 = new object(); + var value2 = new object(); + + // Initially, the values are not present + Assert.Throws(() => editContext.Properties[key1]); + + // Can store and retrieve values + editContext.Properties[key1] = value1; + editContext.Properties[key2] = value2; + Assert.Same(value1, editContext.Properties[key1]); + Assert.Same(value2, editContext.Properties[key2]); + + // Unrelated keys are still not found + Assert.Throws(() => editContext.Properties[key3]); + } + + [Fact] + public void Properties_CanRetrieveViaTryGetValue() + { + // Arrange + var editContext = new EditContext(new object()); + var key1 = new object(); + var key2 = new object(); + var key3 = new object(); + var value1 = new object(); + var value2 = new object(); + + // Initially, the values are not present + Assert.False(editContext.Properties.TryGetValue(key1, out _)); + + // Can store and retrieve values + editContext.Properties[key1] = value1; + editContext.Properties[key2] = value2; + Assert.True(editContext.Properties.TryGetValue(key1, out var retrievedValue1)); + Assert.True(editContext.Properties.TryGetValue(key2, out var retrievedValue2)); + Assert.Same(value1, retrievedValue1); + Assert.Same(value2, retrievedValue2); + + // Unrelated keys are still not found + Assert.False(editContext.Properties.TryGetValue(key3, out _)); + } + + [Fact] + public void Properties_CanRemove() + { + // Arrange + var editContext = new EditContext(new object()); + var key = new object(); + var value = new object(); + editContext.Properties[key] = value; + + // Act + var resultForExistingKey = editContext.Properties.Remove(key); + var resultForNonExistingKey = editContext.Properties.Remove(new object()); + + // Assert + Assert.True(resultForExistingKey); + Assert.False(resultForNonExistingKey); + Assert.False(editContext.Properties.TryGetValue(key, out _)); + Assert.Throws(() => editContext.Properties[key]); + } + class EquatableModel : IEquatable { public string Property { get; set; } = ""; diff --git a/src/Components/Ignitor/src/BlazorClient.cs b/src/Components/Ignitor/src/BlazorClient.cs index 5353659f3670..b19ab8b3dd5f 100644 --- a/src/Components/Ignitor/src/BlazorClient.cs +++ b/src/Components/Ignitor/src/BlazorClient.cs @@ -353,7 +353,7 @@ public async Task ConnectAsync(Uri uri, bool connectAutomatically = true, _hubConnection = builder.Build(); HubConnection.On("JS.AttachComponent", OnAttachComponent); - HubConnection.On("JS.BeginInvokeJS", OnBeginInvokeJS); + HubConnection.On("JS.BeginInvokeJS", OnBeginInvokeJS); HubConnection.On("JS.EndInvokeDotNet", OnEndInvokeDotNet); HubConnection.On("JS.RenderBatch", OnRenderBatch); HubConnection.On("JS.Error", OnError); @@ -401,9 +401,9 @@ private void OnAttachComponent(int componentId, string domSelector) NextAttachComponentReceived?.Completion?.TrySetResult(call); } - private void OnBeginInvokeJS(int asyncHandle, string identifier, string argsJson) + private void OnBeginInvokeJS(int asyncHandle, string identifier, string argsJson, int resultType, long targetInstanceId) { - var call = new CapturedJSInteropCall(asyncHandle, identifier, argsJson); + var call = new CapturedJSInteropCall(asyncHandle, identifier, argsJson, resultType, targetInstanceId); Operations?.JSInteropCalls.Enqueue(call); JSInterop?.Invoke(call); diff --git a/src/Components/Ignitor/src/CapturedJSInteropCall.cs b/src/Components/Ignitor/src/CapturedJSInteropCall.cs index 4af491a58abd..0dc8b0fa11a1 100644 --- a/src/Components/Ignitor/src/CapturedJSInteropCall.cs +++ b/src/Components/Ignitor/src/CapturedJSInteropCall.cs @@ -5,15 +5,19 @@ namespace Ignitor { public class CapturedJSInteropCall { - public CapturedJSInteropCall(int asyncHandle, string identifier, string argsJson) + public CapturedJSInteropCall(int asyncHandle, string identifier, string argsJson, int resultType, long targetInstanceId) { AsyncHandle = asyncHandle; Identifier = identifier; ArgsJson = argsJson; + ResultType = resultType; + TargetInstanceId = targetInstanceId; } public int AsyncHandle { get; } public string Identifier { get; } public string ArgsJson { get; } + public int ResultType { get; } + public long TargetInstanceId { get; } } } diff --git a/src/Components/Server/src/Circuits/RemoteJSRuntime.cs b/src/Components/Server/src/Circuits/RemoteJSRuntime.cs index 41f03eab0fa4..3a3cb11d27c3 100644 --- a/src/Components/Server/src/Circuits/RemoteJSRuntime.cs +++ b/src/Components/Server/src/Circuits/RemoteJSRuntime.cs @@ -71,7 +71,7 @@ private void EndInvokeDotNetCore(string callId, bool success, object resultOrErr JsonSerializer.Serialize(new[] { callId, success, resultOrError }, JsonSerializerOptions)); } - protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) + protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson, JSCallResultType resultType, long targetInstanceId) { if (_clientProxy is null) { @@ -83,7 +83,7 @@ protected override void BeginInvokeJS(long asyncHandle, string identifier, strin Log.BeginInvokeJS(_logger, asyncHandle, identifier); - _clientProxy.SendAsync("JS.BeginInvokeJS", asyncHandle, identifier, argsJson); + _clientProxy.SendAsync("JS.BeginInvokeJS", asyncHandle, identifier, argsJson, (int)resultType, targetInstanceId); } public static class Log diff --git a/src/Components/Server/src/Circuits/ServerComponentDeserializer.cs b/src/Components/Server/src/Circuits/ServerComponentDeserializer.cs index a8ff087396b1..5d0203dbcac4 100644 --- a/src/Components/Server/src/Circuits/ServerComponentDeserializer.cs +++ b/src/Components/Server/src/Circuits/ServerComponentDeserializer.cs @@ -59,13 +59,13 @@ internal class ServerComponentDeserializer { private readonly IDataProtector _dataProtector; private readonly ILogger _logger; - private readonly ServerComponentTypeCache _rootComponentTypeCache; + private readonly RootComponentTypeCache _rootComponentTypeCache; private readonly ComponentParameterDeserializer _parametersDeserializer; public ServerComponentDeserializer( IDataProtectionProvider dataProtectionProvider, ILogger logger, - ServerComponentTypeCache rootComponentTypeCache, + RootComponentTypeCache rootComponentTypeCache, ComponentParameterDeserializer parametersDeserializer) { // When we protect the data we use a time-limited data protector with the diff --git a/src/Components/Server/src/DependencyInjection/ComponentServiceCollectionExtensions.cs b/src/Components/Server/src/DependencyInjection/ComponentServiceCollectionExtensions.cs index cd65b7fdc4f1..d3868d082b07 100644 --- a/src/Components/Server/src/DependencyInjection/ComponentServiceCollectionExtensions.cs +++ b/src/Components/Server/src/DependencyInjection/ComponentServiceCollectionExtensions.cs @@ -57,7 +57,7 @@ public static IServerSideBlazorBuilder AddServerSideBlazor(this IServiceCollecti services.TryAddEnumerable(ServiceDescriptor.Singleton, ConfigureStaticFilesOptions>()); services.TryAddSingleton(); services.TryAddSingleton(); - services.TryAddSingleton(); + services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); services.TryAddSingleton(); diff --git a/src/Components/Server/src/Microsoft.AspNetCore.Components.Server.csproj b/src/Components/Server/src/Microsoft.AspNetCore.Components.Server.csproj index d13c7467af32..a2c37793e29a 100644 --- a/src/Components/Server/src/Microsoft.AspNetCore.Components.Server.csproj +++ b/src/Components/Server/src/Microsoft.AspNetCore.Components.Server.csproj @@ -1,4 +1,4 @@ - + $(DefaultNetCoreTargetFramework) Runtime server features for ASP.NET Core Components. @@ -54,6 +54,8 @@ + + diff --git a/src/Components/Server/test/Circuits/ServerComponentDeserializerTest.cs b/src/Components/Server/test/Circuits/ServerComponentDeserializerTest.cs index 907fa961923a..e1f5dc34a1f0 100644 --- a/src/Components/Server/test/Circuits/ServerComponentDeserializerTest.cs +++ b/src/Components/Server/test/Circuits/ServerComponentDeserializerTest.cs @@ -320,7 +320,7 @@ private ServerComponentDeserializer CreateServerComponentDeserializer() return new ServerComponentDeserializer( _ephemeralDataProtectionProvider, NullLogger.Instance, - new ServerComponentTypeCache(), + new RootComponentTypeCache(), new ComponentParameterDeserializer(NullLogger.Instance, new ComponentParametersTypeCache())); } diff --git a/src/Components/Server/src/Circuits/ComponentParametersTypeCache.cs b/src/Components/Shared/src/ComponentParametersTypeCache.cs similarity index 100% rename from src/Components/Server/src/Circuits/ComponentParametersTypeCache.cs rename to src/Components/Shared/src/ComponentParametersTypeCache.cs diff --git a/src/Components/Server/src/Circuits/ServerComponentTypeCache.cs b/src/Components/Shared/src/RootComponentTypeCache.cs similarity index 90% rename from src/Components/Server/src/Circuits/ServerComponentTypeCache.cs rename to src/Components/Shared/src/RootComponentTypeCache.cs index 4c694795d679..bbb135063df9 100644 --- a/src/Components/Server/src/Circuits/ServerComponentTypeCache.cs +++ b/src/Components/Shared/src/RootComponentTypeCache.cs @@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Components { // A cache for root component types - internal class ServerComponentTypeCache + internal class RootComponentTypeCache { private readonly ConcurrentDictionary _typeToKeyLookUp = new ConcurrentDictionary(); @@ -39,14 +39,14 @@ private static Type ResolveType(Key key, Assembly[] assemblies) return assembly.GetType(key.Type, throwOnError: false, ignoreCase: false); } - private struct Key : IEquatable + private readonly struct Key : IEquatable { public Key(string assembly, string type) => (Assembly, Type) = (assembly, type); - public string Assembly { get; set; } + public string Assembly { get; } - public string Type { get; set; } + public string Type { get; } public override bool Equals(object obj) => Equals((Key)obj); diff --git a/src/Components/Web.JS/dist/Release/blazor.server.js b/src/Components/Web.JS/dist/Release/blazor.server.js index f7afbadbe378..a444b4e075b3 100644 --- a/src/Components/Web.JS/dist/Release/blazor.server.js +++ b/src/Components/Web.JS/dist/Release/blazor.server.js @@ -1,19 +1,19 @@ -!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=50)}([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";(function(e){n.d(t,"e",(function(){return c})),n.d(t,"a",(function(){return u})),n.d(t,"c",(function(){return l})),n.d(t,"g",(function(){return f})),n.d(t,"i",(function(){return h})),n.d(t,"j",(function(){return p})),n.d(t,"f",(function(){return d})),n.d(t,"d",(function(){return g})),n.d(t,"b",(function(){return y})),n.d(t,"h",(function(){return v}));var r=n(0),o=n(5),i=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]-1&&this.subject.observers.splice(e,1),0===this.subject.observers.length&&this.subject.cancelCallback&&this.subject.cancelCallback().catch((function(e){}))},e}(),y=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 v(){var e="X-SignalR-User-Agent";return l.isNode&&(e="User-Agent"),[e,b(c,m(),E(),w())]}function b(e,t,n,r){var o="Microsoft SignalR/",i=e.split(".");return o+=i[0]+"."+i[1],o+=" ("+e+"; ",o+=t&&""!==t?t+"; ":"Unknown OS; ",o+=""+n,o+=r?"; "+r:"; Unknown Runtime Version",o+=")"}function m(){if(!l.isNode)return"";switch(e.platform){case"win32":return"Windows NT";case"darwin":return"macOS";case"linux":return"Linux";default:return e.platform}}function w(){if(l.isNode)return e.versions.node}function E(){return l.isNode?"NodeJS":"Browser"}}).call(this,n(13))},function(e,t,n){"use strict";n.r(t),n.d(t,"AbortError",(function(){return s})),n.d(t,"HttpError",(function(){return i})),n.d(t,"TimeoutError",(function(){return a})),n.d(t,"HttpClient",(function(){return l})),n.d(t,"HttpResponse",(function(){return u})),n.d(t,"DefaultHttpClient",(function(){return S})),n.d(t,"HubConnection",(function(){return O})),n.d(t,"HubConnectionState",(function(){return I})),n.d(t,"HubConnectionBuilder",(function(){return re})),n.d(t,"MessageType",(function(){return b})),n.d(t,"LogLevel",(function(){return f.a})),n.d(t,"HttpTransportType",(function(){return x})),n.d(t,"TransferFormat",(function(){return P})),n.d(t,"NullLogger",(function(){return $.a})),n.d(t,"JsonHubProtocol",(function(){return ee})),n.d(t,"Subject",(function(){return _})),n.d(t,"VERSION",(function(){return h.e}));var r,o=(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)}),i=function(e){function t(t,n){var r=this,o=this.constructor.prototype;return(r=e.call(this,t)||this).statusCode=n,r.__proto__=o,r}return o(t,e),t}(Error),a=function(e){function t(t){void 0===t&&(t="A timeout occurred.");var n=this,r=this.constructor.prototype;return(n=e.call(this,t)||this).__proto__=r,n}return o(t,e),t}(Error),s=function(e){function t(t){void 0===t&&(t="An abort occurred.");var n=this,r=this.constructor.prototype;return(n=e.call(this,t)||this).__proto__=r,n}return o(t,e),t}(Error),c=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]=200&&o.status<300?n(new u(o.status,o.statusText,o.response||o.responseText)):r(new i(o.statusText,o.status))},o.onerror=function(){t.logger.log(f.a.Warning,"Error from HTTP request. "+o.status+": "+o.statusText+"."),r(new i(o.statusText,o.status))},o.ontimeout=function(){t.logger.log(f.a.Warning,"Timeout from HTTP request."),r(new a)},o.send(e.content||"")})):Promise.reject(new Error("No url defined.")):Promise.reject(new Error("No method defined."))},t}(l),E=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)}}(),S=function(e){function t(t){var n=e.call(this)||this;if("undefined"!=typeof fetch||h.c.isNode)n.httpClient=new v(t);else{if("undefined"==typeof XMLHttpRequest)throw new Error("No usable HttpClient found.");n.httpClient=new w(t)}return n}return E(t,e),t.prototype.send=function(e){return e.abortSignal&&e.abortSignal.aborted?Promise.reject(new s):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}(l),C=n(42);!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"}(b||(b={}));var I,_=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 x.WebSockets:if(!this.options.WebSocket)throw new Error("'WebSocket' is not supported in your environment.");return new Y(this.httpClient,this.accessTokenFactory,this.logger,this.options.logMessageContent||!1,this.options.WebSocket,this.options.headers||{});case x.ServerSentEvents:if(!this.options.EventSource)throw new Error("'EventSource' is not supported in your environment.");return new H(this.httpClient,this.accessTokenFactory,this.logger,this.options.logMessageContent||!1,this.options.EventSource,this.options.withCredentials,this.options.headers||{});case x.LongPolling:return new B(this.httpClient,this.accessTokenFactory,this.logger,this.options.logMessageContent||!1,this.options.withCredentials,this.options.headers||{});default:throw new Error("Unknown transport: "+e+".")}},e.prototype.startTransport=function(e,t){var n=this;return this.transport.onreceive=this.onreceive,this.transport.onclose=function(e){return n.stopConnection(e)},this.transport.connect(e,t)},e.prototype.resolveTransportOrError=function(e,t,n){var r=x[e.transport];if(null==r)return this.logger.log(f.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(f.a.Debug,"Skipping transport '"+x[r]+"' because it was disabled by the client."),new Error("'"+x[r]+"' is disabled by the client.");if(!(e.transferFormats.map((function(e){return P[e]})).indexOf(n)>=0))return this.logger.log(f.a.Debug,"Skipping transport '"+x[r]+"' because it does not support the requested transfer format '"+P[n]+"'."),new Error("'"+x[r]+"' does not support "+P[n]+".");if(r===x.WebSockets&&!this.options.WebSocket||r===x.ServerSentEvents&&!this.options.EventSource)return this.logger.log(f.a.Debug,"Skipping transport '"+x[r]+"' because it is not supported in your environment.'"),new Error("'"+x[r]+"' is not supported in your environment.");this.logger.log(f.a.Debug,"Selecting transport '"+x[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){var t=this;if(this.logger.log(f.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)throw this.logger.log(f.a.Warning,"Call to HttpConnection.stopConnection("+e+") was ignored because the connection is still in the connecting state."),new Error("HttpConnection.stopConnection("+e+") was called while the connection is still in the connecting state.");if("Disconnecting"===this.connectionState&&this.stopPromiseResolver(),e?this.logger.log(f.a.Error,"Connection disconnected with error '"+e+"'."):this.logger.log(f.a.Information,"Connection disconnected."),this.sendQueue&&(this.sendQueue.stop().catch((function(e){t.logger.log(f.a.Error,"TransportSendQueue.stop() threw error '"+e+"'.")})),this.sendQueue=void 0),this.connectionId=void 0,this.connectionState="Disconnected",this.connectionStarted){this.connectionStarted=!1;try{this.onclose&&this.onclose(e)}catch(t){this.logger.log(f.a.Error,"HttpConnection.onclose("+e+") threw error '"+t+"'.")}}}else this.logger.log(f.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(!h.c.isBrowser||!window.document)throw new Error("Cannot resolve '"+e+"'.");var t=window.document.createElement("a");return t.href=e,this.logger.log(f.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",-1===(n+=-1===t?"":e.substring(t)).indexOf("negotiateVersion")&&(n+=-1===t?"?":"&",n+="negotiateVersion="+this.negotiateVersion),n},e}();var G=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 V(this,void 0,void 0,(function(){var t,n,r;return K(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]0&&o[o.length-1])||6!==i[0]&&2!==i[0])){s=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]-1&&this.subject.observers.splice(e,1),0===this.subject.observers.length&&this.subject.cancelCallback&&this.subject.cancelCallback().catch((function(e){}))},e}(),y=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 v(){var e="X-SignalR-User-Agent";return l.isNode&&(e="User-Agent"),[e,b(c,m(),E(),w())]}function b(e,t,n,r){var o="Microsoft SignalR/",i=e.split(".");return o+=i[0]+"."+i[1],o+=" ("+e+"; ",o+=t&&""!==t?t+"; ":"Unknown OS; ",o+=""+n,o+=r?"; "+r:"; Unknown Runtime Version",o+=")"}function m(){if(!l.isNode)return"";switch(e.platform){case"win32":return"Windows NT";case"darwin":return"macOS";case"linux":return"Linux";default:return e.platform}}function w(){if(l.isNode)return e.versions.node}function E(){return l.isNode?"NodeJS":"Browser"}}).call(this,n(13))},function(e,t,n){"use strict";n.r(t),n.d(t,"AbortError",(function(){return a})),n.d(t,"HttpError",(function(){return i})),n.d(t,"TimeoutError",(function(){return s})),n.d(t,"HttpClient",(function(){return l})),n.d(t,"HttpResponse",(function(){return u})),n.d(t,"DefaultHttpClient",(function(){return S})),n.d(t,"HubConnection",(function(){return O})),n.d(t,"HubConnectionState",(function(){return I})),n.d(t,"HubConnectionBuilder",(function(){return re})),n.d(t,"MessageType",(function(){return b})),n.d(t,"LogLevel",(function(){return f.a})),n.d(t,"HttpTransportType",(function(){return x})),n.d(t,"TransferFormat",(function(){return P})),n.d(t,"NullLogger",(function(){return $.a})),n.d(t,"JsonHubProtocol",(function(){return ee})),n.d(t,"Subject",(function(){return _})),n.d(t,"VERSION",(function(){return h.e}));var r,o=(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)}),i=function(e){function t(t,n){var r=this,o=this.constructor.prototype;return(r=e.call(this,t)||this).statusCode=n,r.__proto__=o,r}return o(t,e),t}(Error),s=function(e){function t(t){void 0===t&&(t="A timeout occurred.");var n=this,r=this.constructor.prototype;return(n=e.call(this,t)||this).__proto__=r,n}return o(t,e),t}(Error),a=function(e){function t(t){void 0===t&&(t="An abort occurred.");var n=this,r=this.constructor.prototype;return(n=e.call(this,t)||this).__proto__=r,n}return o(t,e),t}(Error),c=Object.assign||function(e){for(var t,n=1,r=arguments.length;n0&&o[o.length-1])||6!==i[0]&&2!==i[0])){s=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]=200&&o.status<300?n(new u(o.status,o.statusText,o.response||o.responseText)):r(new i(o.statusText,o.status))},o.onerror=function(){t.logger.log(f.a.Warning,"Error from HTTP request. "+o.status+": "+o.statusText+"."),r(new i(o.statusText,o.status))},o.ontimeout=function(){t.logger.log(f.a.Warning,"Timeout from HTTP request."),r(new s)},o.send(e.content||"")})):Promise.reject(new Error("No url defined.")):Promise.reject(new Error("No method defined."))},t}(l),E=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)}}(),S=function(e){function t(t){var n=e.call(this)||this;if("undefined"!=typeof fetch||h.c.isNode)n.httpClient=new v(t);else{if("undefined"==typeof XMLHttpRequest)throw new Error("No usable HttpClient found.");n.httpClient=new w(t)}return n}return E(t,e),t.prototype.send=function(e){return e.abortSignal&&e.abortSignal.aborted?Promise.reject(new 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}(l),C=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"}(b||(b={}));var I,_=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])){s=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]0&&o[o.length-1])||6!==i[0]&&2!==i[0])){s=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]0&&o[o.length-1])||6!==i[0]&&2!==i[0])){s=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]0&&o[o.length-1])||6!==i[0]&&2!==i[0])){s=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]0&&o[o.length-1])||6!==i[0]&&2!==i[0])){s=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]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 x.WebSockets:if(!this.options.WebSocket)throw new Error("'WebSocket' is not supported in your environment.");return new J(this.httpClient,this.accessTokenFactory,this.logger,this.options.logMessageContent||!1,this.options.WebSocket,this.options.headers||{});case x.ServerSentEvents:if(!this.options.EventSource)throw new Error("'EventSource' is not supported in your environment.");return new H(this.httpClient,this.accessTokenFactory,this.logger,this.options.logMessageContent||!1,this.options.EventSource,this.options.withCredentials,this.options.headers||{});case x.LongPolling:return new B(this.httpClient,this.accessTokenFactory,this.logger,this.options.logMessageContent||!1,this.options.withCredentials,this.options.headers||{});default:throw new Error("Unknown transport: "+e+".")}},e.prototype.startTransport=function(e,t){var n=this;return this.transport.onreceive=this.onreceive,this.transport.onclose=function(e){return n.stopConnection(e)},this.transport.connect(e,t)},e.prototype.resolveTransportOrError=function(e,t,n){var r=x[e.transport];if(null==r)return this.logger.log(f.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(f.a.Debug,"Skipping transport '"+x[r]+"' because it was disabled by the client."),new Error("'"+x[r]+"' is disabled by the client.");if(!(e.transferFormats.map((function(e){return P[e]})).indexOf(n)>=0))return this.logger.log(f.a.Debug,"Skipping transport '"+x[r]+"' because it does not support the requested transfer format '"+P[n]+"'."),new Error("'"+x[r]+"' does not support "+P[n]+".");if(r===x.WebSockets&&!this.options.WebSocket||r===x.ServerSentEvents&&!this.options.EventSource)return this.logger.log(f.a.Debug,"Skipping transport '"+x[r]+"' because it is not supported in your environment.'"),new Error("'"+x[r]+"' is not supported in your environment.");this.logger.log(f.a.Debug,"Selecting transport '"+x[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){var t=this;if(this.logger.log(f.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)throw this.logger.log(f.a.Warning,"Call to HttpConnection.stopConnection("+e+") was ignored because the connection is still in the connecting state."),new Error("HttpConnection.stopConnection("+e+") was called while the connection is still in the connecting state.");if("Disconnecting"===this.connectionState&&this.stopPromiseResolver(),e?this.logger.log(f.a.Error,"Connection disconnected with error '"+e+"'."):this.logger.log(f.a.Information,"Connection disconnected."),this.sendQueue&&(this.sendQueue.stop().catch((function(e){t.logger.log(f.a.Error,"TransportSendQueue.stop() threw error '"+e+"'.")})),this.sendQueue=void 0),this.connectionId=void 0,this.connectionState="Disconnected",this.connectionStarted){this.connectionStarted=!1;try{this.onclose&&this.onclose(e)}catch(t){this.logger.log(f.a.Error,"HttpConnection.onclose("+e+") threw error '"+t+"'.")}}}else this.logger.log(f.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(!h.c.isBrowser||!window.document)throw new Error("Cannot resolve '"+e+"'.");var t=window.document.createElement("a");return t.href=e,this.logger.log(f.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",-1===(n+=-1===t?"":e.substring(t)).indexOf("negotiateVersion")&&(n+=-1===t?"?":"&",n+="negotiateVersion="+this.negotiateVersion),n},e}();var G=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 V(this,void 0,void 0,(function(){var t,n,r;return K(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])){s=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]0&&!t)throw new Error("New logical elements must start empty, or allowExistingContents must be true");return r in e||(e[r]=[]),e}function a(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 s=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 s=i.nextSibling;if(n.insertBefore(i,t),i===o)break;i=s}n.removeChild(t)})),t.forEach((function(e){n[e.toSiblingIndex]=e.moveRangeStart}))},t.getClosestDomElement=l},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(19);i.inherits=n(15);var s=n(36),a=n(41);i.inherits(f,s);for(var c=o(a.prototype),u=0;u * @license MIT */ -var r=n(51),o=n(52),i=n(53);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=!1;if((void 0===t||t<0)&&(t=0),t>this.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 k(this,t,n);case"ascii":return T(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 O(this,t,n);default:if(r)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),r=!0}}function y(e,t,n){var r=e[t];e[t]=e[n],e[n]=r}function v(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:b(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):b(e,[t],n,r,o);throw new TypeError("val must be string, number or Buffer")}function b(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 k(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<=4096)return String.fromCharCode.apply(String,e);var n="",r=0;for(;r0&&(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 m(this,e,t,n);case"utf8":case"utf-8":return w(this,e,t,n);case"ascii":return E(this,e,t,n);case"latin1":case"binary":return S(this,e,t,n);case"base64":return C(this,e,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return I(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)}};function T(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 L(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 D(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 M(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 j(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 A(e,t,n,r,i){return i||j(e,0,n,4),o.write(e,t,n,r,23,4),n+4}function B(e,t,n,r,i){return i||j(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||R(e,1,this.length),this[e]},c.prototype.readUInt16LE=function(e,t){return t||R(e,2,this.length),this[e]|this[e+1]<<8},c.prototype.readUInt16BE=function(e,t){return t||R(e,2,this.length),this[e]<<8|this[e+1]},c.prototype.readUInt32LE=function(e,t){return t||R(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||R(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||R(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||R(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||R(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},c.prototype.readInt16LE=function(e,t){t||R(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||R(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||R(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||R(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||R(e,4,this.length),o.read(this,e,!0,23,4)},c.prototype.readFloatBE=function(e,t){return t||R(e,4,this.length),o.read(this,e,!1,23,4)},c.prototype.readDoubleLE=function(e,t){return t||R(e,8,this.length),o.read(this,e,!0,52,8)},c.prototype.readDoubleBE=function(e,t){return t||R(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)||L(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||L(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||L(this,e,t,2,65535,0),c.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):D(this,e,t,!0),t+2},c.prototype.writeUInt16BE=function(e,t,n){return e=+e,t|=0,n||L(this,e,t,2,65535,0),c.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):D(this,e,t,!1),t+2},c.prototype.writeUInt32LE=function(e,t,n){return e=+e,t|=0,n||L(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):M(this,e,t,!0),t+4},c.prototype.writeUInt32BE=function(e,t,n){return e=+e,t|=0,n||L(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):M(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);L(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);L(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||L(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||L(this,e,t,2,32767,-32768),c.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):D(this,e,t,!0),t+2},c.prototype.writeInt16BE=function(e,t,n){return e=+e,t|=0,n||L(this,e,t,2,32767,-32768),c.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):D(this,e,t,!1),t+2},c.prototype.writeInt32LE=function(e,t,n){return e=+e,t|=0,n||L(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):M(this,e,t,!0),t+4},c.prototype.writeInt32BE=function(e,t,n){return e=+e,t|=0,n||L(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):M(this,e,t,!1),t+4},c.prototype.writeFloatLE=function(e,t,n){return A(this,e,t,!0,n)},c.prototype.writeFloatBE=function(e,t,n){return A(this,e,t,!1,n)},c.prototype.writeDoubleLE=function(e,t,n){return B(this,e,t,!0,n)},c.prototype.writeDoubleBE=function(e,t,n){return B(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(N,"")).length<2)return"";for(;e.length%4!=0;)e+="=";return e}(e))}function q(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";var r=n(14).Buffer,o=n(54),i=n(20),a=n(67),s=n(70),c=n(71);e.exports=function(e){var t=[],n=[];return{encode:c(t,(e=e||{forceFloat64:!1,compatibilityMode:!1,disableTimestampEncoding:!1}).forceFloat64,e.compatibilityMode,e.disableTimestampEncoding),decode:s(n),register:function(e,t,n,a){return o(t,"must have a constructor"),o(n,"must have an encode function"),o(e>=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=p("_blazorLogicalChildren"),o=p("_blazorLogicalParent"),i=p("_blazorLogicalEnd");function a(e,t){if(e.childNodes.length>0&&!t)throw new Error("New logical elements must start empty, or allowExistingContents must be true");return r in e||(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){var n,r,o=e.exports={};function i(){throw new Error("setTimeout has not been defined")}function a(){throw new Error("clearTimeout has not been defined")}function s(e){if(n===setTimeout)return setTimeout(e,0);if((n===i||!n)&&setTimeout)return n=setTimeout,setTimeout(e,0);try{return n(e,0)}catch(t){try{return n.call(null,e,0)}catch(t){return n.call(this,e,0)}}}!function(){try{n="function"==typeof setTimeout?setTimeout:i}catch(e){n=i}try{r="function"==typeof clearTimeout?clearTimeout:a}catch(e){r=a}}();var c,u=[],l=!1,f=-1;function h(){l&&c&&(l=!1,c.length?u=c.concat(u):f=-1,u.length&&p())}function p(){if(!l){var e=s(h);l=!0;for(var t=u.length;t;){for(c=u,u=[];++f1)for(var n=1;nthis.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&&e.invokeMethodAsync("OnSpacerAfterVisible",r.boundingClientRect.bottom-r.intersectionRect.bottom,i,a)}}))}),{root:o(t),rootMargin:i+"px"});a.observe(t),a.observe(n);var s=u(t),c=u(n);function u(e){var t=new MutationObserver((function(){a.unobserve(e),a.observe(e)}));return t.observe(e,{attributes:!0}),t}r[e._id]={intersectionObserver:a,mutationObserverBefore:s,mutationObserverAfter:c}},dispose:function(e){var t=r[e._id];t&&(t.intersectionObserver.disconnect(),t.mutationObserverBefore.disconnect(),t.mutationObserverAfter.disconnect(),e.dispose(),delete r[e._id])}};var r={};function o(e){return e?"visible"!==getComputedStyle(e).overflowY?e:o(e.parentElement):null}},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){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).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=a.trys,(o=o.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]=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")),x(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(56);var k=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function T(){var e=new Date,t=[_(e.getHours()),_(e.getMinutes()),_(e.getSeconds())].join(":");return[e.getDate(),k[e.getMonth()],t].join(" ")}function x(e,t){return Object.prototype.hasOwnProperty.call(e,t)}t.log=function(){console.log("%s - %s",T(),t.format.apply(t,arguments))},t.inherits=n(57),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 P="undefined"!=typeof Symbol?Symbol("util.promisify.custom"):void 0;function O(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(P&&e[P]){var t;if("function"!=typeof(t=e[P]))throw new TypeError('The "util.promisify.custom" argument must be of type Function');return Object.defineProperty(t,P,{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):_(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>=8388608?e=8388608:(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 C(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"),P(e)}function _(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 R(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(L,t,e))}function L(e,t){e.endEmitted||0!==e.length||(e.endEmitted=!0,t.readable=!1,t.emit("end"))}function D(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?R(this):C(this),null;if(0===(e=S(e,t))&&t.ended)return 0===t.length&&R(this),null;var r,o=t.needReadable;return p("need readable",o),(0===t.length||t.length-e0?O(e,t):null)?(t.needReadable=!0,e=0):t.length-=e,0===t.length&&(t.ended||(t.needReadable=!0),n!==e&&t.ended&&R(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,P(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!==D(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||(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 h(){if(!this.fired)return this.target.removeListener(this.type,this.wrapFn),this.fired=!0,0===arguments.length?this.listener.call(this.target):this.listener.apply(this.target,arguments)}function p(e,t,n){var r={fired:!1,wrapFn:void 0,target:e,type:t,listener:n},o=h.bind(r);return o.listener=n,r.wrapFn=o,o}function d(e,t,n){var r=e._events;if(void 0===r)return[];var o=r[t];return void 0===o?[]:"function"==typeof o?n?[o.listener||o]:[o]:n?function(e){for(var t=new Array(e.length),n=0;n0&&(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=y(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 d(this,e,!0)},s.prototype.rawListeners=function(e){return d(this,e,!1)},s.listenerCount=function(e,t){return"function"==typeof e.listenerCount?e.listenerCount(t):g.call(e,t)},s.prototype.listenerCount=g,s.prototype.eventNames=function(){return this._eventsCount>0?r(this._events):[]}},function(e,t,n){e.exports=n(36).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(63).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(19);u.inherits=n(15);var l={deprecate:n(66)},f=n(37),h=n(14).Buffer,p=o.Uint8Array||function(){};var d,g=n(38);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(13),n(64).setImmediate,n(8))},function(e,t,n){"use strict";e.exports=a;var r=n(9),o=n(19);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.lengths?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(10).Buffer)},,,,,,,,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){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).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=a.trys,(o=o.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]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},a=this&&this.__spread||function(){for(var e=[],t=0;t0?a-4:a;for(n=0;n>16&255,c[l++]=t>>8&255,c[l++]=255&t;2===s&&(t=o[e.charCodeAt(n)]<<2|o[e.charCodeAt(n+1)]>>4,c[l++]=255&t);1===s&&(t=o[e.charCodeAt(n)]<<10|o[e.charCodeAt(n+1)]<<4|o[e.charCodeAt(n+2)]>>2,c[l++]=t>>8&255,c[l++]=255&t);return c},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(55); +var r=n(53),o=n(54),i=n(55);function s(){return c.TYPED_ARRAY_SUPPORT?2147483647:1073741823}function a(e,t){if(s()=s())throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+s().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=!1;if((void 0===t||t<0)&&(t=0),t>this.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 k(this,t,n);case"ascii":return T(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 O(this,t,n);default:if(r)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),r=!0}}function y(e,t,n){var r=e[t];e[t]=e[n],e[n]=r}function v(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:b(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):b(e,[t],n,r,o);throw new TypeError("val must be string, number or Buffer")}function b(e,t,n,r,o){var i,s=1,a=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;s=2,a/=2,c/=2,n/=2}function u(e,t){return 1===s?e[t]:e.readUInt16BE(t*s)}if(o){var l=-1;for(i=n;ia&&(n=a-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 s=0;s>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 k(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],s=e[o+2],128==(192&i)&&128==(192&s)&&(c=(15&u)<<12|(63&i)<<6|63&s)>2047&&(c<55296||c>57343)&&(l=c);break;case 4:i=e[o+1],s=e[o+2],a=e[o+3],128==(192&i)&&128==(192&s)&&128==(192&a)&&(c=(15&u)<<18|(63&i)<<12|(63&s)<<6|63&a)>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<=4096)return String.fromCharCode.apply(String,e);var n="",r=0;for(;r0&&(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),s=(n>>>=0)-(t>>>=0),a=Math.min(i,s),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 m(this,e,t,n);case"utf8":case"utf-8":return w(this,e,t,n);case"ascii":return E(this,e,t,n);case"latin1":case"binary":return S(this,e,t,n);case"base64":return C(this,e,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return I(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)}};function T(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 D(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 L(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 j(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 M(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 A(e,t,n,r,i){return i||M(e,0,n,4),o.write(e,t,n,r,23,4),n+4}function B(e,t,n,r,i){return i||M(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||R(e,1,this.length),this[e]},c.prototype.readUInt16LE=function(e,t){return t||R(e,2,this.length),this[e]|this[e+1]<<8},c.prototype.readUInt16BE=function(e,t){return t||R(e,2,this.length),this[e]<<8|this[e+1]},c.prototype.readUInt32LE=function(e,t){return t||R(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||R(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||R(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||R(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||R(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},c.prototype.readInt16LE=function(e,t){t||R(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||R(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||R(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||R(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||R(e,4,this.length),o.read(this,e,!0,23,4)},c.prototype.readFloatBE=function(e,t){return t||R(e,4,this.length),o.read(this,e,!1,23,4)},c.prototype.readDoubleLE=function(e,t){return t||R(e,8,this.length),o.read(this,e,!0,52,8)},c.prototype.readDoubleBE=function(e,t){return t||R(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)||D(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||D(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||D(this,e,t,2,65535,0),c.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):L(this,e,t,!0),t+2},c.prototype.writeUInt16BE=function(e,t,n){return e=+e,t|=0,n||D(this,e,t,2,65535,0),c.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):L(this,e,t,!1),t+2},c.prototype.writeUInt32LE=function(e,t,n){return e=+e,t|=0,n||D(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):j(this,e,t,!0),t+4},c.prototype.writeUInt32BE=function(e,t,n){return e=+e,t|=0,n||D(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):j(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);D(this,e,t,n,o-1,-o)}var i=0,s=1,a=0;for(this[t]=255&e;++i>0)-a&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);D(this,e,t,n,o-1,-o)}var i=n-1,s=1,a=0;for(this[t+i]=255&e;--i>=0&&(s*=256);)e<0&&0===a&&0!==this[t+i+1]&&(a=1),this[t+i]=(e/s>>0)-a&255;return t+n},c.prototype.writeInt8=function(e,t,n){return e=+e,t|=0,n||D(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||D(this,e,t,2,32767,-32768),c.TYPED_ARRAY_SUPPORT?(this[t]=255&e,this[t+1]=e>>>8):L(this,e,t,!0),t+2},c.prototype.writeInt16BE=function(e,t,n){return e=+e,t|=0,n||D(this,e,t,2,32767,-32768),c.TYPED_ARRAY_SUPPORT?(this[t]=e>>>8,this[t+1]=255&e):L(this,e,t,!1),t+2},c.prototype.writeInt32LE=function(e,t,n){return e=+e,t|=0,n||D(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):j(this,e,t,!0),t+4},c.prototype.writeInt32BE=function(e,t,n){return e=+e,t|=0,n||D(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):j(this,e,t,!1),t+4},c.prototype.writeFloatLE=function(e,t,n){return A(this,e,t,!0,n)},c.prototype.writeFloatBE=function(e,t,n){return A(this,e,t,!1,n)},c.prototype.writeDoubleLE=function(e,t,n){return B(this,e,t,!0,n)},c.prototype.writeDoubleBE=function(e,t,n){return B(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(s+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(N,"")).length<2)return"";for(;e.length%4!=0;)e+="=";return e}(e))}function q(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";var r=n(14).Buffer,o=n(56),i=n(20),s=n(69),a=n(72),c=n(73);e.exports=function(e){var t=[],n=[];return{encode:c(t,(e=e||{forceFloat64:!1,compatibilityMode:!1,disableTimestampEncoding:!1}).forceFloat64,e.compatibilityMode,e.disableTimestampEncoding),decode:a(n),register:function(e,t,n,s){return o(t,"must have a constructor"),o(n,"must have an encode function"),o(e>=0,"must have a non-negative type"),o(s,"must have a decode function"),this.registerEncoder((function(e){return e instanceof t}),(function(t){var o=i(),s=r.allocUnsafe(1);return s.writeInt8(e,0),o.append(s),o.append(n(t)),o})),this.registerDecoder(e,s),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:s.encoder,decoder:s.decoder,buffer:!0,type:"msgpack5",IncompleteBufferError:a.IncompleteBufferError}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),n(23),n(17);var r=n(24),o=n(7),i={},s=!1;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(e);if(!r)throw new Error("Could not find any element matching selector '"+e+"'.");a(n||0,o.toLogicalElement(r,!0),t)},t.getRendererer=function(e){return i[e]},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),c=r.count(o),u=t.referenceFrames(),l=r.values(u),f=t.diffReader,h=0;h1)for(var n=1;nthis.length)&&(r=this.length),n>=this.length)return e||i.alloc(0);if(r<=0)return e||i.alloc(0);var o,s,a=!!e,c=this._offset(n),u=r-n,l=u,f=a&&t||0,h=c[1];if(0===n&&r==this.length){if(!a)return 1===this._bufs.length?this._bufs[0]:i.concat(this._bufs,this.length);for(s=0;s(o=this._bufs[s].length-h))){this._bufs[s].copy(e,f,h,h+l);break}this._bufs[s].copy(e,f,h),f+=o,l-=o,h&&(h=0)}return e},s.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 s(o)},s.prototype.toString=function(e,t,n){return this.slice(t,n).toString(e)},s.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},s.prototype.duplicate=function(){for(var e=0,t=new s;e0&&e.invokeMethodAsync("OnSpacerAfterVisible",r.boundingClientRect.bottom-r.intersectionRect.bottom,i,s)}}))}),{root:o(t),rootMargin:i+"px"});s.observe(t),s.observe(n);var a=u(t),c=u(n);function u(e){var t=new MutationObserver((function(){s.unobserve(e),s.observe(e)}));return t.observe(e,{attributes:!0}),t}r[e._id]={intersectionObserver:s,mutationObserverBefore:a,mutationObserverAfter:c}},dispose:function(e){var t=r[e._id];t&&(t.intersectionObserver.disconnect(),t.mutationObserverBefore.disconnect(),t.mutationObserverAfter.disconnect(),e.dispose(),delete r[e._id])}};var r={};function o(e){return e?"visible"!==getComputedStyle(e).overflowY?e:o(e.parentElement):null}},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 s(e){try{c(r.next(e))}catch(e){i(e)}}function a(e){try{c(r.throw(e))}catch(e){i(e)}}function c(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}c((r=r.apply(e,t||[])).next())}))},o=this&&this.__generator||function(e,t){var n,r,o,i,s={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function a(i){return function(a){return function(i){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(o=2&i[0]?r.return:i[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,i[1])).done)return o;switch(r=0,o&&(i=[2&i[0],o.value]),i[0]){case 0:case 1:o=i;break;case 4:return s.label++,{value:i[1],done:!1};case 5:s.label++,r=i[1],i=[0];continue;case 7:i=s.ops.pop(),s.trys.pop();continue;default:if(!(o=s.trys,(o=o.length>0&&o[o.length-1])||6!==i[0]&&2!==i[0])){s=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1].*)$/;function i(e,t){var n=e.currentElement;if(n&&n.nodeType===Node.COMMENT_NODE&&n.textContent){var r=new RegExp(o).exec(n.textContent),i=r&&r.groups&&r.groups.descriptor;if(!i)return;try{var a=function(e){var t=JSON.parse(e),n=t.type;if("server"!==n&&"webassembly"!==n)throw new Error("Invalid component type '"+n+"'.");return t}(i);switch(t){case"webassembly":return function(e,t,n){var r=e.type,o=e.assembly,i=e.typeName,a=e.parameterDefinitions,c=e.parameterValues,u=e.prerenderId;if("webassembly"!==r)return;if(!o)throw new Error("assembly must be defined when using a descriptor.");if(!i)throw new Error("typeName must be defined when using a descriptor.");if(u){var l=s(u,n);if(!l)throw new Error("Could not find an end component comment for '"+t+"'");return{type:r,assembly:o,typeName:i,parameterDefinitions:a&&atob(a),parameterValues:c&&atob(c),start:t,prerenderId:u,end:l}}return{type:r,assembly:o,typeName:i,parameterDefinitions:a&&atob(a),parameterValues:c&&atob(c),start:t}}(a,n,e);case"server":return function(e,t,n){var r=e.type,o=e.descriptor,i=e.sequence,a=e.prerenderId;if("server"!==r)return;if(!o)throw new Error("descriptor must be defined when using a descriptor.");if(void 0===i)throw new Error("sequence must be defined when using a descriptor.");if(!Number.isInteger(i))throw new Error("Error parsing the sequence '"+i+"' for component '"+JSON.stringify(e)+"'");if(a){var c=s(a,n);if(!c)throw new Error("Could not find an end component comment for '"+t+"'");return{type:r,sequence:i,descriptor:o,start:t,prerenderId:a,end:c}}return{type:r,sequence:i,descriptor:o,start:t}}(a,n,e)}}catch(e){throw new Error("Found malformed component comment at "+n.textContent)}}}function s(e,t){for(;t.next()&&t.currentElement;){var n=t.currentElement;if(n.nodeType===Node.COMMENT_NODE&&n.textContent){var r=new RegExp(o).exec(n.textContent),i=r&&r[1];if(i)return a(i,e),n}}}function a(e,t){var n=JSON.parse(e);if(1!==Object.keys(n).length)throw new Error("Invalid end of component comment: '"+e+"'");var r=n.prerenderId;if(!r)throw new Error("End of component comment must have a value for the prerendered property: '"+e+"'");if(r!==t)throw new Error("End of component comment prerendered property must match the start comment prerender id: '"+t+"', '"+r+"'")}var c=function(){function e(e){this.childNodes=e,this.currentIndex=-1,this.length=e.length}return e.prototype.next=function(){return this.currentIndex++,this.currentIndex=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=a.styles[t];return n?"["+a.colors[n][0]+"m"+e+"["+a.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 s=Object.keys(n),a=function(e){var t={};return e.forEach((function(e,n){t[e]=!0})),t}(s);if(e.showHidden&&(s=Object.getOwnPropertyNames(n)),S(n)&&(s.indexOf("message")>=0||s.indexOf("description")>=0))return f(n);if(0===s.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!==s.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=[],s=0,a=t.length;s=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 s,a,c;if((c=Object.getOwnPropertyDescriptor(t,o)||{value:t[o]}).get?a=c.set?e.stylize("[Getter/Setter]","special"):e.stylize("[Getter]","special"):c.set&&(a=e.stylize("[Setter]","special")),x(r,o)||(s="["+o+"]"),a||(e.seen.indexOf(c.value)<0?(a=g(n)?l(e,c.value,null):l(e,c.value,n-1)).indexOf("\n")>-1&&(a=i?a.split("\n").map((function(e){return" "+e})).join("\n").substr(2):"\n"+a.split("\n").map((function(e){return" "+e})).join("\n")):a=e.stylize("[Circular]","special")),b(s)){if(i&&o.match(/^\d+$/))return a;(s=JSON.stringify(""+o)).match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)?(s=s.substr(1,s.length-2),s=e.stylize(s,"name")):(s=s.replace(/'/g,"\\'").replace(/\\"/g,'"').replace(/(^"|"$)/g,"'"),s=e.stylize(s,"string"))}return s+": "+a}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(),!s[n])if(new RegExp("\\b"+n+"\\b","i").test(i)){var r=e.pid;s[n]=function(){var e=t.format.apply(t,arguments);console.error("%s %d: %s",n,r,e)}}else s[n]=function(){};return s[n]},t.inspect=a,a.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]},a.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(58);var k=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function T(){var e=new Date,t=[_(e.getHours()),_(e.getMinutes()),_(e.getSeconds())].join(":");return[e.getDate(),k[e.getMonth()],t].join(" ")}function x(e,t){return Object.prototype.hasOwnProperty.call(e,t)}t.log=function(){console.log("%s - %s",T(),t.format.apply(t,arguments))},t.inherits=n(59),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 P="undefined"!=typeof Symbol?Symbol("util.promisify.custom"):void 0;function O(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(P&&e[P]){var t;if("function"!=typeof(t=e[P]))throw new TypeError('The "util.promisify.custom" argument must be of type Function');return Object.defineProperty(t,P,{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||s.objectMode||Object.getPrototypeOf(t)===u.prototype||(t=function(e){return u.from(e)}(t)),r?s.endEmitted?e.emit("error",new Error("stream.unshift() after end event")):E(e,s,t,!0):s.ended?e.emit("error",new Error("stream.push() after EOF")):(s.reading=!1,s.decoder&&!n?(t=s.decoder.write(t),s.objectMode||0!==t.length?E(e,s,t,!1):_(e,s)):E(e,s,t,!1))):r||(s.reading=!1));return function(e){return!e.ended&&(e.needReadable||e.lengtht.highWaterMark&&(t.highWaterMark=function(e){return e>=8388608?e=8388608:(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 C(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"),P(e)}function _(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(s===i.length?o+=i:o+=i.slice(0,e),0===(e-=s)){s===i.length?(++r,n.next?t.head=n.next:t.head=t.tail=null):(t.head=n,n.data=i.slice(s));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,s=e>i.length?i.length:e;if(i.copy(n,n.length-e,0,s),0===(e-=s)){s===i.length?(++o,r.next?t.head=r.next:t.head=t.tail=null):(t.head=r,r.data=i.slice(s));break}++o}return t.length-=o,n}(e,t);return r}(e,t.buffer,t.decoder),n);var n}function R(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(D,t,e))}function D(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?R(this):C(this),null;if(0===(e=S(e,t))&&t.ended)return 0===t.length&&R(this),null;var r,o=t.needReadable;return p("need readable",o),(0===t.length||t.length-e0?O(e,t):null)?(t.needReadable=!0,e=0):t.length-=e,0===t.length&&(t.ended||(t.needReadable=!0),n!==e&&t.ended&&R(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&&a(e,"data")&&(t.flowing=!0,P(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===a(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]?s(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||(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&&s.length>o&&!s.warned){s.warned=!0;var c=new Error("Possible EventEmitter memory leak detected. "+s.length+" "+String(t)+" listeners added. Use emitter.setMaxListeners() to increase limit");c.name="MaxListenersExceededWarning",c.emitter=e,c.type=t,c.count=s.length,a=c,console&&console.warn&&console.warn(a)}return e}function h(){if(!this.fired)return this.target.removeListener(this.type,this.wrapFn),this.fired=!0,0===arguments.length?this.listener.call(this.target):this.listener.apply(this.target,arguments)}function p(e,t,n){var r={fired:!1,wrapFn:void 0,target:e,type:t,listener:n},o=h.bind(r);return o.listener=n,r.wrapFn=o,o}function d(e,t,n){var r=e._events;if(void 0===r)return[];var o=r[t];return void 0===o?[]:"function"==typeof o?n?[o.listener||o]:[o]:n?function(e){for(var t=new Array(e.length),n=0;n0&&(s=t[0]),s instanceof Error)throw s;var a=new Error("Unhandled error."+(s?" ("+s.message+")":""));throw a.context=s,a}var c=o[e];if(void 0===c)return!1;if("function"==typeof c)i(c,this,t);else{var u=c.length,l=y(c,u);for(n=0;n=0;i--)if(n[i]===t||n[i].listener===t){s=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},a.prototype.listeners=function(e){return d(this,e,!0)},a.prototype.rawListeners=function(e){return d(this,e,!1)},a.listenerCount=function(e,t){return"function"==typeof e.listenerCount?e.listenerCount(t):g.call(e,t)},a.prototype.listenerCount=g,a.prototype.eventNames=function(){return this._eventsCount>0?r(this._events):[]}},function(e,t,n){e.exports=n(37).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,s=this._writableState&&this._writableState.destroyed;return i||s?(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(65).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=a,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 s(e){return e<=127?0:e>>5==6?2:e>>4==14?3:e>>3==30?4:e>>6==2?-1:-2}function a(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 s(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 a,c=!t.browser&&["v0.10","v0.9."].indexOf(t.version.slice(0,5))>-1?r:i.nextTick;b.WritableState=v;var u=n(19);u.inherits=n(15);var l={deprecate:n(68)},f=n(38),h=n(14).Buffer,p=o.Uint8Array||function(){};var d,g=n(39);function y(){}function v(e,t){a=a||n(9),e=e||{};var r=t instanceof a;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 s=S(n);s||n.corked||n.bufferProcessing||!n.bufferedRequest||E(e,n),r?c(w,e,n,s,o):w(e,n,s,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 s(this)}function b(e){if(a=a||n(9),!(d.call(b,this)||this instanceof a))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,s){t.writelen=r,t.writecb=s,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 a=0,c=!0;n;)o[a]=n,n.isBuf||(c=!1),n=n.next,a+=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 s(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,s=!1,a=!o.objectMode&&(r=e,h.isBuffer(r)||r instanceof p);return a&&!h.isBuffer(e)&&(e=function(e){return h.from(e)}(e)),"function"==typeof t&&(n=t,t=null),a?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):(a||function(e,t,n,r){var o=!0,s=!1;return null===n?s=new TypeError("May not write null values to stream"):"string"==typeof n||void 0===n||t.objectMode||(s=new TypeError("Invalid non-string/buffer chunk")),s&&(e.emit("error",s),i.nextTick(r,s),o=!1),o}(this,o,e,n))&&(o.pendingcb++,s=function(e,t,n,r,o,i){if(!n){var s=function(e,t,n){e.objectMode||!1===e.decodeStrings||"string"!=typeof t||(t=h.from(t,n));return t}(t,r,o);r!==s&&(n=!0,o="buffer",r=s)}var a=t.objectMode?1:r.length;t.length+=a;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(13),n(66).setImmediate,n(8))},function(e,t,n){"use strict";e.exports=s;var r=n(9),o=n(19);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.lengtha?s.slice(a).buffer:null}else{var c,u=t;if(-1===(c=u.indexOf(r.a.RecordSeparator)))throw new Error("Message is incomplete.");a=c+1;n=u.substring(0,a),i=u.length>a?u.substring(a):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(10).Buffer)},,,,,,,,,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 s(e){try{c(r.next(e))}catch(e){i(e)}}function a(e){try{c(r.throw(e))}catch(e){i(e)}}function c(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}c((r=r.apply(e,t||[])).next())}))},o=this&&this.__generator||function(e,t){var n,r,o,i,s={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function a(i){return function(a){return function(i){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(o=2&i[0]?r.return:i[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,i[1])).done)return o;switch(r=0,o&&(i=[2&i[0],o.value]),i[0]){case 0:case 1:o=i;break;case 4:return s.label++,{value:i[1],done:!1};case 5:s.label++,r=i[1],i=[0];continue;case 7:i=s.ops.pop(),s.trys.pop();continue;default:if(!(o=s.trys,(o=o.length>0&&o[o.length-1])||6!==i[0]&&2!==i[0])){s=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]0)&&!(r=i.next()).done;)s.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 s},s=this&&this.__spread||function(){for(var e=[],t=0;t0?s-4:s;for(n=0;n>16&255,c[l++]=t>>8&255,c[l++]=255&t;2===a&&(t=o[e.charCodeAt(n)]<<2|o[e.charCodeAt(n+1)]>>4,c[l++]=255&t);1===a&&(t=o[e.charCodeAt(n)]<<10|o[e.charCodeAt(n+1)]<<4|o[e.charCodeAt(n+2)]>>2,c[l++]=t>>8&255,c[l++]=255&t);return c},t.fromByteArray=function(e){for(var t,n=e.length,o=n%3,i=[],s=0,a=n-o;sa?a:s+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,s="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",a=0,c=s.length;a0)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,s=[],a=t;a>18&63]+r[i>>12&63]+r[i>>6&63]+r[63&i]);return s.join("")}o["-".charCodeAt(0)]=62,o["_".charCodeAt(0)]=63},function(e,t){t.read=function(e,t,n,r,o){var i,s,a=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+=a;l>0;i=256*i+e[t+f],f+=h,l-=8);for(s=i&(1<<-l)-1,i>>=-l,l+=r;l>0;s=256*s+e[t+f],f+=h,l-=8);if(0===i)i=1-u;else{if(i===c)return s?NaN:1/0*(p?-1:1);s+=Math.pow(2,r),i-=u}return(p?-1:1)*s*Math.pow(2,i-r)},t.write=function(e,t,n,r,o,i){var s,a,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?(a=isNaN(t)?1:0,s=l):(s=Math.floor(Math.log(t)/Math.LN2),t*(c=Math.pow(2,-s))<1&&(s--,c*=2),(t+=s+f>=1?h/c:h*Math.pow(2,1-f))*c>=2&&(s++,c/=2),s+f>=l?(a=0,s=l):s+f>=1?(a=(t*c-1)*Math.pow(2,o),s+=f):(a=t*Math.pow(2,f-1)*Math.pow(2,o),s=0));o>=8;e[n+p]=255&a,p+=d,a/=256,o-=8);for(s=s<0;e[n+p]=255&s,p+=d,s/=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(57); /*! * The buffer module from node.js, for the browser. * * @author Feross Aboukhadijeh * @license MIT - */function o(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(s=l[u],!m(e[s],t[s],n,r))return!1;return!0}(e,t,n,r))}return n?e===t:e==t}function w(e){return"[object Arguments]"==Object.prototype.toString.call(e)}function E(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 S(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&&v(o,n,"Missing expected exception"+r);var i="string"==typeof r,s=!e&&o&&!n;if((!e&&a.isError(o)&&i&&E(o,n)||s)&&v(o,n,"Got unwanted exception"+r),e&&o&&n&&!E(o,n)||!e&&o)throw o}h.AssertionError=function(e){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=function(e){return g(y(e.actual),128)+" "+e.operator+" "+g(y(e.expected),128)}(this),this.generatedMessage=!0);var t=e.stackStartFunction||v;if(Error.captureStackTrace)Error.captureStackTrace(this,t);else{var n=new Error;if(n.stack){var r=n.stack,o=d(t),i=r.indexOf("\n"+o);if(i>=0){var a=r.indexOf("\n",i+1);r=r.substring(a+1)}this.stack=r}}},a.inherits(h.AssertionError,Error),h.fail=v,h.ok=b,h.equal=function(e,t,n){e!=t&&v(e,t,n,"==",h.equal)},h.notEqual=function(e,t,n){e==t&&v(e,t,n,"!=",h.notEqual)},h.deepEqual=function(e,t,n){m(e,t,!1)||v(e,t,n,"deepEqual",h.deepEqual)},h.deepStrictEqual=function(e,t,n){m(e,t,!0)||v(e,t,n,"deepStrictEqual",h.deepStrictEqual)},h.notDeepEqual=function(e,t,n){m(e,t,!1)&&v(e,t,n,"notDeepEqual",h.notDeepEqual)},h.notDeepStrictEqual=function e(t,n,r){m(t,n,!0)&&v(t,n,r,"notDeepStrictEqual",e)},h.strictEqual=function(e,t,n){e!==t&&v(e,t,n,"===",h.strictEqual)},h.notStrictEqual=function(e,t,n){e===t&&v(e,t,n,"!==",h.notStrictEqual)},h.throws=function(e,t,n){S(!0,e,t,n)},h.doesNotThrow=function(e,t,n){S(!1,e,t,n)},h.ifError=function(e){if(e)throw e},h.strict=r((function e(t,n){t||v(t,!0,n,"==",e)}),h,{equal:h.strictEqual,deepEqual:h.deepStrictEqual,notEqual:h.notStrictEqual,notDeepEqual:h.notDeepStrictEqual}),h.strict.strict=h.strict;var C=Object.keys||function(e){var t=[];for(var n in e)s.call(e,n)&&t.push(n);return t}}).call(this,n(8))},function(e,t,n){"use strict"; + */function o(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(a=l[u],!m(e[a],t[a],n,r))return!1;return!0}(e,t,n,r))}return n?e===t:e==t}function w(e){return"[object Arguments]"==Object.prototype.toString.call(e)}function E(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 S(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&&v(o,n,"Missing expected exception"+r);var i="string"==typeof r,a=!e&&o&&!n;if((!e&&s.isError(o)&&i&&E(o,n)||a)&&v(o,n,"Got unwanted exception"+r),e&&o&&n&&!E(o,n)||!e&&o)throw o}h.AssertionError=function(e){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=function(e){return g(y(e.actual),128)+" "+e.operator+" "+g(y(e.expected),128)}(this),this.generatedMessage=!0);var t=e.stackStartFunction||v;if(Error.captureStackTrace)Error.captureStackTrace(this,t);else{var n=new Error;if(n.stack){var r=n.stack,o=d(t),i=r.indexOf("\n"+o);if(i>=0){var s=r.indexOf("\n",i+1);r=r.substring(s+1)}this.stack=r}}},s.inherits(h.AssertionError,Error),h.fail=v,h.ok=b,h.equal=function(e,t,n){e!=t&&v(e,t,n,"==",h.equal)},h.notEqual=function(e,t,n){e==t&&v(e,t,n,"!=",h.notEqual)},h.deepEqual=function(e,t,n){m(e,t,!1)||v(e,t,n,"deepEqual",h.deepEqual)},h.deepStrictEqual=function(e,t,n){m(e,t,!0)||v(e,t,n,"deepStrictEqual",h.deepStrictEqual)},h.notDeepEqual=function(e,t,n){m(e,t,!1)&&v(e,t,n,"notDeepEqual",h.notDeepEqual)},h.notDeepStrictEqual=function e(t,n,r){m(t,n,!0)&&v(t,n,r,"notDeepStrictEqual",e)},h.strictEqual=function(e,t,n){e!==t&&v(e,t,n,"===",h.strictEqual)},h.notStrictEqual=function(e,t,n){e===t&&v(e,t,n,"!==",h.notStrictEqual)},h.throws=function(e,t,n){S(!0,e,t,n)},h.doesNotThrow=function(e,t,n){S(!1,e,t,n)},h.ifError=function(e){if(e)throw e},h.strict=r((function e(t,n){t||v(t,!0,n,"==",e)}),h,{equal:h.strictEqual,deepEqual:h.deepStrictEqual,notEqual:h.notStrictEqual,notDeepEqual:h.notDeepStrictEqual}),h.strict.strict=h.strict;var C=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,n){"use strict"; /* object-assign (c) Sindre Sorhus @license MIT -*/var r=Object.getOwnPropertySymbols,o=Object.prototype.hasOwnProperty,i=Object.prototype.propertyIsEnumerable;function a(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach((function(e){r[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(e){return!1}}()?Object.assign:function(e,t){for(var n,s,c=a(e),u=1;u0?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(10),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),a.prototype=Object.create(o.prototype),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(65),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(35)).Stream=t,t.Readable=t,t.Writable=n(40),t.Duplex=n(9),t.Transform=n(41),t.PassThrough=n(69)},function(e,t,n){"use strict";e.exports=i;var r=n(41),o=n(19);function i(e){if(!(this instanceof i))return new i(e);r.call(this,e)}o.inherits=n(15),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(34).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;a.1)&&((n=r.allocUnsafe(9))[0]=203,n.writeDoubleBE(e,1)),n}e.exports=function(e,t,n,a){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(!a&&"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,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 i(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 i(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 i(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";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){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).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=a.trys,(o=o.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.fatalError?(this.logger.log(s.LogLevel.Debug,"Received a new batch "+e+" but errored out on a previous batch "+(this.nextBatchId-1)),[4,n.send("OnRenderCompleted",this.nextBatchId-1,this.fatalError.toString())]):[3,4]:[3,5];case 3:return o.sent(),[2];case 4:return this.logger.log(s.LogLevel.Debug,"Waiting for batch "+this.nextBatchId+". Batch "+e+" not processed."),[2];case 5:return o.trys.push([5,7,,8]),this.nextBatchId++,this.logger.log(s.LogLevel.Debug,"Applying batch "+e+"."),i.renderBatch(this.browserRendererId,new a.OutOfProcessRenderBatch(t)),[4,this.completeBatch(n,e)];case 6:return o.sent(),[3,8];case 7:throw r=o.sent(),this.fatalError=r.toString(),this.logger.log(s.LogLevel.Error,"There was an error applying batch "+e+"."),n.send("OnRenderCompleted",e,r.toString()),r;case 8:return[2]}}))}))},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}();t.RenderQueue=c},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(74),o=n(75),i=function(){function e(e){this.batchData=e;var t=new u(e);this.arrayRangeReader=new l(e),this.arrayBuilderSegmentReader=new f(e),this.diffReader=new a(e),this.editReader=new s(e,t),this.frameReader=new c(e,t)}return e.prototype.updatedComponents=function(){return o.readInt32LE(this.batchData,this.batchData.length-20)},e.prototype.referenceFrames=function(){return o.readInt32LE(this.batchData,this.batchData.length-16)},e.prototype.disposedComponentIds=function(){return o.readInt32LE(this.batchData,this.batchData.length-12)},e.prototype.disposedEventHandlerIds=function(){return o.readInt32LE(this.batchData,this.batchData.length-8)},e.prototype.updatedComponentsEntry=function(e,t){var n=e+4*t;return o.readInt32LE(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 o.readInt32LE(this.batchData,n)},e.prototype.disposedEventHandlerIdsEntry=function(e,t){var n=e+8*t;return o.readUint64LE(this.batchData,n)},e}();t.OutOfProcessRenderBatch=i;var a=function(){function e(e){this.batchDataUint8=e}return e.prototype.componentId=function(e){return o.readInt32LE(this.batchDataUint8,e)},e.prototype.edits=function(e){return e+4},e.prototype.editsEntry=function(e,t){return e+16*t},e}(),s=function(){function e(e,t){this.batchDataUint8=e,this.stringReader=t}return e.prototype.editType=function(e){return o.readInt32LE(this.batchDataUint8,e)},e.prototype.siblingIndex=function(e){return o.readInt32LE(this.batchDataUint8,e+4)},e.prototype.newTreeIndex=function(e){return o.readInt32LE(this.batchDataUint8,e+8)},e.prototype.moveToSiblingIndex=function(e){return o.readInt32LE(this.batchDataUint8,e+8)},e.prototype.removedAttributeName=function(e){var t=o.readInt32LE(this.batchDataUint8,e+12);return this.stringReader.readString(t)},e}(),c=function(){function e(e,t){this.batchDataUint8=e,this.stringReader=t}return e.prototype.frameType=function(e){return o.readInt32LE(this.batchDataUint8,e)},e.prototype.subtreeLength=function(e){return o.readInt32LE(this.batchDataUint8,e+4)},e.prototype.elementReferenceCaptureId=function(e){var t=o.readInt32LE(this.batchDataUint8,e+4);return this.stringReader.readString(t)},e.prototype.componentId=function(e){return o.readInt32LE(this.batchDataUint8,e+8)},e.prototype.elementName=function(e){var t=o.readInt32LE(this.batchDataUint8,e+8);return this.stringReader.readString(t)},e.prototype.textContent=function(e){var t=o.readInt32LE(this.batchDataUint8,e+4);return this.stringReader.readString(t)},e.prototype.markupContent=function(e){var t=o.readInt32LE(this.batchDataUint8,e+4);return this.stringReader.readString(t)},e.prototype.attributeName=function(e){var t=o.readInt32LE(this.batchDataUint8,e+4);return this.stringReader.readString(t)},e.prototype.attributeValue=function(e){var t=o.readInt32LE(this.batchDataUint8,e+8);return this.stringReader.readString(t)},e.prototype.attributeEventHandlerId=function(e){return o.readUint64LE(this.batchDataUint8,e+12)},e}(),u=function(){function e(e){this.batchDataUint8=e,this.stringTableStartIndex=o.readInt32LE(e,e.length-4)}return e.prototype.readString=function(e){if(-1===e)return null;var t=o.readInt32LE(this.batchDataUint8,this.stringTableStartIndex+4*e),n=o.readLEB128(this.batchDataUint8,t),i=t+o.numLEB128Bytes(n),a=new Uint8Array(this.batchDataUint8.buffer,this.batchDataUint8.byteOffset+i,n);return r.decodeUtf8(a)},e}(),l=function(){function e(e){this.batchDataUint8=e}return e.prototype.count=function(e){return o.readInt32LE(this.batchDataUint8,e)},e.prototype.values=function(e){return e+4},e}(),f=function(){function e(e){this.batchDataUint8=e}return e.prototype.offset=function(e){return 0},e.prototype.count=function(e){return o.readInt32LE(this.batchDataUint8,e)},e.prototype.values=function(e){return e+4},e}()},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=Math.pow(2,32),o=Math.pow(2,21)-1;function i(e,t){return e[t]+(e[t+1]<<8)+(e[t+2]<<16)+(e[t+3]<<24>>>0)}t.readInt32LE=function(e,t){return e[t]|e[t+1]<<8|e[t+2]<<16|e[t+3]<<24},t.readUint32LE=i,t.readUint64LE=function(e,t){var n=i(e,t+4);if(n>o)throw new Error("Cannot read uint64 with high order part "+n+", because the result would exceed Number.MAX_SAFE_INTEGER.");return n*r+i(e,t)},t.readLEB128=function(e,t){for(var n=0,r=0,o=0;o<4;o++){var i=e[t+o];if(n|=(127&i)<=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){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).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=a.trys,(o=o.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]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.MaximumFirstRetryInterval?e.MaximumFirstRetryInterval:t.retryIntervalMilliseconds,[4,this.delay(r)]):[3,7];case 2:if(o.sent(),this.isDisposed)return[3,7];o.label=3;case 3:return o.trys.push([3,5,,6]),[4,this.reconnectCallback()];case 4:return o.sent()||this.reconnectDisplay.rejected(),[2];case 5:return i=o.sent(),this.logger.log(s.LogLevel.Error,i),[3,6];case 6:return n++,[3,1];case 7:return this.reconnectDisplay.failed(),[2]}}))}))},e.prototype.delay=function(e){return new Promise((function(t){return setTimeout(t,e)}))},e.MaximumFirstRetryInterval=3e3,e}()},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){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).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=a.trys,(o=o.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]reloading the page if you're unable to reconnect.",this.message.querySelector("a").addEventListener("click",(function(){return location.reload()}))},e.prototype.rejected=function(){this.button.style.display="none",this.reloadParagraph.style.display="none",this.loader.style.display="none",this.message.innerHTML="Could not reconnect to the server. Reload the page to restore functionality.",this.message.querySelector("a").addEventListener("click",(function(){return location.reload()}))},e.prototype.getLoader=function(){var e=this.document.createElement("div");return e.style.cssText=["border: 0.3em solid #f3f3f3","border-top: 0.3em solid #3498db","border-radius: 50%","width: 2em","height: 2em","display: inline-block"].join(";"),e.animate([{transform:"rotate(0deg)"},{transform:"rotate(360deg)"}],{duration:2e3,iterations:1/0}),e},e}();t.DefaultReconnectDisplay=a},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(t,n,r){this.dialog=t,this.maxRetries=n,this.document=r,this.document=r;var o=this.document.getElementById(e.MaxRetriesId);o&&(o.innerText=this.maxRetries.toString())}return e.prototype.show=function(){this.removeClasses(),this.dialog.classList.add(e.ShowClassName)},e.prototype.update=function(t){var n=this.document.getElementById(e.CurrentAttemptId);n&&(n.innerText=t.toString())},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.rejected=function(){this.removeClasses(),this.dialog.classList.add(e.RejectedClassName)},e.prototype.removeClasses=function(){this.dialog.classList.remove(e.ShowClassName,e.HideClassName,e.FailedClassName,e.RejectedClassName)},e.ShowClassName="components-reconnect-show",e.HideClassName="components-reconnect-hide",e.FailedClassName="components-reconnect-failed",e.RejectedClassName="components-reconnect-rejected",e.MaxRetriesId="components-reconnect-max-retries",e.CurrentAttemptId="components-reconnect-current-attempt",e}();t.UserSpecifiedDisplay=r},function(e,t,n){"use strict";n.r(t),n.d(t,"VERSION",(function(){return l})),n.d(t,"MessagePackHubProtocol",(function(){return u}));var r=n(10),o=n(11),i=n(2),a=function(){function e(){}return e.write=function(e){var t=e.byteLength||e.length,n=[];do{var r=127&t;(t>>=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=Object.assign||function(e){for(var t,n=1,r=arguments.length;n=3?e[2]:void 0,error:e[1],type:i.MessageType.Close}},e.prototype.createPingMessage=function(e){if(e.length<1)throw new Error("Invalid payload for Ping message.");return{type:i.MessageType.Ping}},e.prototype.createInvocationMessage=function(e,t){if(t.length<5)throw new Error("Invalid payload for Invocation message.");var n=t[2];return n?{arguments:t[4],headers:e,invocationId:n,streamIds:[],target:t[3],type:i.MessageType.Invocation}:{arguments:t[4],headers:e,streamIds:[],target:t[3],type:i.MessageType.Invocation}},e.prototype.createStreamItemMessage=function(e,t){if(t.length<4)throw new Error("Invalid payload for StreamItem message.");return{headers:e,invocationId:t[2],item:t[3],type:i.MessageType.StreamItem}},e.prototype.createCompletionMessage=function(e,t){if(t.length<4)throw new Error("Invalid payload for Completion message.");var n,r,o=t[3];if(o!==this.voidResult&&t.length<5)throw new Error("Invalid payload for Completion message.");switch(o){case this.errorResult:n=t[4];break;case this.nonVoidResult:r=t[4]}return{error:n,headers:e,invocationId:t[2],result:r,type:i.MessageType.Completion}},e.prototype.writeInvocation=function(e){var t,n=o(this.messagePackOptions);return t=e.streamIds?n.encode([i.MessageType.Invocation,e.headers||{},e.invocationId||null,e.target,e.arguments,e.streamIds]):n.encode([i.MessageType.Invocation,e.headers||{},e.invocationId||null,e.target,e.arguments]),a.write(t.slice())},e.prototype.writeStreamInvocation=function(e){var t,n=o(this.messagePackOptions);return t=e.streamIds?n.encode([i.MessageType.StreamInvocation,e.headers||{},e.invocationId,e.target,e.arguments,e.streamIds]):n.encode([i.MessageType.StreamInvocation,e.headers||{},e.invocationId,e.target,e.arguments]),a.write(t.slice())},e.prototype.writeStreamItem=function(e){var t=o(this.messagePackOptions).encode([i.MessageType.StreamItem,e.headers||{},e.invocationId,e.item]);return a.write(t.slice())},e.prototype.writeCompletion=function(e){var t,n=o(this.messagePackOptions),r=e.error?this.errorResult:e.result?this.nonVoidResult:this.voidResult;switch(r){case this.errorResult:t=n.encode([i.MessageType.Completion,e.headers||{},e.invocationId,r,e.error]);break;case this.voidResult:t=n.encode([i.MessageType.Completion,e.headers||{},e.invocationId,r]);break;case this.nonVoidResult:t=n.encode([i.MessageType.Completion,e.headers||{},e.invocationId,r,e.result])}return a.write(t.slice())},e.prototype.writeCancelInvocation=function(e){var t=o(this.messagePackOptions).encode([i.MessageType.CancelInvocation,e.headers||{},e.invocationId]);return a.write(t.slice())},e.prototype.readHeaders=function(e){var t=e[1];if("object"!=typeof t)throw new Error("Invalid headers.");return t},e}(),l="5.0.0-dev"}]); \ No newline at end of file +*/var r=Object.getOwnPropertySymbols,o=Object.prototype.hasOwnProperty,i=Object.prototype.propertyIsEnumerable;function s(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach((function(e){r[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(e){return!1}}()?Object.assign:function(e,t){for(var n,a,c=s(e),u=1;u0?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),s=this.head,a=0;s;)t=s.data,n=i,o=a,t.copy(n,o),a+=s.data.length,s=s.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(10),o=r.Buffer;function i(e,t){for(var n in e)t[n]=e[n]}function s(e,t,n){return o(e,t,n)}o.from&&o.alloc&&o.allocUnsafe&&o.allocUnsafeSlow?e.exports=r:(i(r,t),t.Buffer=s),s.prototype=Object.create(o.prototype),i(o,s),s.from=function(e,t,n){if("number"==typeof e)throw new TypeError("Argument must not be a number");return o(e,t,n)},s.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},s.allocUnsafe=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return o(e)},s.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(67),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,s,a,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)}:(s="setImmediate$"+Math.random()+"$",a=function(t){t.source===e&&"string"==typeof t.data&&0===t.data.indexOf(s)&&d(+t.data.slice(s.length))},e.addEventListener?e.addEventListener("message",a,!1):e.attachEvent("onmessage",a),r=function(t){e.postMessage(s+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=a},function(e,t,n){(t=e.exports=n(36)).Stream=t,t.Readable=t,t.Writable=n(41),t.Duplex=n(9),t.Transform=n(42),t.PassThrough=n(71)},function(e,t,n){"use strict";e.exports=i;var r=n(42),o=n(19);function i(e){if(!(this instanceof i))return new i(e);r.call(this,e)}o.inherits=n(15),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(35).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 s=e.readUInt32BE(t+0),a=e.readUInt32BE(t+4);return(4294967296*s+a)*(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),s(e,r,i,3));case 221:return o<5?null:(i=e.readUInt32BE(r+1),s(e,r,i,5));case 222:return i=e.readUInt16BE(r+1),a(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 s(e,r,i=15&h,1);if(128==(240&h))return a(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 s(e,t,r,o){var s,a=[],c=0;for(t+=o,s=0;s.1)&&((n=r.allocUnsafe(9))[0]=203,n.writeDoubleBE(e,1)),n}e.exports=function(e,t,n,s){function a(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(a(t,!0)),e}),o().append(l));else{if(!s&&"function"==typeof c.getDate)return function(e){var t,n=1*e,i=Math.floor(n/1e3),s=1e6*(n-1e3*i);if(s||i>4294967295){(t=new r(10))[0]=215,t[1]=-1;var a=4*s,c=i/Math.pow(2,32),u=a+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,s,a=[];for(n=0;n>8),a.push(255&s)):(a.push(201),a.push(s>>24),a.push(s>>16&255),a.push(s>>8&255),a.push(255&s));return o().append(r.from(a)).append(i)}(c)||function(e){var t,n,i=[],s=0;for(t in e)e.hasOwnProperty(t)&&void 0!==e[t]&&"function"!=typeof e[t]&&(++s,i.push(a(t,!0)),i.push(a(e[t],!0)));s<16?(n=r.allocUnsafe(1))[0]=128|s:((n=r.allocUnsafe(3))[0]=222,n.writeUInt16BE(s,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 i(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 i(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 i(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 s=1,a=t+7;a>=t;a--){var c=(255^e[a])+s;e[a]=255&c,s=c>>8}}(l,1,c)}}}if(!l)throw new Error("not implemented yet");return u?l:l.slice()}return a}},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 s(e){try{c(r.next(e))}catch(e){i(e)}}function a(e){try{c(r.throw(e))}catch(e){i(e)}}function c(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}c((r=r.apply(e,t||[])).next())}))},o=this&&this.__generator||function(e,t){var n,r,o,i,s={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function a(i){return function(a){return function(i){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(o=2&i[0]?r.return:i[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,i[1])).done)return o;switch(r=0,o&&(i=[2&i[0],o.value]),i[0]){case 0:case 1:o=i;break;case 4:return s.label++,{value:i[1],done:!1};case 5:s.label++,r=i[1],i=[0];continue;case 7:i=s.ops.pop(),s.trys.pop();continue;default:if(!(o=s.trys,(o=o.length>0&&o[o.length-1])||6!==i[0]&&2!==i[0])){s=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]this.nextBatchId?this.fatalError?(this.logger.log(a.LogLevel.Debug,"Received a new batch "+e+" but errored out on a previous batch "+(this.nextBatchId-1)),[4,n.send("OnRenderCompleted",this.nextBatchId-1,this.fatalError.toString())]):[3,4]:[3,5];case 3:return o.sent(),[2];case 4:return this.logger.log(a.LogLevel.Debug,"Waiting for batch "+this.nextBatchId+". Batch "+e+" not processed."),[2];case 5:return o.trys.push([5,7,,8]),this.nextBatchId++,this.logger.log(a.LogLevel.Debug,"Applying batch "+e+"."),i.renderBatch(this.browserRendererId,new s.OutOfProcessRenderBatch(t)),[4,this.completeBatch(n,e)];case 6:return o.sent(),[3,8];case 7:throw r=o.sent(),this.fatalError=r.toString(),this.logger.log(a.LogLevel.Error,"There was an error applying batch "+e+"."),n.send("OnRenderCompleted",e,r.toString()),r;case 8:return[2]}}))}))},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(a.LogLevel.Warning,"Failed to deliver completion notification for render '"+t+"'."),[3,3];case 3:return[2]}}))}))},e}();t.RenderQueue=c},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(76),o=n(77),i=function(){function e(e){this.batchData=e;var t=new u(e);this.arrayRangeReader=new l(e),this.arrayBuilderSegmentReader=new f(e),this.diffReader=new s(e),this.editReader=new a(e,t),this.frameReader=new c(e,t)}return e.prototype.updatedComponents=function(){return o.readInt32LE(this.batchData,this.batchData.length-20)},e.prototype.referenceFrames=function(){return o.readInt32LE(this.batchData,this.batchData.length-16)},e.prototype.disposedComponentIds=function(){return o.readInt32LE(this.batchData,this.batchData.length-12)},e.prototype.disposedEventHandlerIds=function(){return o.readInt32LE(this.batchData,this.batchData.length-8)},e.prototype.updatedComponentsEntry=function(e,t){var n=e+4*t;return o.readInt32LE(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 o.readInt32LE(this.batchData,n)},e.prototype.disposedEventHandlerIdsEntry=function(e,t){var n=e+8*t;return o.readUint64LE(this.batchData,n)},e}();t.OutOfProcessRenderBatch=i;var s=function(){function e(e){this.batchDataUint8=e}return e.prototype.componentId=function(e){return o.readInt32LE(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 o.readInt32LE(this.batchDataUint8,e)},e.prototype.siblingIndex=function(e){return o.readInt32LE(this.batchDataUint8,e+4)},e.prototype.newTreeIndex=function(e){return o.readInt32LE(this.batchDataUint8,e+8)},e.prototype.moveToSiblingIndex=function(e){return o.readInt32LE(this.batchDataUint8,e+8)},e.prototype.removedAttributeName=function(e){var t=o.readInt32LE(this.batchDataUint8,e+12);return this.stringReader.readString(t)},e}(),c=function(){function e(e,t){this.batchDataUint8=e,this.stringReader=t}return e.prototype.frameType=function(e){return o.readInt32LE(this.batchDataUint8,e)},e.prototype.subtreeLength=function(e){return o.readInt32LE(this.batchDataUint8,e+4)},e.prototype.elementReferenceCaptureId=function(e){var t=o.readInt32LE(this.batchDataUint8,e+4);return this.stringReader.readString(t)},e.prototype.componentId=function(e){return o.readInt32LE(this.batchDataUint8,e+8)},e.prototype.elementName=function(e){var t=o.readInt32LE(this.batchDataUint8,e+8);return this.stringReader.readString(t)},e.prototype.textContent=function(e){var t=o.readInt32LE(this.batchDataUint8,e+4);return this.stringReader.readString(t)},e.prototype.markupContent=function(e){var t=o.readInt32LE(this.batchDataUint8,e+4);return this.stringReader.readString(t)},e.prototype.attributeName=function(e){var t=o.readInt32LE(this.batchDataUint8,e+4);return this.stringReader.readString(t)},e.prototype.attributeValue=function(e){var t=o.readInt32LE(this.batchDataUint8,e+8);return this.stringReader.readString(t)},e.prototype.attributeEventHandlerId=function(e){return o.readUint64LE(this.batchDataUint8,e+12)},e}(),u=function(){function e(e){this.batchDataUint8=e,this.stringTableStartIndex=o.readInt32LE(e,e.length-4)}return e.prototype.readString=function(e){if(-1===e)return null;var t=o.readInt32LE(this.batchDataUint8,this.stringTableStartIndex+4*e),n=o.readLEB128(this.batchDataUint8,t),i=t+o.numLEB128Bytes(n),s=new Uint8Array(this.batchDataUint8.buffer,this.batchDataUint8.byteOffset+i,n);return r.decodeUtf8(s)},e}(),l=function(){function e(e){this.batchDataUint8=e}return e.prototype.count=function(e){return o.readInt32LE(this.batchDataUint8,e)},e.prototype.values=function(e){return e+4},e}(),f=function(){function e(e){this.batchDataUint8=e}return e.prototype.offset=function(e){return 0},e.prototype.count=function(e){return o.readInt32LE(this.batchDataUint8,e)},e.prototype.values=function(e){return e+4},e}()},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=Math.pow(2,32),o=Math.pow(2,21)-1;function i(e,t){return e[t]+(e[t+1]<<8)+(e[t+2]<<16)+(e[t+3]<<24>>>0)}t.readInt32LE=function(e,t){return e[t]|e[t+1]<<8|e[t+2]<<16|e[t+3]<<24},t.readUint32LE=i,t.readUint64LE=function(e,t){var n=i(e,t+4);if(n>o)throw new Error("Cannot read uint64 with high order part "+n+", because the result would exceed Number.MAX_SAFE_INTEGER.");return n*r+i(e,t)},t.readLEB128=function(e,t){for(var n=0,r=0,o=0;o<4;o++){var i=e[t+o];if(n|=(127&i)<=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 s(e){try{c(r.next(e))}catch(e){i(e)}}function a(e){try{c(r.throw(e))}catch(e){i(e)}}function c(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}c((r=r.apply(e,t||[])).next())}))},o=this&&this.__generator||function(e,t){var n,r,o,i,s={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function a(i){return function(a){return function(i){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(o=2&i[0]?r.return:i[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,i[1])).done)return o;switch(r=0,o&&(i=[2&i[0],o.value]),i[0]){case 0:case 1:o=i;break;case 4:return s.label++,{value:i[1],done:!1};case 5:s.label++,r=i[1],i=[0];continue;case 7:i=s.ops.pop(),s.trys.pop();continue;default:if(!(o=s.trys,(o=o.length>0&&o[o.length-1])||6!==i[0]&&2!==i[0])){s=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]0&&o[o.length-1])||6!==i[0]&&2!==i[0])){s=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]e.MaximumFirstRetryInterval?e.MaximumFirstRetryInterval:t.retryIntervalMilliseconds,[4,this.delay(r)]):[3,7];case 2:if(o.sent(),this.isDisposed)return[3,7];o.label=3;case 3:return o.trys.push([3,5,,6]),[4,this.reconnectCallback()];case 4:return o.sent()||this.reconnectDisplay.rejected(),[2];case 5:return i=o.sent(),this.logger.log(a.LogLevel.Error,i),[3,6];case 6:return n++,[3,1];case 7:return this.reconnectDisplay.failed(),[2]}}))}))},e.prototype.delay=function(e){return new Promise((function(t){return setTimeout(t,e)}))},e.MaximumFirstRetryInterval=3e3,e}()},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 s(e){try{c(r.next(e))}catch(e){i(e)}}function a(e){try{c(r.throw(e))}catch(e){i(e)}}function c(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(s,a)}c((r=r.apply(e,t||[])).next())}))},o=this&&this.__generator||function(e,t){var n,r,o,i,s={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:a(0),throw:a(1),return:a(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function a(i){return function(a){return function(i){if(n)throw new TypeError("Generator is already executing.");for(;s;)try{if(n=1,r&&(o=2&i[0]?r.return:i[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,i[1])).done)return o;switch(r=0,o&&(i=[2&i[0],o.value]),i[0]){case 0:case 1:o=i;break;case 4:return s.label++,{value:i[1],done:!1};case 5:s.label++,r=i[1],i=[0];continue;case 7:i=s.ops.pop(),s.trys.pop();continue;default:if(!(o=s.trys,(o=o.length>0&&o[o.length-1])||6!==i[0]&&2!==i[0])){s=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]reloading the page if you're unable to reconnect.",this.message.querySelector("a").addEventListener("click",(function(){return location.reload()}))},e.prototype.rejected=function(){this.button.style.display="none",this.reloadParagraph.style.display="none",this.loader.style.display="none",this.message.innerHTML="Could not reconnect to the server. Reload the page to restore functionality.",this.message.querySelector("a").addEventListener("click",(function(){return location.reload()}))},e.prototype.getLoader=function(){var e=this.document.createElement("div");return e.style.cssText=["border: 0.3em solid #f3f3f3","border-top: 0.3em solid #3498db","border-radius: 50%","width: 2em","height: 2em","display: inline-block"].join(";"),e.animate([{transform:"rotate(0deg)"},{transform:"rotate(360deg)"}],{duration:2e3,iterations:1/0}),e},e}();t.DefaultReconnectDisplay=s},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(t,n,r){this.dialog=t,this.maxRetries=n,this.document=r,this.document=r;var o=this.document.getElementById(e.MaxRetriesId);o&&(o.innerText=this.maxRetries.toString())}return e.prototype.show=function(){this.removeClasses(),this.dialog.classList.add(e.ShowClassName)},e.prototype.update=function(t){var n=this.document.getElementById(e.CurrentAttemptId);n&&(n.innerText=t.toString())},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.rejected=function(){this.removeClasses(),this.dialog.classList.add(e.RejectedClassName)},e.prototype.removeClasses=function(){this.dialog.classList.remove(e.ShowClassName,e.HideClassName,e.FailedClassName,e.RejectedClassName)},e.ShowClassName="components-reconnect-show",e.HideClassName="components-reconnect-hide",e.FailedClassName="components-reconnect-failed",e.RejectedClassName="components-reconnect-rejected",e.MaxRetriesId="components-reconnect-max-retries",e.CurrentAttemptId="components-reconnect-current-attempt",e}();t.UserSpecifiedDisplay=r},function(e,t,n){"use strict";n.r(t),n.d(t,"VERSION",(function(){return l})),n.d(t,"MessagePackHubProtocol",(function(){return u}));var r=n(10),o=n(11),i=n(2),s=function(){function e(){}return e.write=function(e){var t=e.byteLength||e.length,n=[];do{var r=127&t;(t>>=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+s))throw new Error("Incomplete message.");t.push(n.slice?n.slice(o+i,o+i+s):n.subarray(o+i,o+i+s)),o=o+i+s}return t},e}();var a=Object.assign||function(e){for(var t,n=1,r=arguments.length;n=3?e[2]:void 0,error:e[1],type:i.MessageType.Close}},e.prototype.createPingMessage=function(e){if(e.length<1)throw new Error("Invalid payload for Ping message.");return{type:i.MessageType.Ping}},e.prototype.createInvocationMessage=function(e,t){if(t.length<5)throw new Error("Invalid payload for Invocation message.");var n=t[2];return n?{arguments:t[4],headers:e,invocationId:n,streamIds:[],target:t[3],type:i.MessageType.Invocation}:{arguments:t[4],headers:e,streamIds:[],target:t[3],type:i.MessageType.Invocation}},e.prototype.createStreamItemMessage=function(e,t){if(t.length<4)throw new Error("Invalid payload for StreamItem message.");return{headers:e,invocationId:t[2],item:t[3],type:i.MessageType.StreamItem}},e.prototype.createCompletionMessage=function(e,t){if(t.length<4)throw new Error("Invalid payload for Completion message.");var n,r,o=t[3];if(o!==this.voidResult&&t.length<5)throw new Error("Invalid payload for Completion message.");switch(o){case this.errorResult:n=t[4];break;case this.nonVoidResult:r=t[4]}return{error:n,headers:e,invocationId:t[2],result:r,type:i.MessageType.Completion}},e.prototype.writeInvocation=function(e){var t,n=o(this.messagePackOptions);return t=e.streamIds?n.encode([i.MessageType.Invocation,e.headers||{},e.invocationId||null,e.target,e.arguments,e.streamIds]):n.encode([i.MessageType.Invocation,e.headers||{},e.invocationId||null,e.target,e.arguments]),s.write(t.slice())},e.prototype.writeStreamInvocation=function(e){var t,n=o(this.messagePackOptions);return t=e.streamIds?n.encode([i.MessageType.StreamInvocation,e.headers||{},e.invocationId,e.target,e.arguments,e.streamIds]):n.encode([i.MessageType.StreamInvocation,e.headers||{},e.invocationId,e.target,e.arguments]),s.write(t.slice())},e.prototype.writeStreamItem=function(e){var t=o(this.messagePackOptions).encode([i.MessageType.StreamItem,e.headers||{},e.invocationId,e.item]);return s.write(t.slice())},e.prototype.writeCompletion=function(e){var t,n=o(this.messagePackOptions),r=e.error?this.errorResult:e.result?this.nonVoidResult:this.voidResult;switch(r){case this.errorResult:t=n.encode([i.MessageType.Completion,e.headers||{},e.invocationId,r,e.error]);break;case this.voidResult:t=n.encode([i.MessageType.Completion,e.headers||{},e.invocationId,r]);break;case this.nonVoidResult:t=n.encode([i.MessageType.Completion,e.headers||{},e.invocationId,r,e.result])}return s.write(t.slice())},e.prototype.writeCancelInvocation=function(e){var t=o(this.messagePackOptions).encode([i.MessageType.CancelInvocation,e.headers||{},e.invocationId]);return s.write(t.slice())},e.prototype.readHeaders=function(e){var t=e[1];if("object"!=typeof t)throw new Error("Invalid headers.");return t},e}(),l="5.0.0-dev"}]); \ No newline at end of file diff --git a/src/Components/Web.JS/dist/Release/blazor.webassembly.js b/src/Components/Web.JS/dist/Release/blazor.webassembly.js index 3c87e2a0cc3b..df469f8e58c7 100644 --- a/src/Components/Web.JS/dist/Release/blazor.webassembly.js +++ b/src/Components/Web.JS/dist/Release/blazor.webassembly.js @@ -1 +1 @@ -!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=43)}([,,,function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),function(e){window.DotNet=e;var t=[],n={},r={},o=1,i=null;function a(e){t.push(e)}function s(e,t,n,r){var o=c();if(o.invokeDotNetFromJS){var i=JSON.stringify(r,m),a=o.invokeDotNetFromJS(e,t,n,i);return a?f(a):null}throw new Error("The current dispatcher does not support synchronous calls from JS to .NET. Use invokeMethodAsync instead.")}function u(e,t,r,i){if(e&&r)throw new Error("For instance method calls, assemblyName should be null. Received '"+e+"'.");var a=o++,s=new Promise((function(e,t){n[a]={resolve:e,reject:t}}));try{var u=JSON.stringify(i,m);c().beginInvokeDotNetFromJS(a,e,t,r,u)}catch(e){l(a,!1,e)}return s}function c(){if(null!==i)return i;throw new Error("No .NET call dispatcher has been set.")}function l(e,t,r){if(!n.hasOwnProperty(e))throw new Error("There is no pending async call with ID "+e+".");var o=n[e];delete n[e],t?o.resolve(r):o.reject(r)}function f(e){return e?JSON.parse(e,(function(e,n){return t.reduce((function(t,n){return n(e,t)}),n)})):null}function d(e){return e instanceof Error?e.message+"\n"+e.stack:e?e.toString():"null"}function p(e){if(Object.prototype.hasOwnProperty.call(r,e))return r[e];var t,n=window,o="window";if(e.split(".").forEach((function(e){if(!(e in n))throw new Error("Could not find '"+e+"' in '"+o+"'.");t=n,n=n[e],o+="."+e})),n instanceof Function)return n=n.bind(t),r[e]=n,n;throw new Error("The value '"+o+"' is not a function.")}e.attachDispatcher=function(e){i=e},e.attachReviver=a,e.invokeMethod=function(e,t){for(var n=[],r=2;r0&&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&&!t)throw new Error("New logical elements must start empty, or allowExistingContents must be true");return r in e||(e[r]=[]),e}function s(e,t,n){var i=e;if(e instanceof Comment&&(c(i)&&c(i).length>0))throw new Error("Not implemented: inserting non-empty logical container");if(u(i))throw new Error("Not implemented: moving existing logical children");var a=c(t);if(n0;)e(r,0)}var i=r;i.parentNode.removeChild(i)},t.getLogicalParent=u,t.getLogicalSiblingEnd=function(e){return e[i]||null},t.getLogicalChild=function(e,t){return c(e)[t]},t.isSvgElement=function(e){return"http://www.w3.org/2000/svg"===l(e).namespaceURI},t.getLogicalChildrenArray=c,t.permuteLogicalChildren=function(e,t){var n=c(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=u(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,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){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.setPlatform=function(e){return t.platform=e,t.platform}},function(e,t,n){"use strict";var r;Object.defineProperty(t,"__esModule",{value:!0}),t.dispatchEvent=function(e,t){if(!r)throw new Error("eventDispatcher not initialized. Call 'setEventDispatcher' to configure it.");r(e,t)},t.setEventDispatcher=function(e){r=e}},,,,function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(7),o=n(4),i=n(30),a=n(31);window.Blazor={navigateTo:r.navigateTo,_internal:{attachRootComponentToElement:o.attachRootComponentToElement,navigationManager:r.internalFunctions,domWrapper:i.domFunctions,Virtualize:a.Virtualize}}},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0})},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=n(25),o=n(26),i=n(12),a=n(29),s=n(18),u=n(7),c=document.createElement("template"),l=document.createElementNS("http://www.w3.org/2000/svg","g"),f={submit:!0},d={},p=function(){function e(e){var t=this;this.childComponentLocations={},this.browserRendererId=e,this.eventDelegator=new o.EventDelegator((function(e,n,r,o){!function(e,t,n,r,o){f[e.type]&&e.preventDefault();var i={browserRendererId:t,eventHandlerId:n,eventArgsType:r.type,eventFieldInfo:o};s.dispatchEvent(i,r.data)}(e,t.browserRendererId,n,r,o)})),u.attachToEventDelegator(this.eventDelegator)}return e.prototype.attachRootComponentToLogicalElement=function(e,t){this.attachComponentToElement(e,t),d[e]=t},e.prototype.updateComponent=function(e,t,n,r){var o,a=this.childComponentLocations[t];if(!a)throw new Error("No element is currently associated with component "+t);var s=d[t];if(s){var u=i.getLogicalSiblingEnd(s);delete d[t],u?function(e,t){var n=i.getLogicalParent(e);if(!n)throw new Error("Can't clear between nodes. The start node does not have a logical parent.");for(var r=i.getLogicalChildrenArray(n),o=r.indexOf(e)+1,a=r.indexOf(t),s=o;s<=a;s++)i.removeLogicalChild(n,o);e.textContent="!"}(s,u):function(e){var t;for(;t=e.firstChild;)e.removeChild(t)}(s)}var c=null===(o=i.getClosestDomElement(a))||void 0===o?void 0:o.ownerDocument,l=c&&c.activeElement;this.applyEdits(e,t,a,0,n,r),l instanceof HTMLElement&&c&&c.activeElement!==l&&l.focus()},e.prototype.disposeComponent=function(e){delete this.childComponentLocations[e]},e.prototype.disposeEventHandler=function(e){this.eventDelegator.removeListener(e)},e.prototype.attachComponentToElement=function(e,t){this.childComponentLocations[e]=t},e.prototype.applyEdits=function(e,t,n,o,a,s){for(var u,c=0,l=o,f=e.arrayBuilderSegmentReader,d=e.editReader,p=e.frameReader,h=f.values(a),m=f.offset(a),v=m+f.count(a),y=m;y0&&e.invokeMethodAsync("OnSpacerAfterVisible",r.boundingClientRect.bottom-r.intersectionRect.bottom,i,a)}}))}),{root:o(t),rootMargin:i+"px"});a.observe(t),a.observe(n);var s=c(t),u=c(n);function c(e){var t=new MutationObserver((function(){a.unobserve(e),a.observe(e)}));return t.observe(e,{attributes:!0}),t}r[e._id]={intersectionObserver:a,mutationObserverBefore:s,mutationObserverAfter:u}},dispose:function(e){var t=r[e._id];t&&(t.intersectionObserver.disconnect(),t.mutationObserverBefore.disconnect(),t.mutationObserverAfter.disconnect(),e.dispose(),delete r[e._id])}};var r={};function o(e){return e?"visible"!==getComputedStyle(e).overflowY?e:o(e.parentElement):null}},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{u(r.next(e))}catch(e){i(e)}}function s(e){try{u(r.throw(e))}catch(e){i(e)}}function u(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,s)}u((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=a.trys,(o=o.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]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)&&!(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 a=n(3);n(22);var s=n(17),u=n(44),c=n(4),l=n(46),f=n(33),d=n(18),p=n(47),h=n(48),m=n(49),v=!1;function y(e){return r(this,void 0,void 0,(function(){var t,n,f,y,b,g,w,E,_=this;return o(this,(function(I){switch(I.label){case 0:if(v)throw new Error("Blazor has already started.");return v=!0,d.setEventDispatcher((function(e,t){c.getRendererer(e.browserRendererId).eventDelegator.getHandler(e.eventHandlerId)&&u.monoPlatform.invokeWhenHeapUnlocked((function(){return a.DotNet.invokeMethodAsync("Microsoft.AspNetCore.Components.WebAssembly","DispatchEvent",e,JSON.stringify(t))}))})),t=s.setPlatform(u.monoPlatform),window.Blazor.platform=t,window.Blazor._internal.renderBatch=function(e,t){var n=u.monoPlatform.beginHeapLock();try{c.renderBatch(e,new l.SharedMemoryRenderBatch(t))}finally{n.release()}},n=window.Blazor._internal.navigationManager.getBaseURI,f=window.Blazor._internal.navigationManager.getLocationHref,window.Blazor._internal.navigationManager.getUnmarshalledBaseURI=function(){return BINDING.js_string_to_mono_string(n())},window.Blazor._internal.navigationManager.getUnmarshalledLocationHref=function(){return BINDING.js_string_to_mono_string(f())},window.Blazor._internal.navigationManager.listenForNavigationEvents((function(e,t){return r(_,void 0,void 0,(function(){return o(this,(function(n){switch(n.label){case 0:return[4,a.DotNet.invokeMethodAsync("Microsoft.AspNetCore.Components.WebAssembly","NotifyLocationChanged",e,t)];case 1:return n.sent(),[2]}}))}))})),y=null==e?void 0:e.environment,[4,m.BootConfigResult.initAsync(y)];case 1:return b=I.sent(),[4,Promise.all([p.WebAssemblyResourceLoader.initAsync(b.bootConfig,e||{}),h.WebAssemblyConfigLoader.initAsync(b)])];case 2:g=i.apply(void 0,[I.sent(),1]),w=g[0],I.label=3;case 3:return I.trys.push([3,5,,6]),[4,t.start(w)];case 4:return I.sent(),[3,6];case 5:throw E=I.sent(),new Error("Failed to start platform. Reason: "+E);case 6:return t.callEntryPoint(w.bootConfig.entryAssembly),[2]}}))}))}window.Blazor.start=y,f.shouldAutoStart()&&y().catch((function(e){"undefined"!=typeof Module&&Module.printErr?Module.printErr(e):console.error(e)}))},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{u(r.next(e))}catch(e){i(e)}}function s(e){try{u(r.throw(e))}catch(e){i(e)}}function u(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,s)}u((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=a.trys,(o=o.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]>2]}t.monoPlatform={start:function(e){return new Promise((function(t,n){var c,l;s.attachDebuggerHotkey(e),window.Browser={init:function(){}},c=function(){window.Module=function(e,t,n){var c=this,l=e.bootConfig.resources,f=window.Module||{},d=["DEBUGGING ENABLED"];f.print=function(e){return d.indexOf(e)<0&&console.log(e)},f.printErr=function(e){console.error(e),u.showErrorNotification()},f.preRun=f.preRun||[],f.postRun=f.postRun||[],f.preloadPlugins=[];var h,b,g=e.loadResources(l.assembly,(function(e){return"_framework/"+e}),"assembly"),w=e.loadResources(l.pdb||{},(function(e){return"_framework/"+e}),"pdb"),E=e.loadResource("dotnet.wasm","_framework/dotnet.wasm",e.bootConfig.resources.runtime["dotnet.wasm"],"dotnetwasm");return e.bootConfig.resources.runtime.hasOwnProperty("dotnet.timezones.blat")&&(h=e.loadResource("dotnet.timezones.blat","_framework/dotnet.timezones.blat",e.bootConfig.resources.runtime["dotnet.timezones.blat"],"globalization")),e.bootConfig.resources.runtime.hasOwnProperty("icudt.dat")&&(b=e.loadResource("icudt.dat","_framework/icudt.dat",e.bootConfig.resources.runtime["icudt.dat"],"globalization")),f.instantiateWasm=function(e,t){return r(c,void 0,void 0,(function(){var n,r;return o(this,(function(o){switch(o.label){case 0:return o.trys.push([0,3,,4]),[4,E];case 1:return[4,v(o.sent(),e)];case 2:return n=o.sent(),[3,4];case 3:throw r=o.sent(),f.printErr(r),r;case 4:return t(n),[2]}}))})),[]},f.preRun.push((function(){i=cwrap("mono_wasm_add_assembly",null,["string","number","number"]),MONO.loaded_files=[],h&&function(e){r(this,void 0,void 0,(function(){var t,n;return o(this,(function(r){switch(r.label){case 0:return t="blazor:timezonedata",addRunDependency(t),[4,e.response];case 1:return[4,r.sent().arrayBuffer()];case 2:return n=r.sent(),Module.FS_createPath("/","usr",!0,!0),Module.FS_createPath("/usr/","share",!0,!0),Module.FS_createPath("/usr/share/","zoneinfo",!0,!0),MONO.mono_wasm_load_data_archive(new Uint8Array(n),"/usr/share/zoneinfo/"),removeRunDependency(t),[2]}}))}))}(h),b?function(e){r(this,void 0,void 0,(function(){var t,n,r,i,a;return o(this,(function(o){switch(o.label){case 0:return t="blazor:icudata",addRunDependency(t),[4,e.response];case 1:return n=o.sent(),i=Uint8Array.bind,[4,n.arrayBuffer()];case 2:if(r=new(i.apply(Uint8Array,[void 0,o.sent()])),a=MONO.mono_wasm_load_bytes_into_heap(r),!MONO.mono_wasm_load_icu_data(a))throw new Error("Error loading ICU asset.");return removeRunDependency(t),[2]}}))}))}(b):MONO.mono_wasm_setenv("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT","1"),g.forEach((function(e){return _(e,function(e,t){var n=e.lastIndexOf(".");if(n<0)throw new Error("No extension to replace in '"+e+"'");return e.substr(0,n)+t}(e.name,".dll"))})),w.forEach((function(e){return _(e,e.name)})),window.Blazor._internal.dotNetCriticalError=function(e){f.printErr(BINDING.conv_string(e)||"(null)")},window.Blazor._internal.getSatelliteAssemblies=function(t){var n=BINDING.mono_array_to_js_array(t),i=e.bootConfig.resources.satelliteResources;if(i){var a=Promise.all(n.filter((function(e){return i.hasOwnProperty(e)})).map((function(t){return e.loadResources(i[t],(function(e){return"_framework/"+e}),"assembly")})).reduce((function(e,t){return e.concat(t)}),new Array).map((function(e){return r(c,void 0,void 0,(function(){return o(this,(function(t){switch(t.label){case 0:return[4,e.response];case 1:return[2,t.sent().arrayBuffer()]}}))}))})));return BINDING.js_to_mono_obj(a.then((function(e){return e.length&&(window.Blazor._internal.readSatelliteAssemblies=function(){for(var t=BINDING.mono_obj_array_new(e.length),n=0;n>1];var n},readInt32Field:function(e,t){return d(e+(t||0))},readUint64Field:function(e,t){return function(e){var t=e>>2,n=Module.HEAPU32[t+1];if(n>l)throw new Error("Cannot read uint64 with high order part "+n+", because the result would exceed Number.MAX_SAFE_INTEGER.");return n*c+Module.HEAPU32[t]}(e+(t||0))},readFloatField:function(e,t){return n=e+(t||0),Module.HEAPF32[n>>2];var n},readObjectField:function(e,t){return d(e+(t||0))},readStringField:function(e,t,n){var r,o=d(e+(t||0));if(0===o)return null;if(n){var i=BINDING.unbox_mono_obj(o);return"boolean"==typeof i?i?"":null:i}return f?void 0===(r=f.stringCache.get(o))&&(r=BINDING.conv_string(o),f.stringCache.set(o,r)):r=BINDING.conv_string(o),r},readStructField:function(e,t){return e+(t||0)},beginHeapLock:function(){return y(),f=new b},invokeWhenHeapUnlocked:function(e){f?f.enqueuePostReleaseAction(e):e()}};var p=document.createElement("a");function h(e){return e+12}function m(e,t,n){var r="["+e+"] "+t+":"+n;return BINDING.bind_static_method(r)}function v(e,t){return r(this,void 0,void 0,(function(){var n,r;return o(this,(function(o){switch(o.label){case 0:if("function"!=typeof WebAssembly.instantiateStreaming)return[3,4];o.label=1;case 1:return o.trys.push([1,3,,4]),[4,WebAssembly.instantiateStreaming(e.response,t)];case 2:return[2,o.sent().instance];case 3:return n=o.sent(),console.info("Streaming compilation failed. Falling back to ArrayBuffer instantiation. ",n),[3,4];case 4:return[4,e.response.then((function(e){return e.arrayBuffer()}))];case 5:return r=o.sent(),[4,WebAssembly.instantiate(r,t)];case 6:return[2,o.sent().instance]}}))}))}function y(){if(f)throw new Error("Assertion failed - heap is currently locked")}var b=function(){function e(){this.stringCache=new Map}return e.prototype.enqueuePostReleaseAction=function(e){this.postReleaseActions||(this.postReleaseActions=[]),this.postReleaseActions.push(e)},e.prototype.release=function(){var e;if(f!==this)throw new Error("Trying to release a lock which isn't current");for(f=null;null===(e=this.postReleaseActions)||void 0===e?void 0:e.length;){this.postReleaseActions.shift()(),y()}},e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=window.chrome&&navigator.userAgent.indexOf("Edge")<0,o=!0;window.addEventListener("load",(function(){var e=new URLSearchParams(window.location.search);o="true"===e.get("_blazor_debug")}));var i=!1;function a(){return o&&i&&r}t.hasDebuggingEnabled=a,t.attachDebuggerHotkey=function(e){i=!!e.bootConfig.resources.pdb;var t=navigator.platform.match(/^Mac/i)?"Cmd":"Alt";a()&&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&&(i?r?o?((t=document.createElement("a")).href="_framework/debug?url="+encodeURIComponent(location.href),t.target="_blank",t.rel="noopener noreferrer",t.click()):console.error("_blazor_debug query parameter must be set to enable debugging. To enable debugging, go to "+location.href+"?_blazor_debug=true."):console.error("Currently, only Microsoft Edge (80+), or Google Chrome, are 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(17),o=function(){function e(e){this.batchAddress=e,this.arrayRangeReader=i,this.arrayBuilderSegmentReader=a,this.diffReader=s,this.editReader=u,this.frameReader=c}return e.prototype.updatedComponents=function(){return r.platform.readStructField(this.batchAddress,0)},e.prototype.referenceFrames=function(){return r.platform.readStructField(this.batchAddress,i.structLength)},e.prototype.disposedComponentIds=function(){return r.platform.readStructField(this.batchAddress,2*i.structLength)},e.prototype.disposedEventHandlerIds=function(){return r.platform.readStructField(this.batchAddress,3*i.structLength)},e.prototype.updatedComponentsEntry=function(e,t){return l(e,t,s.structLength)},e.prototype.referenceFramesEntry=function(e,t){return l(e,t,c.structLength)},e.prototype.disposedComponentIdsEntry=function(e,t){var n=l(e,t,4);return r.platform.readInt32Field(n)},e.prototype.disposedEventHandlerIdsEntry=function(e,t){var n=l(e,t,8);return r.platform.readUint64Field(n)},e}();t.SharedMemoryRenderBatch=o;var i={structLength:8,values:function(e){return r.platform.readObjectField(e,0)},count:function(e){return r.platform.readInt32Field(e,4)}},a={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)}},s={structLength:4+a.structLength,componentId:function(e){return r.platform.readInt32Field(e,0)},edits:function(e){return r.platform.readStructField(e,4)},editsEntry:function(e,t){return l(e,t,u.structLength)}},u={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)}},c={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,!0)},attributeEventHandlerId:function(e){return r.platform.readUint64Field(e,8)}};function l(e,t,n){return r.platform.getArrayEntryPtr(e,t,n)}},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{u(r.next(e))}catch(e){i(e)}}function s(e){try{u(r.throw(e))}catch(e){i(e)}}function u(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,s)}u((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=a.trys,(o=o.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]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&&!t)throw new Error("New logical elements must start empty, or allowExistingContents must be true");return r in e||(e[r]=[]),e}function s(e,t,n){var i=e;if(e instanceof Comment&&(c(i)&&c(i).length>0))throw new Error("Not implemented: inserting non-empty logical container");if(u(i))throw new Error("Not implemented: moving existing logical children");var a=c(t);if(n0;)e(r,0)}var i=r;i.parentNode.removeChild(i)},t.getLogicalParent=u,t.getLogicalSiblingEnd=function(e){return e[i]||null},t.getLogicalChild=function(e,t){return c(e)[t]},t.isSvgElement=function(e){return"http://www.w3.org/2000/svg"===l(e).namespaceURI},t.getLogicalChildrenArray=c,t.permuteLogicalChildren=function(e,t){var n=c(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=u(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,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){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),n(23),n(17);var r=n(24),o=n(7),i={},a=!1;function s(e,t,n){var o=i[e];o||(o=i[e]=new r.BrowserRenderer(e)),o.attachRootComponentToLogicalElement(n,t)}t.attachRootComponentToLogicalElement=s,t.attachRootComponentToElement=function(e,t,n){var r=document.querySelector(e);if(!r)throw new Error("Could not find any element matching selector '"+e+"'.");s(n||0,o.toLogicalElement(r,!0),t)},t.getRendererer=function(e){return i[e]},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(),s=r.values(o),u=r.count(o),c=t.referenceFrames(),l=r.values(c),f=t.diffReader,d=0;d0&&e.invokeMethodAsync("OnSpacerAfterVisible",r.boundingClientRect.bottom-r.intersectionRect.bottom,i,a)}}))}),{root:o(t),rootMargin:i+"px"});a.observe(t),a.observe(n);var s=c(t),u=c(n);function c(e){var t=new MutationObserver((function(){a.unobserve(e),a.observe(e)}));return t.observe(e,{attributes:!0}),t}r[e._id]={intersectionObserver:a,mutationObserverBefore:s,mutationObserverAfter:u}},dispose:function(e){var t=r[e._id];t&&(t.intersectionObserver.disconnect(),t.mutationObserverBefore.disconnect(),t.mutationObserverAfter.disconnect(),e.dispose(),delete r[e._id])}};var r={};function o(e){return e?"visible"!==getComputedStyle(e).overflowY?e:o(e.parentElement):null}},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{u(r.next(e))}catch(e){i(e)}}function s(e){try{u(r.throw(e))}catch(e){i(e)}}function u(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,s)}u((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=a.trys,(o=o.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].*)$/;function i(e,t){var n=e.currentElement;if(n&&n.nodeType===Node.COMMENT_NODE&&n.textContent){var r=new RegExp(o).exec(n.textContent),i=r&&r.groups&&r.groups.descriptor;if(!i)return;try{var s=function(e){var t=JSON.parse(e),n=t.type;if("server"!==n&&"webassembly"!==n)throw new Error("Invalid component type '"+n+"'.");return t}(i);switch(t){case"webassembly":return function(e,t,n){var r=e.type,o=e.assembly,i=e.typeName,s=e.parameterDefinitions,u=e.parameterValues,c=e.prerenderId;if("webassembly"!==r)return;if(!o)throw new Error("assembly must be defined when using a descriptor.");if(!i)throw new Error("typeName must be defined when using a descriptor.");if(c){var l=a(c,n);if(!l)throw new Error("Could not find an end component comment for '"+t+"'");return{type:r,assembly:o,typeName:i,parameterDefinitions:s&&atob(s),parameterValues:u&&atob(u),start:t,prerenderId:c,end:l}}return{type:r,assembly:o,typeName:i,parameterDefinitions:s&&atob(s),parameterValues:u&&atob(u),start:t}}(s,n,e);case"server":return function(e,t,n){var r=e.type,o=e.descriptor,i=e.sequence,s=e.prerenderId;if("server"!==r)return;if(!o)throw new Error("descriptor must be defined when using a descriptor.");if(void 0===i)throw new Error("sequence must be defined when using a descriptor.");if(!Number.isInteger(i))throw new Error("Error parsing the sequence '"+i+"' for component '"+JSON.stringify(e)+"'");if(s){var u=a(s,n);if(!u)throw new Error("Could not find an end component comment for '"+t+"'");return{type:r,sequence:i,descriptor:o,start:t,prerenderId:s,end:u}}return{type:r,sequence:i,descriptor:o,start:t}}(s,n,e)}}catch(e){throw new Error("Found malformed component comment at "+n.textContent)}}}function a(e,t){for(;t.next()&&t.currentElement;){var n=t.currentElement;if(n.nodeType===Node.COMMENT_NODE&&n.textContent){var r=new RegExp(o).exec(n.textContent),i=r&&r[1];if(i)return s(i,e),n}}}function s(e,t){var n=JSON.parse(e);if(1!==Object.keys(n).length)throw new Error("Invalid end of component comment: '"+e+"'");var r=n.prerenderId;if(!r)throw new Error("End of component comment must have a value for the prerendered property: '"+e+"'");if(r!==t)throw new Error("End of component comment prerendered property must match the start comment prerender id: '"+t+"', '"+r+"'")}var u=function(){function e(e){this.childNodes=e,this.currentIndex=-1,this.length=e.length}return e.prototype.next=function(){return this.currentIndex++,this.currentIndex0&&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=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 a=n(3);n(22);var s=n(17),u=n(45),c=n(12),l=n(47),f=n(33),d=n(18),p=n(48),h=n(49),m=n(50),v=n(51),y=n(34),b=!1;function g(e){return r(this,void 0,void 0,(function(){var t,n,f,g,E,_,I,C,N,A,S,k=this;return o(this,(function(O){switch(O.label){case 0:if(b)throw new Error("Blazor has already started.");return b=!0,d.setEventDispatcher((function(e,t){c.getRendererer(e.browserRendererId).eventDelegator.getHandler(e.eventHandlerId)&&u.monoPlatform.invokeWhenHeapUnlocked((function(){return a.DotNet.invokeMethodAsync("Microsoft.AspNetCore.Components.WebAssembly","DispatchEvent",e,JSON.stringify(t))}))})),window.Blazor._internal.invokeJSFromDotNet=w,t=s.setPlatform(u.monoPlatform),window.Blazor.platform=t,window.Blazor._internal.renderBatch=function(e,t){var n=u.monoPlatform.beginHeapLock();try{c.renderBatch(e,new l.SharedMemoryRenderBatch(t))}finally{n.release()}},n=window.Blazor._internal.navigationManager.getBaseURI,f=window.Blazor._internal.navigationManager.getLocationHref,window.Blazor._internal.navigationManager.getUnmarshalledBaseURI=function(){return BINDING.js_string_to_mono_string(n())},window.Blazor._internal.navigationManager.getUnmarshalledLocationHref=function(){return BINDING.js_string_to_mono_string(f())},window.Blazor._internal.navigationManager.listenForNavigationEvents((function(e,t){return r(k,void 0,void 0,(function(){return o(this,(function(n){switch(n.label){case 0:return[4,a.DotNet.invokeMethodAsync("Microsoft.AspNetCore.Components.WebAssembly","NotifyLocationChanged",e,t)];case 1:return n.sent(),[2]}}))}))})),g=null==e?void 0:e.environment,E=m.BootConfigResult.initAsync(g),_=y.discoverComponents(document,"webassembly"),I=new v.WebAssemblyComponentAttacher(_),window.Blazor._internal.registeredComponents={getRegisteredComponentsCount:function(){return I.getCount()},getId:function(e){return I.getId(e)},getAssembly:function(e){return BINDING.js_string_to_mono_string(I.getAssembly(e))},getTypeName:function(e){return BINDING.js_string_to_mono_string(I.getTypeName(e))},getParameterDefinitions:function(e){return BINDING.js_string_to_mono_string(I.getParameterDefinitions(e)||"")},getParameterValues:function(e){return BINDING.js_string_to_mono_string(I.getParameterValues(e)||"")}},window.Blazor._internal.attachRootComponentToElement=function(e,t,n){var r=I.resolveRegisteredElement(e);r?c.attachRootComponentToLogicalElement(n,r,t):c.attachRootComponentToElement(e,t,n)},[4,E];case 1:return C=O.sent(),[4,Promise.all([p.WebAssemblyResourceLoader.initAsync(C.bootConfig,e||{}),h.WebAssemblyConfigLoader.initAsync(C)])];case 2:N=i.apply(void 0,[O.sent(),1]),A=N[0],O.label=3;case 3:return O.trys.push([3,5,,6]),[4,t.start(A)];case 4:return O.sent(),[3,6];case 5:throw S=O.sent(),new Error("Failed to start platform. Reason: "+S);case 6:return t.callEntryPoint(A.bootConfig.entryAssembly),[2]}}))}))}function w(e,t,n,r){var o=u.monoPlatform.readStringField(e,0),i=u.monoPlatform.readInt32Field(e,4),s=u.monoPlatform.readStringField(e,8),c=u.monoPlatform.readUint64Field(e,20);if(null!==s){var l=u.monoPlatform.readUint64Field(e,12);if(0!==l)return a.DotNet.jsCallDispatcher.beginInvokeJSFromDotNet(l,o,s,i,c),0;var f=a.DotNet.jsCallDispatcher.invokeJSFromDotNet(o,s,i,c);return null===f?0:BINDING.js_string_to_mono_string(f)}return a.DotNet.jsCallDispatcher.findJSFunction(o,c).call(null,t,n,r)}window.Blazor.start=g,f.shouldAutoStart()&&g().catch((function(e){"undefined"!=typeof Module&&Module.printErr?Module.printErr(e):console.error(e)}))},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{u(r.next(e))}catch(e){i(e)}}function s(e){try{u(r.throw(e))}catch(e){i(e)}}function u(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,s)}u((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=a.trys,(o=o.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]>2]}t.monoPlatform={start:function(e){return new Promise((function(t,n){var c,l;s.attachDebuggerHotkey(e),window.Browser={init:function(){}},c=function(){window.Module=function(e,t,n){var c=this,l=e.bootConfig.resources,f=window.Module||{},d=["DEBUGGING ENABLED"];f.print=function(e){return d.indexOf(e)<0&&console.log(e)},f.printErr=function(e){console.error(e),u.showErrorNotification()},f.preRun=f.preRun||[],f.postRun=f.postRun||[],f.preloadPlugins=[];var h,b,g=e.loadResources(l.assembly,(function(e){return"_framework/"+e}),"assembly"),w=e.loadResources(l.pdb||{},(function(e){return"_framework/"+e}),"pdb"),E=e.loadResource("dotnet.wasm","_framework/dotnet.wasm",e.bootConfig.resources.runtime["dotnet.wasm"],"dotnetwasm");return e.bootConfig.resources.runtime.hasOwnProperty("dotnet.timezones.blat")&&(h=e.loadResource("dotnet.timezones.blat","_framework/dotnet.timezones.blat",e.bootConfig.resources.runtime["dotnet.timezones.blat"],"globalization")),e.bootConfig.resources.runtime.hasOwnProperty("icudt.dat")&&(b=e.loadResource("icudt.dat","_framework/icudt.dat",e.bootConfig.resources.runtime["icudt.dat"],"globalization")),f.instantiateWasm=function(e,t){return r(c,void 0,void 0,(function(){var n,r;return o(this,(function(o){switch(o.label){case 0:return o.trys.push([0,3,,4]),[4,E];case 1:return[4,v(o.sent(),e)];case 2:return n=o.sent(),[3,4];case 3:throw r=o.sent(),f.printErr(r),r;case 4:return t(n),[2]}}))})),[]},f.preRun.push((function(){i=cwrap("mono_wasm_add_assembly",null,["string","number","number"]),MONO.loaded_files=[],h&&function(e){r(this,void 0,void 0,(function(){var t,n;return o(this,(function(r){switch(r.label){case 0:return t="blazor:timezonedata",addRunDependency(t),[4,e.response];case 1:return[4,r.sent().arrayBuffer()];case 2:return n=r.sent(),Module.FS_createPath("/","usr",!0,!0),Module.FS_createPath("/usr/","share",!0,!0),Module.FS_createPath("/usr/share/","zoneinfo",!0,!0),MONO.mono_wasm_load_data_archive(new Uint8Array(n),"/usr/share/zoneinfo/"),removeRunDependency(t),[2]}}))}))}(h),b?function(e){r(this,void 0,void 0,(function(){var t,n,r,i,a;return o(this,(function(o){switch(o.label){case 0:return t="blazor:icudata",addRunDependency(t),[4,e.response];case 1:return n=o.sent(),i=Uint8Array.bind,[4,n.arrayBuffer()];case 2:if(r=new(i.apply(Uint8Array,[void 0,o.sent()])),a=MONO.mono_wasm_load_bytes_into_heap(r),!MONO.mono_wasm_load_icu_data(a))throw new Error("Error loading ICU asset.");return removeRunDependency(t),[2]}}))}))}(b):MONO.mono_wasm_setenv("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT","1"),g.forEach((function(e){return _(e,function(e,t){var n=e.lastIndexOf(".");if(n<0)throw new Error("No extension to replace in '"+e+"'");return e.substr(0,n)+t}(e.name,".dll"))})),w.forEach((function(e){return _(e,e.name)})),window.Blazor._internal.dotNetCriticalError=function(e){f.printErr(BINDING.conv_string(e)||"(null)")},window.Blazor._internal.getSatelliteAssemblies=function(t){var n=BINDING.mono_array_to_js_array(t),i=e.bootConfig.resources.satelliteResources;if(i){var a=Promise.all(n.filter((function(e){return i.hasOwnProperty(e)})).map((function(t){return e.loadResources(i[t],(function(e){return"_framework/"+e}),"assembly")})).reduce((function(e,t){return e.concat(t)}),new Array).map((function(e){return r(c,void 0,void 0,(function(){return o(this,(function(t){switch(t.label){case 0:return[4,e.response];case 1:return[2,t.sent().arrayBuffer()]}}))}))})));return BINDING.js_to_mono_obj(a.then((function(e){return e.length&&(window.Blazor._internal.readSatelliteAssemblies=function(){for(var t=BINDING.mono_obj_array_new(e.length),n=0;n>1];var n},readInt32Field:function(e,t){return d(e+(t||0))},readUint64Field:function(e,t){return function(e){var t=e>>2,n=Module.HEAPU32[t+1];if(n>l)throw new Error("Cannot read uint64 with high order part "+n+", because the result would exceed Number.MAX_SAFE_INTEGER.");return n*c+Module.HEAPU32[t]}(e+(t||0))},readFloatField:function(e,t){return n=e+(t||0),Module.HEAPF32[n>>2];var n},readObjectField:function(e,t){return d(e+(t||0))},readStringField:function(e,t,n){var r,o=d(e+(t||0));if(0===o)return null;if(n){var i=BINDING.unbox_mono_obj(o);return"boolean"==typeof i?i?"":null:i}return f?void 0===(r=f.stringCache.get(o))&&(r=BINDING.conv_string(o),f.stringCache.set(o,r)):r=BINDING.conv_string(o),r},readStructField:function(e,t){return e+(t||0)},beginHeapLock:function(){return y(),f=new b},invokeWhenHeapUnlocked:function(e){f?f.enqueuePostReleaseAction(e):e()}};var p=document.createElement("a");function h(e){return e+12}function m(e,t,n){var r="["+e+"] "+t+":"+n;return BINDING.bind_static_method(r)}function v(e,t){return r(this,void 0,void 0,(function(){var n,r;return o(this,(function(o){switch(o.label){case 0:if("function"!=typeof WebAssembly.instantiateStreaming)return[3,4];o.label=1;case 1:return o.trys.push([1,3,,4]),[4,WebAssembly.instantiateStreaming(e.response,t)];case 2:return[2,o.sent().instance];case 3:return n=o.sent(),console.info("Streaming compilation failed. Falling back to ArrayBuffer instantiation. ",n),[3,4];case 4:return[4,e.response.then((function(e){return e.arrayBuffer()}))];case 5:return r=o.sent(),[4,WebAssembly.instantiate(r,t)];case 6:return[2,o.sent().instance]}}))}))}function y(){if(f)throw new Error("Assertion failed - heap is currently locked")}var b=function(){function e(){this.stringCache=new Map}return e.prototype.enqueuePostReleaseAction=function(e){this.postReleaseActions||(this.postReleaseActions=[]),this.postReleaseActions.push(e)},e.prototype.release=function(){var e;if(f!==this)throw new Error("Trying to release a lock which isn't current");for(f=null;null===(e=this.postReleaseActions)||void 0===e?void 0:e.length;){this.postReleaseActions.shift()(),y()}},e}()},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=window.chrome&&navigator.userAgent.indexOf("Edge")<0,o=!0;window.addEventListener("load",(function(){var e=new URLSearchParams(window.location.search);o="true"===e.get("_blazor_debug")}));var i=!1;function a(){return o&&i&&r}t.hasDebuggingEnabled=a,t.attachDebuggerHotkey=function(e){i=!!e.bootConfig.resources.pdb;var t=navigator.platform.match(/^Mac/i)?"Cmd":"Alt";a()&&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&&(i?r?o?((t=document.createElement("a")).href="_framework/debug?url="+encodeURIComponent(location.href),t.target="_blank",t.rel="noopener noreferrer",t.click()):console.error("_blazor_debug query parameter must be set to enable debugging. To enable debugging, go to "+location.href+"?_blazor_debug=true."):console.error("Currently, only Microsoft Edge (80+), or Google Chrome, are 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(17),o=function(){function e(e){this.batchAddress=e,this.arrayRangeReader=i,this.arrayBuilderSegmentReader=a,this.diffReader=s,this.editReader=u,this.frameReader=c}return e.prototype.updatedComponents=function(){return r.platform.readStructField(this.batchAddress,0)},e.prototype.referenceFrames=function(){return r.platform.readStructField(this.batchAddress,i.structLength)},e.prototype.disposedComponentIds=function(){return r.platform.readStructField(this.batchAddress,2*i.structLength)},e.prototype.disposedEventHandlerIds=function(){return r.platform.readStructField(this.batchAddress,3*i.structLength)},e.prototype.updatedComponentsEntry=function(e,t){return l(e,t,s.structLength)},e.prototype.referenceFramesEntry=function(e,t){return l(e,t,c.structLength)},e.prototype.disposedComponentIdsEntry=function(e,t){var n=l(e,t,4);return r.platform.readInt32Field(n)},e.prototype.disposedEventHandlerIdsEntry=function(e,t){var n=l(e,t,8);return r.platform.readUint64Field(n)},e}();t.SharedMemoryRenderBatch=o;var i={structLength:8,values:function(e){return r.platform.readObjectField(e,0)},count:function(e){return r.platform.readInt32Field(e,4)}},a={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)}},s={structLength:4+a.structLength,componentId:function(e){return r.platform.readInt32Field(e,0)},edits:function(e){return r.platform.readStructField(e,4)},editsEntry:function(e,t){return l(e,t,u.structLength)}},u={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)}},c={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,!0)},attributeEventHandlerId:function(e){return r.platform.readUint64Field(e,8)}};function l(e,t,n){return r.platform.getArrayEntryPtr(e,t,n)}},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{u(r.next(e))}catch(e){i(e)}}function s(e){try{u(r.throw(e))}catch(e){i(e)}}function u(e){var t;e.done?o(e.value):(t=e.value,t instanceof n?t:new n((function(e){e(t)}))).then(a,s)}u((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=a.trys,(o=o.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]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]): Promise { options.reconnectionHandler = options.reconnectionHandler || window['Blazor'].defaultReconnectionHandler; logger.log(LogLevel.Information, 'Starting up blazor server-side application.'); - const components = discoverComponents(document); + const components = discoverComponents(document, 'server') as ServerComponentDescriptor[]; const circuit = new CircuitDescriptor(components); const initialConnection = await initializeConnection(options, logger, circuit); @@ -97,7 +98,7 @@ async function initializeConnection(options: CircuitStartOptions, logger: Logger connection.on('JS.AttachComponent', (componentId, selector) => attachRootComponentToLogicalElement(0, circuit.resolveElement(selector), componentId)); connection.on('JS.BeginInvokeJS', DotNet.jsCallDispatcher.beginInvokeJSFromDotNet); - connection.on('JS.EndInvokeDotNet', (args: string) => DotNet.jsCallDispatcher.endInvokeDotNetFromJS(...(JSON.parse(args) as [string, boolean, unknown]))); + connection.on('JS.EndInvokeDotNet', (args: string) => DotNet.jsCallDispatcher.endInvokeDotNetFromJS(...(DotNet.parseJsonWithRevivers(args) as [string, boolean, unknown]))); const renderQueue = RenderQueue.getOrCreate(logger); connection.on('JS.RenderBatch', (batchId: number, batchData: Uint8Array) => { diff --git a/src/Components/Web.JS/src/Boot.WebAssembly.ts b/src/Components/Web.JS/src/Boot.WebAssembly.ts index c7e8bda3c5ed..05fc2e8ba89f 100644 --- a/src/Components/Web.JS/src/Boot.WebAssembly.ts +++ b/src/Components/Web.JS/src/Boot.WebAssembly.ts @@ -2,7 +2,7 @@ import { DotNet } from '@microsoft/dotnet-js-interop'; import './GlobalExports'; import * as Environment from './Environment'; import { monoPlatform } from './Platform/Mono/MonoPlatform'; -import { renderBatch, getRendererer } from './Rendering/Renderer'; +import { renderBatch, getRendererer, attachRootComponentToElement, attachRootComponentToLogicalElement } from './Rendering/Renderer'; import { SharedMemoryRenderBatch } from './Rendering/RenderBatch/SharedMemoryRenderBatch'; import { shouldAutoStart } from './BootCommon'; import { setEventDispatcher } from './Rendering/RendererEventDispatcher'; @@ -11,6 +11,8 @@ import { WebAssemblyConfigLoader } from './Platform/WebAssemblyConfigLoader'; import { BootConfigResult } from './Platform/BootConfig'; import { Pointer } from './Platform/Platform'; import { WebAssemblyStartOptions } from './Platform/WebAssemblyStartOptions'; +import { WebAssemblyComponentAttacher } from './Platform/WebAssemblyComponentAttacher'; +import { discoverComponents, WebAssemblyComponentDescriptor } from './Services/ComponentDescriptorDiscovery'; let started = false; @@ -32,6 +34,9 @@ async function boot(options?: Partial): Promise { } }); + // Configure JS interop + window['Blazor']._internal.invokeJSFromDotNet = invokeJSFromDotNet; + // Configure environment for execution under Mono WebAssembly with shared-memory rendering const platform = Environment.setPlatform(monoPlatform); window['Blazor'].platform = platform; @@ -68,8 +73,31 @@ async function boot(options?: Partial): Promise { const environment = options?.environment; // Fetch the resources and prepare the Mono runtime - const bootConfigResult = await BootConfigResult.initAsync(environment); + const bootConfigPromise = BootConfigResult.initAsync(environment); + + // Leverage the time while we are loading boot.config.json from the network to discover any potentially registered component on + // the document. + const discoveredComponents = discoverComponents(document, 'webassembly') as WebAssemblyComponentDescriptor[]; + const componentAttacher = new WebAssemblyComponentAttacher(discoveredComponents); + window['Blazor']._internal.registeredComponents = { + getRegisteredComponentsCount: () => componentAttacher.getCount(), + getId: (index) => componentAttacher.getId(index), + getAssembly: (id) => BINDING.js_string_to_mono_string(componentAttacher.getAssembly(id)), + getTypeName: (id) => BINDING.js_string_to_mono_string(componentAttacher.getTypeName(id)), + getParameterDefinitions: (id) => BINDING.js_string_to_mono_string(componentAttacher.getParameterDefinitions(id) || ''), + getParameterValues: (id) => BINDING.js_string_to_mono_string(componentAttacher.getParameterValues(id) || ''), + }; + + window['Blazor']._internal.attachRootComponentToElement = (selector, componentId, rendererId) => { + const element = componentAttacher.resolveRegisteredElement(selector); + if (!element) { + attachRootComponentToElement(selector, componentId, rendererId); + } else { + attachRootComponentToLogicalElement(rendererId, element, componentId); + } + }; + const bootConfigResult = await bootConfigPromise; const [resourceLoader] = await Promise.all([ WebAssemblyResourceLoader.initAsync(bootConfigResult.bootConfig, options || {}), WebAssemblyConfigLoader.initAsync(bootConfigResult)]); @@ -84,6 +112,28 @@ async function boot(options?: Partial): Promise { platform.callEntryPoint(resourceLoader.bootConfig.entryAssembly); } +function invokeJSFromDotNet(callInfo: Pointer, arg0: any, arg1: any, arg2: any): any { + const functionIdentifier = monoPlatform.readStringField(callInfo, 0)!; + const resultType = monoPlatform.readInt32Field(callInfo, 4); + const marshalledCallArgsJson = monoPlatform.readStringField(callInfo, 8); + const targetInstanceId = monoPlatform.readUint64Field(callInfo, 20); + + if (marshalledCallArgsJson !== null) { + const marshalledCallAsyncHandle = monoPlatform.readUint64Field(callInfo, 12); + + if (marshalledCallAsyncHandle !== 0) { + DotNet.jsCallDispatcher.beginInvokeJSFromDotNet(marshalledCallAsyncHandle, functionIdentifier, marshalledCallArgsJson, resultType, targetInstanceId); + return 0; + } else { + const resultJson = DotNet.jsCallDispatcher.invokeJSFromDotNet(functionIdentifier, marshalledCallArgsJson, resultType, targetInstanceId)!; + return resultJson === null ? 0 : BINDING.js_string_to_mono_string(resultJson); + } + } else { + const func = DotNet.jsCallDispatcher.findJSFunction(functionIdentifier, targetInstanceId); + return func.call(null, arg0, arg1, arg2); + } +} + window['Blazor'].start = boot; if (shouldAutoStart()) { boot().catch(error => { diff --git a/src/Components/Web.JS/src/GlobalExports.ts b/src/Components/Web.JS/src/GlobalExports.ts index 54015e2f97d7..d0b9de6699bf 100644 --- a/src/Components/Web.JS/src/GlobalExports.ts +++ b/src/Components/Web.JS/src/GlobalExports.ts @@ -8,7 +8,6 @@ window['Blazor'] = { navigateTo, _internal: { - attachRootComponentToElement, navigationManager: navigationManagerInternalFunctions, domWrapper: domFunctions, Virtualize, diff --git a/src/Components/Web.JS/src/Platform/Circuits/CircuitManager.ts b/src/Components/Web.JS/src/Platform/Circuits/CircuitManager.ts index 1f4a49495de1..2c3650a7a26d 100644 --- a/src/Components/Web.JS/src/Platform/Circuits/CircuitManager.ts +++ b/src/Components/Web.JS/src/Platform/Circuits/CircuitManager.ts @@ -1,12 +1,13 @@ import { internalFunctions as navigationManagerFunctions } from '../../Services/NavigationManager'; import { toLogicalRootCommentElement, LogicalElement } from '../../Rendering/LogicalElements'; +import { ServerComponentDescriptor } from '../../Services/ComponentDescriptorDiscovery'; export class CircuitDescriptor { public circuitId?: string; - public components: ComponentDescriptor[]; + public components: ServerComponentDescriptor[]; - public constructor(components: ComponentDescriptor[]) { + public constructor(components: ServerComponentDescriptor[]) { this.circuitId = undefined; this.components = components; } @@ -54,221 +55,3 @@ export class CircuitDescriptor { } } -interface ComponentMarker { - type: string; - sequence: number; - descriptor: string; -} - -export class ComponentDescriptor { - public type: string; - - public start: Node; - - public end?: Node; - - public sequence: number; - - public descriptor: string; - - public constructor(type: string, start: Node, end: Node | undefined, sequence: number, descriptor: string) { - this.type = type; - this.start = start; - this.end = end; - this.sequence = sequence; - this.descriptor = descriptor; - } - - public toRecord(): ComponentMarker { - const result = { type: this.type, sequence: this.sequence, descriptor: this.descriptor }; - return result; - } -} - -export function discoverComponents(document: Document): ComponentDescriptor[] { - const componentComments = resolveComponentComments(document); - const discoveredComponents: ComponentDescriptor[] = []; - for (let i = 0; i < componentComments.length; i++) { - const componentComment = componentComments[i]; - const entry = new ComponentDescriptor( - componentComment.type, - componentComment.start, - componentComment.end, - componentComment.sequence, - componentComment.descriptor, - ); - - discoveredComponents.push(entry); - } - - return discoveredComponents.sort((a, b) => a.sequence - b.sequence); -} - - -interface ComponentComment { - type: 'server'; - sequence: number; - descriptor: string; - start: Node; - end?: Node; - prerenderId?: string; -} - -function resolveComponentComments(node: Node): ComponentComment[] { - if (!node.hasChildNodes()) { - return []; - } - - const result: ComponentComment[] = []; - const childNodeIterator = new ComponentCommentIterator(node.childNodes); - while (childNodeIterator.next() && childNodeIterator.currentElement) { - const componentComment = getComponentComment(childNodeIterator); - if (componentComment) { - result.push(componentComment); - } else { - const childResults = resolveComponentComments(childNodeIterator.currentElement); - for (let j = 0; j < childResults.length; j++) { - const childResult = childResults[j]; - result.push(childResult); - } - } - } - - return result; -} - -const blazorCommentRegularExpression = /\W*Blazor:[^{]*(.*)$/; - -function getComponentComment(commentNodeIterator: ComponentCommentIterator): ComponentComment | undefined { - const candidateStart = commentNodeIterator.currentElement; - - if (!candidateStart || candidateStart.nodeType !== Node.COMMENT_NODE) { - return; - } - if (candidateStart.textContent) { - const componentStartComment = new RegExp(blazorCommentRegularExpression); - const definition = componentStartComment.exec(candidateStart.textContent); - const json = definition && definition[1]; - - if (json) { - try { - return createComponentComment(json, candidateStart, commentNodeIterator); - } catch (error) { - throw new Error(`Found malformed component comment at ${candidateStart.textContent}`); - } - } else { - return; - } - } -} - -function createComponentComment(json: string, start: Node, iterator: ComponentCommentIterator): ComponentComment { - const payload = JSON.parse(json) as ComponentComment; - const { type, sequence, descriptor, prerenderId } = payload; - if (type !== 'server') { - throw new Error(`Invalid component type '${type}'.`); - } - - if (!descriptor) { - throw new Error('descriptor must be defined when using a descriptor.'); - } - - if (sequence === undefined) { - throw new Error('sequence must be defined when using a descriptor.'); - } - - if (!Number.isInteger(sequence)) { - throw new Error(`Error parsing the sequence '${sequence}' for component '${json}'`); - } - - if (!prerenderId) { - return { - type, - sequence: sequence, - descriptor, - start, - }; - } else { - const end = getComponentEndComment(prerenderId, iterator); - if (!end) { - throw new Error(`Could not find an end component comment for '${start}'`); - } - - return { - type, - sequence, - descriptor, - start, - prerenderId, - end, - }; - } -} - -function getComponentEndComment(prerenderedId: string, iterator: ComponentCommentIterator): ChildNode | undefined { - while (iterator.next() && iterator.currentElement) { - const node = iterator.currentElement; - if (node.nodeType !== Node.COMMENT_NODE) { - continue; - } - if (!node.textContent) { - continue; - } - - const definition = new RegExp(blazorCommentRegularExpression).exec(node.textContent); - const json = definition && definition[1]; - if (!json) { - continue; - } - - validateEndComponentPayload(json, prerenderedId); - - return node; - } - - return undefined; -} - -function validateEndComponentPayload(json: string, prerenderedId: string): void { - const payload = JSON.parse(json) as ComponentComment; - if (Object.keys(payload).length !== 1) { - throw new Error(`Invalid end of component comment: '${json}'`); - } - const prerenderedEndId = payload.prerenderId; - if (!prerenderedEndId) { - throw new Error(`End of component comment must have a value for the prerendered property: '${json}'`); - } - if (prerenderedEndId !== prerenderedId) { - throw new Error(`End of component comment prerendered property must match the start comment prerender id: '${prerenderedId}', '${prerenderedEndId}'`); - } -} - -class ComponentCommentIterator { - - private childNodes: NodeListOf; - - private currentIndex: number; - - private length: number; - - public currentElement: ChildNode | undefined; - - public constructor(childNodes: NodeListOf) { - this.childNodes = childNodes; - this.currentIndex = -1; - this.length = childNodes.length; - } - - public next(): boolean { - this.currentIndex++; - if (this.currentIndex < this.length) { - this.currentElement = this.childNodes[this.currentIndex]; - return true; - } else { - this.currentElement = undefined; - return false; - } - } -} - - diff --git a/src/Components/Web.JS/src/Platform/WebAssemblyComponentAttacher.ts b/src/Components/Web.JS/src/Platform/WebAssemblyComponentAttacher.ts new file mode 100644 index 000000000000..8f7221662505 --- /dev/null +++ b/src/Components/Web.JS/src/Platform/WebAssemblyComponentAttacher.ts @@ -0,0 +1,51 @@ +import { LogicalElement, toLogicalRootCommentElement } from '../Rendering/LogicalElements'; +import { WebAssemblyComponentDescriptor } from '../Services/ComponentDescriptorDiscovery'; + +export class WebAssemblyComponentAttacher { + public preregisteredComponents: WebAssemblyComponentDescriptor[]; + + private componentsById: { [index: number]: WebAssemblyComponentDescriptor }; + + public constructor(components: WebAssemblyComponentDescriptor[]) { + this.preregisteredComponents = components; + const componentsById = {}; + for (let index = 0; index < components.length; index++) { + const component = components[index]; + componentsById[component.id] = component; + } + this.componentsById = componentsById; + } + + public resolveRegisteredElement(id: string): LogicalElement | undefined { + const parsedId = Number.parseInt(id); + if (!Number.isNaN(parsedId)) { + return toLogicalRootCommentElement(this.componentsById[parsedId].start as Comment, this.componentsById[parsedId].end as Comment); + } else { + return undefined; + } + } + + public getParameterValues(id: number): string | undefined { + return this.componentsById[id].parameterValues; + } + + public getParameterDefinitions(id: number): string | undefined { + return this.componentsById[id].parameterDefinitions; + } + + public getTypeName(id: number): string { + return this.componentsById[id].typeName; + } + + public getAssembly(id: number): string { + return this.componentsById[id].assembly; + } + + public getId(index: number): number { + return this.preregisteredComponents[index].id; + } + + public getCount(): number { + return this.preregisteredComponents.length; + } +} diff --git a/src/Components/Web.JS/src/Services/ComponentDescriptorDiscovery.ts b/src/Components/Web.JS/src/Services/ComponentDescriptorDiscovery.ts new file mode 100644 index 000000000000..002ea522390d --- /dev/null +++ b/src/Components/Web.JS/src/Services/ComponentDescriptorDiscovery.ts @@ -0,0 +1,354 @@ +export function discoverComponents(document: Document, type: 'webassembly' | 'server'): ServerComponentDescriptor[] | WebAssemblyComponentDescriptor[] { + switch (type){ + case 'webassembly': + return discoverWebAssemblyComponents(document); + case 'server': + return discoverServerComponents(document); + } +} + +function discoverServerComponents(document: Document): ServerComponentDescriptor[] { + const componentComments = resolveComponentComments(document, 'server') as ServerComponentComment[]; + const discoveredComponents: ServerComponentDescriptor[] = []; + for (let i = 0; i < componentComments.length; i++) { + const componentComment = componentComments[i]; + const entry = new ServerComponentDescriptor( + componentComment.type, + componentComment.start, + componentComment.end, + componentComment.sequence, + componentComment.descriptor, + ); + + discoveredComponents.push(entry); + } + + return discoveredComponents.sort((a, b): number => a.sequence - b.sequence); +} + +function discoverWebAssemblyComponents(document: Document): WebAssemblyComponentDescriptor[] { + const componentComments = resolveComponentComments(document, 'webassembly') as WebAssemblyComponentDescriptor[]; + const discoveredComponents: WebAssemblyComponentDescriptor[] = []; + for (let i = 0; i < componentComments.length; i++) { + const componentComment = componentComments[i]; + const entry = new WebAssemblyComponentDescriptor( + componentComment.type, + componentComment.start, + componentComment.end, + componentComment.assembly, + componentComment.typeName, + componentComment.parameterDefinitions, + componentComment.parameterValues, + ); + + discoveredComponents.push(entry); + } + + return discoveredComponents.sort((a, b): number => a.id - b.id); +} + +interface ComponentComment { + type: 'server' | 'webassembly'; + prerenderId?: string; +} + +interface ServerComponentComment { + type: 'server'; + sequence: number; + descriptor: string; + start: Node; + end?: Node; + prerenderId?: string; +} + +interface WebAssemblyComponentComment { + type: 'webassembly'; + typeName: string; + assembly: string; + parameterDefinitions?: string; + parameterValues?: string; + prerenderId?: string; + start: Node; + end?: Node; +} + +function resolveComponentComments(node: Node, type: 'webassembly' | 'server'): ComponentComment[] { + if (!node.hasChildNodes()) { + return []; + } + + const result: ComponentComment[] = []; + const childNodeIterator = new ComponentCommentIterator(node.childNodes); + while (childNodeIterator.next() && childNodeIterator.currentElement) { + const componentComment = getComponentComment(childNodeIterator, type); + if (componentComment) { + result.push(componentComment); + } else { + const childResults = resolveComponentComments(childNodeIterator.currentElement, type); + for (let j = 0; j < childResults.length; j++) { + const childResult = childResults[j]; + result.push(childResult); + } + } + } + + return result; +} + +const blazorCommentRegularExpression = /\W*Blazor:[^{]*(?.*)$/; + +function getComponentComment(commentNodeIterator: ComponentCommentIterator, type: 'webassembly' | 'server'): ComponentComment | undefined { + const candidateStart = commentNodeIterator.currentElement; + + if (!candidateStart || candidateStart.nodeType !== Node.COMMENT_NODE) { + return; + } + if (candidateStart.textContent) { + const componentStartComment = new RegExp(blazorCommentRegularExpression); + const definition = componentStartComment.exec(candidateStart.textContent); + const json = definition && definition.groups && definition.groups['descriptor']; + + if (json) { + try { + const componentComment = parseCommentPayload(json); + switch (type) { + case 'webassembly': + return createWebAssemblyComponentComment(componentComment as WebAssemblyComponentComment, candidateStart, commentNodeIterator); + case 'server': + return createServerComponentComment(componentComment as ServerComponentComment, candidateStart, commentNodeIterator); + } + } catch (error) { + throw new Error(`Found malformed component comment at ${candidateStart.textContent}`); + } + } else { + return; + } + } +} + +function parseCommentPayload(json: string): ComponentComment { + const payload = JSON.parse(json) as ComponentComment; + const { type } = payload; + if (type !== 'server' && type !== 'webassembly') { + throw new Error(`Invalid component type '${type}'.`); + } + + return payload; +} + +function createServerComponentComment(payload: ServerComponentComment, start: Node, iterator: ComponentCommentIterator): ServerComponentComment | undefined { + const { type, descriptor, sequence, prerenderId } = payload; + if (type !== 'server') { + return undefined; + } + + if (!descriptor) { + throw new Error('descriptor must be defined when using a descriptor.'); + } + + if (sequence === undefined) { + throw new Error('sequence must be defined when using a descriptor.'); + } + + if (!Number.isInteger(sequence)) { + throw new Error(`Error parsing the sequence '${sequence}' for component '${JSON.stringify(payload)}'`); + } + + if (!prerenderId) { + return { + type, + sequence: sequence, + descriptor, + start, + }; + } else { + const end = getComponentEndComment(prerenderId, iterator); + if (!end) { + throw new Error(`Could not find an end component comment for '${start}'`); + } + + return { + type, + sequence, + descriptor, + start, + prerenderId, + end, + }; + } +} + +function createWebAssemblyComponentComment(payload: WebAssemblyComponentComment, start: Node, iterator: ComponentCommentIterator): WebAssemblyComponentComment | undefined { + const { type, assembly, typeName, parameterDefinitions, parameterValues, prerenderId } = payload; + if (type !== 'webassembly') { + return undefined; + } + + if (!assembly) { + throw new Error('assembly must be defined when using a descriptor.'); + } + + if (!typeName) { + throw new Error('typeName must be defined when using a descriptor.'); + } + + if (!prerenderId) { + return { + type, + assembly, + typeName, + // Parameter definitions and values come Base64 encoded from the server, since they contain random data and can make the + // comment invalid. We could unencode them in .NET Code, but that would be slower to do and we can leverage the fact that + // JS provides a native function that will be much faster and that we are doing this work while we are fetching + // blazor.boot.json + parameterDefinitions: parameterDefinitions && atob(parameterDefinitions), + parameterValues: parameterValues && atob(parameterValues), + start, + }; + } else { + const end = getComponentEndComment(prerenderId, iterator); + if (!end) { + throw new Error(`Could not find an end component comment for '${start}'`); + } + + return { + type, + assembly, + typeName, + // Same comment as above. + parameterDefinitions: parameterDefinitions && atob(parameterDefinitions), + parameterValues: parameterValues && atob(parameterValues), + start, + prerenderId, + end, + }; + } +} + +function getComponentEndComment(prerenderedId: string, iterator: ComponentCommentIterator): ChildNode | undefined { + while (iterator.next() && iterator.currentElement) { + const node = iterator.currentElement; + if (node.nodeType !== Node.COMMENT_NODE) { + continue; + } + if (!node.textContent) { + continue; + } + + const definition = new RegExp(blazorCommentRegularExpression).exec(node.textContent); + const json = definition && definition[1]; + if (!json) { + continue; + } + + validateEndComponentPayload(json, prerenderedId); + + return node; + } + + return undefined; +} + +function validateEndComponentPayload(json: string, prerenderedId: string): void { + const payload = JSON.parse(json) as ComponentComment; + if (Object.keys(payload).length !== 1) { + throw new Error(`Invalid end of component comment: '${json}'`); + } + const prerenderedEndId = payload.prerenderId; + if (!prerenderedEndId) { + throw new Error(`End of component comment must have a value for the prerendered property: '${json}'`); + } + if (prerenderedEndId !== prerenderedId) { + throw new Error(`End of component comment prerendered property must match the start comment prerender id: '${prerenderedId}', '${prerenderedEndId}'`); + } +} + +class ComponentCommentIterator { + + private childNodes: NodeListOf; + + private currentIndex: number; + + private length: number; + + public currentElement: ChildNode | undefined; + + public constructor(childNodes: NodeListOf) { + this.childNodes = childNodes; + this.currentIndex = -1; + this.length = childNodes.length; + } + + public next(): boolean { + this.currentIndex++; + if (this.currentIndex < this.length) { + this.currentElement = this.childNodes[this.currentIndex]; + return true; + } else { + this.currentElement = undefined; + return false; + } + } +} + +interface ServerComponentMarker { + type: string; + sequence: number; + descriptor: string; +} + +export class ServerComponentDescriptor { + public type: string; + + public start: Node; + + public end?: Node; + + public sequence: number; + + public descriptor: string; + + public constructor(type: string, start: Node, end: Node | undefined, sequence: number, descriptor: string) { + this.type = type; + this.start = start; + this.end = end; + this.sequence = sequence; + this.descriptor = descriptor; + } + + public toRecord(): ServerComponentMarker { + const result = { type: this.type, sequence: this.sequence, descriptor: this.descriptor }; + return result; + } +} + +export class WebAssemblyComponentDescriptor { + private static globalId = 1; + + public type: 'webassembly'; + + public typeName: string; + + public assembly: string; + + public parameterDefinitions?: string; + + public parameterValues?: string; + + public id: number; + + public start: Node; + + public end?: Node; + + public constructor(type: 'webassembly', start: Node, end: Node | undefined, assembly: string, typeName: string, parameterDefinitions?: string, parameterValues?: string) { + this.id = WebAssemblyComponentDescriptor.globalId++; + this.type = type; + this.assembly = assembly; + this.typeName = typeName; + this.parameterDefinitions = parameterDefinitions; + this.parameterValues = parameterValues; + this.start = start; + this.end = end; + } +} diff --git a/src/Components/Web/src/Forms/EditContextFieldClassExtensions.cs b/src/Components/Web/src/Forms/EditContextFieldClassExtensions.cs index 687328043ad5..fb8bac72bbf2 100644 --- a/src/Components/Web/src/Forms/EditContextFieldClassExtensions.cs +++ b/src/Components/Web/src/Forms/EditContextFieldClassExtensions.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.Linq; using System.Linq.Expressions; namespace Microsoft.AspNetCore.Components.Forms @@ -13,6 +12,8 @@ namespace Microsoft.AspNetCore.Components.Forms /// public static class EditContextFieldClassExtensions { + private readonly static object FieldCssClassProviderKey = new object(); + /// /// Gets a string that indicates the status of the specified field as a CSS class. This will include /// some combination of "modified", "valid", or "invalid", depending on the status of the field. @@ -24,23 +25,34 @@ public static string FieldCssClass(this EditContext editContext, Express => FieldCssClass(editContext, FieldIdentifier.Create(accessor)); /// - /// Gets a string that indicates the status of the specified field as a CSS class. This will include - /// some combination of "modified", "valid", or "invalid", depending on the status of the field. + /// Gets a string that indicates the status of the specified field as a CSS class. /// /// The . /// An identifier for the field. /// A string that indicates the status of the field. public static string FieldCssClass(this EditContext editContext, in FieldIdentifier fieldIdentifier) { - var isValid = !editContext.GetValidationMessages(fieldIdentifier).Any(); - if (editContext.IsModified(fieldIdentifier)) - { - return isValid ? "modified valid" : "modified invalid"; - } - else + var provider = editContext.Properties.TryGetValue(FieldCssClassProviderKey, out var customProvider) + ? (FieldCssClassProvider)customProvider + : FieldCssClassProvider.Instance; + + return provider.GetFieldCssClass(editContext, fieldIdentifier); + } + + /// + /// Associates the supplied with the supplied . + /// This customizes the field CSS class names used within the . + /// + /// The . + /// The . + public static void SetFieldCssClassProvider(this EditContext editContext, FieldCssClassProvider fieldCssClassProvider) + { + if (fieldCssClassProvider is null) { - return isValid ? "valid" : "invalid"; + throw new ArgumentNullException(nameof(fieldCssClassProvider)); } + + editContext.Properties[FieldCssClassProviderKey] = fieldCssClassProvider; } } } diff --git a/src/Components/Web/src/Forms/FieldCssClassProvider.cs b/src/Components/Web/src/Forms/FieldCssClassProvider.cs new file mode 100644 index 000000000000..dd56db64aa9f --- /dev/null +++ b/src/Components/Web/src/Forms/FieldCssClassProvider.cs @@ -0,0 +1,35 @@ +// 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.Linq; + +namespace Microsoft.AspNetCore.Components.Forms +{ + /// + /// Supplies CSS class names for form fields to represent their validation state or other + /// state information from an . + /// + public class FieldCssClassProvider + { + internal readonly static FieldCssClassProvider Instance = new FieldCssClassProvider(); + + /// + /// Gets a string that indicates the status of the specified field as a CSS class. + /// + /// The . + /// The . + /// A CSS class name string. + public virtual string GetFieldCssClass(EditContext editContext, in FieldIdentifier fieldIdentifier) + { + var isValid = !editContext.GetValidationMessages(fieldIdentifier).Any(); + if (editContext.IsModified(fieldIdentifier)) + { + return isValid ? "modified valid" : "modified invalid"; + } + else + { + return isValid ? "valid" : "invalid"; + } + } + } +} diff --git a/src/Components/WebAssembly/DevServer/src/Microsoft.AspNetCore.Components.WebAssembly.DevServer.csproj b/src/Components/WebAssembly/DevServer/src/Microsoft.AspNetCore.Components.WebAssembly.DevServer.csproj index c77d3e91e2a8..a1a012b76efa 100644 --- a/src/Components/WebAssembly/DevServer/src/Microsoft.AspNetCore.Components.WebAssembly.DevServer.csproj +++ b/src/Components/WebAssembly/DevServer/src/Microsoft.AspNetCore.Components.WebAssembly.DevServer.csproj @@ -11,17 +11,26 @@ true false + + + true + true - - - - - + + + + - @@ -36,19 +45,5 @@ - - - <_RuntimeFramework Include="@(RuntimeFramework)" /> - - - - - - - - - - - diff --git a/src/Components/WebAssembly/JSInterop/src/InternalCalls.cs b/src/Components/WebAssembly/JSInterop/src/InternalCalls.cs index a28e45c41061..6f9141ee7aaa 100644 --- a/src/Components/WebAssembly/JSInterop/src/InternalCalls.cs +++ b/src/Components/WebAssembly/JSInterop/src/InternalCalls.cs @@ -16,12 +16,7 @@ internal static class InternalCalls // in driver.c in the Mono distribution /// See: https://github.com/mono/mono/blob/90574987940959fe386008a850982ea18236a533/sdks/wasm/src/driver.c#L318-L319 - // We're passing asyncHandle by ref not because we want it to be writable, but so it gets - // passed as a pointer (4 bytes). We can pass 4-byte values, but not 8-byte ones. [MethodImpl(MethodImplOptions.InternalCall)] - public static extern string InvokeJSMarshalled(out string exception, ref long asyncHandle, string functionIdentifier, string argsJson); - - [MethodImpl(MethodImplOptions.InternalCall)] - public static extern TRes InvokeJSUnmarshalled(out string exception, string functionIdentifier, [AllowNull] T0 arg0, [AllowNull] T1 arg1, [AllowNull] T2 arg2); + public static extern TRes InvokeJS(out string exception, ref JSCallInfo callInfo, [AllowNull] T0 arg0, [AllowNull] T1 arg1, [AllowNull] T2 arg2); } } diff --git a/src/Components/WebAssembly/JSInterop/src/JSCallInfo.cs b/src/Components/WebAssembly/JSInterop/src/JSCallInfo.cs new file mode 100644 index 000000000000..5053a35bfd7b --- /dev/null +++ b/src/Components/WebAssembly/JSInterop/src/JSCallInfo.cs @@ -0,0 +1,27 @@ +// 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.Runtime.InteropServices; +using Microsoft.JSInterop; + +namespace WebAssembly.JSInterop +{ + [StructLayout(LayoutKind.Explicit, Pack = 4)] + internal struct JSCallInfo + { + [FieldOffset(0)] + public string FunctionIdentifier; + + [FieldOffset(4)] + public JSCallResultType ResultType; + + [FieldOffset(8)] + public string MarshalledCallArgsJson; + + [FieldOffset(12)] + public long MarshalledCallAsyncHandle; + + [FieldOffset(20)] + public long TargetInstanceId; + } +} diff --git a/src/Components/WebAssembly/JSInterop/src/WebAssemblyJSRuntime.cs b/src/Components/WebAssembly/JSInterop/src/WebAssemblyJSRuntime.cs index 54bd6eb8f85b..14e1600b9f33 100644 --- a/src/Components/WebAssembly/JSInterop/src/WebAssemblyJSRuntime.cs +++ b/src/Components/WebAssembly/JSInterop/src/WebAssemblyJSRuntime.cs @@ -14,19 +14,37 @@ namespace Microsoft.JSInterop.WebAssembly public abstract class WebAssemblyJSRuntime : JSInProcessRuntime, IJSUnmarshalledRuntime { /// - protected override string InvokeJS(string identifier, string argsJson) + protected override string InvokeJS(string identifier, string argsJson, JSCallResultType resultType, long targetInstanceId) { - var noAsyncHandle = default(long); - var result = InternalCalls.InvokeJSMarshalled(out var exception, ref noAsyncHandle, identifier, argsJson); + var callInfo = new JSCallInfo + { + FunctionIdentifier = identifier, + TargetInstanceId = targetInstanceId, + ResultType = resultType, + MarshalledCallArgsJson = argsJson ?? "[]", + MarshalledCallAsyncHandle = default + }; + + var result = InternalCalls.InvokeJS(out var exception, ref callInfo, null, null, null); + return exception != null ? throw new JSException(exception) : result; } /// - protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) + protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson, JSCallResultType resultType, long targetInstanceId) { - InternalCalls.InvokeJSMarshalled(out _, ref asyncHandle, identifier, argsJson); + var callInfo = new JSCallInfo + { + FunctionIdentifier = identifier, + TargetInstanceId = targetInstanceId, + ResultType = resultType, + MarshalledCallArgsJson = argsJson ?? "[]", + MarshalledCallAsyncHandle = asyncHandle + }; + + InternalCalls.InvokeJS(out _, ref callInfo, null, null, null); } protected override void EndInvokeDotNet(DotNetInvocationInfo callInfo, in DotNetInvocationResult dispatchResult) @@ -39,7 +57,7 @@ protected override void EndInvokeDotNet(DotNetInvocationInfo callInfo, in DotNet // We pass 0 as the async handle because we don't want the JS-side code to // send back any notification (we're just providing a result for an existing async call) var args = JsonSerializer.Serialize(new[] { callInfo.CallId, dispatchResult.Success, resultOrError }, JsonSerializerOptions); - BeginInvokeJS(0, "DotNet.jsCallDispatcher.endInvokeDotNetFromJS", args); + BeginInvokeJS(0, "DotNet.jsCallDispatcher.endInvokeDotNetFromJS", args, JSCallResultType.Default, 0); } /// @@ -57,7 +75,14 @@ TResult IJSUnmarshalledRuntime.InvokeUnmarshalled(string identi /// TResult IJSUnmarshalledRuntime.InvokeUnmarshalled(string identifier, T0 arg0, T1 arg1, T2 arg2) { - var result = InternalCalls.InvokeJSUnmarshalled(out var exception, identifier, arg0, arg1, arg2); + var callInfo = new JSCallInfo + { + FunctionIdentifier = identifier, + ResultType = ResultTypeFromGeneric() + }; + + var result = InternalCalls.InvokeJS(out var exception, ref callInfo, arg0, arg1, arg2); + return exception != null ? throw new JSException(exception) : result; diff --git a/src/Components/WebAssembly/WebAssembly/src/Hosting/RegisteredComponentsInterop.cs b/src/Components/WebAssembly/WebAssembly/src/Hosting/RegisteredComponentsInterop.cs new file mode 100644 index 000000000000..adb34bdb70f3 --- /dev/null +++ b/src/Components/WebAssembly/WebAssembly/src/Hosting/RegisteredComponentsInterop.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. + +namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting +{ + internal class RegisteredComponentsInterop + { + private static readonly string Prefix = "Blazor._internal.registeredComponents."; + + public static readonly string GetRegisteredComponentsCount = Prefix + "getRegisteredComponentsCount"; + + public static readonly string GetId = Prefix + "getId"; + + public static readonly string GetAssembly = Prefix + "getAssembly"; + + public static readonly string GetTypeName = Prefix + "getTypeName"; + + public static readonly string GetParameterDefinitions = Prefix + "getParameterDefinitions"; + + public static readonly string GetParameterValues = Prefix + "getParameterValues"; + } +} diff --git a/src/Components/WebAssembly/WebAssembly/src/Hosting/RootComponentMapping.cs b/src/Components/WebAssembly/WebAssembly/src/Hosting/RootComponentMapping.cs index 22429279fdd0..bf21666dcd5f 100644 --- a/src/Components/WebAssembly/WebAssembly/src/Hosting/RootComponentMapping.cs +++ b/src/Components/WebAssembly/WebAssembly/src/Hosting/RootComponentMapping.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections; using Microsoft.AspNetCore.Components; namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting @@ -16,7 +17,7 @@ public readonly struct RootComponentMapping /// and . /// /// The component type. Must implement . - /// The DOM element selector. + /// The DOM element selector or component registration id for the component. public RootComponentMapping(Type componentType, string selector) { if (componentType is null) @@ -38,6 +39,19 @@ public RootComponentMapping(Type componentType, string selector) ComponentType = componentType; Selector = selector; + Parameters = ParameterView.Empty; + } + + /// + /// Creates a new instance of with the provided + /// and . + /// + /// The component type. Must implement . + /// The DOM element selector or registration id for the component. + /// The parameters to pass to the component. + public RootComponentMapping(Type componentType, string selector, ParameterView parameters) : this(componentType, selector) + { + Parameters = parameters; } /// @@ -49,5 +63,10 @@ public RootComponentMapping(Type componentType, string selector) /// Gets the DOM element selector. /// public string Selector { get; } + + /// + /// Gets the parameters to pass to the root component. + /// + public ParameterView Parameters { get; } } } diff --git a/src/Components/WebAssembly/WebAssembly/src/Hosting/RootComponentMappingCollection.cs b/src/Components/WebAssembly/WebAssembly/src/Hosting/RootComponentMappingCollection.cs index ab4e1d82233e..c4567a9ffaef 100644 --- a/src/Components/WebAssembly/WebAssembly/src/Hosting/RootComponentMappingCollection.cs +++ b/src/Components/WebAssembly/WebAssembly/src/Hosting/RootComponentMappingCollection.cs @@ -34,6 +34,17 @@ public void Add(string selector) where TComponent : IComponent /// The component type. Must implement . /// The DOM element selector. public void Add(Type componentType, string selector) + { + Add(componentType, selector, ParameterView.Empty); + } + + /// + /// Adds a component mapping to the collection. + /// + /// The component type. Must implement . + /// The DOM element selector. + /// The parameters to the root component. + public void Add(Type componentType, string selector, ParameterView parameters) { if (componentType is null) { @@ -45,7 +56,7 @@ public void Add(Type componentType, string selector) throw new ArgumentNullException(nameof(selector)); } - Add(new RootComponentMapping(componentType, selector)); + Add(new RootComponentMapping(componentType, selector, parameters)); } /// diff --git a/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHost.cs b/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHost.cs index c81dd2ac28a9..c26caaee9925 100644 --- a/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHost.cs +++ b/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHost.cs @@ -138,7 +138,7 @@ internal async Task RunAsyncCore(CancellationToken cancellationToken) for (var i = 0; i < rootComponents.Length; i++) { var rootComponent = rootComponents[i]; - await _renderer.AddComponentAsync(rootComponent.ComponentType, rootComponent.Selector); + await _renderer.AddComponentAsync(rootComponent.ComponentType, rootComponent.Selector, rootComponent.Parameters); } await tcs.Task; diff --git a/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHostBuilder.cs b/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHostBuilder.cs index a65e4117cd1c..9e6215c70a75 100644 --- a/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHostBuilder.cs +++ b/src/Components/WebAssembly/WebAssembly/src/Hosting/WebAssemblyHostBuilder.cs @@ -21,6 +21,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Hosting public sealed class WebAssemblyHostBuilder { private Func _createServiceProvider; + private RootComponentTypeCache _rootComponentCache; /// /// Creates an instance of using the most common @@ -57,6 +58,7 @@ internal WebAssemblyHostBuilder(WebAssemblyJSRuntimeInvoker jsRuntimeInvoker) // Retrieve required attributes from JSRuntimeInvoker InitializeNavigationManager(jsRuntimeInvoker); + InitializeRegisteredRootComponents(jsRuntimeInvoker); InitializeDefaultServices(); var hostEnvironment = InitializeEnvironment(jsRuntimeInvoker); @@ -68,6 +70,38 @@ internal WebAssemblyHostBuilder(WebAssemblyJSRuntimeInvoker jsRuntimeInvoker) }; } + private void InitializeRegisteredRootComponents(WebAssemblyJSRuntimeInvoker jsRuntimeInvoker) + { + var componentsCount = jsRuntimeInvoker.InvokeUnmarshalled(RegisteredComponentsInterop.GetRegisteredComponentsCount, null, null, null); + if (componentsCount == 0) + { + return; + } + + var registeredComponents = new WebAssemblyComponentMarker[componentsCount]; + for (var i = 0; i < componentsCount; i++) + { + var id = jsRuntimeInvoker.InvokeUnmarshalled(RegisteredComponentsInterop.GetId, i, null, null); + var assembly = jsRuntimeInvoker.InvokeUnmarshalled(RegisteredComponentsInterop.GetAssembly, id, null, null); + var typeName = jsRuntimeInvoker.InvokeUnmarshalled(RegisteredComponentsInterop.GetTypeName, id, null, null); + var serializedParameterDefinitions = jsRuntimeInvoker.InvokeUnmarshalled(RegisteredComponentsInterop.GetParameterDefinitions, id, null, null); + var serializedParameterValues = jsRuntimeInvoker.InvokeUnmarshalled(RegisteredComponentsInterop.GetParameterValues, id, null, null); + registeredComponents[i] = new WebAssemblyComponentMarker(WebAssemblyComponentMarker.ClientMarkerType, assembly, typeName, serializedParameterDefinitions, serializedParameterValues, id.ToString()); + } + + var componentDeserializer = WebAssemblyComponentParameterDeserializer.Instance; + foreach (var registeredComponent in registeredComponents) + { + _rootComponentCache = new RootComponentTypeCache(); + var componentType = _rootComponentCache.GetRootComponent(registeredComponent.Assembly, registeredComponent.TypeName); + var definitions = componentDeserializer.GetParameterDefinitions(registeredComponent.ParameterDefinitions); + var values = componentDeserializer.GetParameterValues(registeredComponent.ParameterValues); + var parameters = componentDeserializer.DeserializeParameters(definitions, values); + + RootComponents.Add(componentType, registeredComponent.PrerenderId, parameters); + } + } + private void InitializeNavigationManager(WebAssemblyJSRuntimeInvoker jsRuntimeInvoker) { var baseUri = jsRuntimeInvoker.InvokeUnmarshalled(BrowserNavigationManagerInterop.GetBaseUri, null, null, null); @@ -190,7 +224,8 @@ internal void InitializeDefaultServices() Services.AddSingleton(WebAssemblyNavigationManager.Instance); Services.AddSingleton(WebAssemblyNavigationInterception.Instance); Services.AddSingleton(new LazyAssemblyLoader(DefaultWebAssemblyJSRuntime.Instance)); - Services.AddLogging(builder => { + Services.AddLogging(builder => + { builder.AddProvider(new WebAssemblyConsoleLoggerProvider(DefaultWebAssemblyJSRuntime.Instance)); }); } diff --git a/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj b/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj index 4dde2eaae611..906e03fe82e6 100644 --- a/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj +++ b/src/Components/WebAssembly/WebAssembly/src/Microsoft.AspNetCore.Components.WebAssembly.csproj @@ -1,4 +1,4 @@ - + $(DefaultNetCoreTargetFramework) @@ -15,13 +15,7 @@ - + @@ -31,6 +25,11 @@ + + + + + diff --git a/src/Components/WebAssembly/WebAssembly/src/Prerendering/ClientComponentParameterDeserializer.cs b/src/Components/WebAssembly/WebAssembly/src/Prerendering/ClientComponentParameterDeserializer.cs new file mode 100644 index 000000000000..855c79e101fc --- /dev/null +++ b/src/Components/WebAssembly/WebAssembly/src/Prerendering/ClientComponentParameterDeserializer.cs @@ -0,0 +1,88 @@ +// 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.Text.Json; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; + +namespace Microsoft.AspNetCore.Components +{ + internal class WebAssemblyComponentParameterDeserializer + { + private readonly ComponentParametersTypeCache _parametersCache; + + public WebAssemblyComponentParameterDeserializer( + ComponentParametersTypeCache parametersCache) + { + _parametersCache = parametersCache; + } + + public static WebAssemblyComponentParameterDeserializer Instance { get; } = new WebAssemblyComponentParameterDeserializer(new ComponentParametersTypeCache()); + + public ParameterView DeserializeParameters(IList parametersDefinitions, IList parameterValues) + { + var parametersDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + + if (parameterValues.Count != parametersDefinitions.Count) + { + // Mismatched number of definition/parameter values. + throw new InvalidOperationException($"The number of parameter definitions '{parametersDefinitions.Count}' does not match the number parameter values '{parameterValues.Count}'."); + } + + for (var i = 0; i < parametersDefinitions.Count; i++) + { + var definition = parametersDefinitions[i]; + if (definition.Name == null) + { + throw new InvalidOperationException("The name is missing in a parameter definition."); + } + + if (definition.TypeName == null && definition.Assembly == null) + { + parametersDictionary[definition.Name] = null; + } + else if (definition.TypeName == null || definition.Assembly == null) + { + throw new InvalidOperationException($"The parameter definition for '{definition.Name}' is incomplete: Type='{definition.TypeName}' Assembly='{definition.Assembly}'."); + } + else + { + var parameterType = _parametersCache.GetParameterType(definition.Assembly, definition.TypeName); + if (parameterType == null) + { + throw new InvalidOperationException($"The parameter '{definition.Name} with type '{definition.TypeName}' in assembly '{definition.Assembly}' could not be found."); + } + try + { + var value = (JsonElement)parameterValues[i]; + var parameterValue = JsonSerializer.Deserialize( + value.GetRawText(), + parameterType, + WebAssemblyComponentSerializationSettings.JsonSerializationOptions); + + parametersDictionary[definition.Name] = parameterValue; + } + catch (Exception e) + { + throw new InvalidOperationException("Could not parse the parameter value for parameter '{definition.Name}' of type '{definition.TypeName}' and assembly '{definition.Assembly}'.", e); + } + } + } + + return ParameterView.FromDictionary(parametersDictionary); + } + + public ComponentParameter[] GetParameterDefinitions(string parametersDefinitions) + { + return JsonSerializer.Deserialize(parametersDefinitions, WebAssemblyComponentSerializationSettings.JsonSerializationOptions); + } + + public IList GetParameterValues(string parameterValues) + { + return JsonSerializer.Deserialize>(parameterValues, WebAssemblyComponentSerializationSettings.JsonSerializationOptions); + } + } +} diff --git a/src/Components/WebAssembly/WebAssembly/src/Rendering/WebAssemblyRenderer.cs b/src/Components/WebAssembly/WebAssembly/src/Rendering/WebAssemblyRenderer.cs index 19886474576e..ca033e1107fe 100644 --- a/src/Components/WebAssembly/WebAssembly/src/Rendering/WebAssemblyRenderer.cs +++ b/src/Components/WebAssembly/WebAssembly/src/Rendering/WebAssemblyRenderer.cs @@ -47,13 +47,14 @@ public WebAssemblyRenderer(IServiceProvider serviceProvider, ILoggerFactory logg /// /// The type of the component. /// A CSS selector that uniquely identifies a DOM element. + /// The parameters for the component. /// A that represents the asynchronous rendering of the added component. /// /// Callers of this method may choose to ignore the returned if they do not /// want to await the rendering of the added component. /// - public Task AddComponentAsync(string domElementSelector) where TComponent : IComponent - => AddComponentAsync(typeof(TComponent), domElementSelector); + public Task AddComponentAsync(string domElementSelector, ParameterView parameters) where TComponent : IComponent + => AddComponentAsync(typeof(TComponent), domElementSelector, parameters); /// /// Associates the with the , @@ -61,12 +62,13 @@ public Task AddComponentAsync(string domElementSelector) where TComp /// /// The type of the component. /// A CSS selector that uniquely identifies a DOM element. + /// The list of root component parameters. /// A that represents the asynchronous rendering of the added component. /// /// Callers of this method may choose to ignore the returned if they do not /// want to await the rendering of the added component. /// - public Task AddComponentAsync(Type componentType, string domElementSelector) + public Task AddComponentAsync(Type componentType, string domElementSelector, ParameterView parameters) { var component = InstantiateComponent(componentType); var componentId = AssignRootComponentId(component); @@ -83,7 +85,7 @@ public Task AddComponentAsync(Type componentType, string domElementSelector) componentId, _webAssemblyRendererId); - return RenderRootComponentAsync(componentId); + return RenderRootComponentAsync(componentId, parameters); } /// diff --git a/src/Components/WebAssembly/WebAssembly/test/TestWebAssemblyJSRuntimeInvoker.cs b/src/Components/WebAssembly/WebAssembly/test/TestWebAssemblyJSRuntimeInvoker.cs index e1b5f9f6160f..54c198ffcd7a 100644 --- a/src/Components/WebAssembly/WebAssembly/test/TestWebAssemblyJSRuntimeInvoker.cs +++ b/src/Components/WebAssembly/WebAssembly/test/TestWebAssemblyJSRuntimeInvoker.cs @@ -29,6 +29,8 @@ public override TResult InvokeUnmarshalled(string identifie case "Blazor._internal.navigationManager.getUnmarshalledLocationHref": var testHref = "https://www.example.com/awesome-part-that-will-be-truncated-in-tests/cool"; return (TResult)(object)testHref; + case "Blazor._internal.registeredComponents.getRegisteredComponentsCount": + return (TResult)(object)0; default: throw new NotImplementedException($"{nameof(TestWebAssemblyJSRuntimeInvoker)} has no implementation for '{identifier}'."); } diff --git a/src/Components/WebAssembly/testassets/Wasm.Authentication.Server/Startup.cs b/src/Components/WebAssembly/testassets/Wasm.Authentication.Server/Startup.cs index 1224ee978fdc..038e01f0c711 100644 --- a/src/Components/WebAssembly/testassets/Wasm.Authentication.Server/Startup.cs +++ b/src/Components/WebAssembly/testassets/Wasm.Authentication.Server/Startup.cs @@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; @@ -57,6 +58,11 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseWebAssemblyDebugging(); } + app.UseCookiePolicy(new CookiePolicyOptions + { + MinimumSameSitePolicy = SameSiteMode.Lax + }); + app.UseBlazorFrameworkFiles(); app.UseStaticFiles(); diff --git a/src/Components/WebAssembly/testassets/Wasm.Authentication.Server/Wasm.Authentication.Server.csproj b/src/Components/WebAssembly/testassets/Wasm.Authentication.Server/Wasm.Authentication.Server.csproj index 142bfe2145f3..8daf5092666a 100644 --- a/src/Components/WebAssembly/testassets/Wasm.Authentication.Server/Wasm.Authentication.Server.csproj +++ b/src/Components/WebAssembly/testassets/Wasm.Authentication.Server/Wasm.Authentication.Server.csproj @@ -10,10 +10,12 @@ + + diff --git a/src/Components/benchmarkapps/Wasm.Performance/Directory.Build.props b/src/Components/benchmarkapps/Wasm.Performance/Directory.Build.props new file mode 100644 index 000000000000..0556b5271ed6 --- /dev/null +++ b/src/Components/benchmarkapps/Wasm.Performance/Directory.Build.props @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/Components/benchmarkapps/Wasm.Performance/Driver/Program.cs b/src/Components/benchmarkapps/Wasm.Performance/Driver/Program.cs index c3c3ae618fbf..c72ab9dccac4 100644 --- a/src/Components/benchmarkapps/Wasm.Performance/Driver/Program.cs +++ b/src/Components/benchmarkapps/Wasm.Performance/Driver/Program.cs @@ -16,6 +16,7 @@ using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using OpenQA.Selenium; using DevHostServerProgram = Microsoft.AspNetCore.Components.WebAssembly.DevServer.Server.Program; namespace Wasm.Performance.Driver @@ -81,7 +82,20 @@ public static async Task Main(string[] args) { BenchmarkResultTask = new TaskCompletionSource(); using var runCancellationToken = new CancellationTokenSource(timeForEachRun); - using var registration = runCancellationToken.Token.Register(() => BenchmarkResultTask.TrySetException(new TimeoutException($"Timed out after {timeForEachRun}"))); + using var registration = runCancellationToken.Token.Register(() => + { + string exceptionMessage = $"Timed out after {timeForEachRun}."; + try + { + var innerHtml = browser.FindElement(By.CssSelector(":first-child")).GetAttribute("innerHTML"); + exceptionMessage += Environment.NewLine + "Browser state: " + Environment.NewLine + innerHtml; + } + catch + { + // Do nothing; + } + BenchmarkResultTask.TrySetException(new TimeoutException(exceptionMessage)); + }); var results = await BenchmarkResultTask.Task; @@ -89,6 +103,11 @@ public static async Task Main(string[] args) includeMetadata: firstRun, isStressRun: isStressRun); + if (!isStressRun) + { + PrettyPrint(results); + } + firstRun = false; } while (isStressRun && !stressRunCancellation.IsCancellationRequested); @@ -230,6 +249,17 @@ private static void FormatAsBenchmarksOutput(BenchmarkResult benchmarkResult, bo Console.WriteLine(builder); } + static void PrettyPrint(BenchmarkResult benchmarkResult) + { + Console.WriteLine(); + Console.WriteLine("| Name | Description | Duration | NumExecutions | "); + Console.WriteLine("--------------------------"); + foreach (var result in benchmarkResult.ScenarioResults) + { + Console.WriteLine($"| {result.Descriptor.Name} | {result.Name} | {result.Duration} | {result.NumExecutions} |"); + } + } + static IHost StartTestApp() { var args = new[] diff --git a/src/Components/benchmarkapps/Wasm.Performance/Driver/Selenium.cs b/src/Components/benchmarkapps/Wasm.Performance/Driver/Selenium.cs index f9d7ef47efa2..974854909960 100644 --- a/src/Components/benchmarkapps/Wasm.Performance/Driver/Selenium.cs +++ b/src/Components/benchmarkapps/Wasm.Performance/Driver/Selenium.cs @@ -16,12 +16,7 @@ class Selenium const int SeleniumPort = 4444; static bool RunHeadlessBrowser = true; - static bool PoolForBrowserLogs = -#if DEBUG - true; -#else - false; -#endif + static bool PoolForBrowserLogs = true; private static async ValueTask WaitForServerAsync(int port, CancellationToken cancellationToken) { diff --git a/src/Components/benchmarkapps/Wasm.Performance/Driver/Wasm.Performance.Driver.csproj b/src/Components/benchmarkapps/Wasm.Performance/Driver/Wasm.Performance.Driver.csproj index 66aea279ee62..346ddc48f16b 100644 --- a/src/Components/benchmarkapps/Wasm.Performance/Driver/Wasm.Performance.Driver.csproj +++ b/src/Components/benchmarkapps/Wasm.Performance/Driver/Wasm.Performance.Driver.csproj @@ -12,13 +12,18 @@ + + - + + + + $(DefaultNetCoreTargetFramework) true + + false diff --git a/src/Components/test/E2ETest/Microsoft.AspNetCore.Components.E2ETests.csproj b/src/Components/test/E2ETest/Microsoft.AspNetCore.Components.E2ETests.csproj index 256ea831e195..4a363519fb7b 100644 --- a/src/Components/test/E2ETest/Microsoft.AspNetCore.Components.E2ETests.csproj +++ b/src/Components/test/E2ETest/Microsoft.AspNetCore.Components.E2ETests.csproj @@ -14,6 +14,9 @@ true false + + true + @@ -41,7 +44,6 @@ - @@ -56,6 +58,8 @@ + + diff --git a/src/Components/test/E2ETest/Tests/ClientRenderingMultpleComponentsTest.cs b/src/Components/test/E2ETest/Tests/ClientRenderingMultpleComponentsTest.cs new file mode 100644 index 000000000000..7e4df2898e75 --- /dev/null +++ b/src/Components/test/E2ETest/Tests/ClientRenderingMultpleComponentsTest.cs @@ -0,0 +1,95 @@ +// 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.Linq; +using System.Text.Json; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Components.E2ETest.Infrastructure; +using Microsoft.AspNetCore.Components.E2ETest.Infrastructure.ServerFixtures; +using Microsoft.AspNetCore.E2ETesting; +using OpenQA.Selenium; +using TestServer; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.AspNetCore.Components.E2ETests.Tests +{ + public class ClientRenderingMultpleComponentsTest : ServerTestBase> + { + private const string MarkerPattern = ".*?.*?"; + + public ClientRenderingMultpleComponentsTest( + BrowserFixture browserFixture, + BasicTestAppServerSiteFixture serverFixture, + ITestOutputHelper output) + : base(browserFixture, serverFixture, output) + { + } + + public DateTime LastLogTimeStamp { get; set; } = DateTime.MinValue; + + public override async Task InitializeAsync() + { + await base.InitializeAsync(); + + // Capture the last log timestamp so that we can filter logs when we + // check for duplicate connections. + var lastLog = Browser.Manage().Logs.GetLog(LogType.Browser).LastOrDefault(); + if (lastLog != null) + { + LastLogTimeStamp = lastLog.Timestamp; + } + } + + [Fact] + public void CanRenderMultipleRootComponents() + { + Navigate("/Client/multiple-components"); + + var greets = Browser.FindElements(By.CssSelector(".greet-wrapper .greet")).Select(e => e.Text).ToArray(); + + Assert.Equal(7, greets.Length); // 1 statically rendered + 5 prerendered + 1 server prerendered + Assert.DoesNotContain("Hello Red fish", greets); + Assert.Single(greets, "Hello John"); + Assert.Single(greets, "Hello Abraham"); + Assert.Equal(2, greets.Where(g => g == "Hello Blue fish").Count()); + Assert.Equal(3, greets.Where(g => string.Equals("Hello", g)).Count()); // 3 server prerendered without parameters + var content = Browser.FindElement(By.Id("test-container")).GetAttribute("innerHTML"); + var markers = ReadMarkers(content); + var componentSequence = markers.Select(m => m.Item1.PrerenderId != null).ToArray(); + Assert.Equal(13, componentSequence.Length); + + // Once the app starts, output changes + BeginInteractivity(); + + Browser.Exists(By.CssSelector("h3.interactive")); + var updatedGreets = Browser.FindElements(By.CssSelector(".greet-wrapper .greet")).Select(e => e.Text).ToArray(); + Assert.Equal(7, updatedGreets.Where(g => string.Equals("Hello Alfred", g)).Count()); + Assert.Equal(2, updatedGreets.Where(g => g == "Hello Red fish").Count()); + Assert.Equal(2, updatedGreets.Where(g => g == "Hello Blue fish").Count()); + Assert.Single(updatedGreets.Where(g => string.Equals("Hello Albert", g))); + Assert.Single(updatedGreets.Where(g => string.Equals("Hello Abraham", g))); + } + + private (WebAssemblyComponentMarker, WebAssemblyComponentMarker)[] ReadMarkers(string content) + { + content = content.Replace("\r\n", ""); + var matches = Regex.Matches(content, MarkerPattern); + var markers = matches.Select(s => JsonSerializer.Deserialize( + s.Groups[1].Value, + WebAssemblyComponentSerializationSettings.JsonSerializationOptions)); + + var prerenderMarkers = markers.Where(m => m.PrerenderId != null).GroupBy(p => p.PrerenderId).Select(g => (g.First(), g.Skip(1).First())).ToArray(); + var nonPrerenderMarkers = markers.Where(m => m.PrerenderId == null).Select(g => (g, (WebAssemblyComponentMarker)default)).ToArray(); + + return prerenderMarkers.Concat(nonPrerenderMarkers).ToArray(); + } + + private void BeginInteractivity() + { + Browser.FindElement(By.Id("load-boot-script")).Click(); + } + } +} diff --git a/src/Components/test/E2ETest/Tests/FormsTest.cs b/src/Components/test/E2ETest/Tests/FormsTest.cs index 265314d4abf2..9c4f840dcf19 100644 --- a/src/Components/test/E2ETest/Tests/FormsTest.cs +++ b/src/Components/test/E2ETest/Tests/FormsTest.cs @@ -560,6 +560,23 @@ public void SelectComponentSupportsOptionsComponent() Browser.Equal("", () => selectWithoutComponent.GetAttribute("value")); } + [Fact] + public void RespectsCustomFieldCssClassProvider() + { + var appElement = MountTypicalValidationComponent(); + var socksInput = appElement.FindElement(By.ClassName("socks")).FindElement(By.TagName("input")); + var messagesAccessor = CreateValidationMessagesAccessor(appElement); + + // Validates on edit + Browser.Equal("valid-socks", () => socksInput.GetAttribute("class")); + socksInput.SendKeys("Purple\t"); + Browser.Equal("modified valid-socks", () => socksInput.GetAttribute("class")); + + // Can become invalid + socksInput.SendKeys(" with yellow spots\t"); + Browser.Equal("modified invalid-socks", () => socksInput.GetAttribute("class")); + } + [Fact] public void NavigateOnSubmitWorks() { diff --git a/src/Components/test/E2ETest/Tests/InteropTest.cs b/src/Components/test/E2ETest/Tests/InteropTest.cs index 2397ea3f9d37..69d84233a07b 100644 --- a/src/Components/test/E2ETest/Tests/InteropTest.cs +++ b/src/Components/test/E2ETest/Tests/InteropTest.cs @@ -55,10 +55,13 @@ public void CanInvokeDotNetMethods() ["result7Async"] = @"[{""id"":6,""isValid"":true,""data"":{""source"":""Some random text with at least 6 characters"",""start"":6,""length"":6}},6,123,24,48,6.25]", ["result8Async"] = @"[{""id"":7,""isValid"":false,""data"":{""source"":""Some random text with at least 7 characters"",""start"":7,""length"":7}},7,123,28,56,7.25,[0.5,1.5,2.5,3.5,4.5,5.5,6.5]]", ["result9Async"] = @"[{""id"":8,""isValid"":true,""data"":{""source"":""Some random text with at least 8 characters"",""start"":8,""length"":8}},8,123,32,64,8.25,[0.5,1.5,2.5,3.5,4.5,5.5,6.5,7.5],{""source"":""Some random text with at least 7 characters"",""start"":9,""length"":9}]", + ["roundTripJSObjectReferenceAsync"] = @"""successful""", + ["invokeDisposedJSObjectReferenceExceptionAsync"] = @"""JS object instance with ID", ["AsyncThrowSyncException"] = @"""System.InvalidOperationException: Threw a sync exception!", ["AsyncThrowAsyncException"] = @"""System.InvalidOperationException: Threw an async exception!", ["SyncExceptionFromAsyncMethod"] = "Function threw a sync exception!", ["AsyncExceptionFromAsyncMethod"] = "Function threw an async exception!", + ["JSObjectReferenceInvokeNonFunctionException"] = "The value 'nonFunction' is not a function.", ["resultReturnDotNetObjectByRefAsync"] = "1001", ["instanceMethodThisTypeNameAsync"] = @"""JavaScriptInterop""", ["instanceMethodStringValueUpperAsync"] = @"""MY STRING""", @@ -69,6 +72,10 @@ public void CanInvokeDotNetMethods() ["testDtoAsync"] = "Same", ["returnPrimitiveAsync"] = "123", ["returnArrayAsync"] = "first,second", + ["jsObjectReference.identity"] = "Invoked from JSObjectReference", + ["jsObjectReference.nested.add"] = "5", + ["addViaJSObjectReference"] = "5", + ["jsObjectReferenceModule"] = "Returned from module!", ["syncGenericInstanceMethod"] = @"""Initial value""", ["asyncGenericInstanceMethod"] = @"""Updated value 1""", }; @@ -93,6 +100,8 @@ public void CanInvokeDotNetMethods() ["result7"] = @"[{""id"":6,""isValid"":true,""data"":{""source"":""Some random text with at least 6 characters"",""start"":6,""length"":6}},6,123,24,48,6.25]", ["result8"] = @"[{""id"":7,""isValid"":false,""data"":{""source"":""Some random text with at least 7 characters"",""start"":7,""length"":7}},7,123,28,56,7.25,[0.5,1.5,2.5,3.5,4.5,5.5,6.5]]", ["result9"] = @"[{""id"":8,""isValid"":true,""data"":{""source"":""Some random text with at least 8 characters"",""start"":8,""length"":8}},8,123,32,64,8.25,[0.5,1.5,2.5,3.5,4.5,5.5,6.5,7.5],{""source"":""Some random text with at least 7 characters"",""start"":9,""length"":9}]", + ["roundTripJSObjectReference"] = @"""successful""", + ["invokeDisposedJSObjectReferenceException"] = @"""JS object instance with ID", ["ThrowException"] = @"""System.InvalidOperationException: Threw an exception!", ["ExceptionFromSyncMethod"] = "Function threw an exception!", ["resultReturnDotNetObjectByRefSync"] = "1000", @@ -100,6 +109,7 @@ public void CanInvokeDotNetMethods() ["instanceMethodStringValueUpper"] = @"""MY STRING""", ["instanceMethodIncomingByRef"] = "123", ["instanceMethodOutgoingByRef"] = "1234", + ["jsInProcessObjectReference.identity"] = "Invoked from JSInProcessObjectReference", ["stringValueUpperSync"] = "MY STRING", ["testDtoNonSerializedValueSync"] = "99999", ["testDtoSync"] = "Same", diff --git a/src/Components/test/testassets/BasicTestApp/FormsTest/CustomFieldCssClassProvider.cs b/src/Components/test/testassets/BasicTestApp/FormsTest/CustomFieldCssClassProvider.cs new file mode 100644 index 000000000000..bee221d307e9 --- /dev/null +++ b/src/Components/test/testassets/BasicTestApp/FormsTest/CustomFieldCssClassProvider.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; +using System.Linq; +using Microsoft.AspNetCore.Components.Forms; + +namespace BasicTestApp.FormsTest +{ + // For E2E testing, this is a rough example of a field CSS class provider that looks for + // a custom attribute defining CSS class names. It isn't very efficient (it does reflection + // and allocates on every invocation) but is sufficient for testing purposes. + public class CustomFieldCssClassProvider : FieldCssClassProvider + { + public override string GetFieldCssClass(EditContext editContext, in FieldIdentifier fieldIdentifier) + { + var cssClassName = base.GetFieldCssClass(editContext, fieldIdentifier); + + // If we can find a [CustomValidationClassName], use it + var propertyInfo = fieldIdentifier.Model.GetType().GetProperty(fieldIdentifier.FieldName); + if (propertyInfo != null) + { + var customValidationClassName = (CustomValidationClassNameAttribute)propertyInfo + .GetCustomAttributes(typeof(CustomValidationClassNameAttribute), true) + .FirstOrDefault(); + if (customValidationClassName != null) + { + cssClassName = string.Join(' ', cssClassName.Split(' ').Select(token => token switch + { + "valid" => customValidationClassName.Valid ?? token, + "invalid" => customValidationClassName.Invalid ?? token, + _ => token, + })); + } + } + + return cssClassName; + } + } + + [AttributeUsage(AttributeTargets.Property)] + public class CustomValidationClassNameAttribute : Attribute + { + public string Valid { get; set; } + public string Invalid { get; set; } + } +} diff --git a/src/Components/test/testassets/BasicTestApp/FormsTest/TypicalValidationComponent.razor b/src/Components/test/testassets/BasicTestApp/FormsTest/TypicalValidationComponent.razor index 89a53802ee18..4f91e4653867 100644 --- a/src/Components/test/testassets/BasicTestApp/FormsTest/TypicalValidationComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/FormsTest/TypicalValidationComponent.razor @@ -66,6 +66,9 @@

+

+ Socks color: +

Accepts terms:

@@ -98,6 +101,7 @@ protected override void OnInitialized() { editContext = new EditContext(person); + editContext.SetFieldCssClassProvider(new CustomFieldCssClassProvider()); customValidationMessageStore = new ValidationMessageStore(editContext); } @@ -145,6 +149,9 @@ [Required, EnumDataType(typeof(Country))] public Country? Country { get; set; } = null; + [Required, StringLength(10), CustomValidationClassName(Valid = "valid-socks", Invalid = "invalid-socks")] + public string SocksColor { get; set; } + public string Username { get; set; } } diff --git a/src/Components/test/testassets/BasicTestApp/InteropComponent.razor b/src/Components/test/testassets/BasicTestApp/InteropComponent.razor index d329d68900f1..b24da1366bd6 100644 --- a/src/Components/test/testassets/BasicTestApp/InteropComponent.razor +++ b/src/Components/test/testassets/BasicTestApp/InteropComponent.razor @@ -46,6 +46,8 @@

@SyncExceptionFromAsyncMethod?.Message

@nameof(AsyncExceptionFromAsyncMethod)

@AsyncExceptionFromAsyncMethod?.Message

+

@nameof(JSObjectReferenceInvokeNonFunctionException)

+

@JSObjectReferenceInvokeNonFunctionException?.Message

@if (DoneWithInterop) { @@ -59,6 +61,7 @@ public JSException ExceptionFromSyncMethod { get; set; } public JSException SyncExceptionFromAsyncMethod { get; set; } public JSException AsyncExceptionFromAsyncMethod { get; set; } + public JSException JSObjectReferenceInvokeNonFunctionException { get; set; } public IDictionary ReceiveDotNetObjectByRefResult { get; set; } = new Dictionary(); public IDictionary ReceiveDotNetObjectByRefAsyncResult { get; set; } = new Dictionary(); @@ -134,6 +137,28 @@ ReturnValues["returnArray"] = string.Join(",", ((IJSInProcessRuntime)JSRuntime).Invoke("returnArray").Select(x => x.Source).ToArray()); } + var jsObjectReference = await JSRuntime.InvokeAsync("returnJSObjectReference"); + ReturnValues["jsObjectReference.identity"] = await jsObjectReference.InvokeAsync("identity", "Invoked from JSObjectReference"); + ReturnValues["jsObjectReference.nested.add"] = (await jsObjectReference.InvokeAsync("nested.add", 2, 3)).ToString(); + ReturnValues["addViaJSObjectReference"] = (await JSRuntime.InvokeAsync("addViaJSObjectReference", jsObjectReference, 2, 3)).ToString(); + + try + { + await jsObjectReference.InvokeAsync("nonFunction"); + } + catch (JSException e) + { + JSObjectReferenceInvokeNonFunctionException = e; + } + + var module = await JSRuntime.InvokeAsync("import", "./js/testmodule.js"); + ReturnValues["jsObjectReferenceModule"] = await module.InvokeAsync("identity", "Returned from module!"); + + if (shouldSupportSyncInterop) + { + InvokeInProcessJSInterop(); + } + Invocations = invocations; DoneWithInterop = true; } @@ -163,6 +188,14 @@ ReceiveDotNetObjectByRefResult["testDto"] = result.TestDto.Value == passDotNetObjectByRef ? "Same" : "Different"; } + public void InvokeInProcessJSInterop() + { + var inProcRuntime = ((IJSInProcessRuntime)JSRuntime); + + var jsInProcObjectReference = inProcRuntime.Invoke("returnJSObjectReference"); + ReturnValues["jsInProcessObjectReference.identity"] = jsInProcObjectReference.Invoke("identity", "Invoked from JSInProcessObjectReference"); + } + public class PassDotNetObjectByRefArgs { public string StringValue { get; set; } diff --git a/src/Components/test/testassets/BasicTestApp/InteropTest/JavaScriptInterop.cs b/src/Components/test/testassets/BasicTestApp/InteropTest/JavaScriptInterop.cs index 18cefecd1520..8667bdc2c61f 100644 --- a/src/Components/test/testassets/BasicTestApp/InteropTest/JavaScriptInterop.cs +++ b/src/Components/test/testassets/BasicTestApp/InteropTest/JavaScriptInterop.cs @@ -414,6 +414,47 @@ public static int ExtractNonSerializedValue(DotNetObjectReference objec return objectByRef.Value.GetNonSerializedValue(); } + [JSInvokable] + public static JSObjectReference RoundTripJSObjectReference(JSObjectReference jsObjectReference) + { + return jsObjectReference; + } + + [JSInvokable] + public static async Task RoundTripJSObjectReferenceAsync(JSObjectReference jSObjectReference) + { + await Task.Yield(); + return jSObjectReference; + } + + [JSInvokable] + public static string InvokeDisposedJSObjectReferenceException(JSInProcessObjectReference jsObjectReference) + { + try + { + jsObjectReference.Invoke("noop"); + return "No exception thrown"; + } + catch (JSException e) + { + return e.Message; + } + } + + [JSInvokable] + public static async Task InvokeDisposedJSObjectReferenceExceptionAsync(JSObjectReference jsObjectReference) + { + try + { + await jsObjectReference.InvokeVoidAsync("noop"); + return "No exception thrown"; + } + catch (JSException e) + { + return e.Message; + } + } + [JSInvokable] public InstanceMethodOutput InstanceMethod(InstanceMethodInput input) { diff --git a/src/Components/test/testassets/BasicTestApp/wwwroot/js/jsinteroptests.js b/src/Components/test/testassets/BasicTestApp/wwwroot/js/jsinteroptests.js index dbbba1e0c87b..14ef4714c16e 100644 --- a/src/Components/test/testassets/BasicTestApp/wwwroot/js/jsinteroptests.js +++ b/src/Components/test/testassets/BasicTestApp/wwwroot/js/jsinteroptests.js @@ -30,6 +30,17 @@ async function invokeDotNetInteropMethodsAsync(shouldSupportSyncInterop, dotNetO var returnDotNetObjectByRefResult = DotNet.invokeMethod(assemblyName, 'ReturnDotNetObjectByRef'); results['resultReturnDotNetObjectByRefSync'] = DotNet.invokeMethod(assemblyName, 'ExtractNonSerializedValue', returnDotNetObjectByRefResult['Some sync instance']); + var jsObjectReference = DotNet.createJSObjectReference({ + prop: 'successful', + noop: function () { } + }); + + var returnedObject = DotNet.invokeMethod(assemblyName, 'RoundTripJSObjectReference', jsObjectReference); + results['roundTripJSObjectReference'] = returnedObject && returnedObject.prop; + + DotNet.disposeJSObjectReference(jsObjectReference); + results['invokeDisposedJSObjectReferenceException'] = DotNet.invokeMethod(assemblyName, 'InvokeDisposedJSObjectReferenceException', jsObjectReference); + var instanceMethodResult = instanceMethodsTarget.invokeMethod('InstanceMethod', { stringValue: 'My string', dtoByRef: dotNetObjectByRef @@ -66,6 +77,17 @@ async function invokeDotNetInteropMethodsAsync(shouldSupportSyncInterop, dotNetO const returnDotNetObjectByRefAsync = await DotNet.invokeMethodAsync(assemblyName, 'ReturnDotNetObjectByRefAsync'); results['resultReturnDotNetObjectByRefAsync'] = await DotNet.invokeMethodAsync(assemblyName, 'ExtractNonSerializedValue', returnDotNetObjectByRefAsync['Some async instance']); + var jsObjectReference = DotNet.createJSObjectReference({ + prop: 'successful', + noop: function () { } + }); + + var returnedObject = await DotNet.invokeMethodAsync(assemblyName, 'RoundTripJSObjectReferenceAsync', jsObjectReference); + results['roundTripJSObjectReferenceAsync'] = returnedObject && returnedObject.prop; + + DotNet.disposeJSObjectReference(jsObjectReference); + results['invokeDisposedJSObjectReferenceExceptionAsync'] = await DotNet.invokeMethodAsync(assemblyName, 'InvokeDisposedJSObjectReferenceExceptionAsync', jsObjectReference); + const instanceMethodAsync = await instanceMethodsTarget.invokeMethodAsync('InstanceMethodAsync', { stringValue: 'My string', dtoByRef: dotNetObjectByRef @@ -167,6 +189,8 @@ window.jsInteropTests = { asyncFunctionThrowsAsyncException: asyncFunctionThrowsAsyncException, returnPrimitive: returnPrimitive, returnPrimitiveAsync: returnPrimitiveAsync, + returnJSObjectReference: returnJSObjectReference, + addViaJSObjectReference: addViaJSObjectReference, receiveDotNetObjectByRef: receiveDotNetObjectByRef, receiveDotNetObjectByRefAsync: receiveDotNetObjectByRefAsync }; @@ -195,6 +219,27 @@ function returnArrayAsync() { }); } +function returnJSObjectReference() { + return { + identity: function (value) { + return value; + }, + nonFunction: 123, + nested: { + add: function (a, b) { + return a + b; + } + }, + dispose: function () { + DotNet.disposeJSObjectReference(this); + }, + }; +} + +function addViaJSObjectReference(jsObjectReference, a, b) { + return jsObjectReference.nested.add(a, b); +} + function functionThrowsException() { throw new Error('Function threw an exception!'); } @@ -258,4 +303,4 @@ function receiveDotNetObjectByRefAsync(incomingData) { testDto: testDto }; }); -} \ No newline at end of file +} diff --git a/src/Components/test/testassets/BasicTestApp/wwwroot/js/testmodule.js b/src/Components/test/testassets/BasicTestApp/wwwroot/js/testmodule.js new file mode 100644 index 000000000000..ab7a818ba8b8 --- /dev/null +++ b/src/Components/test/testassets/BasicTestApp/wwwroot/js/testmodule.js @@ -0,0 +1,3 @@ +export function identity(value) { + return value; +} diff --git a/src/Components/test/testassets/TestServer/Components.TestServer.csproj b/src/Components/test/testassets/TestServer/Components.TestServer.csproj index 651b301bece8..59b8fe56856b 100644 --- a/src/Components/test/testassets/TestServer/Components.TestServer.csproj +++ b/src/Components/test/testassets/TestServer/Components.TestServer.csproj @@ -22,10 +22,13 @@ - + + + + <_Parameter1>Microsoft.AspNetCore.Testing.BasicTestApp.ContentRoot diff --git a/src/Components/test/testassets/TestServer/MultipleComponents.cs b/src/Components/test/testassets/TestServer/MultipleComponents.cs index b48de346df70..3a3bf468a62a 100644 --- a/src/Components/test/testassets/TestServer/MultipleComponents.cs +++ b/src/Components/test/testassets/TestServer/MultipleComponents.cs @@ -32,6 +32,18 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) app.UseDeveloperExceptionPage(); } + app.Map("/Client/multiple-components", app => + { + app.UseBlazorFrameworkFiles(); + app.UseStaticFiles(); + app.UseRouting(); + app.UseEndpoints(endpoints => + { + endpoints.MapRazorPages(); + endpoints.MapFallbackToPage("/Client/MultipleComponents"); + }); + }); + app.Map("/multiple-components", app => { app.UseStaticFiles(); diff --git a/src/Components/test/testassets/TestServer/Pages/Client/MultipleComponents.cshtml b/src/Components/test/testassets/TestServer/Pages/Client/MultipleComponents.cshtml new file mode 100644 index 000000000000..0f7078e0e603 --- /dev/null +++ b/src/Components/test/testassets/TestServer/Pages/Client/MultipleComponents.cshtml @@ -0,0 +1,29 @@ +@page "/multiple-components" +@using BasicTestApp.MultipleComponents; + +@{ + Layout = "./MultipleComponentsLayout.cshtml"; +} + + +@(await Html.RenderComponentAsync(RenderMode.WebAssemblyPrerendered)) +@(await Html.RenderComponentAsync(RenderMode.WebAssembly)) + + +
+

Some content before

+ +

Some content between

+ +

Some content after

+
+

Some content before

+ + +

Some content after

+
+
+
+ + +
diff --git a/src/Components/test/testassets/TestServer/Pages/Client/MultipleComponentsLayout.cshtml b/src/Components/test/testassets/TestServer/Pages/Client/MultipleComponentsLayout.cshtml new file mode 100644 index 000000000000..e46aae2983eb --- /dev/null +++ b/src/Components/test/testassets/TestServer/Pages/Client/MultipleComponentsLayout.cshtml @@ -0,0 +1,45 @@ +@using BasicTestApp.MultipleComponents; + + + + + Multiple component entry points + @* We need to make sure base is set to "/" so that the libraries load correctly *@ + + @* This page is used to validate the ability to render multiple root components in a blazor webassembly application. + *@ + + +
+ + + @RenderBody() + + +
+ + @* + So that E2E tests can make assertions about both the prerendered and + interactive states, we only load the .js file when told to. + *@ +
+ + + + + + + + + diff --git a/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs b/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs index 9f97f03c4ef6..96f886e47446 100644 --- a/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs +++ b/src/Hosting/Hosting/src/GenericHost/GenericWebHostBuilder.cs @@ -29,13 +29,18 @@ internal class GenericWebHostBuilder : IWebHostBuilder, ISupportsStartup, ISuppo private AggregateException _hostingStartupErrors; private HostingStartupWebHostBuilder _hostingStartupWebHostBuilder; - public GenericWebHostBuilder(IHostBuilder builder) + public GenericWebHostBuilder(IHostBuilder builder, WebHostBuilderOptions options) { _builder = builder; + var configBuilder = new ConfigurationBuilder() + .AddInMemoryCollection(); - _config = new ConfigurationBuilder() - .AddEnvironmentVariables(prefix: "ASPNETCORE_") - .Build(); + if (!options.SuppressEnvironmentConfiguration) + { + configBuilder.AddEnvironmentVariables(prefix: "ASPNETCORE_"); + } + + _config = configBuilder.Build(); _builder.ConfigureHostConfiguration(config => { diff --git a/src/Hosting/Hosting/src/GenericHostWebHostBuilderExtensions.cs b/src/Hosting/Hosting/src/GenericHostWebHostBuilderExtensions.cs index feb6da2ca4ba..4899a8caf2b5 100644 --- a/src/Hosting/Hosting/src/GenericHostWebHostBuilderExtensions.cs +++ b/src/Hosting/Hosting/src/GenericHostWebHostBuilderExtensions.cs @@ -1,3 +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 Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; @@ -6,9 +9,37 @@ namespace Microsoft.Extensions.Hosting { public static class GenericHostWebHostBuilderExtensions { + /// + /// Adds and configures an ASP.NET Core web application. + /// public static IHostBuilder ConfigureWebHost(this IHostBuilder builder, Action configure) { - var webhostBuilder = new GenericWebHostBuilder(builder); + if (configure is null) + { + throw new ArgumentNullException(nameof(configure)); + } + + return builder.ConfigureWebHost(configure, _ => { }); + } + + /// + /// Adds and configures an ASP.NET Core web application. + /// + public static IHostBuilder ConfigureWebHost(this IHostBuilder builder, Action configure, Action configureWebHostBuilder) + { + if (configure is null) + { + throw new ArgumentNullException(nameof(configure)); + } + + if (configureWebHostBuilder is null) + { + throw new ArgumentNullException(nameof(configureWebHostBuilder)); + } + + var webHostBuilderOptions = new WebHostBuilderOptions(); + configureWebHostBuilder(webHostBuilderOptions); + var webhostBuilder = new GenericWebHostBuilder(builder, webHostBuilderOptions); configure(webhostBuilder); builder.ConfigureServices((context, services) => services.AddHostedService()); return builder; diff --git a/src/Hosting/Hosting/src/WebHostBuilderOptions.cs b/src/Hosting/Hosting/src/WebHostBuilderOptions.cs new file mode 100644 index 000000000000..7fe6caef09e3 --- /dev/null +++ b/src/Hosting/Hosting/src/WebHostBuilderOptions.cs @@ -0,0 +1,17 @@ +// 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.Extensions.Hosting +{ + /// + /// Builder options for use with ConfigureWebHost. + /// + public class WebHostBuilderOptions + { + /// + /// Indicates if "ASPNETCORE_" prefixed environment variables should be added to configuration. + /// They are added by default. + /// + public bool SuppressEnvironmentConfiguration { get; set; } = false; + } +} diff --git a/src/Hosting/Hosting/test/Fakes/GenericWebHostBuilderWrapper.cs b/src/Hosting/Hosting/test/Fakes/GenericWebHostBuilderWrapper.cs index b0bf122d1b33..a8660bedd57e 100644 --- a/src/Hosting/Hosting/test/Fakes/GenericWebHostBuilderWrapper.cs +++ b/src/Hosting/Hosting/test/Fakes/GenericWebHostBuilderWrapper.cs @@ -16,7 +16,7 @@ public class GenericWebHostBuilderWrapper : IWebHostBuilder, ISupportsStartup, I internal GenericWebHostBuilderWrapper(HostBuilder hostBuilder) { - _builder = new GenericWebHostBuilder(hostBuilder); + _builder = new GenericWebHostBuilder(hostBuilder, new WebHostBuilderOptions()); _hostBuilder = hostBuilder; } diff --git a/src/Hosting/Hosting/test/GenericWebHostBuilderTests.cs b/src/Hosting/Hosting/test/GenericWebHostBuilderTests.cs new file mode 100644 index 000000000000..7ac55b1aecb3 --- /dev/null +++ b/src/Hosting/Hosting/test/GenericWebHostBuilderTests.cs @@ -0,0 +1,41 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit; + +namespace Microsoft.AspNetCore.Hosting +{ + // Most functionality is covered by WebHostBuilderTests for compat. Only GenericHost specific functionality is covered here. + public class GenericWebHostBuilderTests + { + [Fact] + public void ReadsAspNetCoreEnvironmentVariables() + { + var randomEnvKey = Guid.NewGuid().ToString(); + Environment.SetEnvironmentVariable("ASPNETCORE_" + randomEnvKey, "true"); + using var host = new HostBuilder() + .ConfigureWebHost(_ => { }) + .Build(); + var config = host.Services.GetRequiredService(); + Assert.Equal("true", config[randomEnvKey]); + Environment.SetEnvironmentVariable("ASPNETCORE_" + randomEnvKey, null); + } + + [Fact] + public void CanSuppressAspNetCoreEnvironmentVariables() + { + var randomEnvKey = Guid.NewGuid().ToString(); + Environment.SetEnvironmentVariable("ASPNETCORE_" + randomEnvKey, "true"); + using var host = new HostBuilder() + .ConfigureWebHost(_ => { }, webHostBulderOptions => { webHostBulderOptions.SuppressEnvironmentConfiguration = true; }) + .Build(); + var config = host.Services.GetRequiredService(); + Assert.Null(config[randomEnvKey]); + Environment.SetEnvironmentVariable("ASPNETCORE_" + randomEnvKey, null); + } + } +} diff --git a/src/Hosting/Hosting/test/WebHostBuilderTests.cs b/src/Hosting/Hosting/test/WebHostBuilderTests.cs index d6b9a5f1a6dc..37ccbe5f21e3 100644 --- a/src/Hosting/Hosting/test/WebHostBuilderTests.cs +++ b/src/Hosting/Hosting/test/WebHostBuilderTests.cs @@ -6,19 +6,15 @@ using System.IO; using System.Linq; using System.Reflection; -using System.Runtime.ExceptionServices; using System.Threading; using System.Threading.Tasks; -using System.Web; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting.Fakes; using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Hosting.Tests.Fakes; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.Http.Features; -using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; diff --git a/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts b/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts index 298472bd820c..bea32595d364 100644 --- a/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts +++ b/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts @@ -6,9 +6,68 @@ export module DotNet { export type JsonReviver = ((key: any, value: any) => any); const jsonRevivers: JsonReviver[] = []; + class JSObject { + _cachedFunctions: Map; + + constructor(private _jsObject: any) + { + this._cachedFunctions = new Map(); + } + + public findFunction(identifier: string) { + const cachedFunction = this._cachedFunctions.get(identifier); + + if (cachedFunction) { + return cachedFunction; + } + + let result: any = this._jsObject; + let lastSegmentValue: any; + + identifier.split('.').forEach(segment => { + if (segment in result) { + lastSegmentValue = result; + result = result[segment]; + } else { + throw new Error(`Could not find '${identifier}' ('${segment}' was undefined).`); + } + }); + + if (result instanceof Function) { + result = result.bind(lastSegmentValue); + this._cachedFunctions.set(identifier, result); + return result; + } else { + throw new Error(`The value '${identifier}' is not a function.`); + } + } + + public getWrappedObject() { + return this._jsObject; + } + } + + const jsObjectIdKey = "__jsObjectId"; + const pendingAsyncCalls: { [id: number]: PendingAsyncCall } = {}; - const cachedJSFunctions: { [identifier: string]: Function } = {}; + const windowJSObjectId = 0; + const cachedJSObjectsById: { [id: number]: JSObject } = { + [windowJSObjectId]: new JSObject(window), + }; + + cachedJSObjectsById[windowJSObjectId]._cachedFunctions.set('import', (url: any) => { + // In most cases developers will want to resolve dynamic imports relative to the base HREF. + // However since we're the one calling the import keyword, they would be resolved relative to + // this framework bundle URL. Fix this by providing an absolute URL. + if (typeof url === 'string' && url.startsWith('./')) { + url = document.baseURI + url.substr(2); + } + + return import(/* webpackIgnore: true */ url); + }); + let nextAsyncCallId = 1; // Start at 1 because zero signals "no response needed" + let nextJsObjectId = 1; // Start at 1 because zero is reserved for "window" let dotNetDispatcher: DotNetCallDispatcher | null = null; @@ -55,6 +114,58 @@ export module DotNet { return invokePossibleInstanceMethodAsync(assemblyName, methodIdentifier, null, args); } + /** + * Creates a JavaScript object reference that can be passed to .NET via interop calls. + * + * @param jsObject The JavaScript Object used to create the JavaScript object reference. + * @returns The JavaScript object reference (this will be the same instance as the given object). + * @throws Error if the given value is not an Object. + */ + export function createJSObjectReference(jsObject: any): any { + if (jsObject && typeof jsObject === 'object') { + cachedJSObjectsById[nextJsObjectId] = new JSObject(jsObject); + + const result = { + [jsObjectIdKey]: nextJsObjectId + }; + + nextJsObjectId++; + + return result; + } else { + throw new Error(`Cannot create a JSObjectReference from the value '${jsObject}'.`); + } + } + + /** + * Disposes the given JavaScript object reference. + * + * @param jsObjectReference The JavaScript Object reference. + */ + export function disposeJSObjectReference(jsObjectReference: any): void { + const id = jsObjectReference && jsObjectReference[jsObjectIdKey]; + + if (typeof id === 'number') { + disposeJSObjectReferenceById(id); + } + } + + /** + * Parses the given JSON string using revivers to restore args passed from .NET to JS. + * + * @param json The JSON stirng to parse. + */ + export function parseJsonWithRevivers(json: string): any { + return json ? JSON.parse(json, (key, initialValue) => { + // Invoke each reviver in order, passing the output from the previous reviver, + // so that each one gets a chance to transform the value + return jsonRevivers.reduce( + (latestValue, reviver) => reviver(key, latestValue), + initialValue + ); + }) : null; + } + function invokePossibleInstanceMethod(assemblyName: string | null, methodIdentifier: string, dotNetObjectId: number | null, args: any[] | null): T { const dispatcher = getRequiredDispatcher(); if (dispatcher.invokeDotNetFromJS) { @@ -114,6 +225,14 @@ export module DotNet { reject: (reason?: any) => void; } + /** + * Represents the type of result expected from a JS interop call. + */ + export enum JSCallResultType { + Default = 0, + JSObjectReference = 1 + } + /** * Represents the ability to dispatch calls from JavaScript to a .NET runtime. */ @@ -158,19 +277,31 @@ export module DotNet { * Finds the JavaScript function matching the specified identifier. * * @param identifier Identifies the globally-reachable function to be returned. + * @param targetInstanceId The instance ID of the target JS object. * @returns A Function instance. */ findJSFunction, // Note that this is used by the JS interop code inside Mono WebAssembly itself + /** + * Disposes the JavaScript object reference with the specified object ID. + * + * @param id The ID of the JavaScript object reference. + */ + disposeJSObjectReferenceById, + /** * Invokes the specified synchronous JavaScript function. * * @param identifier Identifies the globally-reachable function to invoke. * @param argsJson JSON representation of arguments to be passed to the function. + * @param resultType The type of result expected from the JS interop call. + * @param targetInstanceId The instance ID of the target JS object. * @returns JSON representation of the invocation result. */ - invokeJSFromDotNet: (identifier: string, argsJson: string) => { - const result = findJSFunction(identifier).apply(null, parseJsonWithRevivers(argsJson)); + invokeJSFromDotNet: (identifier: string, argsJson: string, resultType: JSCallResultType, targetInstanceId: number) => { + const returnValue = findJSFunction(identifier, targetInstanceId).apply(null, parseJsonWithRevivers(argsJson)); + const result = createJSCallResult(returnValue, resultType); + return result === null || result === undefined ? null : JSON.stringify(result, argReplacer); @@ -182,12 +313,14 @@ export module DotNet { * @param asyncHandle A value identifying the asynchronous operation. This value will be passed back in a later call to endInvokeJSFromDotNet. * @param identifier Identifies the globally-reachable function to invoke. * @param argsJson JSON representation of arguments to be passed to the function. + * @param resultType The type of result expected from the JS interop call. + * @param targetInstanceId The ID of the target JS object instance. */ - beginInvokeJSFromDotNet: (asyncHandle: number, identifier: string, argsJson: string): void => { + beginInvokeJSFromDotNet: (asyncHandle: number, identifier: string, argsJson: string, resultType: JSCallResultType, targetInstanceId: number): void => { // Coerce synchronous functions into async ones, plus treat // synchronous exceptions the same as async ones const promise = new Promise(resolve => { - const synchronousResultOrPromise = findJSFunction(identifier).apply(null, parseJsonWithRevivers(argsJson)); + const synchronousResultOrPromise = findJSFunction(identifier, targetInstanceId).apply(null, parseJsonWithRevivers(argsJson)); resolve(synchronousResultOrPromise); }); @@ -196,7 +329,7 @@ export module DotNet { // On completion, dispatch result back to .NET // Not using "await" because it codegens a lot of boilerplate promise.then( - result => getRequiredDispatcher().endInvokeJSFromDotNet(asyncHandle, true, JSON.stringify([asyncHandle, true, result], argReplacer)), + result => getRequiredDispatcher().endInvokeJSFromDotNet(asyncHandle, true, JSON.stringify([asyncHandle, true, createJSCallResult(result, resultType)], argReplacer)), error => getRequiredDispatcher().endInvokeJSFromDotNet(asyncHandle, false, JSON.stringify([asyncHandle, false, formatError(error)])) ); } @@ -214,17 +347,6 @@ export module DotNet { } } - function parseJsonWithRevivers(json: string): any { - return json ? JSON.parse(json, (key, initialValue) => { - // Invoke each reviver in order, passing the output from the previous reviver, - // so that each one gets a chance to transform the value - return jsonRevivers.reduce( - (latestValue, reviver) => reviver(key, latestValue), - initialValue - ); - }) : null; - } - function formatError(error: any): string { if (error instanceof Error) { return `${error.message}\n${error.stack}`; @@ -233,33 +355,20 @@ export module DotNet { } } - function findJSFunction(identifier: string): Function { - if (Object.prototype.hasOwnProperty.call(cachedJSFunctions, identifier)) { - return cachedJSFunctions[identifier]; - } + function findJSFunction(identifier: string, targetInstanceId: number): Function { + let targetInstance = cachedJSObjectsById[targetInstanceId]; - let result: any = window; - let resultIdentifier = 'window'; - let lastSegmentValue: any; - identifier.split('.').forEach(segment => { - if (segment in result) { - lastSegmentValue = result; - result = result[segment]; - resultIdentifier += '.' + segment; - } else { - throw new Error(`Could not find '${segment}' in '${resultIdentifier}'.`); - } - }); - - if (result instanceof Function) { - result = result.bind(lastSegmentValue); - cachedJSFunctions[identifier] = result; - return result; + if (targetInstance) { + return targetInstance.findFunction(identifier); } else { - throw new Error(`The value '${resultIdentifier}' is not a function.`); + throw new Error(`JS object instance with ID ${targetInstanceId} does not exist (has it been disposed?).`); } } + function disposeJSObjectReferenceById(id: number) { + delete cachedJSObjectsById[id]; + } + class DotNetObject { constructor(private _id: number) { } @@ -292,6 +401,33 @@ export module DotNet { return value; }); + attachReviver(function reviveJSObjectReference(key: any, value: any) { + if (value && typeof value === 'object' && value.hasOwnProperty(jsObjectIdKey)) { + const id = value[jsObjectIdKey]; + const jsObject = cachedJSObjectsById[id]; + + if (jsObject) { + return jsObject.getWrappedObject(); + } else { + throw new Error(`JS object instance with ID ${id} does not exist (has it been disposed?).`); + } + } + + // Unrecognized - let another reviver handle it + return value; + }); + + function createJSCallResult(returnValue: any, resultType: JSCallResultType) { + switch (resultType) { + case JSCallResultType.Default: + return returnValue; + case JSCallResultType.JSObjectReference: + return createJSObjectReference(returnValue); + default: + throw new Error(`Invalid JS call result type '${resultType}'.`); + } + } + function argReplacer(key: string, value: any) { return value instanceof DotNetObject ? value.serializeAsArg() : value; } diff --git a/src/JSInterop/Microsoft.JSInterop.JS/src/tsconfig.json b/src/JSInterop/Microsoft.JSInterop.JS/src/tsconfig.json index f5a2b0e31a92..9393c5bbc31e 100644 --- a/src/JSInterop/Microsoft.JSInterop.JS/src/tsconfig.json +++ b/src/JSInterop/Microsoft.JSInterop.JS/src/tsconfig.json @@ -8,7 +8,8 @@ "lib": ["es2015", "dom", "es2015.promise"], "strict": true, "declaration": true, - "outDir": "dist" + "outDir": "dist", + "module": "ESNext", }, "include": [ "src/**/*.ts" diff --git a/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/JSObjectReferenceJsonConverter.cs b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/JSObjectReferenceJsonConverter.cs new file mode 100644 index 000000000000..f26efa92a138 --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/JSObjectReferenceJsonConverter.cs @@ -0,0 +1,59 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Microsoft.JSInterop.Infrastructure +{ + internal sealed class JSObjectReferenceJsonConverter + : JsonConverter where TJSObjectReference : JSObjectReference + { + private readonly Func _jsObjectReferenceFactory; + + public JSObjectReferenceJsonConverter(Func jsObjectReferenceFactory) + { + _jsObjectReferenceFactory = jsObjectReferenceFactory; + } + + public override TJSObjectReference? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + long id = -1; + + while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) + { + if (reader.TokenType == JsonTokenType.PropertyName) + { + if (id == -1 && reader.ValueTextEquals(JSObjectReference.IdKey.EncodedUtf8Bytes)) + { + reader.Read(); + id = reader.GetInt64(); + } + else + { + throw new JsonException($"Unexcepted JSON property {reader.GetString()}."); + } + } + else + { + throw new JsonException($"Unexcepted JSON token {reader.TokenType}"); + } + } + + if (id == -1) + { + throw new JsonException($"Required property {JSObjectReference.IdKey} not found."); + } + + return _jsObjectReferenceFactory(id); + } + + public override void Write(Utf8JsonWriter writer, TJSObjectReference value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + writer.WriteNumber(JSObjectReference.IdKey, value.Id); + writer.WriteEndObject(); + } + } +} diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSCallResultType.cs b/src/JSInterop/Microsoft.JSInterop/src/JSCallResultType.cs new file mode 100644 index 000000000000..1268113dc71f --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/src/JSCallResultType.cs @@ -0,0 +1,21 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.JSInterop +{ + /// + /// Describes the type of result expected from a JS interop call. + /// + public enum JSCallResultType : int + { + /// + /// Indicates that the returned value is not treated in a special way. + /// + Default = 0, + + /// + /// Indicates that the returned value is to be treated as a JS object reference. + /// + JSObjectReference = 1, + } +} diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSInProcessObjectReference.cs b/src/JSInterop/Microsoft.JSInterop/src/JSInProcessObjectReference.cs new file mode 100644 index 000000000000..bd97a8f7a071 --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/src/JSInProcessObjectReference.cs @@ -0,0 +1,35 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Diagnostics.CodeAnalysis; + +namespace Microsoft.JSInterop +{ + /// + /// Represents a reference to a JavaScript object whose functions can be invoked synchronously. + /// + public class JSInProcessObjectReference : JSObjectReference + { + private readonly JSInProcessRuntime _jsRuntime; + + internal JSInProcessObjectReference(JSInProcessRuntime jsRuntime, long id) : base(jsRuntime, id) + { + _jsRuntime = jsRuntime; + } + + /// + /// Invokes the specified JavaScript function synchronously. + /// + /// The JSON-serializable return type. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function someScope.someFunction on the target instance. + /// JSON-serializable arguments. + /// An instance of obtained by JSON-deserializing the return value. + [return: MaybeNull] + public TValue Invoke(string identifier, params object[] args) + { + ThrowIfDisposed(); + + return _jsRuntime.Invoke(identifier, Id, args); + } + } +} diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntime.cs b/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntime.cs index 04ace4eb3ada..092f29533058 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntime.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSInProcessRuntime.cs @@ -3,6 +3,7 @@ using System.Diagnostics.CodeAnalysis; using System.Text.Json; +using Microsoft.JSInterop.Infrastructure; namespace Microsoft.JSInterop { @@ -12,16 +13,23 @@ namespace Microsoft.JSInterop public abstract class JSInProcessRuntime : JSRuntime, IJSInProcessRuntime { /// - /// Invokes the specified JavaScript function synchronously. + /// Initializes a new instance of . /// - /// The JSON-serializable return type. - /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. - /// JSON-serializable arguments. - /// An instance of obtained by JSON-deserializing the return value. + protected JSInProcessRuntime() + { + JsonSerializerOptions.Converters.Add(new JSObjectReferenceJsonConverter( + id => new JSInProcessObjectReference(this, id))); + } + [return: MaybeNull] - public TValue Invoke(string identifier, params object?[]? args) + internal TValue Invoke(string identifier, long targetInstanceId, params object?[]? args) { - var resultJson = InvokeJS(identifier, JsonSerializer.Serialize(args, JsonSerializerOptions)); + var resultJson = InvokeJS( + identifier, + JsonSerializer.Serialize(args, JsonSerializerOptions), + ResultTypeFromGeneric(), + targetInstanceId); + if (resultJson is null) { return default; @@ -30,12 +38,34 @@ public TValue Invoke(string identifier, params object?[]? args) return JsonSerializer.Deserialize(resultJson, JsonSerializerOptions); } + /// + /// Invokes the specified JavaScript function synchronously. + /// + /// The JSON-serializable return type. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. + /// JSON-serializable arguments. + /// An instance of obtained by JSON-deserializing the return value. + [return: MaybeNull] + public TValue Invoke(string identifier, params object?[]? args) + => Invoke(identifier, 0, args); + + /// + /// Performs a synchronous function invocation. + /// + /// The identifier for the function to invoke. + /// A JSON representation of the arguments. + /// A JSON representation of the result. + protected virtual string? InvokeJS(string identifier, string? argsJson) + => InvokeJS(identifier, argsJson, JSCallResultType.Default, 0); + /// /// Performs a synchronous function invocation. /// /// The identifier for the function to invoke. /// A JSON representation of the arguments. + /// The type of result expected from the invocation. + /// The instance ID of the target JS object. /// A JSON representation of the result. - protected abstract string? InvokeJS(string identifier, string? argsJson); + protected abstract string? InvokeJS(string identifier, string? argsJson, JSCallResultType resultType, long targetInstanceId); } } diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSObjectReference.cs b/src/JSInterop/Microsoft.JSInterop/src/JSObjectReference.cs new file mode 100644 index 000000000000..ef98c2dfc924 --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/src/JSObjectReference.cs @@ -0,0 +1,103 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.JSInterop +{ + /// + /// Represents a reference to a JavaScript object. + /// + public class JSObjectReference : IAsyncDisposable + { + internal static readonly JsonEncodedText IdKey = JsonEncodedText.Encode("__jsObjectId"); + + private readonly JSRuntime _jsRuntime; + + private bool _disposed; + + internal long Id { get; } + + internal JSObjectReference(JSRuntime jsRuntime, long id) + { + _jsRuntime = jsRuntime; + + Id = id; + } + + /// + /// Invokes the specified JavaScript function asynchronously. + /// + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function someScope.someFunction on the target instance. + /// JSON-serializable arguments. + /// A that represents the asynchronous invocation operation. + public async ValueTask InvokeVoidAsync(string identifier, params object[] args) + { + await InvokeAsync(identifier, args); + } + + /// + /// Invokes the specified JavaScript function asynchronously. + /// + /// will apply timeouts to this operation based on the value configured in . To dispatch a call with a different, or no timeout, + /// consider using . + /// + /// + /// The JSON-serializable return type. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function someScope.someFunction on the target instance. + /// JSON-serializable arguments. + /// An instance of obtained by JSON-deserializing the return value. + public ValueTask InvokeAsync(string identifier, params object[] args) + { + ThrowIfDisposed(); + + return _jsRuntime.InvokeAsync(Id, identifier, args); + } + + /// + /// Invokes the specified JavaScript function asynchronously. + /// + /// The JSON-serializable return type. + /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function someScope.someFunction on the target instance. + /// + /// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts + /// () from being applied. + /// + /// JSON-serializable arguments. + /// An instance of obtained by JSON-deserializing the return value. + public ValueTask InvokeAsync(string identifier, CancellationToken cancellationToken, params object[] args) + { + ThrowIfDisposed(); + + return _jsRuntime.InvokeAsync(Id, identifier, cancellationToken, args); + } + + /// + /// Disposes the , freeing its resources and disabling it from further use. + /// + /// A representing the completion of the operation. + public async ValueTask DisposeAsync() + { + if (!_disposed) + { + _disposed = true; + + await _jsRuntime.InvokeVoidAsync("DotNet.jsCallDispatcher.disposeJSObjectReferenceById", Id); + } + } + + /// + /// Throws an exception if this instance has been disposed. + /// + protected void ThrowIfDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(GetType().Name); + } + } + } +} diff --git a/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs b/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs index ff11668dd76f..382e842df806 100644 --- a/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs +++ b/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs @@ -37,6 +37,7 @@ protected JSRuntime() Converters = { new DotNetObjectReferenceJsonConverterFactory(this), + new JSObjectReferenceJsonConverter(id => new JSObjectReference(this, id)), } }; } @@ -51,6 +52,17 @@ protected JSRuntime() /// protected TimeSpan? DefaultAsyncTimeout { get; set; } + /// + /// Creates a from the given generic type. + /// + /// + /// The type of the result of the relevant JS interop call. + /// + protected static JSCallResultType ResultTypeFromGeneric() + => typeof(TResult) == typeof(JSObjectReference) || typeof(TResult) == typeof(JSInProcessObjectReference) ? + JSCallResultType.JSObjectReference : + JSCallResultType.Default; + /// /// Invokes the specified JavaScript function asynchronously. /// @@ -62,17 +74,8 @@ protected JSRuntime() /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. /// JSON-serializable arguments. /// An instance of obtained by JSON-deserializing the return value. - public async ValueTask InvokeAsync(string identifier, object?[]? args) - { - if (DefaultAsyncTimeout.HasValue) - { - using var cts = new CancellationTokenSource(DefaultAsyncTimeout.Value); - // We need to await here due to the using - return await InvokeAsync(identifier, cts.Token, args); - } - - return await InvokeAsync(identifier, CancellationToken.None, args); - } + public ValueTask InvokeAsync(string identifier, object?[]? args) + => InvokeAsync(0, identifier, args); /// /// Invokes the specified JavaScript function asynchronously. @@ -81,11 +84,30 @@ public async ValueTask InvokeAsync(string identifier, object?[]? /// An identifier for the function to invoke. For example, the value "someScope.someFunction" will invoke the function window.someScope.someFunction. /// /// A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts - /// () from being applied. + /// () from being applied. /// /// JSON-serializable arguments. /// An instance of obtained by JSON-deserializing the return value. public ValueTask InvokeAsync(string identifier, CancellationToken cancellationToken, object?[]? args) + => InvokeAsync(0, identifier, cancellationToken, args); + + internal async ValueTask InvokeAsync(long targetInstanceId, string identifier, object?[]? args) + { + if (DefaultAsyncTimeout.HasValue) + { + using var cts = new CancellationTokenSource(DefaultAsyncTimeout.Value); + // We need to await here due to the using + return await InvokeAsync(targetInstanceId, identifier, cts.Token, args); + } + + return await InvokeAsync(targetInstanceId, identifier, CancellationToken.None, args); + } + + internal ValueTask InvokeAsync( + long targetInstanceId, + string identifier, + CancellationToken cancellationToken, + object?[]? args) { var taskId = Interlocked.Increment(ref _nextPendingTaskId); var tcs = new TaskCompletionSource(TaskContinuationOptions.RunContinuationsAsynchronously); @@ -112,7 +134,9 @@ public ValueTask InvokeAsync(string identifier, CancellationToke var argsJson = args?.Any() == true ? JsonSerializer.Serialize(args, JsonSerializerOptions) : null; - BeginInvokeJS(taskId, identifier, argsJson); + var resultType = ResultTypeFromGeneric(); + + BeginInvokeJS(taskId, identifier, argsJson, resultType, targetInstanceId); return new ValueTask(tcs.Task); } @@ -138,7 +162,18 @@ private void CleanupTasksAndRegistrations(long taskId) /// The identifier for the function invocation, or zero if no async callback is required. /// The identifier for the function to invoke. /// A JSON representation of the arguments. - protected abstract void BeginInvokeJS(long taskId, string identifier, string? argsJson); + protected virtual void BeginInvokeJS(long taskId, string identifier, string? argsJson) + => BeginInvokeJS(taskId, identifier, argsJson, JSCallResultType.Default, 0); + + /// + /// Begins an asynchronous function invocation. + /// + /// The identifier for the function invocation, or zero if no async callback is required. + /// The identifier for the function to invoke. + /// A JSON representation of the arguments. + /// The type of result expected from the invocation. + /// The instance ID of the target JS object. + protected abstract void BeginInvokeJS(long taskId, string identifier, string? argsJson, JSCallResultType resultType, long targetInstanceId); /// /// Completes an async JS interop call from JavaScript to .NET diff --git a/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetDispatcherTest.cs b/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetDispatcherTest.cs index 9ffbefe864a6..ddcfe142461f 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetDispatcherTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/DotNetDispatcherTest.cs @@ -885,7 +885,7 @@ public class TestJSRuntime : JSInProcessRuntime public string LastCompletionCallId { get; private set; } public DotNetInvocationResult LastCompletionResult { get; private set; } - protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson) + protected override void BeginInvokeJS(long asyncHandle, string identifier, string argsJson, JSCallResultType resultType, long targetInstanceId) { LastInvocationAsyncHandle = asyncHandle; LastInvocationIdentifier = identifier; @@ -894,7 +894,7 @@ protected override void BeginInvokeJS(long asyncHandle, string identifier, strin _nextInvocationTcs = new TaskCompletionSource(); } - protected override string InvokeJS(string identifier, string argsJson) + protected override string InvokeJS(string identifier, string argsJson, JSCallResultType resultType, long targetInstanceId) { LastInvocationAsyncHandle = default; LastInvocationIdentifier = identifier; diff --git a/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/JSObjectReferenceJsonConverterTest.cs b/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/JSObjectReferenceJsonConverterTest.cs new file mode 100644 index 000000000000..75f4388c15a6 --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/test/Infrastructure/JSObjectReferenceJsonConverterTest.cs @@ -0,0 +1,85 @@ +// 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.JSInterop.Infrastructure +{ + public class JSObjectReferenceJsonConverterTest + { + private readonly JSRuntime JSRuntime = new TestJSRuntime(); + private JsonSerializerOptions JsonSerializerOptions => JSRuntime.JsonSerializerOptions; + + [Fact] + public void Read_Throws_IfJsonIsMissingJSObjectIdProperty() + { + // Arrange + var json = "{}"; + + // Act & Assert + var ex = Assert.Throws(() => JsonSerializer.Deserialize(json, JsonSerializerOptions)); + Assert.Equal("Required property __jsObjectId not found.", ex.Message); + } + + [Fact] + public void Read_Throws_IfJsonContainsUnknownContent() + { + // Arrange + var json = "{\"foo\":2}"; + + // Act & Assert + var ex = Assert.Throws(() => JsonSerializer.Deserialize(json, JsonSerializerOptions)); + Assert.Equal("Unexcepted JSON property foo.", ex.Message); + } + + [Fact] + public void Read_Throws_IfJsonIsIncomplete() + { + // Arrange + var json = $"{{\"__jsObjectId\":5"; + + // Act & Assert + var ex = Record.Exception(() => JsonSerializer.Deserialize(json, JsonSerializerOptions)); + Assert.IsAssignableFrom(ex); + } + + [Fact] + public void Read_Throws_IfJSObjectIdAppearsMultipleTimes() + { + // Arrange + var json = $"{{\"__jsObjectId\":3,\"__jsObjectId\":7}}"; + + // Act & Assert + var ex = Record.Exception(() => JsonSerializer.Deserialize(json, JsonSerializerOptions)); + Assert.IsAssignableFrom(ex); + } + + [Fact] + public void Read_ReadsJson() + { + // Arrange + var expectedId = 3; + var json = $"{{\"__jsObjectId\":{expectedId}}}"; + + // Act + var deserialized = JsonSerializer.Deserialize(json, JsonSerializerOptions)!; + + // Assert + Assert.Equal(expectedId, deserialized?.Id); + } + + [Fact] + public void Write_WritesValidJson() + { + // Arrange + var jsObjectRef = new JSObjectReference(JSRuntime, 7); + + // Act + var json = JsonSerializer.Serialize(jsObjectRef, JsonSerializerOptions); + + // Assert + Assert.Equal($"{{\"__jsObjectId\":{jsObjectRef.Id}}}", json); + } + } +} diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeTest.cs index 7d6f34124037..475752aa6fd3 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JSInProcessRuntimeTest.cs @@ -99,7 +99,7 @@ class TestJSInProcessRuntime : JSInProcessRuntime public string? NextResultJson { get; set; } - protected override string? InvokeJS(string identifier, string? argsJson) + protected override string? InvokeJS(string identifier, string? argsJson, JSCallResultType resultType, long targetInstanceId) { InvokeCalls.Add(new InvokeArgs { Identifier = identifier, ArgsJson = argsJson }); return NextResultJson; @@ -111,7 +111,7 @@ public class InvokeArgs public string? ArgsJson { get; set; } } - protected override void BeginInvokeJS(long asyncHandle, string identifier, string? argsJson) + protected override void BeginInvokeJS(long asyncHandle, string identifier, string? argsJson, JSCallResultType resultType, long targetInstanceId) => throw new NotImplementedException("This test only covers sync calls"); protected internal override void EndInvokeDotNet(DotNetInvocationInfo invocationInfo, in DotNetInvocationResult invocationResult) diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSObjectReferenceTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSObjectReferenceTest.cs new file mode 100644 index 000000000000..2eb4f57fa935 --- /dev/null +++ b/src/JSInterop/Microsoft.JSInterop/test/JSObjectReferenceTest.cs @@ -0,0 +1,105 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.JSInterop.Infrastructure; +using Xunit; + +namespace Microsoft.JSInterop.Tests +{ + public class JSObjectReferenceTest + { + [Fact] + public void JSObjectReference_InvokeAsync_CallsUnderlyingJSRuntimeInvokeAsync() + { + // Arrange + var jsRuntime = new TestJSRuntime(); + var jsObject = new JSObjectReference(jsRuntime, 0); + + // Act + _ = jsObject.InvokeAsync("test", "arg1", "arg2"); + + // Assert + Assert.Equal(1, jsRuntime.BeginInvokeJSInvocationCount); + } + + [Fact] + public void JSInProcessObjectReference_Invoke_CallsUnderlyingJSRuntimeInvoke() + { + // Arrange + var jsRuntime = new TestJSInProcessRuntime(); + var jsObject = new JSInProcessObjectReference(jsRuntime, 0); + + // Act + jsObject.Invoke("test", "arg1", "arg2"); + + // Assert + Assert.Equal(1, jsRuntime.InvokeJSInvocationCount); + } + + [Fact] + public async Task JSObjectReference_Dispose_DisallowsFurtherInteropCalls() + { + // Arrange + var jsRuntime = new TestJSRuntime(); + var jsObject = new JSObjectReference(jsRuntime, 0); + + // Act + _ = jsObject.DisposeAsync(); + + // Assert + await Assert.ThrowsAsync(async () => await jsObject.InvokeAsync("test", "arg1", "arg2")); + await Assert.ThrowsAsync(async () => await jsObject.InvokeAsync("test", CancellationToken.None, "arg1", "arg2")); + } + + [Fact] + public void JSInProcessObjectReference_Dispose_DisallowsFurtherInteropCalls() + { + // Arrange + var jsRuntime = new TestJSInProcessRuntime(); + var jsObject = new JSInProcessObjectReference(jsRuntime, 0); + + // Act + _ = jsObject.DisposeAsync(); + + // Assert + Assert.Throws(() => jsObject.Invoke("test", "arg1", "arg2")); + } + + class TestJSRuntime : JSRuntime + { + public int BeginInvokeJSInvocationCount { get; private set; } + + protected override void BeginInvokeJS(long taskId, string identifier, string? argsJson, JSCallResultType resultType, long targetInstanceId) + { + BeginInvokeJSInvocationCount++; + } + + protected internal override void EndInvokeDotNet(DotNetInvocationInfo invocationInfo, in DotNetInvocationResult invocationResult) + { + } + } + + class TestJSInProcessRuntime : JSInProcessRuntime + { + public int InvokeJSInvocationCount { get; private set; } + + protected override void BeginInvokeJS(long taskId, string identifier, string? argsJson, JSCallResultType resultType, long targetInstanceId) + { + } + + protected override string? InvokeJS(string identifier, string? argsJson, JSCallResultType resultType, long targetInstanceId) + { + InvokeJSInvocationCount++; + + return null; + } + + protected internal override void EndInvokeDotNet(DotNetInvocationInfo invocationInfo, in DotNetInvocationResult invocationResult) + { + } + } + } +} diff --git a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs index 7710ba0e8e38..543b872ab715 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/JSRuntimeTest.cs @@ -377,7 +377,7 @@ protected internal override void EndInvokeDotNet(DotNetInvocationInfo invocation }); } - protected override void BeginInvokeJS(long asyncHandle, string identifier, string? argsJson) + protected override void BeginInvokeJS(long asyncHandle, string identifier, string? argsJson, JSCallResultType resultType, long targetInstanceId) { BeginInvokeCalls.Add(new BeginInvokeAsyncArgs { diff --git a/src/JSInterop/Microsoft.JSInterop/test/TestJSRuntime.cs b/src/JSInterop/Microsoft.JSInterop/test/TestJSRuntime.cs index d2e9cc3ba69b..b0e694c6f920 100644 --- a/src/JSInterop/Microsoft.JSInterop/test/TestJSRuntime.cs +++ b/src/JSInterop/Microsoft.JSInterop/test/TestJSRuntime.cs @@ -8,7 +8,7 @@ namespace Microsoft.JSInterop { internal class TestJSRuntime : JSRuntime { - protected override void BeginInvokeJS(long asyncHandle, string identifier, string? argsJson) + protected override void BeginInvokeJS(long asyncHandle, string identifier, string? argsJson, JSCallResultType resultType, long targetInstanceId) { throw new NotImplementedException(); } diff --git a/src/Mvc/Mvc.Core/src/ModelBinding/FormFileValueProviderFactory.cs b/src/Mvc/Mvc.Core/src/ModelBinding/FormFileValueProviderFactory.cs index df90de503923..a1c46eeba355 100644 --- a/src/Mvc/Mvc.Core/src/ModelBinding/FormFileValueProviderFactory.cs +++ b/src/Mvc/Mvc.Core/src/ModelBinding/FormFileValueProviderFactory.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; @@ -42,6 +42,14 @@ private static async Task AddValueProviderAsync(ValueProviderFactoryContext cont } catch (InvalidDataException ex) { + // ReadFormAsync can throw InvalidDataException if the form content is malformed. + // Wrap it in a ValueProviderException that the CompositeValueProvider special cases. + throw new ValueProviderException(Resources.FormatFailedToReadRequestForm(ex.Message), ex); + } + catch (IOException ex) + { + // ReadFormAsync can throw IOException if the client disconnects. + // Wrap it in a ValueProviderException that the CompositeValueProvider special cases. throw new ValueProviderException(Resources.FormatFailedToReadRequestForm(ex.Message), ex); } diff --git a/src/Mvc/Mvc.Core/src/ModelBinding/FormValueProviderFactory.cs b/src/Mvc/Mvc.Core/src/ModelBinding/FormValueProviderFactory.cs index e7cbed8e1199..d5a3686073c6 100644 --- a/src/Mvc/Mvc.Core/src/ModelBinding/FormValueProviderFactory.cs +++ b/src/Mvc/Mvc.Core/src/ModelBinding/FormValueProviderFactory.cs @@ -44,6 +44,14 @@ private static async Task AddValueProviderAsync(ValueProviderFactoryContext cont } catch (InvalidDataException ex) { + // ReadFormAsync can throw InvalidDataException if the form content is malformed. + // Wrap it in a ValueProviderException that the CompositeValueProvider special cases. + throw new ValueProviderException(Resources.FormatFailedToReadRequestForm(ex.Message), ex); + } + catch (IOException ex) + { + // ReadFormAsync can throw IOException if the client disconnects. + // Wrap it in a ValueProviderException that the CompositeValueProvider special cases. throw new ValueProviderException(Resources.FormatFailedToReadRequestForm(ex.Message), ex); } diff --git a/src/Mvc/Mvc.Core/src/ModelBinding/JQueryFormValueProviderFactory.cs b/src/Mvc/Mvc.Core/src/ModelBinding/JQueryFormValueProviderFactory.cs index fdd8eb2d9370..d2fa89064aa6 100644 --- a/src/Mvc/Mvc.Core/src/ModelBinding/JQueryFormValueProviderFactory.cs +++ b/src/Mvc/Mvc.Core/src/ModelBinding/JQueryFormValueProviderFactory.cs @@ -3,7 +3,10 @@ using System; using System.Globalization; +using System.IO; using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Core; namespace Microsoft.AspNetCore.Mvc.ModelBinding { @@ -34,7 +37,23 @@ private static async Task AddValueProviderAsync(ValueProviderFactoryContext cont { var request = context.ActionContext.HttpContext.Request; - var formCollection = await request.ReadFormAsync(); + IFormCollection formCollection; + try + { + formCollection = await request.ReadFormAsync(); + } + catch (InvalidDataException ex) + { + // ReadFormAsync can throw InvalidDataException if the form content is malformed. + // Wrap it in a ValueProviderException that the CompositeValueProvider special cases. + throw new ValueProviderException(Resources.FormatFailedToReadRequestForm(ex.Message), ex); + } + catch (IOException ex) + { + // ReadFormAsync can throw IOException if the client disconnects. + // Wrap it in a ValueProviderException that the CompositeValueProvider special cases. + throw new ValueProviderException(Resources.FormatFailedToReadRequestForm(ex.Message), ex); + } var valueProvider = new JQueryFormValueProvider( BindingSource.Form, diff --git a/src/Mvc/Mvc.Core/test/ModelBinding/CompositeValueProviderTest.cs b/src/Mvc/Mvc.Core/test/ModelBinding/CompositeValueProviderTest.cs index 43b49ce16c20..cdf2f80b4725 100644 --- a/src/Mvc/Mvc.Core/test/ModelBinding/CompositeValueProviderTest.cs +++ b/src/Mvc/Mvc.Core/test/ModelBinding/CompositeValueProviderTest.cs @@ -5,7 +5,10 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Threading.Tasks; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Abstractions; +using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Primitives; using Moq; using Xunit; @@ -45,6 +48,25 @@ protected override IEnumerableValueProvider GetEnumerableValueProvider( return new CompositeValueProvider() { emptyValueProvider, valueProvider }; } + [Fact] + public async Task TryCreateAsync_AddsModelStateError_WhenValueProviderFactoryThrowsValueProviderException() + { + // Arrange + var factory = new Mock(); + factory.Setup(f => f.CreateValueProviderAsync(It.IsAny())).ThrowsAsync(new ValueProviderException("Some error")); + var actionContext = new ActionContext(new DefaultHttpContext(), new RouteData(), new ActionDescriptor(), new ModelStateDictionary()); + + // Act + var (success, result) = await CompositeValueProvider.TryCreateAsync(actionContext, new[] { factory.Object }); + + // Assert + Assert.False(success); + var modelState = actionContext.ModelState; + Assert.False(modelState.IsValid); + var entry = Assert.Single(modelState); + Assert.Empty(entry.Key); + } + [Fact] public void GetKeysFromPrefixAsync_ReturnsResultFromFirstValueProviderThatReturnsValues() { diff --git a/src/Mvc/Mvc.Core/test/ModelBinding/FormFileValueProviderFactoryTest.cs b/src/Mvc/Mvc.Core/test/ModelBinding/FormFileValueProviderFactoryTest.cs index c0030a0ba522..9926fc19348e 100644 --- a/src/Mvc/Mvc.Core/test/ModelBinding/FormFileValueProviderFactoryTest.cs +++ b/src/Mvc/Mvc.Core/test/ModelBinding/FormFileValueProviderFactoryTest.cs @@ -1,13 +1,16 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Collections.Generic; using System.IO; +using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Primitives; +using Moq; using Xunit; namespace Microsoft.AspNetCore.Mvc.ModelBinding @@ -60,6 +63,59 @@ public async Task CreateValueProviderAsync_AddsValueProvider() v => Assert.IsType(v)); } + [Fact] + public async Task GetValueProviderAsync_ThrowsValueProviderException_IfReadingFormThrowsInvalidDataException() + { + // Arrange + var exception = new InvalidDataException(); + var valueProviderContext = CreateThrowingContext(exception); + + var factory = new FormFileValueProviderFactory(); + + // Act & Assert + var ex = await Assert.ThrowsAsync(() => factory.CreateValueProviderAsync(valueProviderContext)); + Assert.Same(exception, ex.InnerException); + } + + [Fact] + public async Task GetValueProviderAsync_ThrowsValueProviderException_IfReadingFormThrowsInvalidOperationException() + { + // Arrange + var exception = new IOException(); + var valueProviderContext = CreateThrowingContext(exception); + + var factory = new FormFileValueProviderFactory(); + + // Act & Assert + var ex = await Assert.ThrowsAsync(() => factory.CreateValueProviderAsync(valueProviderContext)); + Assert.Same(exception, ex.InnerException); + } + + [Fact] + public async Task GetValueProviderAsync_ThrowsOriginalException_IfReadingFormThrows() + { + // Arrange + var exception = new TimeZoneNotFoundException(); + var valueProviderContext = CreateThrowingContext(exception); + + var factory = new FormFileValueProviderFactory(); + + // Act & Assert + var ex = await Assert.ThrowsAsync(() => factory.CreateValueProviderAsync(valueProviderContext)); + Assert.Same(exception, ex); + } + + private static ValueProviderFactoryContext CreateThrowingContext(Exception exception) + { + var context = new Mock(); + context.Setup(c => c.Request.ContentType).Returns("application/x-www-form-urlencoded"); + context.Setup(c => c.Request.HasFormContentType).Returns(true); + context.Setup(c => c.Request.ReadFormAsync(It.IsAny())).ThrowsAsync(exception); + var actionContext = new ActionContext(context.Object, new RouteData(), new ActionDescriptor()); + var valueProviderContext = new ValueProviderFactoryContext(actionContext); + return valueProviderContext; + } + private static ValueProviderFactoryContext CreateContext(string contentType) { var context = new DefaultHttpContext(); diff --git a/src/Mvc/Mvc.Core/test/ModelBinding/FormValueProviderFactoryTest.cs b/src/Mvc/Mvc.Core/test/ModelBinding/FormValueProviderFactoryTest.cs index 09aff4b20636..53873b0ee6fc 100644 --- a/src/Mvc/Mvc.Core/test/ModelBinding/FormValueProviderFactoryTest.cs +++ b/src/Mvc/Mvc.Core/test/ModelBinding/FormValueProviderFactoryTest.cs @@ -1,13 +1,17 @@ // 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.Globalization; +using System.IO; +using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Primitives; +using Moq; using Xunit; namespace Microsoft.AspNetCore.Mvc.ModelBinding.Test @@ -47,6 +51,59 @@ public async Task GetValueProviderAsync_ReturnsValueProvider_WithCurrentCulture( Assert.Equal(CultureInfo.CurrentCulture, valueProvider.Culture); } + [Fact] + public async Task GetValueProviderAsync_ThrowsValueProviderException_IfReadingFormThrowsInvalidDataException() + { + // Arrange + var exception = new InvalidDataException(); + var valueProviderContext = CreateThrowingContext(exception); + + var factory = new FormValueProviderFactory(); + + // Act & Assert + var ex = await Assert.ThrowsAsync(() => factory.CreateValueProviderAsync(valueProviderContext)); + Assert.Same(exception, ex.InnerException); + } + + [Fact] + public async Task GetValueProviderAsync_ThrowsValueProviderException_IfReadingFormThrowsInvalidOperationException() + { + // Arrange + var exception = new IOException(); + var valueProviderContext = CreateThrowingContext(exception); + + var factory = new FormValueProviderFactory(); + + // Act & Assert + var ex = await Assert.ThrowsAsync(() => factory.CreateValueProviderAsync(valueProviderContext)); + Assert.Same(exception, ex.InnerException); + } + + [Fact] + public async Task GetValueProviderAsync_ThrowsOriginalException_IfReadingFormThrows() + { + // Arrange + var exception = new TimeZoneNotFoundException(); + var valueProviderContext = CreateThrowingContext(exception); + + var factory = new FormValueProviderFactory(); + + // Act & Assert + var ex = await Assert.ThrowsAsync(() => factory.CreateValueProviderAsync(valueProviderContext)); + Assert.Same(exception, ex); + } + + private static ValueProviderFactoryContext CreateThrowingContext(Exception exception) + { + var context = new Mock(); + context.Setup(c => c.Request.ContentType).Returns("application/x-www-form-urlencoded"); + context.Setup(c => c.Request.HasFormContentType).Returns(true); + context.Setup(c => c.Request.ReadFormAsync(It.IsAny())).ThrowsAsync(exception); + var actionContext = new ActionContext(context.Object, new RouteData(), new ActionDescriptor()); + var valueProviderContext = new ValueProviderFactoryContext(actionContext); + return valueProviderContext; + } + private static ValueProviderFactoryContext CreateContext(string contentType) { var context = new DefaultHttpContext(); diff --git a/src/Mvc/Mvc.Core/test/ModelBinding/JQueryFormValueProviderFactoryTest.cs b/src/Mvc/Mvc.Core/test/ModelBinding/JQueryFormValueProviderFactoryTest.cs index d940134a8ddf..186b04c64ebc 100644 --- a/src/Mvc/Mvc.Core/test/ModelBinding/JQueryFormValueProviderFactoryTest.cs +++ b/src/Mvc/Mvc.Core/test/ModelBinding/JQueryFormValueProviderFactoryTest.cs @@ -1,13 +1,17 @@ // 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.Globalization; +using System.IO; +using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Primitives; +using Moq; using Xunit; namespace Microsoft.AspNetCore.Mvc.ModelBinding.Test @@ -132,6 +136,59 @@ public async Task CreatesValueProvider_WithCurrentCulture() Assert.Equal(CultureInfo.CurrentCulture, jqueryFormValueProvider.Culture); } + [Fact] + public async Task GetValueProviderAsync_ThrowsValueProviderException_IfReadingFormThrowsInvalidDataException() + { + // Arrange + var exception = new InvalidDataException(); + var valueProviderContext = CreateThrowingContext(exception); + + var factory = new JQueryFormValueProviderFactory(); + + // Act & Assert + var ex = await Assert.ThrowsAsync(() => factory.CreateValueProviderAsync(valueProviderContext)); + Assert.Same(exception, ex.InnerException); + } + + [Fact] + public async Task GetValueProviderAsync_ThrowsValueProviderException_IfReadingFormThrowsInvalidOperationException() + { + // Arrange + var exception = new IOException(); + var valueProviderContext = CreateThrowingContext(exception); + + var factory = new JQueryFormValueProviderFactory(); + + // Act & Assert + var ex = await Assert.ThrowsAsync(() => factory.CreateValueProviderAsync(valueProviderContext)); + Assert.Same(exception, ex.InnerException); + } + + [Fact] + public async Task GetValueProviderAsync_ThrowsOriginalException_IfReadingFormThrows() + { + // Arrange + var exception = new TimeZoneNotFoundException(); + var valueProviderContext = CreateThrowingContext(exception); + + var factory = new JQueryFormValueProviderFactory(); + + // Act & Assert + var ex = await Assert.ThrowsAsync(() => factory.CreateValueProviderAsync(valueProviderContext)); + Assert.Same(exception, ex); + } + + private static ValueProviderFactoryContext CreateThrowingContext(Exception exception) + { + var context = new Mock(); + context.Setup(c => c.Request.ContentType).Returns("application/x-www-form-urlencoded"); + context.Setup(c => c.Request.HasFormContentType).Returns(true); + context.Setup(c => c.Request.ReadFormAsync(It.IsAny())).ThrowsAsync(exception); + var actionContext = new ActionContext(context.Object, new RouteData(), new ActionDescriptor()); + var valueProviderContext = new ValueProviderFactoryContext(actionContext); + return valueProviderContext; + } + private static ValueProviderFactoryContext CreateContext(string contentType, Dictionary formValues) { var context = new DefaultHttpContext(); diff --git a/src/Mvc/Mvc.TagHelpers/src/ComponentTagHelper.cs b/src/Mvc/Mvc.TagHelpers/src/ComponentTagHelper.cs index 4bac5c8fc74c..0f7a565c25b3 100644 --- a/src/Mvc/Mvc.TagHelpers/src/ComponentTagHelper.cs +++ b/src/Mvc/Mvc.TagHelpers/src/ComponentTagHelper.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; @@ -66,6 +66,8 @@ public RenderMode RenderMode case RenderMode.Server: case RenderMode.ServerPrerendered: case RenderMode.Static: + case RenderMode.WebAssembly: + case RenderMode.WebAssemblyPrerendered: _renderMode = value; break; diff --git a/src/Mvc/Mvc.ViewFeatures/src/ClientComponentSerializer.cs b/src/Mvc/Mvc.ViewFeatures/src/ClientComponentSerializer.cs new file mode 100644 index 000000000000..5f8bf440620a --- /dev/null +++ b/src/Mvc/Mvc.ViewFeatures/src/ClientComponentSerializer.cs @@ -0,0 +1,75 @@ +// 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.Json; +using Microsoft.AspNetCore.Components; + +namespace Microsoft.AspNetCore.Mvc.ViewFeatures +{ + // See the details of the component serialization protocol in WebAssemblyComponentDeserializer.cs on the Components solution. + internal class WebAssemblyComponentSerializer + { + public WebAssemblyComponentMarker SerializeInvocation(Type type, ParameterView parameters, bool prerendered) + { + var assembly = type.Assembly.GetName().Name; + var typeFullName = type.FullName; + var (definitions, values) = ComponentParameter.FromParameterView(parameters); + + // We need to serialize and Base64 encode parameters separately since they can contain arbitrary data that might + // cause the HTML comment to be invalid (like if you serialize a string that contains two consecutive dashes "--"). + var serializedDefinitions = Convert.ToBase64String(JsonSerializer.SerializeToUtf8Bytes(definitions, WebAssemblyComponentSerializationSettings.JsonSerializationOptions)); + var serializedValues = Convert.ToBase64String(JsonSerializer.SerializeToUtf8Bytes(values, WebAssemblyComponentSerializationSettings.JsonSerializationOptions)); + + return prerendered ? WebAssemblyComponentMarker.Prerendered(assembly, typeFullName, serializedDefinitions, serializedValues) : + WebAssemblyComponentMarker.NonPrerendered(assembly, typeFullName, serializedDefinitions, serializedValues); + } + + internal IEnumerable GetPreamble(WebAssemblyComponentMarker record) + { + var serializedStartRecord = JsonSerializer.Serialize( + record, + WebAssemblyComponentSerializationSettings.JsonSerializationOptions); + + if (record.PrerenderId != null) + { + return PrerenderedStart(serializedStartRecord); + } + else + { + return NonPrerenderedSequence(serializedStartRecord); + } + + static IEnumerable PrerenderedStart(string startRecord) + { + yield return ""; + } + + static IEnumerable NonPrerenderedSequence(string record) + { + yield return ""; + } + } + + internal IEnumerable GetEpilogue(WebAssemblyComponentMarker record) + { + var serializedStartRecord = JsonSerializer.Serialize( + record.GetEndRecord(), + WebAssemblyComponentSerializationSettings.JsonSerializationOptions); + + return PrerenderEnd(serializedStartRecord); + + static IEnumerable PrerenderEnd(string endRecord) + { + yield return ""; + } + } + } +} diff --git a/src/Mvc/Mvc.ViewFeatures/src/DependencyInjection/MvcViewFeaturesMvcCoreBuilderExtensions.cs b/src/Mvc/Mvc.ViewFeatures/src/DependencyInjection/MvcViewFeaturesMvcCoreBuilderExtensions.cs index 84ffe50ce1c2..933145e614ed 100644 --- a/src/Mvc/Mvc.ViewFeatures/src/DependencyInjection/MvcViewFeaturesMvcCoreBuilderExtensions.cs +++ b/src/Mvc/Mvc.ViewFeatures/src/DependencyInjection/MvcViewFeaturesMvcCoreBuilderExtensions.cs @@ -177,6 +177,9 @@ internal static void AddViewServices(IServiceCollection services) // Component services for Blazor server-side interop services.TryAddSingleton(); + // Component services for Blazor webassembly interop + services.TryAddSingleton(); + // // View Components // diff --git a/src/Mvc/Mvc.ViewFeatures/src/Microsoft.AspNetCore.Mvc.ViewFeatures.csproj b/src/Mvc/Mvc.ViewFeatures/src/Microsoft.AspNetCore.Mvc.ViewFeatures.csproj index 56f7a3921815..0a93600bcf97 100644 --- a/src/Mvc/Mvc.ViewFeatures/src/Microsoft.AspNetCore.Mvc.ViewFeatures.csproj +++ b/src/Mvc/Mvc.ViewFeatures/src/Microsoft.AspNetCore.Mvc.ViewFeatures.csproj @@ -1,4 +1,4 @@ - + @@ -31,7 +31,9 @@ + + diff --git a/src/Mvc/Mvc.ViewFeatures/src/PublicAPI.Unshipped.txt b/src/Mvc/Mvc.ViewFeatures/src/PublicAPI.Unshipped.txt index 4b6567f9ced7..e371b29c8d18 100644 --- a/src/Mvc/Mvc.ViewFeatures/src/PublicAPI.Unshipped.txt +++ b/src/Mvc/Mvc.ViewFeatures/src/PublicAPI.Unshipped.txt @@ -77,6 +77,8 @@ Microsoft.AspNetCore.Mvc.Rendering.RenderMode Microsoft.AspNetCore.Mvc.Rendering.RenderMode.Server = 2 -> Microsoft.AspNetCore.Mvc.Rendering.RenderMode Microsoft.AspNetCore.Mvc.Rendering.RenderMode.ServerPrerendered = 3 -> Microsoft.AspNetCore.Mvc.Rendering.RenderMode Microsoft.AspNetCore.Mvc.Rendering.RenderMode.Static = 1 -> Microsoft.AspNetCore.Mvc.Rendering.RenderMode +Microsoft.AspNetCore.Mvc.Rendering.RenderMode.WebAssembly = 4 -> Microsoft.AspNetCore.Mvc.Rendering.RenderMode +Microsoft.AspNetCore.Mvc.Rendering.RenderMode.WebAssemblyPrerendered = 5 -> Microsoft.AspNetCore.Mvc.Rendering.RenderMode Microsoft.AspNetCore.Mvc.Rendering.SelectList Microsoft.AspNetCore.Mvc.Rendering.SelectListGroup Microsoft.AspNetCore.Mvc.Rendering.SelectListGroup.Disabled.get -> bool diff --git a/src/Mvc/Mvc.ViewFeatures/src/RazorComponents/ComponentRenderer.cs b/src/Mvc/Mvc.ViewFeatures/src/RazorComponents/ComponentRenderer.cs index 44a618fb04a1..0ba79b592712 100644 --- a/src/Mvc/Mvc.ViewFeatures/src/RazorComponents/ComponentRenderer.cs +++ b/src/Mvc/Mvc.ViewFeatures/src/RazorComponents/ComponentRenderer.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; @@ -15,13 +15,16 @@ internal class ComponentRenderer : IComponentRenderer private static readonly object ComponentSequenceKey = new object(); private readonly StaticComponentRenderer _staticComponentRenderer; private readonly ServerComponentSerializer _serverComponentSerializer; + private readonly WebAssemblyComponentSerializer _WebAssemblyComponentSerializer; public ComponentRenderer( StaticComponentRenderer staticComponentRenderer, - ServerComponentSerializer serverComponentSerializer) + ServerComponentSerializer serverComponentSerializer, + WebAssemblyComponentSerializer WebAssemblyComponentSerializer) { _staticComponentRenderer = staticComponentRenderer; _serverComponentSerializer = serverComponentSerializer; + _WebAssemblyComponentSerializer = WebAssemblyComponentSerializer; } public async Task RenderComponentAsync( @@ -55,6 +58,8 @@ public async Task RenderComponentAsync( RenderMode.Server => NonPrerenderedServerComponent(context, GetOrCreateInvocationId(viewContext), componentType, parameterView), RenderMode.ServerPrerendered => await PrerenderedServerComponentAsync(context, GetOrCreateInvocationId(viewContext), componentType, parameterView), RenderMode.Static => await StaticComponentAsync(context, componentType, parameterView), + RenderMode.WebAssembly => NonPrerenderedWebAssemblyComponent(context, componentType, parameterView), + RenderMode.WebAssemblyPrerendered => await PrerenderedWebAssemblyComponentAsync(context, componentType, parameterView), _ => throw new ArgumentException(Resources.FormatUnsupportedRenderMode(renderMode), nameof(renderMode)), }; } @@ -99,12 +104,36 @@ private async Task PrerenderedServerComponentAsync(HttpContext con _serverComponentSerializer.GetEpilogue(currentInvocation)); } + private async Task PrerenderedWebAssemblyComponentAsync(HttpContext context, Type type, ParameterView parametersCollection) + { + var currentInvocation = _WebAssemblyComponentSerializer.SerializeInvocation( + type, + parametersCollection, + prerendered: true); + + var result = await _staticComponentRenderer.PrerenderComponentAsync( + parametersCollection, + context, + type); + + return new ComponentHtmlContent( + _WebAssemblyComponentSerializer.GetPreamble(currentInvocation), + result, + _WebAssemblyComponentSerializer.GetEpilogue(currentInvocation)); + } + private IHtmlContent NonPrerenderedServerComponent(HttpContext context, ServerComponentInvocationSequence invocationId, Type type, ParameterView parametersCollection) { - var serviceProvider = context.RequestServices; var currentInvocation = _serverComponentSerializer.SerializeInvocation(invocationId, type, parametersCollection, prerendered: false); return new ComponentHtmlContent(_serverComponentSerializer.GetPreamble(currentInvocation)); } + + private IHtmlContent NonPrerenderedWebAssemblyComponent(HttpContext context, Type type, ParameterView parametersCollection) + { + var currentInvocation = _WebAssemblyComponentSerializer.SerializeInvocation(type, parametersCollection, prerendered: false); + + return new ComponentHtmlContent(_WebAssemblyComponentSerializer.GetPreamble(currentInvocation)); + } } } diff --git a/src/Mvc/Mvc.ViewFeatures/src/RazorComponents/StaticComponentRenderer.cs b/src/Mvc/Mvc.ViewFeatures/src/RazorComponents/StaticComponentRenderer.cs index 411e6014a8f4..7a8a8dd62ded 100644 --- a/src/Mvc/Mvc.ViewFeatures/src/RazorComponents/StaticComponentRenderer.cs +++ b/src/Mvc/Mvc.ViewFeatures/src/RazorComponents/StaticComponentRenderer.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; @@ -32,7 +32,7 @@ public async Task> PrerenderComponentAsync( Type componentType) { InitializeStandardComponentServices(httpContext); - var loggerFactory = (ILoggerFactory)httpContext.RequestServices.GetService(typeof (ILoggerFactory)); + var loggerFactory = (ILoggerFactory)httpContext.RequestServices.GetService(typeof(ILoggerFactory)); using (var htmlRenderer = new HtmlRenderer(httpContext.RequestServices, loggerFactory, _encoder.Encode)) { ComponentRenderedText result = default; @@ -71,15 +71,15 @@ private void InitializeStandardComponentServices(HttpContext httpContext) { _initialized = true; + var navigationManager = (IHostEnvironmentNavigationManager)httpContext.RequestServices.GetRequiredService(); + navigationManager?.Initialize(GetContextBaseUri(httpContext.Request), GetFullUri(httpContext.Request)); + var authenticationStateProvider = httpContext.RequestServices.GetService() as IHostEnvironmentAuthenticationStateProvider; if (authenticationStateProvider != null) { var authenticationState = new AuthenticationState(httpContext.User); authenticationStateProvider.SetAuthenticationState(Task.FromResult(authenticationState)); } - - var navigationManager = (IHostEnvironmentNavigationManager)httpContext.RequestServices.GetRequiredService(); - navigationManager?.Initialize(GetContextBaseUri(httpContext.Request), GetFullUri(httpContext.Request)); } } diff --git a/src/Mvc/Mvc.ViewFeatures/src/RenderMode.cs b/src/Mvc/Mvc.ViewFeatures/src/RenderMode.cs index f6816e68ff4f..d0422753cb3e 100644 --- a/src/Mvc/Mvc.ViewFeatures/src/RenderMode.cs +++ b/src/Mvc/Mvc.ViewFeatures/src/RenderMode.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. namespace Microsoft.AspNetCore.Mvc.Rendering @@ -30,5 +30,16 @@ public enum RenderMode /// ServerPrerendered = 3, + /// + /// Renders a marker for a Blazor webassembly application. This doesn't include any output from the component. + /// When the user-agent starts, it uses this marker to bootstrap a blazor client-side application. + /// + WebAssembly = 4, + + /// + /// Renders the component into static HTML and includes a marker for a Blazor webassembly application. + /// When the user-agent starts, it uses this marker to bootstrap a blazor client-side application. + /// + WebAssemblyPrerendered = 5, } } diff --git a/src/Mvc/Mvc.ViewFeatures/test/RazorComponents/ComponentRendererTest.cs b/src/Mvc/Mvc.ViewFeatures/test/RazorComponents/ComponentRendererTest.cs index f9e403ca2f6e..e20a4203b931 100644 --- a/src/Mvc/Mvc.ViewFeatures/test/RazorComponents/ComponentRendererTest.cs +++ b/src/Mvc/Mvc.ViewFeatures/test/RazorComponents/ComponentRendererTest.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using System.Text; using System.Text.Encodings.Web; using System.Text.Json; using System.Text.RegularExpressions; @@ -26,13 +27,239 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures { public class ComponentRendererTest { - private const string PrerenderedServerComponentPattern = "^(?.+?)$"; - private const string ServerComponentPattern = "^$"; + private const string PrerenderedComponentPattern = "^(?.+?)$"; + private const string ComponentPattern = "^$"; private static readonly IDataProtectionProvider _dataprotectorProvider = new EphemeralDataProtectionProvider(); private readonly ComponentRenderer renderer = GetComponentRenderer(); + [Fact] + public async Task CanRender_ParameterlessComponent_ClientMode() + { + // Arrange + var viewContext = GetViewContext(); + var writer = new StringWriter(); + + // Act + var result = await renderer.RenderComponentAsync(viewContext, typeof(TestComponent), RenderMode.WebAssembly, null); + result.WriteTo(writer, HtmlEncoder.Default); + var content = writer.ToString(); + var match = Regex.Match(content, ComponentPattern); + + // Assert + Assert.True(match.Success); + var marker = JsonSerializer.Deserialize(match.Groups[1].Value, ServerComponentSerializationSettings.JsonSerializationOptions); + Assert.Null(marker.PrerenderId); + Assert.Equal("webassembly", marker.Type); + Assert.Equal(typeof(TestComponent).Assembly.GetName().Name, marker.Assembly); + Assert.Equal(typeof(TestComponent).FullName, marker.TypeName); + } + + [Fact] + public async Task CanPrerender_ParameterlessComponent_ClientMode() + { + // Arrange + var viewContext = GetViewContext(); + var writer = new StringWriter(); + + // Act + var result = await renderer.RenderComponentAsync(viewContext, typeof(TestComponent), RenderMode.WebAssemblyPrerendered, null); + result.WriteTo(writer, HtmlEncoder.Default); + var content = writer.ToString(); + var match = Regex.Match(content, PrerenderedComponentPattern, RegexOptions.Multiline); + + // Assert + Assert.True(match.Success); + var preamble = match.Groups["preamble"].Value; + var preambleMarker = JsonSerializer.Deserialize(preamble, ServerComponentSerializationSettings.JsonSerializationOptions); + Assert.NotNull(preambleMarker.PrerenderId); + Assert.Equal("webassembly", preambleMarker.Type); + Assert.Equal(typeof(TestComponent).Assembly.GetName().Name, preambleMarker.Assembly); + Assert.Equal(typeof(TestComponent).FullName, preambleMarker.TypeName); + + var prerenderedContent = match.Groups["content"].Value; + Assert.Equal("

Hello world!

", prerenderedContent); + + var epilogue = match.Groups["epilogue"].Value; + var epilogueMarker = JsonSerializer.Deserialize(epilogue, ServerComponentSerializationSettings.JsonSerializationOptions); + Assert.Equal(preambleMarker.PrerenderId, epilogueMarker.PrerenderId); + Assert.Null(epilogueMarker.Assembly); + Assert.Null(epilogueMarker.TypeName); + Assert.Null(epilogueMarker.Type); + Assert.Null(epilogueMarker.ParameterDefinitions); + Assert.Null(epilogueMarker.ParameterValues); + } + + [Fact] + public async Task CanRender_ComponentWithParameters_ClientMode() + { + // Arrange + var viewContext = GetViewContext(); + var writer = new StringWriter(); + + // Act + var result = await renderer.RenderComponentAsync(viewContext, typeof(GreetingComponent), + RenderMode.WebAssembly, + new + { + Name = "Daniel" + }); + result.WriteTo(writer, HtmlEncoder.Default); + var content = writer.ToString(); + var match = Regex.Match(content, ComponentPattern); + + // Assert + Assert.True(match.Success); + var marker = JsonSerializer.Deserialize(match.Groups[1].Value, ServerComponentSerializationSettings.JsonSerializationOptions); + Assert.Null(marker.PrerenderId); + Assert.Equal("webassembly", marker.Type); + Assert.Equal(typeof(GreetingComponent).Assembly.GetName().Name, marker.Assembly); + Assert.Equal(typeof(GreetingComponent).FullName, marker.TypeName); + + var parameterDefinition = Assert.Single( + JsonSerializer.Deserialize(Convert.FromBase64String(marker.ParameterDefinitions), WebAssemblyComponentSerializationSettings.JsonSerializationOptions)); + Assert.Equal("Name", parameterDefinition.Name); + Assert.Equal("System.String", parameterDefinition.TypeName); + Assert.Equal("System.Private.CoreLib", parameterDefinition.Assembly); + + var value = Assert.Single(JsonSerializer.Deserialize(Convert.FromBase64String(marker.ParameterValues), WebAssemblyComponentSerializationSettings.JsonSerializationOptions)); + var rawValue = Assert.IsType(value); + Assert.Equal("Daniel", rawValue.GetString()); + } + + [Fact] + public async Task CanRender_ComponentWithNullParameters_ClientMode() + { + // Arrange + var viewContext = GetViewContext(); + var writer = new StringWriter(); + + // Act + var result = await renderer.RenderComponentAsync(viewContext, typeof(GreetingComponent), + RenderMode.WebAssembly, + new + { + Name = (string)null + }); + result.WriteTo(writer, HtmlEncoder.Default); + var content = writer.ToString(); + var match = Regex.Match(content, ComponentPattern); + + // Assert + Assert.True(match.Success); + var marker = JsonSerializer.Deserialize(match.Groups[1].Value, ServerComponentSerializationSettings.JsonSerializationOptions); + Assert.Null(marker.PrerenderId); + Assert.Equal("webassembly", marker.Type); + Assert.Equal(typeof(GreetingComponent).Assembly.GetName().Name, marker.Assembly); + Assert.Equal(typeof(GreetingComponent).FullName, marker.TypeName); + + var parameterDefinition = Assert.Single(JsonSerializer.Deserialize(Convert.FromBase64String(marker.ParameterDefinitions), WebAssemblyComponentSerializationSettings.JsonSerializationOptions)); + Assert.Equal("Name", parameterDefinition.Name); + Assert.Null(parameterDefinition.TypeName); + Assert.Null(parameterDefinition.Assembly); + + var value = Assert.Single(JsonSerializer.Deserialize(Convert.FromBase64String(marker.ParameterValues), WebAssemblyComponentSerializationSettings.JsonSerializationOptions)); + Assert.Null(value); + } + + [Fact] + public async Task CanPrerender_ComponentWithParameters_ClientMode() + { + // Arrange + var viewContext = GetViewContext(); + var writer = new StringWriter(); + + // Act + var result = await renderer.RenderComponentAsync(viewContext, typeof(GreetingComponent), + RenderMode.WebAssemblyPrerendered, + new + { + Name = "Daniel" + }); + result.WriteTo(writer, HtmlEncoder.Default); + var content = writer.ToString(); + var match = Regex.Match(content, PrerenderedComponentPattern, RegexOptions.Multiline); + + // Assert + Assert.True(match.Success); + var preamble = match.Groups["preamble"].Value; + var preambleMarker = JsonSerializer.Deserialize(preamble, ServerComponentSerializationSettings.JsonSerializationOptions); + Assert.NotNull(preambleMarker.PrerenderId); + Assert.Equal("webassembly", preambleMarker.Type); + Assert.Equal(typeof(GreetingComponent).Assembly.GetName().Name, preambleMarker.Assembly); + Assert.Equal(typeof(GreetingComponent).FullName, preambleMarker.TypeName); + + var parameterDefinition = Assert.Single(JsonSerializer.Deserialize(Convert.FromBase64String(preambleMarker.ParameterDefinitions), WebAssemblyComponentSerializationSettings.JsonSerializationOptions)); + Assert.Equal("Name", parameterDefinition.Name); + Assert.Equal("System.String", parameterDefinition.TypeName); + Assert.Equal("System.Private.CoreLib", parameterDefinition.Assembly); + + var value = Assert.Single(JsonSerializer.Deserialize(Convert.FromBase64String(preambleMarker.ParameterValues), WebAssemblyComponentSerializationSettings.JsonSerializationOptions)); + var rawValue = Assert.IsType(value); + Assert.Equal("Daniel", rawValue.GetString()); + + var prerenderedContent = match.Groups["content"].Value; + Assert.Equal("

Hello Daniel!

", prerenderedContent); + + var epilogue = match.Groups["epilogue"].Value; + var epilogueMarker = JsonSerializer.Deserialize(epilogue, ServerComponentSerializationSettings.JsonSerializationOptions); + Assert.Equal(preambleMarker.PrerenderId, epilogueMarker.PrerenderId); + Assert.Null(epilogueMarker.Assembly); + Assert.Null(epilogueMarker.TypeName); + Assert.Null(epilogueMarker.Type); + Assert.Null(epilogueMarker.ParameterDefinitions); + Assert.Null(epilogueMarker.ParameterValues); + } + + [Fact] + public async Task CanPrerender_ComponentWithNullParameters_ClientMode() + { + // Arrange + var viewContext = GetViewContext(); + var writer = new StringWriter(); + + // Act + var result = await renderer.RenderComponentAsync(viewContext, typeof(GreetingComponent), + RenderMode.WebAssemblyPrerendered, + new + { + Name = (string)null + }); + result.WriteTo(writer, HtmlEncoder.Default); + var content = writer.ToString(); + var match = Regex.Match(content, PrerenderedComponentPattern, RegexOptions.Multiline); + + // Assert + Assert.True(match.Success); + var preamble = match.Groups["preamble"].Value; + var preambleMarker = JsonSerializer.Deserialize(preamble, ServerComponentSerializationSettings.JsonSerializationOptions); + Assert.NotNull(preambleMarker.PrerenderId); + Assert.Equal("webassembly", preambleMarker.Type); + Assert.Equal(typeof(GreetingComponent).Assembly.GetName().Name, preambleMarker.Assembly); + Assert.Equal(typeof(GreetingComponent).FullName, preambleMarker.TypeName); + + var parameterDefinition = Assert.Single(JsonSerializer.Deserialize(Convert.FromBase64String(preambleMarker.ParameterDefinitions), WebAssemblyComponentSerializationSettings.JsonSerializationOptions)); + Assert.Equal("Name", parameterDefinition.Name); + Assert.Null(parameterDefinition.TypeName); + Assert.Null(parameterDefinition.Assembly); + + var value = Assert.Single(JsonSerializer.Deserialize(Convert.FromBase64String(preambleMarker.ParameterValues), WebAssemblyComponentSerializationSettings.JsonSerializationOptions)); + Assert.Null(value); + + var prerenderedContent = match.Groups["content"].Value; + Assert.Equal("

Hello (null)!

", prerenderedContent); + + var epilogue = match.Groups["epilogue"].Value; + var epilogueMarker = JsonSerializer.Deserialize(epilogue, ServerComponentSerializationSettings.JsonSerializationOptions); + Assert.Equal(preambleMarker.PrerenderId, epilogueMarker.PrerenderId); + Assert.Null(epilogueMarker.Assembly); + Assert.Null(epilogueMarker.TypeName); + Assert.Null(epilogueMarker.Type); + Assert.Null(epilogueMarker.ParameterDefinitions); + Assert.Null(epilogueMarker.ParameterValues); + } + [Fact] public async Task CanRender_ParameterlessComponent() { @@ -60,7 +287,7 @@ public async Task CanRender_ParameterlessComponent_ServerMode() // Act var result = await renderer.RenderComponentAsync(viewContext, typeof(TestComponent), RenderMode.Server, null); var content = HtmlContentUtilities.HtmlContentToString(result); - var match = Regex.Match(content, ServerComponentPattern); + var match = Regex.Match(content, ComponentPattern); // Assert Assert.True(match.Success); @@ -89,7 +316,7 @@ public async Task CanPrerender_ParameterlessComponent_ServerMode() // Act var result = await renderer.RenderComponentAsync(viewContext, typeof(TestComponent), RenderMode.ServerPrerendered, null); var content = HtmlContentUtilities.HtmlContentToString(result); - var match = Regex.Match(content, PrerenderedServerComponentPattern, RegexOptions.Multiline); + var match = Regex.Match(content, PrerenderedComponentPattern, RegexOptions.Multiline); // Assert Assert.True(match.Success); @@ -130,11 +357,11 @@ public async Task CanRenderMultipleServerComponents() // Act var firstResult = await renderer.RenderComponentAsync(viewContext, typeof(TestComponent), RenderMode.ServerPrerendered, null); var firstComponent = HtmlContentUtilities.HtmlContentToString(firstResult); - var firstMatch = Regex.Match(firstComponent, PrerenderedServerComponentPattern, RegexOptions.Multiline); + var firstMatch = Regex.Match(firstComponent, PrerenderedComponentPattern, RegexOptions.Multiline); var secondResult = await renderer.RenderComponentAsync(viewContext, typeof(TestComponent), RenderMode.Server, null); var secondComponent = HtmlContentUtilities.HtmlContentToString(secondResult); - var secondMatch = Regex.Match(secondComponent, ServerComponentPattern); + var secondMatch = Regex.Match(secondComponent, ComponentPattern); // Assert Assert.True(firstMatch.Success); @@ -186,7 +413,7 @@ public async Task CanRender_ComponentWithParameters_ServerMode() // Act var result = await renderer.RenderComponentAsync(viewContext, typeof(GreetingComponent), RenderMode.Server, new { Name = "Daniel" }); var content = HtmlContentUtilities.HtmlContentToString(result); - var match = Regex.Match(content, ServerComponentPattern); + var match = Regex.Match(content, ComponentPattern); // Assert Assert.True(match.Success); @@ -225,7 +452,7 @@ public async Task CanRender_ComponentWithNullParameters_ServerMode() var result = await renderer.RenderComponentAsync(viewContext, typeof(GreetingComponent), RenderMode.Server, new { Name = (string)null }); var content = HtmlContentUtilities.HtmlContentToString(result); - var match = Regex.Match(content, ServerComponentPattern); + var match = Regex.Match(content, ComponentPattern); // Assert Assert.True(match.Success); @@ -264,7 +491,7 @@ public async Task CanPrerender_ComponentWithParameters_ServerPrerenderedMode() // Act var result = await renderer.RenderComponentAsync(viewContext, typeof(GreetingComponent), RenderMode.ServerPrerendered, new { Name = "Daniel" }); var content = HtmlContentUtilities.HtmlContentToString(result); - var match = Regex.Match(content, PrerenderedServerComponentPattern, RegexOptions.Multiline); + var match = Regex.Match(content, PrerenderedComponentPattern, RegexOptions.Multiline); // Assert Assert.True(match.Success); @@ -315,7 +542,7 @@ public async Task CanPrerender_ComponentWithNullParameters_ServerPrerenderedMode // Act var result = await renderer.RenderComponentAsync(viewContext, typeof(GreetingComponent), RenderMode.ServerPrerendered, new { Name = (string)null }); var content = HtmlContentUtilities.HtmlContentToString(result); - var match = Regex.Match(content, PrerenderedServerComponentPattern, RegexOptions.Multiline); + var match = Regex.Match(content, PrerenderedComponentPattern, RegexOptions.Multiline); // Assert Assert.True(match.Success); @@ -554,7 +781,7 @@ public async Task CanRender_AsyncComponent() "; // Act - var result = await renderer.RenderComponentAsync(viewContext,typeof(AsyncComponent), RenderMode.Static, null); + var result = await renderer.RenderComponentAsync(viewContext, typeof(AsyncComponent), RenderMode.Static, null); var content = HtmlContentUtilities.HtmlContentToString(result); // Assert @@ -564,7 +791,8 @@ public async Task CanRender_AsyncComponent() private static ComponentRenderer GetComponentRenderer() => new ComponentRenderer( new StaticComponentRenderer(HtmlEncoder.Default), - new ServerComponentSerializer(_dataprotectorProvider)); + new ServerComponentSerializer(_dataprotectorProvider), + new WebAssemblyComponentSerializer()); private static ViewContext GetViewContext(HttpContext context = null, Action configureServices = null) { diff --git a/src/Mvc/test/Mvc.FunctionalTests/ReadFromDisconnectedClientTest.cs b/src/Mvc/test/Mvc.FunctionalTests/ReadFromDisconnectedClientTest.cs new file mode 100644 index 000000000000..869e4607eb2f --- /dev/null +++ b/src/Mvc/test/Mvc.FunctionalTests/ReadFromDisconnectedClientTest.cs @@ -0,0 +1,63 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Json; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; +using Xunit; + +namespace Microsoft.AspNetCore.Mvc.FunctionalTests +{ + // These tests verify the behavior of MVC when responding to a client that simulates a disconnect. + // See https://github.com/dotnet/aspnetcore/issues/13333 + public class ReadFromDisconnectedClientTest : IClassFixture> + { + public ReadFromDisconnectedClientTest(MvcTestFixture fixture) + { + var factory = fixture.Factories.FirstOrDefault() ?? fixture.WithWebHostBuilder(ConfigureWebHostBuilder); + Client = factory.CreateDefaultClient(); + } + + private static void ConfigureWebHostBuilder(IWebHostBuilder builder) => + builder.UseStartup(); + + public HttpClient Client { get; } + + [Fact] + public async Task ActionWithAntiforgery_Returns400_WhenReadingBodyThrows() + { + // Arrange + var request = new HttpRequestMessage(HttpMethod.Post, "ReadFromThrowingRequestBody/AppliesAntiforgeryValidation"); + + // Act + var response = await Client.SendAsync(request); + + // Assert + await response.AssertStatusCodeAsync(HttpStatusCode.BadRequest); + } + + [Fact] + public async Task ActionReadingForm_ReturnsInvalidModelState_WhenReadingBodyThrows() + { + // Arrange + var request = new HttpRequestMessage(HttpMethod.Post, "ReadFromThrowingRequestBody/ReadForm"); + request.Content = new FormUrlEncodedContent(new Dictionary + { + ["key"] = "value", + }); + + // Act + var response = await Client.SendAsync(request); + + // Assert + await response.AssertStatusCodeAsync(HttpStatusCode.BadRequest); + var problem = await response.Content.ReadFromJsonAsync(); + var error = Assert.Single(problem.Errors); + Assert.Empty(error.Key); + } + } +} diff --git a/src/Mvc/test/Mvc.FunctionalTests/RequestFormLimitsTest.cs b/src/Mvc/test/Mvc.FunctionalTests/RequestFormLimitsTest.cs index 285b6cc39739..1a9c1a8f3829 100644 --- a/src/Mvc/test/Mvc.FunctionalTests/RequestFormLimitsTest.cs +++ b/src/Mvc/test/Mvc.FunctionalTests/RequestFormLimitsTest.cs @@ -1,11 +1,10 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; -using System.Reflection; using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; using Xunit; @@ -26,7 +25,7 @@ private static void ConfigureWebHostBuilder(IWebHostBuilder builder) => public HttpClient Client { get; } [Fact] - public async Task RequestFormLimitCheckHappens_BeforeAntiforgeryTokenValidation() + public async Task RequestFormLimitCheckHappens_WithAntiforgeryValidation() { // Arrange var request = new HttpRequestMessage(); @@ -43,11 +42,7 @@ public async Task RequestFormLimitCheckHappens_BeforeAntiforgeryTokenValidation( new FormUrlEncodedContent(kvps)); // Assert - Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); - var result = await response.Content.ReadAsStringAsync(); - Assert.Contains( - "InvalidDataException: Form value count limit 2 exceeded.", - result); + await response.AssertStatusCodeAsync(HttpStatusCode.BadRequest); } [Fact] @@ -103,7 +98,6 @@ public async Task OverrideControllerLevelLimits_UsingDefaultLimits() public async Task RequestSizeLimitCheckHappens_BeforeRequestFormLimits() { // Arrange - var request = new HttpRequestMessage(); var kvps = new List>(); // Request size has a limit of 100 bytes // Request form limits has a value count limit of 2 @@ -129,7 +123,6 @@ public async Task RequestSizeLimitCheckHappens_BeforeRequestFormLimits() public async Task RequestFormLimitsCheckHappens_AfterRequestSizeLimit() { // Arrange - var request = new HttpRequestMessage(); var kvps = new List>(); // Request size has a limit of 100 bytes // Request form limits has a value count limit of 2 @@ -145,11 +138,7 @@ public async Task RequestFormLimitsCheckHappens_AfterRequestSizeLimit() new FormUrlEncodedContent(kvps)); // Assert - Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode); - var result = await response.Content.ReadAsStringAsync(); - Assert.Contains( - "InvalidDataException: Form value count limit 2 exceeded.", - result); + await response.AssertStatusCodeAsync(HttpStatusCode.BadRequest); } [Fact] diff --git a/src/Mvc/test/WebSites/BasicWebSite/Controllers/ReadFromThrowingRequestBodyController .cs b/src/Mvc/test/WebSites/BasicWebSite/Controllers/ReadFromThrowingRequestBodyController .cs new file mode 100644 index 000000000000..07c2c04e4849 --- /dev/null +++ b/src/Mvc/test/WebSites/BasicWebSite/Controllers/ReadFromThrowingRequestBodyController .cs @@ -0,0 +1,33 @@ +// 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.Http; +using Microsoft.AspNetCore.Mvc; + +namespace BasicWebSite.Controllers +{ + public class ReadFromThrowingRequestBodyController : Controller + { + [ValidateAntiForgeryToken] + [HttpPost] + public IActionResult AppliesAntiforgeryValidation() => Ok(); + + [HttpPost] + public IActionResult ReadForm(Person person, IFormFile form) + { + if (!ModelState.IsValid) + { + return ValidationProblem(); + } + + return Ok(); + } + + public class Person + { + public string Name { get; set; } + + public int Age { get; set; } + } + } +} diff --git a/src/Mvc/test/WebSites/BasicWebSite/StartupWhereReadingRequestBodyThrows.cs b/src/Mvc/test/WebSites/BasicWebSite/StartupWhereReadingRequestBodyThrows.cs new file mode 100644 index 000000000000..25b3855d2e7d --- /dev/null +++ b/src/Mvc/test/WebSites/BasicWebSite/StartupWhereReadingRequestBodyThrows.cs @@ -0,0 +1,89 @@ +// 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.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Connections; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Infrastructure; +using Microsoft.Extensions.DependencyInjection; + +namespace BasicWebSite +{ + public class StartupWhereReadingRequestBodyThrows + { + // Set up application services + public void ConfigureServices(IServiceCollection services) + { + services.AddControllersWithViews() + .SetCompatibilityVersion(CompatibilityVersion.Latest); + } + + public void Configure(IApplicationBuilder app) + { + app.UseDeveloperExceptionPage(); + + // Initializes the RequestId service for each request + app.Use((context, next) => + { + context.Request.Body = new ThrowingStream(); + return next(); + }); + + app.UseRouting(); + app.UseEndpoints(endpoints => + { + endpoints.MapDefaultControllerRoute(); + }); + } + + private class ThrowingStream : Stream + { + public override bool CanRead => true; + public override bool CanSeek => false; + public override bool CanWrite => false; + public override long Length => throw new NotSupportedException(); + public override long Position { get; set; } + + public override void Flush() + { + throw new NotSupportedException(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + throw new ConnectionResetException("Some error"); + } + + public override ValueTask ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + { + throw new ConnectionResetException("Some error"); + } + + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + } + } +} diff --git a/src/ProjectTemplates/BlazorTemplates.Tests/BlazorTemplates.Tests.csproj b/src/ProjectTemplates/BlazorTemplates.Tests/BlazorTemplates.Tests.csproj index 2da0e11666ef..6a024814c24f 100644 --- a/src/ProjectTemplates/BlazorTemplates.Tests/BlazorTemplates.Tests.csproj +++ b/src/ProjectTemplates/BlazorTemplates.Tests/BlazorTemplates.Tests.csproj @@ -10,6 +10,10 @@ true true + + + true + false true diff --git a/src/ProjectTemplates/Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj b/src/ProjectTemplates/Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj index b3ba67aeb4ab..3e8a8cbecf8a 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj +++ b/src/ProjectTemplates/Web.ProjectTemplates/Microsoft.DotNet.Web.ProjectTemplates.csproj @@ -59,6 +59,7 @@ + diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Controllers/WeatherForecastController.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Controllers/WeatherForecastController.cs index 15f1df532a52..99971dfae8e5 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Controllers/WeatherForecastController.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Controllers/WeatherForecastController.cs @@ -37,7 +37,7 @@ public class WeatherForecastController : ControllerBase private readonly ILogger _logger; - // The Web API will only accept tokens 1) for users, and 2) having the access_as_user scope for this API + // The Web API will only accept tokens 1) for users, and 2) having the api-scope scope for this API static readonly string[] scopeRequiredByApi = new string[] { "api-scope" }; #if (GenerateApi) @@ -75,7 +75,7 @@ public WeatherForecastController(ILogger logger, { _logger = logger; _graphServiceClient = graphServiceClient; - } + } [HttpGet] public async Task> Get() diff --git a/src/ProjectTemplates/test/WorkerTemplateTest.cs b/src/ProjectTemplates/test/WorkerTemplateTest.cs index d7e471458510..1656ebb0896b 100644 --- a/src/ProjectTemplates/test/WorkerTemplateTest.cs +++ b/src/ProjectTemplates/test/WorkerTemplateTest.cs @@ -21,13 +21,17 @@ public WorkerTemplateTest(ProjectFactoryFixture projectFactory, ITestOutputHelpe public ProjectFactoryFixture ProjectFactory { get; } public ITestOutputHelper Output { get; } - [ConditionalFact] + [ConditionalTheory] [OSSkipCondition(OperatingSystems.Linux, SkipReason = "https://github.com/dotnet/sdk/issues/12831")] - public async Task WorkerTemplateAsync() + [InlineData("C#")] + [InlineData("F#")] + public async Task WorkerTemplateAsync(string language) { - Project = await ProjectFactory.GetOrCreateProject("worker", Output); + Project = await ProjectFactory.GetOrCreateProject( + $"worker-{ language.ToLowerInvariant()[0] }sharp", + Output); - var createResult = await Project.RunDotNetNewAsync("worker"); + var createResult = await Project.RunDotNetNewAsync("worker", language: language); Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", Project, createResult)); var publishResult = await Project.RunDotNetPublishAsync(); diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorDiagnosticFactory.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorDiagnosticFactory.cs index 3d1fbb234a4e..9bef04b5bc24 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorDiagnosticFactory.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/RazorDiagnosticFactory.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; @@ -909,5 +909,21 @@ public static RazorDiagnostic CreateRewriter_InsufficientStack(SourceSpan locati } #endregion + + #region "CSS Rewriter Errors" + + // CSS Rewriter Errors ID Offset = 5000 + + internal static readonly RazorDiagnosticDescriptor CssRewriting_ImportNotAllowed = + new RazorDiagnosticDescriptor( + $"{DiagnosticPrefix}5000", + () => Resources.CssRewriter_ImportNotAllowed, + RazorDiagnosticSeverity.Error); + public static RazorDiagnostic CreateCssRewriting_ImportNotAllowed(SourceSpan location) + { + return RazorDiagnostic.Create(CssRewriting_ImportNotAllowed, location); + } + + #endregion } } diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Resources.resx b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Resources.resx index 65fdf317bb17..a6fe1ab23554 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Resources.resx +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Resources.resx @@ -562,4 +562,7 @@ The '{0}' directive expects a boolean literal. + + @import rules are not supported within scoped CSS files because the loading order would be undefined. @import may only be placed in non-scoped CSS files. + \ No newline at end of file diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Tools/src/RewriteCssCommand.cs b/src/Razor/Microsoft.AspNetCore.Razor.Tools/src/RewriteCssCommand.cs index f9e33cae1427..3babba81e0b4 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Tools/src/RewriteCssCommand.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Tools/src/RewriteCssCommand.cs @@ -2,12 +2,15 @@ // 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.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; +using Microsoft.AspNetCore.Razor.Language; +using Microsoft.CodeAnalysis.Text; using Microsoft.Css.Parser.Parser; using Microsoft.Css.Parser.Tokens; using Microsoft.Css.Parser.TreeItems; @@ -57,28 +60,55 @@ protected override bool ValidateArguments() protected override Task ExecuteCoreAsync() { + var allDiagnostics = new ConcurrentQueue(); + Parallel.For(0, Sources.Values.Count, i => { var source = Sources.Values[i]; var output = Outputs.Values[i]; var cssScope = CssScopes.Values[i]; - var inputText = File.ReadAllText(source); - var rewrittenCss = AddScopeToSelectors(inputText, cssScope); - File.WriteAllText(output, rewrittenCss); + using var inputSourceStream = new FileStream(source, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete); + var inputSourceText = SourceText.From(inputSourceStream); + + var rewrittenCss = AddScopeToSelectors(source, inputSourceText, cssScope, out var diagnostics); + if (diagnostics.Any()) + { + foreach (var diagnostic in diagnostics) + { + allDiagnostics.Enqueue(diagnostic); + } + } + else + { + File.WriteAllText(output, rewrittenCss); + } }); - return Task.FromResult(ExitCodeSuccess); + foreach (var diagnostic in allDiagnostics) + { + Error.WriteLine(diagnostic.ToString()); + } + + return Task.FromResult(allDiagnostics.Any() ? ExitCodeFailure : ExitCodeSuccess); } // Public for tests - public static string AddScopeToSelectors(string inputText, string cssScope) + public static string AddScopeToSelectors(string filePath, string inputSource, string cssScope, out IEnumerable diagnostics) + => AddScopeToSelectors(filePath, SourceText.From(inputSource), cssScope, out diagnostics); + + private static string AddScopeToSelectors(string filePath, SourceText inputSourceText, string cssScope, out IEnumerable diagnostics) { var cssParser = new DefaultParserFactory().CreateParser(); + var inputText = inputSourceText.ToString(); var stylesheet = cssParser.Parse(inputText, insertComments: false); var resultBuilder = new StringBuilder(); var previousInsertionPosition = 0; + var foundDiagnostics = new List(); + + var ensureNoImportsVisitor = new EnsureNoImports(filePath, inputSourceText, stylesheet, foundDiagnostics); + ensureNoImportsVisitor.Visit(); var scopeInsertionPositionsVisitor = new FindScopeInsertionEdits(stylesheet); scopeInsertionPositionsVisitor.Visit(); @@ -105,6 +135,7 @@ public static string AddScopeToSelectors(string inputText, string cssScope) resultBuilder.Append(inputText.Substring(previousInsertionPosition)); + diagnostics = foundDiagnostics; return resultBuilder.ToString(); } @@ -257,6 +288,36 @@ protected override void VisitAtDirective(AtDirective item) } } + private class EnsureNoImports : Visitor + { + private readonly string _filePath; + private readonly SourceText _sourceText; + private readonly List _diagnostics; + + public EnsureNoImports(string filePath, SourceText sourceText, ComplexItem root, List diagnostics) : base(root) + { + _filePath = filePath; + _sourceText = sourceText; + _diagnostics = diagnostics; + } + + protected override void VisitAtDirective(AtDirective item) + { + if (item.Children.Count >= 2 + && item.Children[0] is TokenItem firstChild + && firstChild.TokenType == CssTokenType.At + && item.Children[1] is TokenItem secondChild + && string.Equals(secondChild.Text, "import", StringComparison.OrdinalIgnoreCase)) + { + var linePosition = _sourceText.Lines.GetLinePosition(item.Start); + var sourceSpan = new SourceSpan(_filePath, item.Start, linePosition.Line, linePosition.Character, item.Length); + _diagnostics.Add(RazorDiagnosticFactory.CreateCssRewriting_ImportNotAllowed(sourceSpan)); + } + + base.VisitAtDirective(item); + } + } + private class Visitor { private readonly ComplexItem _root; diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Tools/test/RewriteCssCommandTest.cs b/src/Razor/Microsoft.AspNetCore.Razor.Tools/test/RewriteCssCommandTest.cs index 2c2d199f80cf..37b1c37a300b 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Tools/test/RewriteCssCommandTest.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Tools/test/RewriteCssCommandTest.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 Microsoft.AspNetCore.Razor.Language; using Xunit; namespace Microsoft.AspNetCore.Razor.Tools @@ -11,9 +12,10 @@ public class RewriteCssCommandTest public void HandlesEmptyFile() { // Arrange/act - var result = RewriteCssCommand.AddScopeToSelectors(string.Empty, "TestScope"); + var result = RewriteCssCommand.AddScopeToSelectors("file.css", string.Empty, "TestScope", out var diagnostics); // Assert + Assert.Empty(diagnostics); Assert.Equal(string.Empty, result); } @@ -21,11 +23,12 @@ public void HandlesEmptyFile() public void AddsScopeAfterSelector() { // Arrange/act - var result = RewriteCssCommand.AddScopeToSelectors(@" + var result = RewriteCssCommand.AddScopeToSelectors("file.css", @" .myclass { color: red; } -", "TestScope"); +", "TestScope", out var diagnostics); // Assert + Assert.Empty(diagnostics); Assert.Equal(@" .myclass[TestScope] { color: red; } ", result); @@ -35,12 +38,13 @@ public void AddsScopeAfterSelector() public void HandlesMultipleSelectors() { // Arrange/act - var result = RewriteCssCommand.AddScopeToSelectors(@" + var result = RewriteCssCommand.AddScopeToSelectors("file.css", @" .first, .second { color: red; } .third { color: blue; } -", "TestScope"); +", "TestScope", out var diagnostics); // Assert + Assert.Empty(diagnostics); Assert.Equal(@" .first[TestScope], .second[TestScope] { color: red; } .third[TestScope] { color: blue; } @@ -51,11 +55,12 @@ public void HandlesMultipleSelectors() public void HandlesComplexSelectors() { // Arrange/act - var result = RewriteCssCommand.AddScopeToSelectors(@" + var result = RewriteCssCommand.AddScopeToSelectors("file.css", @" .first div > li, body .second:not(.fancy)[attr~=whatever] { color: red; } -", "TestScope"); +", "TestScope", out var diagnostics); // Assert + Assert.Empty(diagnostics); Assert.Equal(@" .first div > li[TestScope], body .second:not(.fancy)[attr~=whatever][TestScope] { color: red; } ", result); @@ -65,11 +70,12 @@ public void HandlesComplexSelectors() public void HandlesSpacesAndCommentsWithinSelectors() { // Arrange/act - var result = RewriteCssCommand.AddScopeToSelectors(@" + var result = RewriteCssCommand.AddScopeToSelectors("file.css", @" .first /* space at end {} */ div , .myclass /* comment at end */ { color: red; } -", "TestScope"); +", "TestScope", out var diagnostics); // Assert + Assert.Empty(diagnostics); Assert.Equal(@" .first /* space at end {} */ div[TestScope] , .myclass[TestScope] /* comment at end */ { color: red; } ", result); @@ -79,12 +85,13 @@ public void HandlesSpacesAndCommentsWithinSelectors() public void RespectsDeepCombinator() { // Arrange/act - var result = RewriteCssCommand.AddScopeToSelectors(@" + var result = RewriteCssCommand.AddScopeToSelectors("file.css", @" .first ::deep .second { color: red; } a ::deep b, c ::deep d { color: blue; } -", "TestScope"); +", "TestScope", out var diagnostics); // Assert + Assert.Empty(diagnostics); Assert.Equal(@" .first[TestScope] .second { color: red; } a[TestScope] b, c[TestScope] d { color: blue; } @@ -95,12 +102,13 @@ public void RespectsDeepCombinator() public void RespectsDeepCombinatorWithDirectDescendant() { // Arrange/act - var result = RewriteCssCommand.AddScopeToSelectors(@" + var result = RewriteCssCommand.AddScopeToSelectors("file.css", @" a > ::deep b { color: red; } c ::deep > d { color: blue; } -", "TestScope"); +", "TestScope", out var diagnostics); // Assert + Assert.Empty(diagnostics); Assert.Equal(@" a[TestScope] > b { color: red; } c[TestScope] > d { color: blue; } @@ -111,12 +119,13 @@ public void RespectsDeepCombinatorWithDirectDescendant() public void RespectsDeepCombinatorWithAdjacentSibling() { // Arrange/act - var result = RewriteCssCommand.AddScopeToSelectors(@" + var result = RewriteCssCommand.AddScopeToSelectors("file.css", @" a + ::deep b { color: red; } c ::deep + d { color: blue; } -", "TestScope"); +", "TestScope", out var diagnostics); // Assert + Assert.Empty(diagnostics); Assert.Equal(@" a[TestScope] + b { color: red; } c[TestScope] + d { color: blue; } @@ -127,12 +136,13 @@ public void RespectsDeepCombinatorWithAdjacentSibling() public void RespectsDeepCombinatorWithGeneralSibling() { // Arrange/act - var result = RewriteCssCommand.AddScopeToSelectors(@" + var result = RewriteCssCommand.AddScopeToSelectors("file.css", @" a ~ ::deep b { color: red; } c ::deep ~ d { color: blue; } -", "TestScope"); +", "TestScope", out var diagnostics); // Assert + Assert.Empty(diagnostics); Assert.Equal(@" a[TestScope] ~ b { color: red; } c[TestScope] ~ d { color: blue; } @@ -143,11 +153,12 @@ public void RespectsDeepCombinatorWithGeneralSibling() public void IgnoresMultipleDeepCombinators() { // Arrange/act - var result = RewriteCssCommand.AddScopeToSelectors(@" + var result = RewriteCssCommand.AddScopeToSelectors("file.css", @" .first ::deep .second ::deep .third { color:red; } -", "TestScope"); +", "TestScope", out var diagnostics); // Assert + Assert.Empty(diagnostics); Assert.Equal(@" .first[TestScope] .second ::deep .third { color:red; } ", result); @@ -157,13 +168,14 @@ public void IgnoresMultipleDeepCombinators() public void RespectsDeepCombinatorWithSpacesAndComments() { // Arrange/act - var result = RewriteCssCommand.AddScopeToSelectors(@" + var result = RewriteCssCommand.AddScopeToSelectors("file.css", @" .a .b /* comment ::deep 1 */ ::deep /* comment ::deep 2 */ .c /* ::deep */ .d { color: red; } ::deep * { color: blue; } /* Leading deep combinator */ another ::deep { color: green } /* Trailing deep combinator */ -", "TestScope"); +", "TestScope", out var diagnostics); // Assert + Assert.Empty(diagnostics); Assert.Equal(@" .a .b[TestScope] /* comment ::deep 1 */ /* comment ::deep 2 */ .c /* ::deep */ .d { color: red; } [TestScope] * { color: blue; } /* Leading deep combinator */ @@ -175,7 +187,7 @@ public void RespectsDeepCombinatorWithSpacesAndComments() public void HandlesAtBlocks() { // Arrange/act - var result = RewriteCssCommand.AddScopeToSelectors(@" + var result = RewriteCssCommand.AddScopeToSelectors("file.css", @" .myclass { color: red; } @media only screen and (max-width: 600px) { @@ -183,9 +195,10 @@ .another .thing { content: 'This should not be a selector: .fake-selector { color: red }' } } -", "TestScope"); +", "TestScope", out var diagnostics); // Assert + Assert.Empty(diagnostics); Assert.Equal(@" .myclass[TestScope] { color: red; } @@ -201,11 +214,12 @@ .another .thing[TestScope] { public void AddsScopeToKeyframeNames() { // Arrange/act - var result = RewriteCssCommand.AddScopeToSelectors(@" + var result = RewriteCssCommand.AddScopeToSelectors("file.css", @" @keyframes my-animation { /* whatever */ } -", "TestScope"); +", "TestScope", out var diagnostics); // Assert + Assert.Empty(diagnostics); Assert.Equal(@" @keyframes my-animation-TestScope { /* whatever */ } ", result); @@ -215,7 +229,7 @@ @keyframes my-animation-TestScope { /* whatever */ } public void RewritesAnimationNamesWhenMatchingKnownKeyframes() { // Arrange/act - var result = RewriteCssCommand.AddScopeToSelectors(@" + var result = RewriteCssCommand.AddScopeToSelectors("file.css", @" .myclass { color: red; animation: /* ignore comment */ my-animation 1s infinite; @@ -228,9 +242,10 @@ public void RewritesAnimationNamesWhenMatchingKnownKeyframes() @keyframes my-animation { /* whatever */ } @keyframes different-animation { /* whatever */ } @keyframes unused-animation { /* whatever */ } -", "TestScope"); +", "TestScope", out var diagnostics); // Assert + Assert.Empty(diagnostics); Assert.Equal(@" .myclass[TestScope] { color: red; @@ -251,14 +266,15 @@ @keyframes unused-animation-TestScope { /* whatever */ } public void RewritesMultipleAnimationNames() { // Arrange/act - var result = RewriteCssCommand.AddScopeToSelectors(@" + var result = RewriteCssCommand.AddScopeToSelectors("file.css", @" .myclass1 { animation-name: my-animation , different-animation } .myclass2 { animation: 4s linear 0s alternate my-animation infinite, different-animation 0s } @keyframes my-animation { } @keyframes different-animation { } -", "TestScope"); +", "TestScope", out var diagnostics); // Assert + Assert.Empty(diagnostics); Assert.Equal(@" .myclass1[TestScope] { animation-name: my-animation-TestScope , different-animation-TestScope } .myclass2[TestScope] { animation: 4s linear 0s alternate my-animation-TestScope infinite, different-animation-TestScope 0s } @@ -266,5 +282,27 @@ @keyframes my-animation-TestScope { } @keyframes different-animation-TestScope { } ", result); } + + [Fact] + public void RejectsImportStatements() + { + // Arrange/act + RewriteCssCommand.AddScopeToSelectors("file.css", @" + @import ""basic-import.css""; + @import ""import-with-media-type.css"" print; + @import ""import-with-media-query.css"" screen and (orientation:landscape); + @ImPoRt /* comment */ ""scheme://path/to/complex-import"" /* another-comment */ screen; + @otheratrule ""should-not-cause-error.css""; + /* @import ""should-be-ignored-because-it-is-in-a-comment.css""; */ + .myclass { color: red; } +", "TestScope", out var diagnostics); + + // Assert + Assert.Collection(diagnostics, + diagnostic => Assert.Equal("file.css(2,5): Error RZ5000: @import rules are not supported within scoped CSS files because the loading order would be undefined. @import may only be placed in non-scoped CSS files.", diagnostic.ToString()), + diagnostic => Assert.Equal("file.css(3,5): Error RZ5000: @import rules are not supported within scoped CSS files because the loading order would be undefined. @import may only be placed in non-scoped CSS files.", diagnostic.ToString()), + diagnostic => Assert.Equal("file.css(4,5): Error RZ5000: @import rules are not supported within scoped CSS files because the loading order would be undefined. @import may only be placed in non-scoped CSS files.", diagnostic.ToString()), + diagnostic => Assert.Equal("file.css(5,5): Error RZ5000: @import rules are not supported within scoped CSS files because the loading order would be undefined. @import may only be placed in non-scoped CSS files.", diagnostic.ToString())); + } } } diff --git a/src/Security/Authentication/Negotiate/samples/NegotiateAuthSample/Startup.cs b/src/Security/Authentication/Negotiate/samples/NegotiateAuthSample/Startup.cs index 2e62846f8f5d..c2b0b0ba7f81 100644 --- a/src/Security/Authentication/Negotiate/samples/NegotiateAuthSample/Startup.cs +++ b/src/Security/Authentication/Negotiate/samples/NegotiateAuthSample/Startup.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.Runtime.InteropServices; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication.Negotiate; using Microsoft.AspNetCore.Builder; @@ -22,6 +23,23 @@ public void ConfigureServices(IServiceCollection services) services.AddAuthentication(NegotiateDefaults.AuthenticationScheme) .AddNegotiate(options => { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + /* + options.EnableLdap("DOMAIN.net"); + + options.EnableLdap(settings => + { + // Mandatory settings + settings.Domain = "DOMAIN.com"; + // Optional settings + settings.MachineAccountName = "machineName"; + settings.MachineAccountPassword = "PassW0rd"; + settings.IgnoreNestedGroups = true; + }); + */ + } + options.Events = new NegotiateEvents() { OnAuthenticationFailed = context => diff --git a/src/Security/Authentication/Negotiate/src/Events/LdapContext.cs b/src/Security/Authentication/Negotiate/src/Events/LdapContext.cs new file mode 100644 index 000000000000..9e6d7a40ac82 --- /dev/null +++ b/src/Security/Authentication/Negotiate/src/Events/LdapContext.cs @@ -0,0 +1,35 @@ +// 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.Http; + +namespace Microsoft.AspNetCore.Authentication.Negotiate +{ + /// + /// State for the RetrieveLdapClaims event. + /// + public class LdapContext : ResultContext + { + /// + /// Creates a new . + /// + /// + /// + /// + /// + public LdapContext( + HttpContext context, + AuthenticationScheme scheme, + NegotiateOptions options, + LdapSettings settings) + : base(context, scheme, options) + { + LdapSettings = settings; + } + + /// + /// The LDAP settings to use for the RetrieveLdapClaims event. + /// + public LdapSettings LdapSettings { get; } + } +} diff --git a/src/Security/Authentication/Negotiate/src/Events/NegotiateEvents.cs b/src/Security/Authentication/Negotiate/src/Events/NegotiateEvents.cs index 0d57be28ebdc..88dfdf2b747b 100644 --- a/src/Security/Authentication/Negotiate/src/Events/NegotiateEvents.cs +++ b/src/Security/Authentication/Negotiate/src/Events/NegotiateEvents.cs @@ -16,6 +16,12 @@ public class NegotiateEvents /// public Func OnAuthenticationFailed { get; set; } = context => Task.CompletedTask; + /// + /// Invoked after the authentication before ClaimsIdentity is populated with claims retrieved through the LDAP connection. + /// This event is invoked when is set to true on . + /// + public Func OnRetrieveLdapClaims { get; set; } = context => Task.CompletedTask; + /// /// Invoked after the authentication is complete and a ClaimsIdentity has been generated. /// @@ -31,6 +37,11 @@ public class NegotiateEvents /// public virtual Task AuthenticationFailed(AuthenticationFailedContext context) => OnAuthenticationFailed(context); + /// + /// Invoked after the authentication before ClaimsIdentity is populated with claims retrieved through the LDAP connection. + /// + public virtual Task RetrieveLdapClaims(LdapContext context) => OnRetrieveLdapClaims(context); + /// /// Invoked after the authentication is complete and a ClaimsIdentity has been generated. /// diff --git a/src/Security/Authentication/Negotiate/src/Internal/LdapAdapter.cs b/src/Security/Authentication/Negotiate/src/Internal/LdapAdapter.cs new file mode 100644 index 000000000000..4ddad3c5e31b --- /dev/null +++ b/src/Security/Authentication/Negotiate/src/Internal/LdapAdapter.cs @@ -0,0 +1,97 @@ +// 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.DirectoryServices.Protocols; +using System.Linq; +using System.Security.Claims; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.Authentication.Negotiate +{ + internal static class LdapAdapter + { + public static async Task RetrieveClaimsAsync(LdapSettings settings, ClaimsIdentity identity, ILogger logger) + { + var user = identity.Name; + var userAccountName = user.Substring(0, user.IndexOf('@')); + var distinguishedName = settings.Domain.Split('.').Select(name => $"dc={name}").Aggregate((a, b) => $"{a},{b}"); + + var filter = $"(&(objectClass=user)(sAMAccountName={userAccountName}))"; // This is using ldap search query language, it is looking on the server for someUser + var searchRequest = new SearchRequest(distinguishedName, filter, SearchScope.Subtree, null); + var searchResponse = (SearchResponse) await Task.Factory.FromAsync( + settings.LdapConnection.BeginSendRequest, + settings.LdapConnection.EndSendRequest, + searchRequest, + PartialResultProcessing.NoPartialResultSupport, + null); + + if (searchResponse.Entries.Count > 0) + { + if (searchResponse.Entries.Count > 1) + { + logger.LogWarning($"More than one response received for query: {filter} with distinguished name: {distinguishedName}"); + } + + var userFound = searchResponse.Entries[0]; //Get the object that was found on ldap + var memberof = userFound.Attributes["memberof"]; // You can access ldap Attributes with Attributes property + + foreach (var group in memberof) + { + // Example distinguished name: CN=TestGroup,DC=KERB,DC=local + var groupDN = $"{Encoding.UTF8.GetString((byte[])group)}"; + var groupCN = groupDN.Split(',')[0].Substring("CN=".Length); + + if (!settings.IgnoreNestedGroups) + { + GetNestedGroups(settings.LdapConnection, identity, distinguishedName, groupCN, logger); + } + else + { + AddRole(identity, groupCN); + } + } + } + else + { + logger.LogWarning($"No response received for query: {filter} with distinguished name: {distinguishedName}"); + } + } + + private static void GetNestedGroups(LdapConnection connection, ClaimsIdentity principal, string distinguishedName, string groupCN, ILogger logger) + { + var filter = $"(&(objectClass=group)(sAMAccountName={groupCN}))"; // This is using ldap search query language, it is looking on the server for someUser + var searchRequest = new SearchRequest(distinguishedName, filter, System.DirectoryServices.Protocols.SearchScope.Subtree, null); + var searchResponse = (SearchResponse)connection.SendRequest(searchRequest); + + if (searchResponse.Entries.Count > 0) + { + if (searchResponse.Entries.Count > 1) + { + logger.LogWarning($"More than one response received for query: {filter} with distinguished name: {distinguishedName}"); + } + + var group = searchResponse.Entries[0]; //Get the object that was found on ldap + string name = group.DistinguishedName; + AddRole(principal, name); + + var memberof = group.Attributes["memberof"]; // You can access ldap Attributes with Attributes property + if (memberof != null) + { + foreach (var member in memberof) + { + var groupDN = $"{Encoding.UTF8.GetString((byte[])member)}"; + var nestedGroupCN = groupDN.Split(',')[0].Substring("CN=".Length); + GetNestedGroups(connection, principal, distinguishedName, nestedGroupCN, logger); + } + } + } + } + + private static void AddRole(ClaimsIdentity identity, string role) + { + identity.AddClaim(new Claim(identity.RoleClaimType, role)); + } + } +} diff --git a/src/Security/Authentication/Negotiate/src/Internal/NegotiateOptionsValidationStartupFilter.cs b/src/Security/Authentication/Negotiate/src/Internal/NegotiateOptionsValidationStartupFilter.cs new file mode 100644 index 000000000000..429a57d81e90 --- /dev/null +++ b/src/Security/Authentication/Negotiate/src/Internal/NegotiateOptionsValidationStartupFilter.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 Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Authentication.Negotiate.Internal +{ + internal class NegotiateOptionsValidationStartupFilter : IStartupFilter + { + private readonly string _authenticationScheme; + + public NegotiateOptionsValidationStartupFilter(string authenticationScheme) + { + _authenticationScheme = authenticationScheme; + } + + public Action Configure(Action next) + { + return builder => + { + // Resolve NegotiateOptions on startup to trigger post configuration and bind LdapConnection if needed + var options = builder.ApplicationServices.GetRequiredService>().Get(_authenticationScheme); + next(builder); + }; + } + } +} diff --git a/src/Security/Authentication/Negotiate/src/LdapSettings.cs b/src/Security/Authentication/Negotiate/src/LdapSettings.cs new file mode 100644 index 000000000000..1e26c26c14d4 --- /dev/null +++ b/src/Security/Authentication/Negotiate/src/LdapSettings.cs @@ -0,0 +1,75 @@ +// 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.DirectoryServices.Protocols; + +namespace Microsoft.AspNetCore.Authentication.Negotiate +{ + /// + /// Options class for configuring LDAP connections on Linux + /// + public class LdapSettings + { + /// + /// Configure whether LDAP connection should be used to resolve claims. + /// This is mainly used on Linux. + /// + public bool EnableLdapClaimResolution { get; set; } + + /// + /// The domain to use for the LDAP connection. This is a mandatory setting. + /// + /// + /// DOMAIN.com + /// + public string Domain { get; set; } + + /// + /// The machine account name to use when opening the LDAP connection. + /// If this is not provided, the machine wide credentials of the + /// domain joined machine will be used. + /// + public string MachineAccountName { get; set; } + + /// + /// The machine account password to use when opening the LDAP connection. + /// This must be provided if a is provided. + /// + public string MachineAccountPassword { get; set; } + + /// + /// This option indicates whether nested groups should be ignored when + /// resolving Roles. The default is false. + /// + public bool IgnoreNestedGroups { get; set; } + + /// + /// The to be used to retrieve role claims. + /// If no explicit connection is provided, an LDAP connection will be + /// automatically created based on the , + /// and + /// options. If provided, this connection will be used and the + /// , and + /// options will not be used to create + /// the . + /// + public LdapConnection LdapConnection { get; set; } + + public void Validate() + { + if (EnableLdapClaimResolution) + { + if (string.IsNullOrEmpty(Domain)) + { + throw new ArgumentException($"{nameof(EnableLdapClaimResolution)} is set to true but {nameof(Domain)} is not set."); + } + + if (string.IsNullOrEmpty(MachineAccountName) && !string.IsNullOrEmpty(MachineAccountPassword)) + { + throw new ArgumentException($"{nameof(MachineAccountPassword)} should only be specified when {nameof(MachineAccountName)} is configured."); + } + } + } + } +} diff --git a/src/Security/Authentication/Negotiate/src/Microsoft.AspNetCore.Authentication.Negotiate.csproj b/src/Security/Authentication/Negotiate/src/Microsoft.AspNetCore.Authentication.Negotiate.csproj index 265ffb533a11..c0aac839f5f7 100644 --- a/src/Security/Authentication/Negotiate/src/Microsoft.AspNetCore.Authentication.Negotiate.csproj +++ b/src/Security/Authentication/Negotiate/src/Microsoft.AspNetCore.Authentication.Negotiate.csproj @@ -10,7 +10,9 @@ + +
diff --git a/src/Security/Authentication/Negotiate/src/NegotiateExtensions.cs b/src/Security/Authentication/Negotiate/src/NegotiateExtensions.cs index f5bbf8cbc860..e47417e17001 100644 --- a/src/Security/Authentication/Negotiate/src/NegotiateExtensions.cs +++ b/src/Security/Authentication/Negotiate/src/NegotiateExtensions.cs @@ -4,6 +4,8 @@ using System; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Negotiate; +using Microsoft.AspNetCore.Authentication.Negotiate.Internal; +using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; @@ -52,6 +54,7 @@ public static AuthenticationBuilder AddNegotiate(this AuthenticationBuilder buil public static AuthenticationBuilder AddNegotiate(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action configureOptions) { builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton, PostConfigureNegotiateOptions>()); + builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton(new NegotiateOptionsValidationStartupFilter(authenticationScheme))); return builder.AddScheme(authenticationScheme, displayName, configureOptions); } } diff --git a/src/Security/Authentication/Negotiate/src/NegotiateHandler.cs b/src/Security/Authentication/Negotiate/src/NegotiateHandler.cs index 835542a42d5a..0ef6697857b4 100644 --- a/src/Security/Authentication/Negotiate/src/NegotiateHandler.cs +++ b/src/Security/Authentication/Negotiate/src/NegotiateHandler.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Runtime.InteropServices; using System.Security.Claims; using System.Security.Principal; using System.Text.Encodings.Web; @@ -324,10 +325,37 @@ protected override async Task HandleAuthenticateAsync() user = new ClaimsPrincipal(new ClaimsIdentity(identity)); } - var authenticatedContext = new AuthenticatedContext(Context, Scheme, Options) + AuthenticatedContext authenticatedContext; + + if (Options.LdapSettings.EnableLdapClaimResolution) { - Principal = user - }; + var ldapContext = new LdapContext(Context, Scheme, Options, Options.LdapSettings) + { + Principal = user + }; + + await Events.RetrieveLdapClaims(ldapContext); + + if (ldapContext.Result != null) + { + return ldapContext.Result; + } + + await LdapAdapter.RetrieveClaimsAsync(ldapContext.LdapSettings, ldapContext.Principal.Identity as ClaimsIdentity, Logger); + + authenticatedContext = new AuthenticatedContext(Context, Scheme, Options) + { + Principal = ldapContext.Principal + }; + } + else + { + authenticatedContext = new AuthenticatedContext(Context, Scheme, Options) + { + Principal = user + }; + } + await Events.Authenticated(authenticatedContext); if (authenticatedContext.Result != null) diff --git a/src/Security/Authentication/Negotiate/src/NegotiateOptions.cs b/src/Security/Authentication/Negotiate/src/NegotiateOptions.cs index 3f5d36b39f6d..40d090265c4b 100644 --- a/src/Security/Authentication/Negotiate/src/NegotiateOptions.cs +++ b/src/Security/Authentication/Negotiate/src/NegotiateOptions.cs @@ -1,6 +1,8 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; + namespace Microsoft.AspNetCore.Authentication.Negotiate { /// @@ -33,6 +35,42 @@ public class NegotiateOptions : AuthenticationSchemeOptions /// public bool PersistNtlmCredentials { get; set; } = true; + /// + /// Configuration settings for LDAP connections used to retrieve claims. + /// This should only be used on Linux systems. + /// + internal LdapSettings LdapSettings { get; } = new LdapSettings(); + + /// + /// Use LDAP connections used to retrieve claims for the given domain. + /// This should only be used on Linux systems. + /// + public void EnableLdap(string domain) + { + if (string.IsNullOrEmpty(domain)) + { + throw new ArgumentNullException(nameof(domain)); + } + + LdapSettings.EnableLdapClaimResolution = true; + LdapSettings.Domain = domain; + } + + /// + /// Use LDAP connections used to retrieve claims using the configured settings. + /// This should only be used on Linux systems. + /// + public void EnableLdap(Action configureSettings) + { + if (configureSettings == null) + { + throw new ArgumentNullException(nameof(configureSettings)); + } + + LdapSettings.EnableLdapClaimResolution = true; + configureSettings(LdapSettings); + } + /// /// Indicates if integrated server Windows Auth is being used instead of this handler. /// See . diff --git a/src/Security/Authentication/Negotiate/src/PostConfigureNegotiateOptions.cs b/src/Security/Authentication/Negotiate/src/PostConfigureNegotiateOptions.cs index 91384c32932b..4fb8a29be41a 100644 --- a/src/Security/Authentication/Negotiate/src/PostConfigureNegotiateOptions.cs +++ b/src/Security/Authentication/Negotiate/src/PostConfigureNegotiateOptions.cs @@ -4,7 +4,9 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.DirectoryServices.Protocols; using System.Linq; +using System.Net; using Microsoft.AspNetCore.Hosting.Server; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -59,6 +61,36 @@ public void PostConfigure(string name, NegotiateOptions options) + " Enable Windows Authentication for the server and the Negotiate Authentication handler will defer to it."); } } + + var ldapSettings = options.LdapSettings; + + if (ldapSettings.EnableLdapClaimResolution) + { + ldapSettings.Validate(); + + if (ldapSettings.LdapConnection == null) + { + var di = new LdapDirectoryIdentifier(server: ldapSettings.Domain, fullyQualifiedDnsHostName: true, connectionless: false); + + if (string.IsNullOrEmpty(ldapSettings.MachineAccountName)) + { + // Use default credentials + ldapSettings.LdapConnection = new LdapConnection(di); + } + else + { + // Use specific specific machine account + var machineAccount = ldapSettings.MachineAccountName + "@" + ldapSettings.Domain; + var credentials = new NetworkCredential(machineAccount, ldapSettings.MachineAccountPassword); + ldapSettings.LdapConnection = new LdapConnection(di, credentials); + } + + ldapSettings.LdapConnection.SessionOptions.ProtocolVersion = 3; //Setting LDAP Protocol to latest version + ldapSettings.LdapConnection.Timeout = TimeSpan.FromMinutes(1); + } + + ldapSettings.LdapConnection.Bind(); // This line actually makes the connection. + } } } } diff --git a/src/Security/Authentication/Negotiate/test/Negotiate.Test/EventTests.cs b/src/Security/Authentication/Negotiate/test/Negotiate.Test/EventTests.cs index 097964020752..af471a5450ed 100644 --- a/src/Security/Authentication/Negotiate/test/Negotiate.Test/EventTests.cs +++ b/src/Security/Authentication/Negotiate/test/Negotiate.Test/EventTests.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Reflection.Metadata; using System.Security.Principal; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; @@ -13,7 +12,6 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.TestHost; -using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Net.Http.Headers; @@ -371,6 +369,27 @@ public async Task OnAuthenticated_Fail_SuppresesCredentials() Assert.Equal(1, callCount); } + [Fact] + public async Task OnRetrieveLdapClaims_DoesNotFireWhenLdapDisabled() + { + var callCount = 0; + using var host = await CreateHostAsync(options => + { + options.Events = new NegotiateEvents() + { + OnRetrieveLdapClaims = context => + { + callCount++; + return Task.CompletedTask; + } + }; + }); + var server = host.GetTestServer(); + + await KerberosStage1And2Auth(server, new TestConnection()); + Assert.Equal(0, callCount); + } + private static async Task KerberosStage1And2Auth(TestServer server, TestConnection testConnection) { await KerberosStage1Auth(server, testConnection); diff --git a/src/Security/Authentication/Negotiate/test/Negotiate.Test/LdapSettingsValidationTests.cs b/src/Security/Authentication/Negotiate/test/Negotiate.Test/LdapSettingsValidationTests.cs new file mode 100644 index 000000000000..6a706d820bc1 --- /dev/null +++ b/src/Security/Authentication/Negotiate/test/Negotiate.Test/LdapSettingsValidationTests.cs @@ -0,0 +1,34 @@ +// 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 Xunit; + +namespace Microsoft.AspNetCore.Authentication.Negotiate.Test +{ + public class LdapSettingsValidationTests + { + [Fact] + public void EnabledWithoutDomainThrows() + { + var settings = new LdapSettings + { + EnableLdapClaimResolution = true + }; + + Assert.Throws(() => settings.Validate()); + } + + [Fact] + public void AccountPasswordWithoutAccountNameThrows() + { + var settings = new LdapSettings + { + EnableLdapClaimResolution = true, + MachineAccountPassword = "Passw0rd" + }; + + Assert.Throws(() => settings.Validate()); + } + } +} diff --git a/src/Security/Authentication/Negotiate/test/Negotiate.Test/ServerDeferralTests.cs b/src/Security/Authentication/Negotiate/test/Negotiate.Test/ServerDeferralTests.cs index efd513b829c1..f038c7ca806f 100644 --- a/src/Security/Authentication/Negotiate/test/Negotiate.Test/ServerDeferralTests.cs +++ b/src/Security/Authentication/Negotiate/test/Negotiate.Test/ServerDeferralTests.cs @@ -29,8 +29,7 @@ public async Task ServerDoesNotSupportAuth_NoError() [Fact] public async Task ServerSupportsAuthButDisabled_Error() { - using var host = await CreateHostAsync(supportsAuth: true, isEnabled: false); - var ex = Assert.Throws(() => host.Services.GetRequiredService>().Value); + var ex = await Assert.ThrowsAsync(async () => await CreateHostAsync(supportsAuth: true, isEnabled: false)); Assert.Equal("The Negotiate Authentication handler cannot be used on a server that directly supports Windows Authentication." + " Enable Windows Authentication for the server and the Negotiate Authentication handler will defer to it.", ex.Message); } diff --git a/src/Servers/IIS/IIS/src/Core/IISConfigurationData.cs b/src/Servers/IIS/IIS/src/Core/IISConfigurationData.cs index 21d00ed097a3..8fdf38446775 100644 --- a/src/Servers/IIS/IIS/src/Core/IISConfigurationData.cs +++ b/src/Servers/IIS/IIS/src/Core/IISConfigurationData.cs @@ -19,6 +19,6 @@ internal struct IISConfigurationData public bool fAnonymousAuthEnable; [MarshalAs(UnmanagedType.BStr)] public string pwzBindings; - public int maxRequestBodySize; + public uint maxRequestBodySize; } } diff --git a/src/Servers/IIS/IIS/src/Core/IISHttpContextOfT.cs b/src/Servers/IIS/IIS/src/Core/IISHttpContextOfT.cs index dad75729e310..c8f9f4505c37 100644 --- a/src/Servers/IIS/IIS/src/Core/IISHttpContextOfT.cs +++ b/src/Servers/IIS/IIS/src/Core/IISHttpContextOfT.cs @@ -3,8 +3,6 @@ using System; using System.Buffers; -using System.Text; -using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting.Server; diff --git a/src/Servers/IIS/IIS/src/IISServerOptions.cs b/src/Servers/IIS/IIS/src/IISServerOptions.cs index bf83ca2e2e85..430f1b3b77f9 100644 --- a/src/Servers/IIS/IIS/src/IISServerOptions.cs +++ b/src/Servers/IIS/IIS/src/IISServerOptions.cs @@ -42,16 +42,17 @@ public class IISServerOptions // https://www.iis.net/configreference/system.webserver/security/requestfiltering/requestlimits#005 private long? _maxRequestBodySize = 30000000; - internal long IisMaxRequestSizeLimit; + internal long IisMaxRequestSizeLimit; // Used for verifying if limit set in managed exceeds native /// /// Gets or sets the maximum allowed size of any request body in bytes. - /// When set to null, the maximum request body size is unlimited. + /// When set to null, the maximum request length will not be restricted in ASP.NET Core. + /// However, the IIS maxAllowedContentLength will still restrict content length requests (30,000,000 by default). /// This limit has no effect on upgraded connections which are always unlimited. /// This can be overridden per-request via . /// /// - /// Defaults to null (unlimited). + /// Defaults to 30,000,000 bytes (~28.6 MB). /// public long? MaxRequestBodySize { diff --git a/src/Servers/IIS/IIS/src/WebHostBuilderIISExtensions.cs b/src/Servers/IIS/IIS/src/WebHostBuilderIISExtensions.cs index fa52725c0acc..0a4dd7e4d744 100644 --- a/src/Servers/IIS/IIS/src/WebHostBuilderIISExtensions.cs +++ b/src/Servers/IIS/IIS/src/WebHostBuilderIISExtensions.cs @@ -49,6 +49,7 @@ public static IWebHostBuilder UseIIS(this IWebHostBuilder hostBuilder) options => { options.ServerAddresses = iisConfigData.pwzBindings.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries); options.ForwardWindowsAuthentication = iisConfigData.fWindowsAuthEnabled || iisConfigData.fBasicAuthEnabled; + options.MaxRequestBodySize = iisConfigData.maxRequestBodySize; options.IisMaxRequestSizeLimit = iisConfigData.maxRequestBodySize; } ); diff --git a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/MaxRequestBodySizeTests.cs b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/MaxRequestBodySizeTests.cs index f8e88efa84f7..e4d951c63f3e 100644 --- a/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/MaxRequestBodySizeTests.cs +++ b/src/Servers/IIS/IIS/test/Common.FunctionalTests/Inprocess/MaxRequestBodySizeTests.cs @@ -54,6 +54,46 @@ public async Task SetIISLimitMaxRequestBodySizeE2EWorks() Assert.True(result.StatusCode == HttpStatusCode.NotFound || result.StatusCode == HttpStatusCode.RequestEntityTooLarge); } + [ConditionalFact] + [RequiresNewHandler] + public async Task SetIISLimitMaxRequestBodySizeE2EWorksWithLargerLimit() + { + var deploymentParameters = Fixture.GetBaseDeploymentParameters(); + deploymentParameters.ServerConfigActionList.Add( + (config, _) => { + config + .RequiredElement("system.webServer") + .GetOrAdd("security") + .GetOrAdd("requestFiltering") + .GetOrAdd("requestLimits", "maxAllowedContentLength", "100000000"); + }); + var deploymentResult = await DeployAsync(deploymentParameters); + + var result = await deploymentResult.HttpClient.PostAsync("/ReadRequestBodyLarger", new StringContent(new string('a', 100000000))); + + Assert.Equal(HttpStatusCode.OK, result.StatusCode); + } + + [ConditionalFact] + [RequiresNewHandler] + public async Task SetIISLimitMaxRequestBodySizeE2EWorksWithIntMaxValue() + { + var deploymentParameters = Fixture.GetBaseDeploymentParameters(); + deploymentParameters.ServerConfigActionList.Add( + (config, _) => { + config + .RequiredElement("system.webServer") + .GetOrAdd("security") + .GetOrAdd("requestFiltering") + .GetOrAdd("requestLimits", "maxAllowedContentLength", "4294967295"); + }); + var deploymentResult = await DeployAsync(deploymentParameters); + + var result = await deploymentResult.HttpClient.PostAsync("/ReadRequestBodyLarger", new StringContent(new string('a', 10000))); + + Assert.Equal(HttpStatusCode.OK, result.StatusCode); + } + [ConditionalFact] [RequiresNewHandler] public async Task IISRejectsContentLengthTooLargeByDefault() @@ -94,7 +134,7 @@ public async Task SetIISLimitMaxRequestBodyLogsWarning() }); var deploymentResult = await DeployAsync(deploymentParameters); - var result = await deploymentResult.HttpClient.PostAsync("/DecreaseRequestLimit", new StringContent("1")); + var result = await deploymentResult.HttpClient.PostAsync("/IncreaseRequestLimit", new StringContent("1")); Assert.Equal(HttpStatusCode.OK, result.StatusCode); StopServer(); diff --git a/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Startup.cs b/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Startup.cs index ef7223b2d55b..0d23030f82ce 100644 --- a/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Startup.cs +++ b/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/Startup.cs @@ -478,6 +478,16 @@ private async Task ReadRequestBody(HttpContext ctx) } } + private async Task ReadRequestBodyLarger(HttpContext ctx) + { + var readBuffer = new byte[4096]; + var result = await ctx.Request.Body.ReadAsync(readBuffer, 0, 4096); + while (result != 0) + { + result = await ctx.Request.Body.ReadAsync(readBuffer, 0, 4096); + } + } + private int _requestsInFlight = 0; private async Task ReadAndCountRequestBody(HttpContext ctx) { @@ -1444,6 +1454,13 @@ public async Task Reset_CompleteAsyncDuringRequestBody_Resets(HttpContext httpCo await Assert.ThrowsAsync(() => readTask); } + public Task IncreaseRequestLimit(HttpContext httpContext) + { + var maxRequestBodySizeFeature = httpContext.Features.Get(); + maxRequestBodySizeFeature.MaxRequestBodySize = 2; + return Task.CompletedTask; + } + internal static readonly HashSet<(string, StringValues, StringValues)> NullTrailers = new HashSet<(string, StringValues, StringValues)>() { ("NullString", (string)null, (string)null), diff --git a/src/Shared/Components/WebAssemblyComponentMarker.cs b/src/Shared/Components/WebAssemblyComponentMarker.cs new file mode 100644 index 000000000000..bd8fab295aea --- /dev/null +++ b/src/Shared/Components/WebAssemblyComponentMarker.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; + +namespace Microsoft.AspNetCore.Components +{ + internal struct WebAssemblyComponentMarker + { + public const string ClientMarkerType = "webassembly"; + + public WebAssemblyComponentMarker(string type, string assembly, string typeName, string parameterDefinitions, string parameterValues, string prereenderId) => + (Type, Assembly, TypeName, ParameterDefinitions, ParameterValues, PrerenderId) = (type, assembly, typeName, parameterDefinitions, parameterValues, prereenderId); + + public string Type { get; set; } + + public string Assembly { get; set; } + + public string TypeName { get; set; } + + public string ParameterDefinitions { get; set; } + + public string ParameterValues { get; set; } + + public string PrerenderId { get; set; } + + internal static WebAssemblyComponentMarker NonPrerendered(string assembly, string typeName, string parameterDefinitions, string parameterValues) => + new WebAssemblyComponentMarker(ClientMarkerType, assembly, typeName, parameterDefinitions, parameterValues, null); + + internal static WebAssemblyComponentMarker Prerendered(string assembly, string typeName, string parameterDefinitions, string parameterValues) => + new WebAssemblyComponentMarker(ClientMarkerType, assembly, typeName, parameterDefinitions, parameterValues, Guid.NewGuid().ToString("N")); + + public WebAssemblyComponentMarker GetEndRecord() + { + if (PrerenderId == null) + { + throw new InvalidOperationException("Can't get an end record for non-prerendered components."); + } + + return new WebAssemblyComponentMarker(null, null, null, null, null, PrerenderId); + } + } +} diff --git a/src/Shared/Components/WebAssemblyComponentSerializationSettings.cs b/src/Shared/Components/WebAssemblyComponentSerializationSettings.cs new file mode 100644 index 000000000000..741ab989d594 --- /dev/null +++ b/src/Shared/Components/WebAssemblyComponentSerializationSettings.cs @@ -0,0 +1,19 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Text.Json; + +namespace Microsoft.AspNetCore.Components +{ + internal static class WebAssemblyComponentSerializationSettings + { + public static readonly JsonSerializerOptions JsonSerializationOptions = + new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + PropertyNameCaseInsensitive = true, + IgnoreNullValues = true + }; + } +} diff --git a/src/SignalR/clients/java/signalr/src/main/java/com/microsoft/signalr/HubConnection.java b/src/SignalR/clients/java/signalr/src/main/java/com/microsoft/signalr/HubConnection.java index 12349152dc1e..b0bcbb364829 100644 --- a/src/SignalR/clients/java/signalr/src/main/java/com/microsoft/signalr/HubConnection.java +++ b/src/SignalR/clients/java/signalr/src/main/java/com/microsoft/signalr/HubConnection.java @@ -66,6 +66,7 @@ public class HubConnection implements AutoCloseable { private final int negotiateVersion = 1; private final Logger logger = LoggerFactory.getLogger(HubConnection.class); private ScheduledExecutorService handshakeTimeout = null; + private Completable start; /** * Sets the server timeout interval for the connection. @@ -341,83 +342,99 @@ public void setBaseUrl(String url) { * @return A Completable that completes when the connection has been established. */ public Completable start() { - if (hubConnectionState != HubConnectionState.DISCONNECTED) { - return Completable.complete(); - } - - handshakeResponseSubject = CompletableSubject.create(); - handshakeReceived = false; - CompletableSubject tokenCompletable = CompletableSubject.create(); - localHeaders.put(UserAgentHelper.getUserAgentName(), UserAgentHelper.createUserAgentString()); - if (headers != null) { - this.localHeaders.putAll(headers); - } + CompletableSubject localStart = CompletableSubject.create(); - accessTokenProvider.subscribe(token -> { - if (token != null && !token.isEmpty()) { - this.localHeaders.put("Authorization", "Bearer " + token); + hubConnectionStateLock.lock(); + try { + if (hubConnectionState != HubConnectionState.DISCONNECTED) { + logger.debug("The connection is in the '{}' state. Waiting for in-progress start to complete or completing this start immediately.", hubConnectionState); + return start; } - tokenCompletable.onComplete(); - }, error -> { - tokenCompletable.onError(error); - }); - stopError = null; - Single negotiate = null; - if (!skipNegotiate) { - negotiate = tokenCompletable.andThen(Single.defer(() -> startNegotiate(baseUrl, 0))); - } else { - negotiate = tokenCompletable.andThen(Single.defer(() -> Single.just(new NegotiateResponse(baseUrl)))); - } + hubConnectionState = HubConnectionState.CONNECTING; + start = localStart; - CompletableSubject start = CompletableSubject.create(); + handshakeResponseSubject = CompletableSubject.create(); + handshakeReceived = false; + CompletableSubject tokenCompletable = CompletableSubject.create(); + localHeaders.put(UserAgentHelper.getUserAgentName(), UserAgentHelper.createUserAgentString()); + if (headers != null) { + this.localHeaders.putAll(headers); + } - negotiate.flatMapCompletable(negotiateResponse -> { - logger.debug("Starting HubConnection."); - if (transport == null) { - Single tokenProvider = negotiateResponse.getAccessToken() != null ? Single.just(negotiateResponse.getAccessToken()) : accessTokenProvider; - switch (transportEnum) { - case LONG_POLLING: - transport = new LongPollingTransport(localHeaders, httpClient, tokenProvider); - break; - default: - transport = new WebSocketTransport(localHeaders, httpClient); + accessTokenProvider.subscribe(token -> { + if (token != null && !token.isEmpty()) { + this.localHeaders.put("Authorization", "Bearer " + token); } + tokenCompletable.onComplete(); + }, error -> { + tokenCompletable.onError(error); + }); + + stopError = null; + Single negotiate = null; + if (!skipNegotiate) { + negotiate = tokenCompletable.andThen(Single.defer(() -> startNegotiate(baseUrl, 0))); + } else { + negotiate = tokenCompletable.andThen(Single.defer(() -> Single.just(new NegotiateResponse(baseUrl)))); } - transport.setOnReceive(this.callback); - transport.setOnClose((message) -> stopConnection(message)); - - return transport.start(negotiateResponse.getFinalUrl()).andThen(Completable.defer(() -> { - ByteBuffer handshake = HandshakeProtocol.createHandshakeRequestMessage( - new HandshakeRequestMessage(protocol.getName(), protocol.getVersion())); - - connectionState = new ConnectionState(this); - - return transport.send(handshake).andThen(Completable.defer(() -> { - timeoutHandshakeResponse(handshakeResponseTimeout, TimeUnit.MILLISECONDS); - return handshakeResponseSubject.andThen(Completable.defer(() -> { - hubConnectionStateLock.lock(); - try { - hubConnectionState = HubConnectionState.CONNECTED; - logger.info("HubConnection started."); - resetServerTimeout(); - //Don't send pings if we're using long polling. - if (transportEnum != TransportEnum.LONG_POLLING) { - activatePingTimer(); + negotiate.flatMapCompletable(negotiateResponse -> { + logger.debug("Starting HubConnection."); + if (transport == null) { + Single tokenProvider = negotiateResponse.getAccessToken() != null ? Single.just(negotiateResponse.getAccessToken()) : accessTokenProvider; + switch (transportEnum) { + case LONG_POLLING: + transport = new LongPollingTransport(localHeaders, httpClient, tokenProvider); + break; + default: + transport = new WebSocketTransport(localHeaders, httpClient); + } + } + + transport.setOnReceive(this.callback); + transport.setOnClose((message) -> stopConnection(message)); + + return transport.start(negotiateResponse.getFinalUrl()).andThen(Completable.defer(() -> { + ByteBuffer handshake = HandshakeProtocol.createHandshakeRequestMessage( + new HandshakeRequestMessage(protocol.getName(), protocol.getVersion())); + + connectionState = new ConnectionState(this); + + return transport.send(handshake).andThen(Completable.defer(() -> { + timeoutHandshakeResponse(handshakeResponseTimeout, TimeUnit.MILLISECONDS); + return handshakeResponseSubject.andThen(Completable.defer(() -> { + hubConnectionStateLock.lock(); + try { + hubConnectionState = HubConnectionState.CONNECTED; + logger.info("HubConnection started."); + resetServerTimeout(); + //Don't send pings if we're using long polling. + if (transportEnum != TransportEnum.LONG_POLLING) { + activatePingTimer(); + } + } finally { + hubConnectionStateLock.unlock(); } - } finally { - hubConnectionStateLock.unlock(); - } - return Completable.complete(); + return Completable.complete(); + })); })); })); - })); - // subscribe makes this a "hot" completable so this runs immediately - }).subscribeWith(start); + // subscribe makes this a "hot" completable so this runs immediately + }).subscribe(() -> { + localStart.onComplete(); + }, error -> { + hubConnectionStateLock.lock(); + hubConnectionState = HubConnectionState.DISCONNECTED; + hubConnectionStateLock.unlock(); + localStart.onError(error); + }); + } finally { + hubConnectionStateLock.unlock(); + } - return start; + return localStart; } private void activatePingTimer() { @@ -445,8 +462,8 @@ public void run() { } private Single startNegotiate(String url, int negotiateAttempts) { - if (hubConnectionState != HubConnectionState.DISCONNECTED) { - return Single.just(null); + if (hubConnectionState != HubConnectionState.CONNECTING) { + throw new RuntimeException("HubConnection trying to negotiate when not in the CONNECTING state."); } return handleNegotiate(url).flatMap(response -> { diff --git a/src/SignalR/clients/java/signalr/src/main/java/com/microsoft/signalr/HubConnectionState.java b/src/SignalR/clients/java/signalr/src/main/java/com/microsoft/signalr/HubConnectionState.java index a357946b6aec..d3fc796ee410 100644 --- a/src/SignalR/clients/java/signalr/src/main/java/com/microsoft/signalr/HubConnectionState.java +++ b/src/SignalR/clients/java/signalr/src/main/java/com/microsoft/signalr/HubConnectionState.java @@ -9,4 +9,5 @@ public enum HubConnectionState { CONNECTED, DISCONNECTED, + CONNECTING, } \ No newline at end of file diff --git a/src/SignalR/clients/java/signalr/src/test/java/com/microsoft/signalr/HubConnectionTest.java b/src/SignalR/clients/java/signalr/src/test/java/com/microsoft/signalr/HubConnectionTest.java index efee55e3ef3a..89b5a0efdd9e 100644 --- a/src/SignalR/clients/java/signalr/src/test/java/com/microsoft/signalr/HubConnectionTest.java +++ b/src/SignalR/clients/java/signalr/src/test/java/com/microsoft/signalr/HubConnectionTest.java @@ -24,6 +24,7 @@ import io.reactivex.Observable; import io.reactivex.Single; import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; import io.reactivex.subjects.CompletableSubject; import io.reactivex.subjects.PublishSubject; import io.reactivex.subjects.ReplaySubject; @@ -2577,9 +2578,10 @@ public void receiveHandshakeResponseAndMessage() { value.getAndUpdate((val) -> val + 1); }); + SingleSubject handshakeMessageTask = mockTransport.getNextSentMessage(); // On start we're going to receive the handshake response and also an invocation in the same payload. hubConnection.start(); - mockTransport.getStartTask().timeout(1, TimeUnit.SECONDS).blockingAwait(); + ByteBuffer sentMessage = handshakeMessageTask.timeout(1, TimeUnit.SECONDS).blockingGet(); String expectedSentMessage = "{\"protocol\":\"json\",\"version\":1}" + RECORD_SEPARATOR; assertEquals(expectedSentMessage, TestUtils.byteBufferToString(mockTransport.getSentMessages()[0])); @@ -2647,7 +2649,7 @@ public void hubConnectionClosesAndRunsOnClosedCallbackAfterCloseMessageWithError } @Test - public void callingStartOnStartedHubConnectionNoOps() { + public void callingStartOnStartedHubConnectionNoops() { HubConnection hubConnection = TestUtils.createHubConnection("http://example.com"); hubConnection.start().timeout(1, TimeUnit.SECONDS).blockingAwait(); assertEquals(HubConnectionState.CONNECTED, hubConnection.getConnectionState()); @@ -2655,7 +2657,35 @@ public void callingStartOnStartedHubConnectionNoOps() { hubConnection.start().timeout(1, TimeUnit.SECONDS).blockingAwait(); assertEquals(HubConnectionState.CONNECTED, hubConnection.getConnectionState()); - hubConnection.stop(); + hubConnection.stop().timeout(1, TimeUnit.SECONDS).blockingAwait(); + assertEquals(HubConnectionState.DISCONNECTED, hubConnection.getConnectionState()); + } + + @Test + public void callingStartOnStartingHubConnectionWaitsForOriginalStart() { + CompletableSubject startedAccessToken = CompletableSubject.create(); + CompletableSubject continueAccessToken = CompletableSubject.create(); + HubConnection hubConnection = HubConnectionBuilder.create("http://example.com") + .withTransportImplementation(new MockTransport(true)) + .withHttpClient(new TestHttpClient()) + .withAccessTokenProvider(Single.defer(() -> { + startedAccessToken.onComplete(); + continueAccessToken.timeout(1, TimeUnit.SECONDS).blockingAwait(); + return Single.just("test"); + }).subscribeOn(Schedulers.newThread())) + .shouldSkipNegotiate(true) + .build(); + Completable start = hubConnection.start(); + startedAccessToken.timeout(1, TimeUnit.SECONDS).blockingAwait(); + assertEquals(HubConnectionState.CONNECTING, hubConnection.getConnectionState()); + + Completable start2 = hubConnection.start(); + continueAccessToken.onComplete(); + start.timeout(1, TimeUnit.SECONDS).blockingAwait(); + start2.timeout(1, TimeUnit.SECONDS).blockingAwait(); + assertEquals(HubConnectionState.CONNECTED, hubConnection.getConnectionState()); + + hubConnection.stop().timeout(1, TimeUnit.SECONDS).blockingAwait(); assertEquals(HubConnectionState.DISCONNECTED, hubConnection.getConnectionState()); } @@ -3595,9 +3625,6 @@ public void userSetAuthHeaderIsNotClearedAfterRedirect() { assertEquals(HubConnectionState.CONNECTED, hubConnection.getConnectionState()); hubConnection.stop().blockingAwait(); assertEquals("ExampleValue", beforeRedirectHeader.get()); - - hubConnection.start().timeout(1, TimeUnit.SECONDS).blockingAwait(); - assertEquals(HubConnectionState.CONNECTED, hubConnection.getConnectionState()); assertEquals("Bearer redirectToken", afterRedirectHeader.get()); // Making sure you can do this after restarting the HubConnection. @@ -3605,9 +3632,6 @@ public void userSetAuthHeaderIsNotClearedAfterRedirect() { assertEquals(HubConnectionState.CONNECTED, hubConnection.getConnectionState()); hubConnection.stop().blockingAwait(); assertEquals("ExampleValue", beforeRedirectHeader.get()); - - hubConnection.start().timeout(1, TimeUnit.SECONDS).blockingAwait(); - assertEquals(HubConnectionState.CONNECTED, hubConnection.getConnectionState()); assertEquals("Bearer redirectToken", afterRedirectHeader.get()); } @@ -3699,7 +3723,7 @@ public void non200FromNegotiateThrowsError() { } @Test - public void hubConnectionCloseCallsStop() throws Exception { + public void hubConnectionCloseCallsStop() { MockTransport mockTransport = new MockTransport(); TestHttpClient client = new TestHttpClient() .on("POST", "http://example.com/negotiate?negotiateVersion=1", (req) -> Single.just(new HttpResponse(200, "", TestUtils.stringToByteBuffer("{\"url\":\"http://testexample.com/\"}")))) diff --git a/src/SignalR/clients/java/signalr/src/test/java/com/microsoft/signalr/sample/Chat.java b/src/SignalR/clients/java/signalr/src/test/java/com/microsoft/signalr/sample/Chat.java index d48c89818552..7a7ab4b8de8c 100644 --- a/src/SignalR/clients/java/signalr/src/test/java/com/microsoft/signalr/sample/Chat.java +++ b/src/SignalR/clients/java/signalr/src/test/java/com/microsoft/signalr/sample/Chat.java @@ -8,7 +8,6 @@ import com.microsoft.signalr.HubConnection; import com.microsoft.signalr.HubConnectionBuilder; - public class Chat { public static void main(final String[] args) throws Exception { System.out.println("Enter the URL of the SignalR Chat you want to join"); @@ -33,7 +32,7 @@ public static void main(final String[] args) throws Exception { while (!message.equals("leave")) { // Scans the next token of the input as an int. message = reader.nextLine(); - hubConnection.send("Send", message); + hubConnection.send("Send", "Java", message); } hubConnection.stop().blockingAwait(); diff --git a/src/SignalR/clients/ts/FunctionalTests/Startup.cs b/src/SignalR/clients/ts/FunctionalTests/Startup.cs index 8a3ccfa767f7..a53eec75d204 100644 --- a/src/SignalR/clients/ts/FunctionalTests/Startup.cs +++ b/src/SignalR/clients/ts/FunctionalTests/Startup.cs @@ -163,9 +163,21 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger< { if (context.Request.Path.Value.Contains("/negotiate")) { - context.Response.Cookies.Append("testCookie", "testValue"); - context.Response.Cookies.Append("testCookie2", "testValue2"); - context.Response.Cookies.Append("expiredCookie", "doesntmatter", new CookieOptions() { Expires = DateTimeOffset.Now.AddHours(-1) }); + var cookieOptions = new CookieOptions(); + var expiredCookieOptions = new CookieOptions() { Expires = DateTimeOffset.Now.AddHours(-1) }; + if (context.Request.IsHttps) + { + cookieOptions.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None; + cookieOptions.Secure = true; + + expiredCookieOptions.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None; + expiredCookieOptions.Secure = true; + } + context.Response.Cookies.Append("testCookie", "testValue", cookieOptions); + context.Response.Cookies.Append("testCookie2", "testValue2", cookieOptions); + + cookieOptions.Expires = DateTimeOffset.Now.AddHours(-1); + context.Response.Cookies.Append("expiredCookie", "doesntmatter", expiredCookieOptions); } await next.Invoke(); diff --git a/src/SignalR/clients/ts/FunctionalTests/ts/Common.ts b/src/SignalR/clients/ts/FunctionalTests/ts/Common.ts index 668a73853633..4eb412145a48 100644 --- a/src/SignalR/clients/ts/FunctionalTests/ts/Common.ts +++ b/src/SignalR/clients/ts/FunctionalTests/ts/Common.ts @@ -60,6 +60,7 @@ console.log(`Using SignalR HTTPS Server: '${ENDPOINT_BASE_HTTPS_URL}'`); console.log(`Jasmine DEFAULT_TIMEOUT_INTERVAL: ${jasmine.DEFAULT_TIMEOUT_INTERVAL}`); export const ECHOENDPOINT_URL = ENDPOINT_BASE_URL + "/echo"; +export const HTTPS_ECHOENDPOINT_URL = ENDPOINT_BASE_HTTPS_URL + "/echo"; export function getHttpTransportTypes(): HttpTransportType[] { const transportTypes = []; @@ -131,3 +132,14 @@ export function eachHttpClient(action: (transport: HttpClient) => void) { return action(t); }); } + +// Run test in Node or Chrome, but not on macOS +export const shouldRunHttpsTests = + // Need to have an HTTPS URL + !!ENDPOINT_BASE_HTTPS_URL && + + // Run on Node, unless macOS + (process && process.platform !== "darwin") && + + // Only run under Chrome browser + (typeof navigator === "undefined" || navigator.userAgent.search("Chrome") !== -1); diff --git a/src/SignalR/clients/ts/FunctionalTests/ts/ConnectionTests.ts b/src/SignalR/clients/ts/FunctionalTests/ts/ConnectionTests.ts index b3445891b3f3..b377c98112c6 100644 --- a/src/SignalR/clients/ts/FunctionalTests/ts/ConnectionTests.ts +++ b/src/SignalR/clients/ts/FunctionalTests/ts/ConnectionTests.ts @@ -5,7 +5,7 @@ // tslint:disable:no-floating-promises import { HttpTransportType, IHttpConnectionOptions, TransferFormat } from "@microsoft/signalr"; -import { DEFAULT_TIMEOUT_INTERVAL, eachHttpClient, eachTransport, ECHOENDPOINT_URL } from "./Common"; +import { DEFAULT_TIMEOUT_INTERVAL, eachHttpClient, eachTransport, ECHOENDPOINT_URL, HTTPS_ECHOENDPOINT_URL, shouldRunHttpsTests } from "./Common"; import { TestLogger } from "./TestLogger"; // We want to continue testing HttpConnection, but we don't export it anymore. So just pull it in directly from the source file. @@ -15,6 +15,8 @@ import "./LogBannerReporter"; jasmine.DEFAULT_TIMEOUT_INTERVAL = DEFAULT_TIMEOUT_INTERVAL; +const USED_ECHOENDPOINT_URL = shouldRunHttpsTests ? HTTPS_ECHOENDPOINT_URL : ECHOENDPOINT_URL; + const commonOptions: IHttpConnectionOptions = { logMessageContent: true, logger: TestLogger.instance, @@ -23,7 +25,7 @@ const commonOptions: IHttpConnectionOptions = { describe("connection", () => { it("can connect to the server without specifying transport explicitly", (done) => { const message = "Hello World!"; - const connection = new HttpConnection(ECHOENDPOINT_URL, { + const connection = new HttpConnection(USED_ECHOENDPOINT_URL, { ...commonOptions, }); @@ -53,7 +55,7 @@ describe("connection", () => { const message = "Hello World!"; // the url should be resolved relative to the document.location.host // and the leading '/' should be automatically added to the url - const connection = new HttpConnection(ECHOENDPOINT_URL, { + const connection = new HttpConnection(USED_ECHOENDPOINT_URL, { ...commonOptions, httpClient, transport: transportType, @@ -83,7 +85,7 @@ describe("connection", () => { const message = "Hello World!"; // DON'T use commonOptions because we want to specifically test the scenario where logMessageContent is not set. - const connection = new HttpConnection(ECHOENDPOINT_URL, { + const connection = new HttpConnection(USED_ECHOENDPOINT_URL, { httpClient, logger: TestLogger.instance, transport: transportType, @@ -119,7 +121,7 @@ describe("connection", () => { const message = "Hello World!"; // DON'T use commonOptions because we want to specifically test the scenario where logMessageContent is set to true (even if commonOptions changes). - const connection = new HttpConnection(ECHOENDPOINT_URL, { + const connection = new HttpConnection(USED_ECHOENDPOINT_URL, { httpClient, logMessageContent: true, logger: TestLogger.instance, @@ -167,7 +169,7 @@ describe("connection", () => { const message = "Hello World!"; // The server will set some response headers for the '/negotiate' endpoint - const connection = new HttpConnection(ECHOENDPOINT_URL, { + const connection = new HttpConnection(USED_ECHOENDPOINT_URL, { ...commonOptions, httpClient, transport: transportType, diff --git a/src/SignalR/clients/ts/FunctionalTests/ts/HubConnectionTests.ts b/src/SignalR/clients/ts/FunctionalTests/ts/HubConnectionTests.ts index a802d7942446..600617d5f3cb 100644 --- a/src/SignalR/clients/ts/FunctionalTests/ts/HubConnectionTests.ts +++ b/src/SignalR/clients/ts/FunctionalTests/ts/HubConnectionTests.ts @@ -8,7 +8,7 @@ import { AbortError, DefaultHttpClient, HttpClient, HttpRequest, HttpResponse, H import { MessagePackHubProtocol } from "@microsoft/signalr-protocol-msgpack"; import { getUserAgentHeader, Platform } from "@microsoft/signalr/dist/esm/Utils"; -import { DEFAULT_TIMEOUT_INTERVAL, eachTransport, eachTransportAndProtocolAndHttpClient, ENDPOINT_BASE_HTTPS_URL, ENDPOINT_BASE_URL } from "./Common"; +import { DEFAULT_TIMEOUT_INTERVAL, eachTransport, eachTransportAndProtocolAndHttpClient, ENDPOINT_BASE_HTTPS_URL, ENDPOINT_BASE_URL, shouldRunHttpsTests } from "./Common"; import "./LogBannerReporter"; import { TestLogger } from "./TestLogger"; @@ -18,6 +18,7 @@ import * as RX from "rxjs"; const TESTHUBENDPOINT_URL = ENDPOINT_BASE_URL + "/testhub"; const TESTHUBENDPOINT_HTTPS_URL = ENDPOINT_BASE_HTTPS_URL ? (ENDPOINT_BASE_HTTPS_URL + "/testhub") : undefined; +const HTTPORHTTPS_TESTHUBENDPOINT_URL = shouldRunHttpsTests ? TESTHUBENDPOINT_HTTPS_URL : TESTHUBENDPOINT_URL; const TESTHUB_NOWEBSOCKETS_ENDPOINT_URL = ENDPOINT_BASE_URL + "/testhub-nowebsockets"; const TESTHUB_REDIRECT_ENDPOINT_URL = ENDPOINT_BASE_URL + "/redirect?numRedirects=0&baseUrl=" + ENDPOINT_BASE_URL; @@ -28,17 +29,6 @@ const commonOptions: IHttpConnectionOptions = { logMessageContent: true, }; -// Run test in Node or Chrome, but not on macOS -const shouldRunHttpsTests = - // Need to have an HTTPS URL - !!TESTHUBENDPOINT_HTTPS_URL && - - // Run on Node, unless macOS - (process && process.platform !== "darwin") && - - // Only run under Chrome browser - (typeof navigator === "undefined" || navigator.userAgent.search("Chrome") !== -1); - function getConnectionBuilder(transportType?: HttpTransportType, url?: string, options?: IHttpConnectionOptions): HubConnectionBuilder { let actualOptions: IHttpConnectionOptions = options || {}; if (transportType) { @@ -599,7 +589,7 @@ describe("hubConnection", () => { } it("preserves cookies between requests", async (done) => { - const hubConnection = getConnectionBuilder(transportType).build(); + const hubConnection = getConnectionBuilder(transportType, HTTPORHTTPS_TESTHUBENDPOINT_URL).build(); await hubConnection.start(); const cookieValue = await hubConnection.invoke("GetCookie", "testCookie"); const cookieValue2 = await hubConnection.invoke("GetCookie", "testCookie2"); @@ -610,7 +600,7 @@ describe("hubConnection", () => { }); it("expired cookies are not preserved", async (done) => { - const hubConnection = getConnectionBuilder(transportType).build(); + const hubConnection = getConnectionBuilder(transportType, HTTPORHTTPS_TESTHUBENDPOINT_URL).build(); await hubConnection.start(); const cookieValue = await hubConnection.invoke("GetCookie", "expiredCookie"); expect(cookieValue).toBeNull(); diff --git a/src/Tools/dotnet-watch/src/DotnetToolSettings.xml b/src/Tools/dotnet-watch/src/DotnetToolSettings.xml new file mode 100644 index 000000000000..f077af6da491 --- /dev/null +++ b/src/Tools/dotnet-watch/src/DotnetToolSettings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/Tools/dotnet-watch/src/dotnet-watch.csproj b/src/Tools/dotnet-watch/src/dotnet-watch.csproj index cbe5cf13c95e..29750598258e 100644 --- a/src/Tools/dotnet-watch/src/dotnet-watch.csproj +++ b/src/Tools/dotnet-watch/src/dotnet-watch.csproj @@ -10,6 +10,13 @@ false false + + + true + true @@ -20,8 +27,13 @@ - - + + + - - - <_RuntimeFramework Include="@(RuntimeFramework)" /> - - - - - - - - - - - - diff --git a/src/Tools/dotnet-watch/test/TestProjects/KitchenSink/.net/objDebug/net5.0/.NETCoreApp,Version=v5.0.AssemblyAttributes.cs b/src/Tools/dotnet-watch/test/TestProjects/KitchenSink/.net/objDebug/net5.0/.NETCoreApp,Version=v5.0.AssemblyAttributes.cs new file mode 100644 index 000000000000..2f7e5ec5afa1 --- /dev/null +++ b/src/Tools/dotnet-watch/test/TestProjects/KitchenSink/.net/objDebug/net5.0/.NETCoreApp,Version=v5.0.AssemblyAttributes.cs @@ -0,0 +1,4 @@ +// +using System; +using System.Reflection; +[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v5.0", FrameworkDisplayName = "")] diff --git a/src/Tools/dotnet-watch/test/TestProjects/KitchenSink/.net/objDebug/net5.0/KitchenSink.AssemblyInfo.cs b/src/Tools/dotnet-watch/test/TestProjects/KitchenSink/.net/objDebug/net5.0/KitchenSink.AssemblyInfo.cs new file mode 100644 index 000000000000..1172632a8716 --- /dev/null +++ b/src/Tools/dotnet-watch/test/TestProjects/KitchenSink/.net/objDebug/net5.0/KitchenSink.AssemblyInfo.cs @@ -0,0 +1,28 @@ +//------------------------------------------------------------------------------ +// +// 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. +// +//------------------------------------------------------------------------------ + +using System; +using System.Reflection; + +[assembly: System.Reflection.AssemblyMetadataAttribute("SourceCommitUrl", "https://github.com/dotnet/aspnetcore/tree/")] +[assembly: System.Reflection.AssemblyMetadataAttribute("Serviceable", "True")] +[assembly: System.Reflection.AssemblyCompanyAttribute("Microsoft Corporation")] +[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] +[assembly: System.Reflection.AssemblyCopyrightAttribute("© Microsoft Corporation. All rights reserved.")] +[assembly: System.Reflection.AssemblyFileVersionAttribute("42.42.42.42424")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("5.0.0-dev")] +[assembly: System.Reflection.AssemblyProductAttribute("Microsoft ASP.NET Core")] +[assembly: System.Reflection.AssemblyTitleAttribute("KitchenSink")] +[assembly: System.Reflection.AssemblyVersionAttribute("5.0.0.0")] +[assembly: System.Reflection.AssemblyMetadataAttribute("RepositoryUrl", "https://github.com/dotnet/aspnetcore")] +[assembly: System.Resources.NeutralResourcesLanguageAttribute("en-US")] + +// Generated by the MSBuild WriteCodeFragment class. + diff --git a/src/Tools/dotnet-watch/test/TestProjects/KitchenSink/.net/objDebug/net5.0/KitchenSink.AssemblyInfoInputs.cache b/src/Tools/dotnet-watch/test/TestProjects/KitchenSink/.net/objDebug/net5.0/KitchenSink.AssemblyInfoInputs.cache new file mode 100644 index 000000000000..9042200dfcf3 --- /dev/null +++ b/src/Tools/dotnet-watch/test/TestProjects/KitchenSink/.net/objDebug/net5.0/KitchenSink.AssemblyInfoInputs.cache @@ -0,0 +1 @@ +5426b0432f23f134c800ff0d43b24a23681aac3d diff --git a/src/Tools/dotnet-watch/test/dotnet-watch.Tests.csproj b/src/Tools/dotnet-watch/test/dotnet-watch.Tests.csproj index d171daa39d84..b9c9d9fe3c2e 100644 --- a/src/Tools/dotnet-watch/test/dotnet-watch.Tests.csproj +++ b/src/Tools/dotnet-watch/test/dotnet-watch.Tests.csproj @@ -5,6 +5,13 @@ Microsoft.DotNet.Watcher.Tools.Tests $(DefaultItemExcludes);TestProjects\**\* DotNetWatcherToolsTests + + + true + true @@ -14,6 +21,7 @@ +