diff --git a/.nuke/temp/build.2022-09-27_08-37-17.log b/.nuke similarity index 100% rename from .nuke/temp/build.2022-09-27_08-37-17.log rename to .nuke diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json deleted file mode 100644 index 5bbc3d6915..0000000000 --- a/.nuke/build.schema.json +++ /dev/null @@ -1,148 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Build Schema", - "$ref": "#/definitions/build", - "definitions": { - "build": { - "type": "object", - "properties": { - "Configuration": { - "type": "string", - "description": "configuration" - }, - "Continue": { - "type": "boolean", - "description": "Indicates to continue a previously failed build attempt" - }, - "ForceNugetVersion": { - "type": "string", - "description": "force-nuget-version" - }, - "Help": { - "type": "boolean", - "description": "Shows the help text for this build assembly" - }, - "Host": { - "type": "string", - "description": "Host for execution. Default is 'automatic'", - "enum": [ - "AppVeyor", - "AzurePipelines", - "Bamboo", - "Bitbucket", - "Bitrise", - "GitHubActions", - "GitLab", - "Jenkins", - "Rider", - "SpaceAutomation", - "TeamCity", - "Terminal", - "TravisCI", - "VisualStudio", - "VSCode" - ] - }, - "NoLogo": { - "type": "boolean", - "description": "Disables displaying the NUKE logo" - }, - "Partition": { - "type": "string", - "description": "Partition to use on CI" - }, - "Plan": { - "type": "boolean", - "description": "Shows the execution plan (HTML)" - }, - "Profile": { - "type": "array", - "description": "Defines the profiles to load", - "items": { - "type": "string" - } - }, - "Root": { - "type": "string", - "description": "Root directory during build execution" - }, - "Skip": { - "type": "array", - "description": "List of targets to be skipped. Empty list skips all dependencies", - "items": { - "type": "string", - "enum": [ - "CiAzureLinux", - "CiAzureOSX", - "CiAzureWindows", - "Clean", - "Compile", - "CompileHtmlPreviewer", - "CompileNative", - "CreateIntermediateNugetPackages", - "CreateNugetPackages", - "GenerateCppHeaders", - "Package", - "RunCoreLibsTests", - "RunDesignerTests", - "RunHtmlPreviewerTests", - "RunLeakTests", - "RunRenderTests", - "RunTests", - "ZipFiles" - ] - } - }, - "SkipPreviewer": { - "type": "boolean", - "description": "skip-previewer" - }, - "SkipTests": { - "type": "boolean", - "description": "skip-tests" - }, - "Solution": { - "type": "string", - "description": "Path to a solution file that is automatically loaded. Default is Avalonia.sln" - }, - "Target": { - "type": "array", - "description": "List of targets to be invoked. Default is '{default_target}'", - "items": { - "type": "string", - "enum": [ - "CiAzureLinux", - "CiAzureOSX", - "CiAzureWindows", - "Clean", - "Compile", - "CompileHtmlPreviewer", - "CompileNative", - "CreateIntermediateNugetPackages", - "CreateNugetPackages", - "GenerateCppHeaders", - "Package", - "RunCoreLibsTests", - "RunDesignerTests", - "RunHtmlPreviewerTests", - "RunLeakTests", - "RunRenderTests", - "RunTests", - "ZipFiles" - ] - } - }, - "Verbosity": { - "type": "string", - "description": "Logging verbosity during build execution. Default is 'Normal'", - "enum": [ - "Minimal", - "Normal", - "Quiet", - "Verbose" - ] - } - } - } - } -} \ No newline at end of file diff --git a/.nuke/parameters.json b/.nuke/parameters.json deleted file mode 100644 index 42521bb7dd..0000000000 --- a/.nuke/parameters.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "$schema": "./build.schema.json", - "Solution": "" -} \ No newline at end of file diff --git a/.nuke/temp/build-attempt.log b/.nuke/temp/build-attempt.log deleted file mode 100644 index a926c68865..0000000000 --- a/.nuke/temp/build-attempt.log +++ /dev/null @@ -1,5 +0,0 @@ -47be5be6de651ed0c6d6996318ce431a -GenerateCppHeaders -Clean -CompileNative -CompileHtmlPreviewer diff --git a/.nuke/temp/build.2022-09-27_08-36-07.log b/.nuke/temp/build.2022-09-27_08-36-07.log deleted file mode 100644 index 2cba08f7bc..0000000000 --- a/.nuke/temp/build.2022-09-27_08-36-07.log +++ /dev/null @@ -1,30 +0,0 @@ -V | | ArgumentsFromParametersFile.OnBuildCreated (150) -V | | Passing value for Build.Solution () -V | | InjectParameterValues.OnBuildCreated (100) -W | | Could not inject value for Build.Solution -System.ArgumentException: Solution 'C:\Users\User\repos\Avalonia\Avalonia.sln' contains duplicated NestedProjects entries: - - {4D36CEC8-53F2-40A5-9A37-79AAE356E2DA} - at Nuke.Common.Assert.True(Boolean condition, String message, String argumentExpression) in /_/source/Nuke.Common/Assert.cs:line 34 - at Nuke.Common.Utilities.Collections.EnumerableExtensions.ToDictionarySafe[T,TKey,TValue](IEnumerable`1 enumerable, Func`2 keySelector, Func`2 valueSelector, String duplicationMessage) in /_/source/Nuke.Common/Utilities/Collections/Enumerable.ToDictionary.cs:line 46 - at Nuke.Common.ProjectModel.SolutionSerializer.GetGlobalSection(String[] lines, String name, String solutionFile) in /_/source/Nuke.Common/ProjectModel/SolutionSerializer.cs:line 92 - at Nuke.Common.ProjectModel.SolutionSerializer.DeserializeFromContent[T](String[] content, String solutionFile) in /_/source/Nuke.Common/ProjectModel/SolutionSerializer.cs:line 63 - at Nuke.Common.ProjectModel.SolutionSerializer.DeserializeFromFile[T](String solutionFile) in /_/source/Nuke.Common/ProjectModel/SolutionSerializer.cs:line 23 -V | | HandleShellCompletion.OnBuildCreated (75) -V | | GenerateBuildServerConfigurations.OnBuildCreated (50) -V | | InvokeBuildServerConfigurationGeneration.OnBuildCreated (45) -V | | UpdateNotification.OnBuildCreated (10) -V | | UnsetVisualStudioEnvironmentVariables.OnBuildCreated (0) -V | | HandleVisualStudioDebugging.OnBuildCreated (0) -V | | HandleSingleFileExecution.OnBuildCreated (-50) -V | | EventInvoker.OnBuildCreated (-3.4028235E+38) -V | | HandleHelpRequests.OnBuildInitialized (5) -V | | Telemetry.OnBuildInitialized (0) -V | | InjectNonParameterValues.OnBuildInitialized (-100) -V | | EventInvoker.OnBuildInitialized (-3.4028235E+38) -V | | Target-unrelated exception was thrown -System.NullReferenceException: Object reference not set to an instance of an object. - at Build.OnBuildInitialized() in C:\Users\User\repos\Avalonia\nukebuild\Build.cs:line 42 - at Nuke.Common.Execution.EventInvoker.OnBuildInitialized(NukeBuild build, IReadOnlyCollection`1 executableTargets, IReadOnlyCollection`1 executionPlan) in /_/source/Nuke.Common/Execution/EventInvoker.cs:line 27 - at Nuke.Common.Utilities.Collections.EnumerableExtensions.ForEach[T](IEnumerable`1 enumerable, Action`1 action) in /_/source/Nuke.Common/Utilities/Collections/Enumerable.ForEach.cs:line 17 - at Nuke.Common.NukeBuild.ExecuteExtension[TExtension](Expression`1 action) - at Nuke.Common.Execution.BuildManager.Execute[T](Expression`1[] defaultTargetExpressions) in /_/source/Nuke.Common/Execution/BuildManager.cs:line 58 diff --git a/.nuke/temp/build.log b/.nuke/temp/build.log deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/azure-pipelines.yml b/azure-pipelines.yml index a5dfbc16a5..8dbe304d7e 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -30,9 +30,9 @@ jobs: vmImage: 'ubuntu-20.04' steps: - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 3.1.423' + displayName: 'Use .NET Core SDK 3.1.418' inputs: - version: 3.1.423 + version: 3.1.418 - task: UseDotNet@2 displayName: 'Use .NET Core SDK 6.0.401' @@ -72,9 +72,9 @@ jobs: vmImage: 'macos-12' steps: - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 3.1.423' + displayName: 'Use .NET Core SDK 3.1.418' inputs: - version: 3.1.423 + version: 3.1.418 - task: UseDotNet@2 displayName: 'Use .NET Core SDK 6.0.401' @@ -155,9 +155,9 @@ jobs: SolutionDir: '$(Build.SourcesDirectory)' steps: - task: UseDotNet@2 - displayName: 'Use .NET Core SDK 3.1.423' + displayName: 'Use .NET Core SDK 3.1.418' inputs: - version: 3.1.423 + version: 3.1.418 - task: UseDotNet@2 displayName: 'Use .NET Core SDK 6.0.401' @@ -179,7 +179,7 @@ jobs: displayName: 'Install Nuke' inputs: script: | - dotnet tool install --global Nuke.GlobalTool --version 6.2.1 + dotnet tool install --global Nuke.GlobalTool --version 0.24.0 - task: CmdLine@2 displayName: 'Run Nuke' diff --git a/build.cmd b/build.cmd deleted file mode 100755 index b08cc590f4..0000000000 --- a/build.cmd +++ /dev/null @@ -1,7 +0,0 @@ -:; set -eo pipefail -:; SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) -:; ${SCRIPT_DIR}/build.sh "$@" -:; exit $? - -@ECHO OFF -powershell -ExecutionPolicy ByPass -NoProfile -File "%~dp0build.ps1" %* diff --git a/build.ps1 b/build.ps1 index 997e5b423f..985e8abcee 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,12 +1,13 @@ [CmdletBinding()] Param( + #[switch]$CustomParam, [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] [string[]]$BuildArguments ) -Write-Output "PowerShell $($PSVersionTable.PSEdition) version $($PSVersionTable.PSVersion)" +Write-Output "Windows PowerShell $($Host.Version)" -Set-StrictMode -Version 2.0; $ErrorActionPreference = "Stop"; $ConfirmPreference = "None"; trap { Write-Error $_ -ErrorAction Continue; exit 1 } +Set-StrictMode -Version 2.0; $ErrorActionPreference = "Stop"; $ConfirmPreference = "None"; trap { exit 1 } $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent ########################################################################### @@ -14,15 +15,15 @@ $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent ########################################################################### $BuildProjectFile = "$PSScriptRoot\nukebuild\_build.csproj" -$TempDirectory = "$PSScriptRoot\\.nuke\temp" +$TempDirectory = "$PSScriptRoot\\.tmp" $DotNetGlobalFile = "$PSScriptRoot\\global.json" -$DotNetInstallUrl = "https://dot.net/v1/dotnet-install.ps1" +$DotNetInstallUrl = "https://raw.githubusercontent.com/dotnet/cli/master/scripts/obtain/dotnet-install.ps1" $DotNetChannel = "Current" $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = 1 $env:DOTNET_CLI_TELEMETRY_OPTOUT = 1 -$env:DOTNET_MULTILEVEL_LOOKUP = 0 +$env:NUGET_XMLDOC_MODE = "skip" ########################################################################### # EXECUTION @@ -33,37 +34,38 @@ function ExecSafe([scriptblock] $cmd) { if ($LASTEXITCODE) { exit $LASTEXITCODE } } -# If dotnet CLI is installed globally and it matches requested version, use for execution +# If global.json exists, load expected version +if (Test-Path $DotNetGlobalFile) { + $DotNetGlobal = $(Get-Content $DotNetGlobalFile | Out-String | ConvertFrom-Json) + if ($DotNetGlobal.PSObject.Properties["sdk"] -and $DotNetGlobal.sdk.PSObject.Properties["version"]) { + $DotNetVersion = $DotNetGlobal.sdk.version + } +} + +# If dotnet is installed locally, and expected version is not set or installation matches the expected version if ($null -ne (Get-Command "dotnet" -ErrorAction SilentlyContinue) -and ` - $(dotnet --version) -and $LASTEXITCODE -eq 0) { + (!(Test-Path variable:DotNetVersion) -or $(& dotnet --version) -eq $DotNetVersion)) { $env:DOTNET_EXE = (Get-Command "dotnet").Path } else { + $DotNetDirectory = "$TempDirectory\dotnet-win" + $env:DOTNET_EXE = "$DotNetDirectory\dotnet.exe" + # Download install script $DotNetInstallFile = "$TempDirectory\dotnet-install.ps1" - New-Item -ItemType Directory -Path $TempDirectory -Force | Out-Null - [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + mkdir -force $TempDirectory > $null (New-Object System.Net.WebClient).DownloadFile($DotNetInstallUrl, $DotNetInstallFile) - # If global.json exists, load expected version - if (Test-Path $DotNetGlobalFile) { - $DotNetGlobal = $(Get-Content $DotNetGlobalFile | Out-String | ConvertFrom-Json) - if ($DotNetGlobal.PSObject.Properties["sdk"] -and $DotNetGlobal.sdk.PSObject.Properties["version"]) { - $DotNetVersion = $DotNetGlobal.sdk.version - } - } - # Install by channel or version - $DotNetDirectory = "$TempDirectory\dotnet-win" if (!(Test-Path variable:DotNetVersion)) { - ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Channel $DotNetChannel -NoPath } + ExecSafe { & $DotNetInstallFile -InstallDir $DotNetDirectory -Channel $DotNetChannel -NoPath } } else { - ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath } + ExecSafe { & $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath } } - $env:DOTNET_EXE = "$DotNetDirectory\dotnet.exe" + + $env:PATH="$DotNetDirectory;$env:PATH" } -Write-Output "Microsoft (R) .NET SDK version $(& $env:DOTNET_EXE --version)" +Write-Output "Microsoft (R) .NET Core SDK version $(& $env:DOTNET_EXE --version)" -ExecSafe { & $env:DOTNET_EXE build $BuildProjectFile /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet } -ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile --no-build -- $BuildArguments } +ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile -- $BuildArguments } diff --git a/build.sh b/build.sh index 76919a5351..9532b4fbe0 100755 --- a/build.sh +++ b/build.sh @@ -1,6 +1,16 @@ #!/usr/bin/env bash -bash --version 2>&1 | head -n 1 +echo $(bash --version 2>&1 | head -n 1) + +#CUSTOMPARAM=0 +BUILD_ARGUMENTS=() +for i in "$@"; do + case $(echo $1 | awk '{print tolower($0)}') in + # -custom-param) CUSTOMPARAM=1;; + *) BUILD_ARGUMENTS+=("$1") ;; + esac + shift +done set -eo pipefail SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) @@ -10,53 +20,11 @@ SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) ########################################################################### BUILD_PROJECT_FILE="$SCRIPT_DIR/nukebuild/_build.csproj" -TEMP_DIRECTORY="$SCRIPT_DIR//.nuke/temp" - -DOTNET_GLOBAL_FILE="$SCRIPT_DIR//global.json" -DOTNET_INSTALL_URL="https://dot.net/v1/dotnet-install.sh" -DOTNET_CHANNEL="Current" export DOTNET_CLI_TELEMETRY_OPTOUT=1 export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 -export DOTNET_MULTILEVEL_LOOKUP=0 +export NUGET_XMLDOC_MODE="skip" -########################################################################### -# EXECUTION -########################################################################### +dotnet --info -function FirstJsonValue { - perl -nle 'print $1 if m{"'"$1"'": "([^"]+)",?}' <<< "${@:2}" -} - -# If dotnet CLI is installed globally and it matches requested version, use for execution -if [ -x "$(command -v dotnet)" ] && dotnet --version &>/dev/null; then - export DOTNET_EXE="$(command -v dotnet)" -else - # Download install script - DOTNET_INSTALL_FILE="$TEMP_DIRECTORY/dotnet-install.sh" - mkdir -p "$TEMP_DIRECTORY" - curl -Lsfo "$DOTNET_INSTALL_FILE" "$DOTNET_INSTALL_URL" - chmod +x "$DOTNET_INSTALL_FILE" - - # If global.json exists, load expected version - if [[ -f "$DOTNET_GLOBAL_FILE" ]]; then - DOTNET_VERSION=$(FirstJsonValue "version" "$(cat "$DOTNET_GLOBAL_FILE")") - if [[ "$DOTNET_VERSION" == "" ]]; then - unset DOTNET_VERSION - fi - fi - - # Install by channel or version - DOTNET_DIRECTORY="$TEMP_DIRECTORY/dotnet-unix" - if [[ -z ${DOTNET_VERSION+x} ]]; then - "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --channel "$DOTNET_CHANNEL" --no-path - else - "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --version "$DOTNET_VERSION" --no-path - fi - export DOTNET_EXE="$DOTNET_DIRECTORY/dotnet" -fi - -echo "Microsoft (R) .NET SDK version $("$DOTNET_EXE" --version)" - -"$DOTNET_EXE" build "$BUILD_PROJECT_FILE" /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet -"$DOTNET_EXE" run --project "$BUILD_PROJECT_FILE" --no-build -- "$@" +dotnet run --project "$BUILD_PROJECT_FILE" -- ${BUILD_ARGUMENTS[@]} diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index b0047b1f37..4bbb667154 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -39,7 +39,6 @@ partial class Build : NukeBuild BuildParameters Parameters { get; set; } protected override void OnBuildInitialized() { - Console.WriteLine($"{Solution.Name}"); Parameters = new BuildParameters(this); Information("Building version {0} of Avalonia ({1}) using version {2} of Nuke.", Parameters.Version, @@ -144,7 +143,6 @@ partial class Build : NukeBuild void RunCoreTest(string projectName) { Information($"Running tests from {projectName}"); - Console.WriteLine(Solution == null); var project = Solution.GetProject(projectName).NotNull("project != null"); foreach (var fw in project.GetTargetFrameworks()) @@ -165,7 +163,7 @@ partial class Build : NukeBuild .EnableNoBuild() .EnableNoRestore() .When(Parameters.PublishTestResults, _ => _ - .SetLoggers("trx") + .SetLogger("trx") .SetResultsDirectory(Parameters.TestResultsRoot))); } } @@ -231,7 +229,7 @@ partial class Build : NukeBuild $"{XunitPath.DoubleQuoteIfNeeded()} --propagate-exit-code -- {testAssembly}", timeout: 120_000); } - ControlFlow.ExecuteWithRetry(DoMemoryTest, delay: TimeSpan.FromSeconds(3)); + ControlFlow.ExecuteWithRetry(DoMemoryTest, waitInSeconds: 3); }); Target ZipFiles => _ => _ diff --git a/nukebuild/BuildParameters.cs b/nukebuild/BuildParameters.cs index c5fed21136..1826623674 100644 --- a/nukebuild/BuildParameters.cs +++ b/nukebuild/BuildParameters.cs @@ -74,11 +74,11 @@ public partial class Build MSBuildSolution = RootDirectory / "dirs.proj"; // PARAMETERS - IsLocalBuild = NukeBuild.IsLocalBuild; + IsLocalBuild = Host == HostType.Console; IsRunningOnUnix = Environment.OSVersion.Platform == PlatformID.Unix || Environment.OSVersion.Platform == PlatformID.MacOSX; IsRunningOnWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - IsRunningOnAzure = NukeBuild.IsServerBuild || + IsRunningOnAzure = Host == HostType.AzurePipelines || Environment.GetEnvironmentVariable("LOGNAME") == "vsts"; if (IsRunningOnAzure) diff --git a/nukebuild/BuildTasksPatcher.cs b/nukebuild/BuildTasksPatcher.cs index e3766ae23f..5fd331035a 100644 --- a/nukebuild/BuildTasksPatcher.cs +++ b/nukebuild/BuildTasksPatcher.cs @@ -17,8 +17,12 @@ public class BuildTasksPatcher { if (entry.Name == "Avalonia.Build.Tasks.dll") { - var temp = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".dll"); + var tempDir = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempDir); + var temp = Path.Combine(tempDir, Guid.NewGuid() + ".dll"); var output = temp + ".output"; + File.Copy(typeof(Microsoft.Build.Framework.ITask).Assembly.GetModules()[0].FullyQualifiedName, + Path.Combine(tempDir, "Microsoft.Build.Framework.dll")); var patched = new MemoryStream(); try { @@ -57,10 +61,8 @@ public class BuildTasksPatcher { try { - if (File.Exists(temp)) - File.Delete(temp); - if (File.Exists(output)) - File.Delete(output); + if(Directory.Exists(tempDir)) + Directory.Delete(tempDir, true); } catch { diff --git a/nukebuild/DotNetConfigHelper.cs b/nukebuild/DotNetConfigHelper.cs index eca1e2684d..932525288c 100644 --- a/nukebuild/DotNetConfigHelper.cs +++ b/nukebuild/DotNetConfigHelper.cs @@ -46,7 +46,7 @@ public class DotNetConfigHelper public DotNetConfigHelper SetVerbosity(DotNetVerbosity verbosity) { Build = Build?.SetVerbosity(verbosity); - Pack = Pack?.SetVerbosity(verbosity); + Pack = Pack?.SetVerbostiy(verbosity); Test = Test?.SetVerbosity(verbosity); return this; } @@ -54,4 +54,4 @@ public class DotNetConfigHelper public static implicit operator DotNetConfigHelper(DotNetBuildSettings s) => new DotNetConfigHelper(s); public static implicit operator DotNetConfigHelper(DotNetPackSettings s) => new DotNetConfigHelper(s); public static implicit operator DotNetConfigHelper(DotNetTestSettings s) => new DotNetConfigHelper(s); -} +} \ No newline at end of file diff --git a/nukebuild/_build.csproj b/nukebuild/_build.csproj index b9bb4f24b8..aa5724c75e 100644 --- a/nukebuild/_build.csproj +++ b/nukebuild/_build.csproj @@ -1,5 +1,4 @@  - Exe netcoreapp3.1 @@ -7,11 +6,10 @@ False CS0649;CS0169 - 1 - + diff --git a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj index 32f199ea8b..0667644643 100644 --- a/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj +++ b/samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj @@ -2,7 +2,7 @@ WinExe - net7.0 + net6.0 true true diff --git a/src/Avalonia.Base/Platform/Storage/IStorageFile.cs b/src/Avalonia.Base/Platform/Storage/IStorageFile.cs index 46aa6efa72..4aa84e3ec4 100644 --- a/src/Avalonia.Base/Platform/Storage/IStorageFile.cs +++ b/src/Avalonia.Base/Platform/Storage/IStorageFile.cs @@ -18,6 +18,7 @@ public interface IStorageFile : IStorageItem /// /// Opens a stream for read access. /// + /// Task OpenReadAsync(); /// @@ -28,5 +29,6 @@ public interface IStorageFile : IStorageItem /// /// Opens stream for writing to the file. /// + /// Task OpenWriteAsync(); } diff --git a/src/Avalonia.Base/Utilities/AvaloniaResourcesIndex.cs b/src/Avalonia.Base/Utilities/AvaloniaResourcesIndex.cs index bd3b86f06d..b8692bb771 100644 --- a/src/Avalonia.Base/Utilities/AvaloniaResourcesIndex.cs +++ b/src/Avalonia.Base/Utilities/AvaloniaResourcesIndex.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; using System.IO; -using System.Runtime.CompilerServices; using System.Runtime.Serialization; -using System.Runtime.Serialization.Json; using System.Xml.Linq; using System.Linq; diff --git a/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs b/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs index c20b2f656e..75758d1315 100644 --- a/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs +++ b/src/Avalonia.Build.Tasks/GenerateAvaloniaResourcesTask.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.Serialization; -using System.Runtime.Serialization.Json; using System.Text; using Avalonia.Markup.Xaml.PortableXaml; using Avalonia.Utilities; diff --git a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj index d719135a7f..b16ee84b4d 100644 --- a/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj +++ b/src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj @@ -9,9 +9,6 @@ %(Filename) - - - diff --git a/src/Avalonia.X11/X11IconLoader.cs b/src/Avalonia.X11/X11IconLoader.cs index ffac3c55e2..632a7d39a2 100644 --- a/src/Avalonia.X11/X11IconLoader.cs +++ b/src/Avalonia.X11/X11IconLoader.cs @@ -9,13 +9,6 @@ namespace Avalonia.X11 { class X11IconLoader : IPlatformIconLoader { - private readonly X11Info _x11; - - public X11IconLoader(X11Info x11) - { - _x11 = x11; - } - IWindowIconImpl LoadIcon(Bitmap bitmap) { var rv = new X11IconData(bitmap); diff --git a/src/Avalonia.X11/X11Platform.cs b/src/Avalonia.X11/X11Platform.cs index 81c2a69aac..381cdd74c3 100644 --- a/src/Avalonia.X11/X11Platform.cs +++ b/src/Avalonia.X11/X11Platform.cs @@ -81,7 +81,7 @@ namespace Avalonia.X11 .Bind().ToConstant(new X11CursorFactory(Display)) .Bind().ToConstant(new X11Clipboard(this)) .Bind().ToConstant(new PlatformSettingsStub()) - .Bind().ToConstant(new X11IconLoader(Info)) + .Bind().ToConstant(new X11IconLoader()) .Bind().ToConstant(new LinuxMountedVolumeInfoProvider()) .Bind().ToConstant(new X11PlatformLifetimeEvents(this)); diff --git a/src/Shared/ModuleInitializer.cs b/src/Shared/ModuleInitializer.cs index b5bb31ed81..a72929e06f 100644 --- a/src/Shared/ModuleInitializer.cs +++ b/src/Shared/ModuleInitializer.cs @@ -1,6 +1,6 @@ namespace System.Runtime.CompilerServices { -#if !NET5_0_OR_GREATER +#if NETSTANDARD2_0 internal class ModuleInitializerAttribute : Attribute { diff --git a/src/Web/Avalonia.Web.Sample/Avalonia.Web.Sample.csproj b/src/Web/Avalonia.Web.Sample/Avalonia.Web.Sample.csproj index e1f0d74835..13aad8c13e 100644 --- a/src/Web/Avalonia.Web.Sample/Avalonia.Web.Sample.csproj +++ b/src/Web/Avalonia.Web.Sample/Avalonia.Web.Sample.csproj @@ -30,6 +30,10 @@ + + + + diff --git a/src/Web/Avalonia.Web.Sample/EmbedSample.Browser.cs b/src/Web/Avalonia.Web.Sample/EmbedSample.Browser.cs new file mode 100644 index 0000000000..5baa4a6b35 --- /dev/null +++ b/src/Web/Avalonia.Web.Sample/EmbedSample.Browser.cs @@ -0,0 +1,44 @@ +using System; +using System.Runtime.InteropServices.JavaScript; + +using Avalonia; +using Avalonia.Platform; +using Avalonia.Web; + +using ControlCatalog.Pages; + +namespace ControlCatalog.Web; + +public class EmbedSampleWeb : INativeDemoControl +{ + public IPlatformHandle CreateControl(bool isSecond, IPlatformHandle parent, Func createDefault) + { + if (isSecond) + { + var iframe = EmbedInterop.CreateElement("iframe"); + iframe.SetProperty("src", "https://www.youtube.com/embed/kZCIporjJ70"); + + return new JSObjectControlHandle(iframe); + } + else + { + var defaultHandle = (JSObjectControlHandle)createDefault(); + + _ = JSHost.ImportAsync("embed.js", "./embed.js").ContinueWith(_ => + { + EmbedInterop.AddAppButton(defaultHandle.Object); + }); + + return defaultHandle; + } + } +} + +internal static partial class EmbedInterop +{ + [JSImport("globalThis.document.createElement")] + public static partial JSObject CreateElement(string tagName); + + [JSImport("addAppButton", "embed.js")] + public static partial void AddAppButton(JSObject parentObject); +} diff --git a/src/Web/Avalonia.Web.Sample/Logo.svg b/src/Web/Avalonia.Web.Sample/Logo.svg new file mode 100644 index 0000000000..9685a23af1 --- /dev/null +++ b/src/Web/Avalonia.Web.Sample/Logo.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Web/Avalonia.Web.Sample/Program.cs b/src/Web/Avalonia.Web.Sample/Program.cs index 2570aa2d58..464c9925dd 100644 --- a/src/Web/Avalonia.Web.Sample/Program.cs +++ b/src/Web/Avalonia.Web.Sample/Program.cs @@ -1,12 +1,19 @@ using Avalonia; using Avalonia.Web; using ControlCatalog; +using ControlCatalog.Web; internal partial class Program { private static void Main(string[] args) { - Emscripten.Log(EM_LOG.ERROR, "MyError"); + Emscripten.Log(EM_LOG.INFO, "Call from Main"); + + BuildAvaloniaApp() + .AfterSetup(_ => + { + ControlCatalog.Pages.EmbedSample.Implementation = new EmbedSampleWeb(); + }).SetupBrowserApp("out"); BuildAvaloniaApp().SetupBrowserApp("out"); } diff --git a/src/Web/Avalonia.Web.Sample/app.css b/src/Web/Avalonia.Web.Sample/app.css new file mode 100644 index 0000000000..04ea5bee19 --- /dev/null +++ b/src/Web/Avalonia.Web.Sample/app.css @@ -0,0 +1,38 @@ +#out { + height: 100vh; + width: 100vw +} + +#avalonia-splash { + position: absolute; + height: 100%; + width: 100%; + color: whitesmoke; + background: #171C2C; + font-family: 'Nunito', sans-serif; +} + +#avalonia-splash a{ + color: whitesmoke; + text-decoration: none; +} + +.center { + display: flex; + justify-content: center; + height: 250px; +} + +.splash-close { + animation: fadeOut 1s forwards; +} + +@keyframes fadeOut { + from { + opacity: 1; + } + + to { + opacity: 0; + } +} diff --git a/src/Web/Avalonia.Web.Sample/embed.js b/src/Web/Avalonia.Web.Sample/embed.js new file mode 100644 index 0000000000..f393c80314 --- /dev/null +++ b/src/Web/Avalonia.Web.Sample/embed.js @@ -0,0 +1,11 @@ +export function addAppButton(parent) { + var button = globalThis.document.createElement('button'); + button.innerText = 'Hello world'; + var clickCount = 0; + button.onclick = () => { + clickCount++; + button.innerText = 'Click count ' + clickCount; + }; + parent.appendChild(button); + return button; +} diff --git a/src/Web/Avalonia.Web.Sample/favicon.ico b/src/Web/Avalonia.Web.Sample/favicon.ico new file mode 100644 index 0000000000..da8d49ff9b Binary files /dev/null and b/src/Web/Avalonia.Web.Sample/favicon.ico differ diff --git a/src/Web/Avalonia.Web.Sample/index.html b/src/Web/Avalonia.Web.Sample/index.html index a4e017a9aa..ee023790fb 100644 --- a/src/Web/Avalonia.Web.Sample/index.html +++ b/src/Web/Avalonia.Web.Sample/index.html @@ -10,10 +10,21 @@ + -
+
+
+
+

Powered by

+ + Avalonia Logo + Avalonia + +
+
+
diff --git a/src/Web/Avalonia.Web/Avalonia.Web.csproj b/src/Web/Avalonia.Web/Avalonia.Web.csproj index fd5f976f4b..04b31b1a2f 100644 --- a/src/Web/Avalonia.Web/Avalonia.Web.csproj +++ b/src/Web/Avalonia.Web/Avalonia.Web.csproj @@ -1,6 +1,7 @@ net7.0 + preview enable true diff --git a/src/Web/Avalonia.Web/AvaloniaView.cs b/src/Web/Avalonia.Web/AvaloniaView.cs index 73da939110..e81620ffde 100644 --- a/src/Web/Avalonia.Web/AvaloniaView.cs +++ b/src/Web/Avalonia.Web/AvaloniaView.cs @@ -6,9 +6,11 @@ using Avalonia.Controls.Platform; using Avalonia.Input; using Avalonia.Input.Raw; using Avalonia.Input.TextInput; -using Avalonia.Platform.Storage; using Avalonia.Rendering.Composition; +using Avalonia.Threading; using Avalonia.Web.Interop; +using Avalonia.Web.Skia; + using SkiaSharp; namespace Avalonia.Web @@ -23,6 +25,7 @@ namespace Avalonia.Web private readonly JSObject _canvas; private readonly JSObject _nativeControlsContainer; private readonly JSObject _inputElement; + private readonly JSObject? _splash; private GLInfo? _jsGlInfo = null; private double _dpi = 1; @@ -59,11 +62,23 @@ namespace Avalonia.Web _inputElement = hostContent.GetPropertyAsJSObject("inputElement") ?? throw new InvalidOperationException("InputElement cannot be null"); + _splash = DomHelper.GetElementById("avalonia-splash"); + _canvas.SetProperty("id", $"avaloniaCanvas{_canvasCount++}"); _topLevelImpl = new BrowserTopLevelImpl(this); - _topLevel = new EmbeddableControlRoot(_topLevelImpl); + _topLevel = new WebEmbeddableControlRoot(_topLevelImpl, () => + { + Dispatcher.UIThread.Post(() => + { + if (_splash != null) + { + DomHelper.AddCssClass(_splash, "splash-close"); + } + }); + }); + _topLevelImpl.SetCssCursor = (cursor) => { InputHelper.SetCursor(_containerElement, cursor); // macOS @@ -318,14 +333,7 @@ namespace Avalonia.Web internal INativeControlHostImpl GetNativeControlHostImpl() { - throw new NotImplementedException(); - //return _nativeControlHost ?? throw new InvalidOperationException("Blazor View wasn't initialized yet"); - } - - internal IStorageProvider GetStorageProvider() - { - throw new NotImplementedException(); - //return _storageProvider ?? throw new InvalidOperationException("Blazor View wasn't initialized yet"); + return new BrowserNativeControlHost(_nativeControlsContainer); } private void ForceBlit() diff --git a/src/Web/Avalonia.Web/BrowserNativeControlHost.cs b/src/Web/Avalonia.Web/BrowserNativeControlHost.cs new file mode 100644 index 0000000000..4cdcf627e6 --- /dev/null +++ b/src/Web/Avalonia.Web/BrowserNativeControlHost.cs @@ -0,0 +1,136 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices.JavaScript; + +using Avalonia.Controls.Platform; +using Avalonia.Platform; +using Avalonia.Web.Interop; + +namespace Avalonia.Web +{ + internal class BrowserNativeControlHost : INativeControlHostImpl + { + private readonly JSObject _hostElement; + + public BrowserNativeControlHost(JSObject element) + { + _hostElement = element; + } + + public INativeControlHostDestroyableControlHandle CreateDefaultChild(IPlatformHandle parent) + { + var element = NativeControlHostHelper.CreateDefaultChild(null); + return new JSObjectControlHandle(element); + } + + public INativeControlHostControlTopLevelAttachment CreateNewAttachment(Func create) + { + Attachment? a = null; + try + { + var child = create(new JSObjectControlHandle(_hostElement)); + var attachmenetReference = NativeControlHostHelper.CreateAttachment(); + // It has to be assigned to the variable before property setter is called so we dispose it on exception +#pragma warning disable IDE0017 // Simplify object initialization + a = new Attachment(attachmenetReference, child); +#pragma warning restore IDE0017 // Simplify object initialization + a.AttachedTo = this; + return a; + } + catch + { + a?.Dispose(); + throw; + } + } + + public INativeControlHostControlTopLevelAttachment CreateNewAttachment(IPlatformHandle handle) + { + var attachmenetReference = NativeControlHostHelper.CreateAttachment(); + var a = new Attachment(attachmenetReference, handle); + a.AttachedTo = this; + return a; + } + + public bool IsCompatibleWith(IPlatformHandle handle) => handle is JSObjectControlHandle; + + private class Attachment : INativeControlHostControlTopLevelAttachment + { + private const string InitializeWithChildHandleSymbol = "InitializeWithChildHandle"; + private const string AttachToSymbol = "AttachTo"; + private const string ShowInBoundsSymbol = "ShowInBounds"; + private const string HideWithSizeSymbol = "HideWithSize"; + private const string ReleaseChildSymbol = "ReleaseChild"; + + private JSObject? _native; + private BrowserNativeControlHost? _attachedTo; + + public Attachment(JSObject native, IPlatformHandle handle) + { + _native = native; + NativeControlHostHelper.InitializeWithChildHandle(_native, ((JSObjectControlHandle)handle).Object); + } + + public void Dispose() + { + if (_native != null) + { + NativeControlHostHelper.ReleaseChild(_native); + _native.Dispose(); + _native = null; + } + } + + public INativeControlHostImpl? AttachedTo + { + get => _attachedTo!; + set + { + CheckDisposed(); + + var host = (BrowserNativeControlHost?)value; + if (host == null) + { + NativeControlHostHelper.AttachTo(_native, null); + } + else + { + NativeControlHostHelper.AttachTo(_native, host._hostElement); + } + _attachedTo = host; + } + } + + public bool IsCompatibleWith(INativeControlHostImpl host) => host is BrowserNativeControlHost; + + public void HideWithSize(Size size) + { + CheckDisposed(); + if (_attachedTo == null) + return; + + NativeControlHostHelper.HideWithSize(_native, Math.Max(1, size.Width), Math.Max(1, size.Height)); + } + + public void ShowInBounds(Rect bounds) + { + CheckDisposed(); + + if (_attachedTo == null) + throw new InvalidOperationException("Native control isn't attached to a toplevel"); + + bounds = new Rect(bounds.X, bounds.Y, Math.Max(1, bounds.Width), + Math.Max(1, bounds.Height)); + + NativeControlHostHelper.ShowInBounds(_native, bounds.X, bounds.Y, bounds.Width, bounds.Height); + } + + [MemberNotNull(nameof(_native))] + private void CheckDisposed() + { + if (_native == null) + throw new ObjectDisposedException(nameof(Attachment)); + } + } + } +} diff --git a/src/Web/Avalonia.Web/BrowserSingleViewLifetime.cs b/src/Web/Avalonia.Web/BrowserSingleViewLifetime.cs index 02d9b01319..091ab3f68c 100644 --- a/src/Web/Avalonia.Web/BrowserSingleViewLifetime.cs +++ b/src/Web/Avalonia.Web/BrowserSingleViewLifetime.cs @@ -3,6 +3,7 @@ using System; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Media; +using Avalonia.Web.Skia; namespace Avalonia.Web { diff --git a/src/Web/Avalonia.Web/BrowserTopLevelImpl.cs b/src/Web/Avalonia.Web/BrowserTopLevelImpl.cs index 047b462a64..b955da6df2 100644 --- a/src/Web/Avalonia.Web/BrowserTopLevelImpl.cs +++ b/src/Web/Avalonia.Web/BrowserTopLevelImpl.cs @@ -11,8 +11,8 @@ using Avalonia.Platform; using Avalonia.Platform.Storage; using Avalonia.Rendering; using Avalonia.Rendering.Composition; - -#nullable enable +using Avalonia.Web.Skia; +using Avalonia.Web.Storage; namespace Avalonia.Web { @@ -35,13 +35,11 @@ namespace Avalonia.Web AcrylicCompensationLevels = new AcrylicPlatformCompensationLevels(1, 1, 1); _touchDevice = new TouchDevice(); _penDevice = new PenDevice(); + NativeControlHost = _avaloniaView.GetNativeControlHostImpl(); } public ulong Timestamp => (ulong)_sw.ElapsedMilliseconds; - - - public void SetClientSize(Size newSize, double dpi) { if (Math.Abs(RenderScaling - dpi) > 0.0001) @@ -222,7 +220,7 @@ namespace Avalonia.Web public ITextInputMethodImpl TextInputMethod => _avaloniaView; - public INativeControlHostImpl? NativeControlHost => _avaloniaView.GetNativeControlHostImpl(); - public IStorageProvider StorageProvider => _avaloniaView.GetStorageProvider(); + public INativeControlHostImpl? NativeControlHost { get; } + public IStorageProvider StorageProvider { get; } = new BrowserStorageProvider(); } } diff --git a/src/Web/Avalonia.Web/Interop/DomHelper.cs b/src/Web/Avalonia.Web/Interop/DomHelper.cs index 1167d21a6b..c02bc4dae3 100644 --- a/src/Web/Avalonia.Web/Interop/DomHelper.cs +++ b/src/Web/Avalonia.Web/Interop/DomHelper.cs @@ -6,11 +6,14 @@ namespace Avalonia.Web.Interop; internal static partial class DomHelper { [JSImport("globalThis.document.getElementById")] - internal static partial JSObject GetElementById(string id); + internal static partial JSObject? GetElementById(string id); [JSImport("AvaloniaDOM.createAvaloniaHost", "avalonia.ts")] public static partial JSObject CreateAvaloniaHost(JSObject element); + [JSImport("AvaloniaDOM.addClass", "avalonia.ts")] + public static partial void AddCssClass(JSObject element, string className); + [JSImport("SizeWatcher.observe", "avalonia.ts")] public static partial JSObject ObserveSize( JSObject canvas, diff --git a/src/Web/Avalonia.Web/Interop/InputHelper.cs b/src/Web/Avalonia.Web/Interop/InputHelper.cs index e728acdc52..b32b6566f2 100644 --- a/src/Web/Avalonia.Web/Interop/InputHelper.cs +++ b/src/Web/Avalonia.Web/Interop/InputHelper.cs @@ -1,8 +1,6 @@ using System; using System.Runtime.InteropServices.JavaScript; using System.Threading.Tasks; -using static System.Net.Mime.MediaTypeNames; -using static System.Runtime.InteropServices.JavaScript.JSType; namespace Avalonia.Web.Interop; @@ -39,7 +37,7 @@ internal static partial class InputHelper Func pointerUp, [JSMarshalAs>] Func wheel); - + [JSImport("InputHelper.subscribeInputEvents", "avalonia.ts")] public static partial void SubscribeInputEvents( @@ -72,9 +70,9 @@ internal static partial class InputHelper [JSImport("InputHelper.setBounds", "avalonia.ts")] public static partial void SetBounds(JSObject htmlElement, int x, int y, int width, int height, int caret); - [JSImport("navigator.clipboard.readText")] + [JSImport("globalThis.navigator.clipboard.readText")] public static partial Task ReadClipboardTextAsync(); - [JSImport("navigator.clipboard.writeText")] + [JSImport("globalThis.navigator.clipboard.writeText")] public static partial Task WriteClipboardTextAsync(string text); } diff --git a/src/Web/Avalonia.Web/Interop/NativeControlHostHelper.cs b/src/Web/Avalonia.Web/Interop/NativeControlHostHelper.cs new file mode 100644 index 0000000000..5cc86bf622 --- /dev/null +++ b/src/Web/Avalonia.Web/Interop/NativeControlHostHelper.cs @@ -0,0 +1,28 @@ +using System; +using System.Runtime.InteropServices.JavaScript; + +namespace Avalonia.Web.Interop; + +internal static partial class NativeControlHostHelper +{ + [JSImport("NativeControlHost.createDefaultChild", "avalonia.ts")] + internal static partial JSObject CreateDefaultChild(JSObject? parent); + + [JSImport("NativeControlHost.createAttachment", "avalonia.ts")] + internal static partial JSObject CreateAttachment(); + + [JSImport("NativeControlHost.initializeWithChildHandle", "avalonia.ts")] + internal static partial void InitializeWithChildHandle(JSObject element, JSObject child); + + [JSImport("NativeControlHost.attachTo", "avalonia.ts")] + internal static partial void AttachTo(JSObject element, JSObject? host); + + [JSImport("NativeControlHost.showInBounds", "avalonia.ts")] + internal static partial void ShowInBounds(JSObject element, double x, double y, double width, double height); + + [JSImport("NativeControlHost.hideWithSize", "avalonia.ts")] + internal static partial void HideWithSize(JSObject element, double width, double height); + + [JSImport("NativeControlHost.releaseChild", "avalonia.ts")] + internal static partial void ReleaseChild(JSObject element); +} diff --git a/src/Web/Avalonia.Web/Interop/StorageHelper.cs b/src/Web/Avalonia.Web/Interop/StorageHelper.cs new file mode 100644 index 0000000000..4dc55fd959 --- /dev/null +++ b/src/Web/Avalonia.Web/Interop/StorageHelper.cs @@ -0,0 +1,55 @@ +using System.Runtime.InteropServices.JavaScript; +using System.Threading.Tasks; + +namespace Avalonia.Web.Interop; + +internal static partial class StorageHelper +{ + [JSImport("Caniuse.canShowOpenFilePicker", "avalonia.ts")] + public static partial bool CanShowOpenFilePicker(); + + [JSImport("Caniuse.canShowSaveFilePicker", "avalonia.ts")] + public static partial bool CanShowSaveFilePicker(); + + [JSImport("Caniuse.canShowDirectoryPicker", "avalonia.ts")] + public static partial bool CanShowDirectoryPicker(); + + [JSImport("StorageProvider.selectFolderDialog", "storage.ts")] + public static partial Task SelectFolderDialog(JSObject? startIn); + + [JSImport("StorageProvider.openFileDialog", "storage.ts")] + public static partial Task OpenFileDialog(JSObject? startIn, bool multiple, + [JSMarshalAs>] object[]? types, bool excludeAcceptAllOption); + + [JSImport("StorageProvider.saveFileDialog", "storage.ts")] + public static partial Task SaveFileDialog(JSObject? startIn, string? suggestedName, + [JSMarshalAs>] object[]? types, bool excludeAcceptAllOption); + + [JSImport("StorageProvider.openBookmark", "storage.ts")] + public static partial Task OpenBookmark(string key); + + [JSImport("StorageItem.saveBookmark", "storage.ts")] + public static partial Task SaveBookmark(JSObject item); + + [JSImport("StorageItem.deleteBookmark", "storage.ts")] + public static partial Task DeleteBookmark(JSObject item); + + [JSImport("StorageItem.getProperties", "storage.ts")] + public static partial Task GetProperties(JSObject item); + + [JSImport("StorageItem.openWrite", "storage.ts")] + public static partial Task OpenWrite(JSObject item); + + [JSImport("StorageItem.openRead", "storage.ts")] + public static partial Task OpenRead(JSObject item); + + [JSImport("StorageItem.getItems", "storage.ts")] + [return: JSMarshalAs>] + public static partial Task GetItems(JSObject item); + + [JSImport("StorageItems.itemsArray", "storage.ts")] + public static partial JSObject[] ItemsArray(JSObject item); + + [JSImport("StorageProvider.createAcceptType", "storage.ts")] + public static partial JSObject CreateAcceptType(string description, string[] mimeTypes); +} diff --git a/src/Web/Avalonia.Web/Interop/StreamHelper.cs b/src/Web/Avalonia.Web/Interop/StreamHelper.cs index c77d87fbe4..0a83bbb871 100644 --- a/src/Web/Avalonia.Web/Interop/StreamHelper.cs +++ b/src/Web/Avalonia.Web/Interop/StreamHelper.cs @@ -13,43 +13,28 @@ internal static partial class StreamHelper public static partial void Seek(JSObject stream, [JSMarshalAs] long position); [JSImport("StreamHelper.truncate", "avalonia.ts")] - public static partial void Truncate(JSObject stream, [JSMarshalAs] long position); - - [JSImport("StreamHelper.write", "avalonia.ts")] - public static partial void Write(JSObject stream, [JSMarshalAs] Span data); + public static partial void Truncate(JSObject stream, [JSMarshalAs] long size); [JSImport("StreamHelper.write", "avalonia.ts")] public static partial Task WriteAsync(JSObject stream, [JSMarshalAs] ArraySegment data); - [JSImport("StreamHelper.close", "avalonia.ts")] - public static partial void Close(JSObject stream); - [JSImport("StreamHelper.close", "avalonia.ts")] public static partial Task CloseAsync(JSObject stream); - [JSImport("StreamHelper.size", "avalonia.ts")] - [return: JSMarshalAs] - public static partial long Size(JSObject stream); - [JSImport("StreamHelper.byteLength", "avalonia.ts")] [return: JSMarshalAs] public static partial long ByteLength(JSObject stream); - [JSImport("StreamHelper.sliceToArray", "avalonia.ts")] - [return: JSMarshalAs] - public static partial Span Slice(JSObject stream, [JSMarshalAs] long offset, int count); + [JSImport("StreamHelper.sliceArrayBuffer", "avalonia.ts")] + private static partial Task SliceToArrayBuffer(JSObject stream, [JSMarshalAs] long offset, int count); + + [JSImport("StreamHelper.toMemoryView", "avalonia.ts")] + [return: JSMarshalAs>] + private static partial byte[] ArrayBufferToMemoryView(JSObject stream); - public static async Task> SliceAsync(JSObject stream, long offset, int count) + public static async Task SliceAsync(JSObject stream, long offset, int count) { - using var buffer = await SliceToBufferAsync(stream, offset, count); - return BufferToArray(buffer); + using var buffer = await SliceToArrayBuffer(stream, offset, count); + return ArrayBufferToMemoryView(buffer); } - - [JSImport("StreamHelper.slice", "avalonia.ts")] - [return: JSMarshalAs>] - private static partial Task SliceToBufferAsync(JSObject stream, [JSMarshalAs] long offset, int count); - - [JSImport("StreamHelper.toArray", "avalonia.ts")] - [return: JSMarshalAs] - private static partial ArraySegment BufferToArray(JSObject stream); } diff --git a/src/Web/Avalonia.Web/JSObjectControlHandle.cs b/src/Web/Avalonia.Web/JSObjectControlHandle.cs new file mode 100644 index 0000000000..e56ca123eb --- /dev/null +++ b/src/Web/Avalonia.Web/JSObjectControlHandle.cs @@ -0,0 +1,30 @@ +using System; +using System.Runtime.InteropServices.JavaScript; + +using Avalonia.Controls.Platform; + +namespace Avalonia.Web; + +public class JSObjectControlHandle : INativeControlHostDestroyableControlHandle +{ + internal const string ElementReferenceDescriptor = "JSObject"; + + public JSObjectControlHandle(JSObject reference) + { + Object = reference; + } + + public JSObject Object { get; } + + public IntPtr Handle => throw new NotSupportedException(); + + public string? HandleDescriptor => ElementReferenceDescriptor; + + public void Destroy() + { + if (Object is JSObject inProcess && !inProcess.IsDisposed) + { + inProcess.Dispose(); + } + } +} diff --git a/src/Web/Avalonia.Web/BrowserSkiaGpu.cs b/src/Web/Avalonia.Web/Skia/BrowserSkiaGpu.cs similarity index 95% rename from src/Web/Avalonia.Web/BrowserSkiaGpu.cs rename to src/Web/Avalonia.Web/Skia/BrowserSkiaGpu.cs index 20765b5449..f80838232b 100644 --- a/src/Web/Avalonia.Web/BrowserSkiaGpu.cs +++ b/src/Web/Avalonia.Web/Skia/BrowserSkiaGpu.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Avalonia.Skia; -namespace Avalonia.Web +namespace Avalonia.Web.Skia { public class BrowserSkiaGpu : ISkiaGpu { diff --git a/src/Web/Avalonia.Web/BrowserSkiaGpuRenderSession.cs b/src/Web/Avalonia.Web/Skia/BrowserSkiaGpuRenderSession.cs similarity index 97% rename from src/Web/Avalonia.Web/BrowserSkiaGpuRenderSession.cs rename to src/Web/Avalonia.Web/Skia/BrowserSkiaGpuRenderSession.cs index 022ed20022..a7f7d9db3d 100644 --- a/src/Web/Avalonia.Web/BrowserSkiaGpuRenderSession.cs +++ b/src/Web/Avalonia.Web/Skia/BrowserSkiaGpuRenderSession.cs @@ -1,7 +1,7 @@ using Avalonia.Skia; using SkiaSharp; -namespace Avalonia.Web +namespace Avalonia.Web.Skia { internal class BrowserSkiaGpuRenderSession : ISkiaGpuRenderSession { diff --git a/src/Web/Avalonia.Web/BrowserSkiaGpuRenderTarget.cs b/src/Web/Avalonia.Web/Skia/BrowserSkiaGpuRenderTarget.cs similarity index 97% rename from src/Web/Avalonia.Web/BrowserSkiaGpuRenderTarget.cs rename to src/Web/Avalonia.Web/Skia/BrowserSkiaGpuRenderTarget.cs index 644ebf19c8..dba9b34166 100644 --- a/src/Web/Avalonia.Web/BrowserSkiaGpuRenderTarget.cs +++ b/src/Web/Avalonia.Web/Skia/BrowserSkiaGpuRenderTarget.cs @@ -1,7 +1,7 @@ using Avalonia.Skia; using SkiaSharp; -namespace Avalonia.Web +namespace Avalonia.Web.Skia { internal class BrowserSkiaGpuRenderTarget : ISkiaGpuRenderTarget { diff --git a/src/Web/Avalonia.Web/BrowserSkiaRasterSurface.cs b/src/Web/Avalonia.Web/Skia/BrowserSkiaRasterSurface.cs similarity index 98% rename from src/Web/Avalonia.Web/BrowserSkiaRasterSurface.cs rename to src/Web/Avalonia.Web/Skia/BrowserSkiaRasterSurface.cs index 0946e72d1d..c7005583ac 100644 --- a/src/Web/Avalonia.Web/BrowserSkiaRasterSurface.cs +++ b/src/Web/Avalonia.Web/Skia/BrowserSkiaRasterSurface.cs @@ -5,7 +5,7 @@ using Avalonia.Platform; using Avalonia.Skia; using SkiaSharp; -namespace Avalonia.Web +namespace Avalonia.Web.Skia { internal class BrowserSkiaRasterSurface : IBrowserSkiaSurface, IFramebufferPlatformSurface, IDisposable { diff --git a/src/Web/Avalonia.Web/BrowserSkiaSurface.cs b/src/Web/Avalonia.Web/Skia/BrowserSkiaSurface.cs similarity index 95% rename from src/Web/Avalonia.Web/BrowserSkiaSurface.cs rename to src/Web/Avalonia.Web/Skia/BrowserSkiaSurface.cs index 6970cb1f22..27a206c0ec 100644 --- a/src/Web/Avalonia.Web/BrowserSkiaSurface.cs +++ b/src/Web/Avalonia.Web/Skia/BrowserSkiaSurface.cs @@ -1,7 +1,7 @@ using Avalonia.Web.Interop; using SkiaSharp; -namespace Avalonia.Web +namespace Avalonia.Web.Skia { internal class BrowserSkiaSurface : IBrowserSkiaSurface { @@ -14,7 +14,7 @@ namespace Avalonia.Web Scaling = scaling; Origin = origin; } - + public SKColorType ColorType { get; set; } public PixelSize Size { get; set; } diff --git a/src/Web/Avalonia.Web/IBrowserSkiaSurface.cs b/src/Web/Avalonia.Web/Skia/IBrowserSkiaSurface.cs similarity index 84% rename from src/Web/Avalonia.Web/IBrowserSkiaSurface.cs rename to src/Web/Avalonia.Web/Skia/IBrowserSkiaSurface.cs index 4b492f6b17..7301ae45cd 100644 --- a/src/Web/Avalonia.Web/IBrowserSkiaSurface.cs +++ b/src/Web/Avalonia.Web/Skia/IBrowserSkiaSurface.cs @@ -1,4 +1,4 @@ -namespace Avalonia.Web +namespace Avalonia.Web.Skia { internal interface IBrowserSkiaSurface { diff --git a/src/Web/Avalonia.Web/Storage/BlobReadableStream.cs b/src/Web/Avalonia.Web/Storage/BlobReadableStream.cs index a48f809c06..640c2fd745 100644 --- a/src/Web/Avalonia.Web/Storage/BlobReadableStream.cs +++ b/src/Web/Avalonia.Web/Storage/BlobReadableStream.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; namespace Avalonia.Web.Storage; -[System.Runtime.Versioning.SupportedOSPlatform("browser")] // gets rid of callsite warnings +[System.Runtime.Versioning.SupportedOSPlatform("browser")] internal class BlobReadableStream : Stream { private JSObject? _jSReference; @@ -20,7 +20,7 @@ internal class BlobReadableStream : Stream _length = StreamHelper.ByteLength(JSReference); } - private JSObject JSReference => _jSReference ?? throw new ObjectDisposedException(nameof(JSWriteableStream)); + private JSObject JSReference => _jSReference ?? throw new ObjectDisposedException(nameof(WriteableStream)); public override bool CanRead => true; @@ -55,21 +55,8 @@ internal class BlobReadableStream : Stream => throw new NotSupportedException(); public override int Read(byte[] buffer, int offset, int count) - => Read(buffer.AsSpan(offset, count)); - - public override int Read(Span buffer) { - var numBytesToRead = (int)Math.Min(buffer.Length, Length - _position); - var bytesRead = StreamHelper.Slice(JSReference, _position, numBytesToRead); - if (bytesRead.Length != numBytesToRead) - { - throw new EndOfStreamException("Failed to read the requested number of bytes from the stream."); - } - - _position += bytesRead.Length; - bytesRead.CopyTo(buffer); - - return bytesRead.Length; + throw new InvalidOperationException("Browser supports only ReadAsync"); } public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) @@ -79,15 +66,15 @@ internal class BlobReadableStream : Stream { var numBytesToRead = (int)Math.Min(buffer.Length, Length - _position); var bytesRead = await StreamHelper.SliceAsync(JSReference, _position, numBytesToRead); - if (bytesRead.Count != numBytesToRead) + if (bytesRead.Length != numBytesToRead) { throw new EndOfStreamException("Failed to read the requested number of bytes from the stream."); } - _position += bytesRead.Count; - bytesRead.AsMemory().CopyTo(buffer); + _position += bytesRead.Length; + bytesRead.CopyTo(buffer); - return bytesRead.Count; + return bytesRead.Length; } protected override void Dispose(bool disposing) diff --git a/src/Web/Avalonia.Web/Storage/BrowserStorageProvider.cs b/src/Web/Avalonia.Web/Storage/BrowserStorageProvider.cs new file mode 100644 index 0000000000..55ae2bdef0 --- /dev/null +++ b/src/Web/Avalonia.Web/Storage/BrowserStorageProvider.cs @@ -0,0 +1,257 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices.JavaScript; +using System.Runtime.Versioning; +using System.Threading.Tasks; + +using Avalonia.Platform.Storage; +using Avalonia.Web.Interop; + +namespace Avalonia.Web.Storage; + +internal record FilePickerAcceptType(string Description, IReadOnlyDictionary> Accept); + +[SupportedOSPlatform("browser")] +internal class BrowserStorageProvider : IStorageProvider +{ + internal const string PickerCancelMessage = "The user aborted a request"; + internal const string NoPermissionsMessage = "Permissions denied"; + + private readonly Lazy> _lazyModule = new(() => JSHost.ImportAsync("storage.ts", "./storage.js")); + + public bool CanOpen => StorageHelper.CanShowOpenFilePicker(); + public bool CanSave => StorageHelper.CanShowSaveFilePicker(); + public bool CanPickFolder => StorageHelper.CanShowDirectoryPicker(); + + public async Task> OpenFilePickerAsync(FilePickerOpenOptions options) + { + _ = await _lazyModule.Value; + var startIn = (options.SuggestedStartLocation as JSStorageItem)?.FileHandle; + + var (types, exludeAll) = ConvertFileTypes(options.FileTypeFilter); + + try + { + using var items = await StorageHelper.OpenFileDialog(startIn, options.AllowMultiple, types, exludeAll); + if (items is null) + { + return Array.Empty(); + } + + var itemsArray = StorageHelper.ItemsArray(items); + return itemsArray.Select(item => new JSStorageFile(item)).ToArray(); + } + catch (JSException ex) when (ex.Message.Contains(PickerCancelMessage, StringComparison.Ordinal)) + { + return Array.Empty(); + } + finally + { + if (types is not null) + { + foreach (var type in types) + { + type.Dispose(); + } + } + } + } + + public async Task SaveFilePickerAsync(FilePickerSaveOptions options) + { + _ = await _lazyModule.Value; + var startIn = (options.SuggestedStartLocation as JSStorageItem)?.FileHandle; + + var (types, exludeAll) = ConvertFileTypes(options.FileTypeChoices); + + try + { + var item = await StorageHelper.SaveFileDialog(startIn, options.SuggestedFileName, types, exludeAll); + return item is not null ? new JSStorageFile(item) : null; + } + catch (JSException ex) when (ex.Message.Contains(PickerCancelMessage, StringComparison.Ordinal)) + { + return null; + } + finally + { + if (types is not null) + { + foreach (var type in types) + { + type.Dispose(); + } + } + } + } + + public async Task> OpenFolderPickerAsync(FolderPickerOpenOptions options) + { + _ = await _lazyModule.Value; + var startIn = (options.SuggestedStartLocation as JSStorageItem)?.FileHandle; + + try + { + var item = await StorageHelper.SelectFolderDialog(startIn); + return item is not null ? new[] { new JSStorageFolder(item) } : Array.Empty(); + } + catch (JSException ex) when (ex.Message.Contains(PickerCancelMessage, StringComparison.Ordinal)) + { + return Array.Empty(); + } + } + + public async Task OpenFileBookmarkAsync(string bookmark) + { + _ = await _lazyModule.Value; + var item = await StorageHelper.OpenBookmark(bookmark); + return item is not null ? new JSStorageFile(item) : null; + } + + public async Task OpenFolderBookmarkAsync(string bookmark) + { + _ = await _lazyModule.Value; + var item = await StorageHelper.OpenBookmark(bookmark); + return item is not null ? new JSStorageFolder(item) : null; + } + + private static (JSObject[]? types, bool excludeAllOption) ConvertFileTypes(IEnumerable? input) + { + var types = input? + .Where(t => t.MimeTypes?.Any() == true && t != FilePickerFileTypes.All) + .Select(t => StorageHelper.CreateAcceptType(t.Name, t.MimeTypes!.ToArray())) + .ToArray(); + if (types?.Length == 0) + { + types = null; + } + + var inlcudeAll = input?.Contains(FilePickerFileTypes.All) == true || types is null; + + return (types, !inlcudeAll); + } +} + +internal abstract class JSStorageItem : IStorageBookmarkItem +{ + internal JSObject? _fileHandle; + + protected JSStorageItem(JSObject fileHandle) + { + _fileHandle = fileHandle ?? throw new ArgumentNullException(nameof(fileHandle)); + } + + internal JSObject FileHandle => _fileHandle ?? throw new ObjectDisposedException(nameof(JSStorageItem)); + + public string Name => FileHandle.GetPropertyAsString("name") ?? string.Empty; + + public bool TryGetUri([NotNullWhen(true)] out Uri? uri) + { + uri = new Uri(Name, UriKind.Relative); + return false; + } + + public async Task GetBasicPropertiesAsync() + { + using var properties = await StorageHelper.GetProperties(FileHandle); + var size = (long?)properties?.GetPropertyAsDouble("Size"); + var lastModified = (long?)properties?.GetPropertyAsDouble("LastModified"); + + return new StorageItemProperties( + (ulong?)size, + dateCreated: null, + dateModified: lastModified > 0 ? DateTimeOffset.FromUnixTimeMilliseconds(lastModified.Value) : null); + } + + public bool CanBookmark => true; + + public Task SaveBookmarkAsync() + { + return StorageHelper.SaveBookmark(FileHandle); + } + + public Task GetParentAsync() + { + return Task.FromResult(null); + } + + public Task ReleaseBookmarkAsync() + { + return StorageHelper.DeleteBookmark(FileHandle); + } + + public void Dispose() + { + _fileHandle?.Dispose(); + _fileHandle = null; + } +} + +internal class JSStorageFile : JSStorageItem, IStorageBookmarkFile +{ + public JSStorageFile(JSObject fileHandle) : base(fileHandle) + { + } + + public bool CanOpenRead => true; + public async Task OpenReadAsync() + { + try + { + var blob = await StorageHelper.OpenRead(FileHandle); + return new BlobReadableStream(blob); + } + catch (JSException ex) when (ex.Message == BrowserStorageProvider.NoPermissionsMessage) + { + throw new UnauthorizedAccessException("User denied permissions to open the file", ex); + } + } + + public bool CanOpenWrite => true; + public async Task OpenWriteAsync() + { + try + { + using var properties = await StorageHelper.GetProperties(FileHandle); + var streamWriter = await StorageHelper.OpenWrite(FileHandle); + var size = (long?)properties?.GetPropertyAsDouble("Size") ?? 0; + + return new WriteableStream(streamWriter, size); + } + catch (JSException ex) when (ex.Message == BrowserStorageProvider.NoPermissionsMessage) + { + throw new UnauthorizedAccessException("User denied permissions to open the file", ex); + } + } +} + +internal class JSStorageFolder : JSStorageItem, IStorageBookmarkFolder +{ + public JSStorageFolder(JSObject fileHandle) : base(fileHandle) + { + } + + public async Task> GetItemsAsync() + { + using var items = await StorageHelper.GetItems(FileHandle); + if (items is null) + { + return Array.Empty(); + } + + var itemsArray = StorageHelper.ItemsArray(items); + + return itemsArray + .Select(reference => reference.GetPropertyAsString("kind") switch + { + "directory" => (IStorageItem)new JSStorageFolder(reference), + "file" => new JSStorageFile(reference), + _ => null + }) + .Where(i => i is not null) + .ToArray()!; + } +} diff --git a/src/Web/Avalonia.Web/Storage/WriteableStream.cs b/src/Web/Avalonia.Web/Storage/WriteableStream.cs index 100b7ccfed..f9699fef7a 100644 --- a/src/Web/Avalonia.Web/Storage/WriteableStream.cs +++ b/src/Web/Avalonia.Web/Storage/WriteableStream.cs @@ -6,22 +6,22 @@ using System.Threading.Tasks; namespace Avalonia.Web.Storage; -[System.Runtime.Versioning.SupportedOSPlatform("browser")] // gets rid of callsite warnings +[System.Runtime.Versioning.SupportedOSPlatform("browser")] // Loose wrapper implementaion of a stream on top of FileAPI FileSystemWritableFileStream -internal sealed class JSWriteableStream : Stream +internal sealed class WriteableStream : Stream { private JSObject? _jSReference; // Unfortunatelly we can't read current length/position, so we need to keep it C#-side only. private long _length, _position; - internal JSWriteableStream(JSObject jSReference, long initialLength) + internal WriteableStream(JSObject jSReference, long initialLength) { _jSReference = jSReference; _length = initialLength; } - private JSObject JSReference => _jSReference ?? throw new ObjectDisposedException(nameof(JSWriteableStream)); + private JSObject JSReference => _jSReference ?? throw new ObjectDisposedException(nameof(WriteableStream)); public override bool CanRead => false; @@ -75,20 +75,7 @@ internal sealed class JSWriteableStream : Stream public override void Write(byte[] buffer, int offset, int count) { - StreamHelper.Write(JSReference, buffer.AsSpan(offset, count)); - } - - public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - if (offset != 0 || count != buffer.Length) - { - // TODO, we need to pass prepared buffer to the JS - // Can't use ArrayPool as it can return bigger array than requested - // Can't use Span/Memory, as it's not supported by JS interop yet. - // Alternatively we can pass original buffer and offset+count, so it can be trimmed on the JS side (but is it more efficient tho?) - buffer = buffer.AsMemory(offset, count).ToArray(); - } - return WriteAsyncInternal(buffer, cancellationToken); + throw new InvalidOperationException("Browser supports only WriteAsync"); } public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) @@ -110,7 +97,7 @@ internal sealed class JSWriteableStream : Stream _jSReference = null; try { - StreamHelper.Close(JSReference); + _ = StreamHelper.CloseAsync(jsReference); } finally { @@ -126,7 +113,7 @@ internal sealed class JSWriteableStream : Stream _jSReference = null; try { - await StreamHelper.CloseAsync(JSReference); + await StreamHelper.CloseAsync(jsReference); } finally { diff --git a/src/Web/Avalonia.Web/WebEmbeddableControlRoot.cs b/src/Web/Avalonia.Web/WebEmbeddableControlRoot.cs new file mode 100644 index 0000000000..19f36403ad --- /dev/null +++ b/src/Web/Avalonia.Web/WebEmbeddableControlRoot.cs @@ -0,0 +1,68 @@ +using System; +using Avalonia.Controls.Embedding; +using Avalonia.Media; +using Avalonia.Platform; +using Avalonia.Rendering.SceneGraph; + +namespace Avalonia.Web +{ + internal class WebEmbeddableControlRoot : EmbeddableControlRoot + { + class SplashScreenCloseCustomDrawingOperation : ICustomDrawOperation + { + private bool _hasRendered; + private Action _onFirstRender; + + public SplashScreenCloseCustomDrawingOperation(Action onFirstRender) + { + _onFirstRender = onFirstRender; + } + + public Rect Bounds => Rect.Empty; + + public bool HasRendered => _hasRendered; + + public void Dispose() + { + + } + + public bool Equals(ICustomDrawOperation? other) + { + return false; + } + + public bool HitTest(Point p) + { + return false; + } + + public void Render(IDrawingContextImpl context) + { + _hasRendered = true; + _onFirstRender(); + } + } + + public WebEmbeddableControlRoot(ITopLevelImpl impl, Action onFirstRender) : base(impl) + { + _splashCloseOp = new SplashScreenCloseCustomDrawingOperation(() => + { + _splashCloseOp = null; + onFirstRender(); + }); + } + + private SplashScreenCloseCustomDrawingOperation? _splashCloseOp; + + public override void Render(DrawingContext context) + { + base.Render(context); + + if (_splashCloseOp != null) + { + context.Custom(_splashCloseOp); + } + } + } +} diff --git a/src/Web/Avalonia.Web/webapp/.eslintrc.json b/src/Web/Avalonia.Web/webapp/.eslintrc.json new file mode 100644 index 0000000000..4b7e24987f --- /dev/null +++ b/src/Web/Avalonia.Web/webapp/.eslintrc.json @@ -0,0 +1,47 @@ +{ + "env": { + "browser": true, + "es6": true + }, + "extends": "standard-with-typescript", + "overrides": [], + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module", + "project": [ + "tsconfig.json" + ] + }, + "rules": { + "indent": [ + "warn", + 4 + ], + "@typescript-eslint/indent": [ + "warn", + 4 + ], + "quotes": ["warn", "double"], + "semi": ["error", "always"], + "@typescript-eslint/quotes": ["warn", "double"], + "@typescript-eslint/explicit-function-return-type": "off", + "@typescript-eslint/no-extraneous-class": "off", + "@typescript-eslint/strict-boolean-expressions": "off", + "@typescript-eslint/space-before-function-paren": "off", + "@typescript-eslint/semi": ["error", "always"], + "@typescript-eslint/member-delimiter-style": [ + "error", + { + "multiline": { + "delimiter": "semi", + "requireLast": true + }, + "singleline": { + "delimiter": "semi", + "requireLast": false + } + } + ] + }, + "ignorePatterns": ["types/*"] +} diff --git a/src/Web/Avalonia.Web/webapp/build.js b/src/Web/Avalonia.Web/webapp/build.js index 32f79b0709..6b6df4c300 100644 --- a/src/Web/Avalonia.Web/webapp/build.js +++ b/src/Web/Avalonia.Web/webapp/build.js @@ -10,7 +10,7 @@ require("esbuild").build({ target: "es2016", platform: "browser", sourcemap: "linked", - loader: {".ts": "ts"} - }) - .then(() => console.log("⚡ Done")) - .catch(() => process.exit(1)); \ No newline at end of file + loader: { ".ts": "ts" } +}) + .then(() => console.log("⚡ Done")) + .catch(() => process.exit(1)); diff --git a/src/Web/Avalonia.Web/webapp/modules/avalonia.ts b/src/Web/Avalonia.Web/webapp/modules/avalonia.ts index 4b85434656..6992921f5b 100644 --- a/src/Web/Avalonia.Web/webapp/modules/avalonia.ts +++ b/src/Web/Avalonia.Web/webapp/modules/avalonia.ts @@ -1,18 +1,20 @@ -import { RuntimeAPI } from "../types/dotnet"; -import { SizeWatcher } from "./avalonia/canvas"; -import { DpiWatcher } from "./avalonia/canvas"; -import { Canvas } from "./avalonia/canvas"; +import { RuntimeAPI } from "../types/dotnet"; +import { SizeWatcher, DpiWatcher, Canvas } from "./avalonia/canvas"; import { InputHelper } from "./avalonia/input"; import { AvaloniaDOM } from "./avalonia/dom"; -import { CaretHelper } from "./avalonia/CaretHelper" +import { Caniuse } from "./avalonia/caniuse"; +import { StreamHelper } from "./avalonia/stream"; +import { NativeControlHost } from "./avalonia/NativeControlHost"; export async function createAvaloniaRuntime(api: RuntimeAPI): Promise { api.setModuleImports("avalonia.ts", { + Caniuse, Canvas, InputHelper, SizeWatcher, DpiWatcher, AvaloniaDOM, - CaretHelper + StreamHelper, + NativeControlHost }); } diff --git a/src/Web/Avalonia.Web/webapp/modules/avalonia/caniuse.ts b/src/Web/Avalonia.Web/webapp/modules/avalonia/caniuse.ts new file mode 100644 index 0000000000..6dedcb724f --- /dev/null +++ b/src/Web/Avalonia.Web/webapp/modules/avalonia/caniuse.ts @@ -0,0 +1,13 @@ +export class Caniuse { + public static canShowOpenFilePicker(): boolean { + return typeof window.showOpenFilePicker !== "undefined"; + } + + public static canShowSaveFilePicker(): boolean { + return typeof window.showSaveFilePicker !== "undefined"; + } + + public static canShowDirectoryPicker(): boolean { + return typeof window.showDirectoryPicker !== "undefined"; + } +} diff --git a/src/Web/Avalonia.Web/webapp/modules/avalonia/canvas.ts b/src/Web/Avalonia.Web/webapp/modules/avalonia/canvas.ts index 51f851c9c7..017ca58b8f 100644 --- a/src/Web/Avalonia.Web/webapp/modules/avalonia/canvas.ts +++ b/src/Web/Avalonia.Web/webapp/modules/avalonia/canvas.ts @@ -1,6 +1,4 @@ -declare let Module: EmscriptenModule; - -type SKGLViewInfo = { +interface SKGLViewInfo { context: WebGLRenderingContext | WebGL2RenderingContext | undefined; fboId: number; stencil: number; @@ -9,8 +7,8 @@ type SKGLViewInfo = { } type CanvasElement = { - Canvas: Canvas | undefined -} & HTMLCanvasElement + Canvas: Canvas | undefined; +} & HTMLCanvasElement; export class Canvas { static elements: Map; @@ -24,22 +22,24 @@ export class Canvas { newHeight?: number; public static initGL(element: HTMLCanvasElement, elementId: string, renderFrameCallback: () => void): SKGLViewInfo | null { - var view = Canvas.init(true, element, elementId, renderFrameCallback); - if (!view || !view.glInfo) + const view = Canvas.init(true, element, elementId, renderFrameCallback); + if (!view || !view.glInfo) { return null; + } return view.glInfo; } static init(useGL: boolean, element: HTMLCanvasElement, elementId: string, renderFrameCallback: () => void): Canvas | null { - var htmlCanvas = element as CanvasElement; + const htmlCanvas = element as CanvasElement; if (!htmlCanvas) { - console.error(`No canvas element was provided.`); + console.error("No canvas element was provided."); return null; } - if (!Canvas.elements) + if (!Canvas.elements) { Canvas.elements = new Map(); + } Canvas.elements.set(elementId, element); const view = new Canvas(useGL, element, renderFrameCallback); @@ -56,16 +56,16 @@ export class Canvas { if (useGL) { const ctx = Canvas.createWebGLContext(element); if (!ctx) { - console.error(`Failed to create WebGL context: err ${ctx}`); + console.error("Failed to create WebGL context"); return; } - var GL = (globalThis as any).AvaloniaGL; + const GL = (globalThis as any).AvaloniaGL; // make current GL.makeContextCurrent(ctx); - var GLctx = GL.currentContext.GLctx as WebGLRenderingContext; + const GLctx = GL.currentContext.GLctx as WebGLRenderingContext; // read values const fbo = GLctx.getParameter(GLctx.FRAMEBUFFER_BINDING); @@ -75,17 +75,17 @@ export class Canvas { fboId: fbo ? fbo.id : 0, stencil: GLctx.getParameter(GLctx.STENCIL_BITS), sample: 0, // TODO: GLctx.getParameter(GLctx.SAMPLES) - depth: GLctx.getParameter(GLctx.DEPTH_BITS), + depth: GLctx.getParameter(GLctx.DEPTH_BITS) }; } } - public setEnableRenderLoop(enable: boolean) { + public setEnableRenderLoop(enable: boolean): void { this.renderLoopEnabled = enable; // either start the new frame or cancel the existing one if (enable) { - //console.info(`Enabling render loop with callback ${this.renderFrameCallback._id}...`); + // console.info(`Enabling render loop with callback ${this.renderFrameCallback._id}...`); this.requestAnimationFrame(); } else if (this.renderLoopRequest !== 0) { window.cancelAnimationFrame(this.renderLoopRequest); @@ -93,71 +93,76 @@ export class Canvas { } } - public requestAnimationFrame(renderLoop?: boolean) { + public requestAnimationFrame(renderLoop?: boolean): void { // optionally update the render loop - if (renderLoop !== undefined && this.renderLoopEnabled !== renderLoop) + if (renderLoop !== undefined && this.renderLoopEnabled !== renderLoop) { this.setEnableRenderLoop(renderLoop); + } // skip because we have a render loop - if (this.renderLoopRequest !== 0) + if (this.renderLoopRequest !== 0) { return; + } // add the draw to the next frame this.renderLoopRequest = window.requestAnimationFrame(() => { if (this.glInfo) { - var GL = (globalThis as any).AvaloniaGL; + const GL = (globalThis as any).AvaloniaGL; // make current GL.makeContextCurrent(this.glInfo.context); } - if (this.htmlCanvas.width != this.newWidth) { - this.htmlCanvas.width = this.newWidth || 0; + if (this.htmlCanvas.width !== this.newWidth) { + this.htmlCanvas.width = this.newWidth ?? 0; } - if (this.htmlCanvas.height != this.newHeight) { - this.htmlCanvas.height = this.newHeight || 0; + if (this.htmlCanvas.height !== this.newHeight) { + this.htmlCanvas.height = this.newHeight ?? 0; } this.renderFrameCallback(); this.renderLoopRequest = 0; // we may want to draw the next frame - if (this.renderLoopEnabled) + if (this.renderLoopEnabled) { this.requestAnimationFrame(); + } }); } - public setCanvasSize(width: number, height: number) { + public setCanvasSize(width: number, height: number): void { this.newWidth = width; this.newHeight = height; - if (this.htmlCanvas.width != this.newWidth) { + if (this.htmlCanvas.width !== this.newWidth) { this.htmlCanvas.width = this.newWidth; } - if (this.htmlCanvas.height != this.newHeight) { + if (this.htmlCanvas.height !== this.newHeight) { this.htmlCanvas.height = this.newHeight; } if (this.glInfo) { - var GL = (globalThis as any).AvaloniaGL; + const GL = (globalThis as any).AvaloniaGL; // make current GL.makeContextCurrent(this.glInfo.context); } } - public static setCanvasSize(element: HTMLCanvasElement, width: number, height: number) { + public static setCanvasSize(element: HTMLCanvasElement, width: number, height: number): void { const htmlCanvas = element as CanvasElement; - if (!htmlCanvas || !htmlCanvas.Canvas) + if (!htmlCanvas || !htmlCanvas.Canvas) { return; + } htmlCanvas.Canvas.setCanvasSize(width, height); } - public static requestAnimationFrame(element: HTMLCanvasElement, renderLoop?: boolean) { + public static requestAnimationFrame(element: HTMLCanvasElement, renderLoop?: boolean): void { const htmlCanvas = element as CanvasElement; - if (!htmlCanvas || !htmlCanvas.Canvas) + if (!htmlCanvas || !htmlCanvas.Canvas) { return; + } htmlCanvas.Canvas.requestAnimationFrame(renderLoop); } @@ -176,15 +181,15 @@ export class Canvas { minorVersion: 0, enableExtensionsByDefault: 1, explicitSwapControl: 0, - renderViaOffscreenBackBuffer: 1, + renderViaOffscreenBackBuffer: 1 }; - var GL = (globalThis as any).AvaloniaGL; + const GL = (globalThis as any).AvaloniaGL; let ctx: WebGLRenderingContext = GL.createContext(htmlCanvas, contextAttributes); if (!ctx && contextAttributes.majorVersion > 1) { - console.warn('Falling back to WebGL 1.0'); + console.warn("Falling back to WebGL 1.0"); contextAttributes.majorVersion = 1; contextAttributes.minorVersion = 0; ctx = GL.createContext(htmlCanvas, contextAttributes); @@ -196,9 +201,9 @@ export class Canvas { type SizeWatcherElement = { SizeWatcher: SizeWatcherInstance; -} & HTMLElement +} & HTMLElement; -type SizeWatcherInstance = { +interface SizeWatcherInstance { callback: (width: number, height: number) => void; } @@ -206,17 +211,16 @@ export class SizeWatcher { static observer: ResizeObserver; static elements: Map; - public static observe(element: HTMLElement, elementId: string, callback: (width: number, height: number) => void) { - if (!element || !callback) + public static observe(element: HTMLElement, elementId: string, callback: (width: number, height: number) => void): void { + if (!element || !callback) { return; - - //console.info(`Adding size watcher observation with callback ${callback._id}...`); + } SizeWatcher.init(); const watcherElement = element as SizeWatcherElement; watcherElement.SizeWatcher = { - callback: callback + callback }; SizeWatcher.elements.set(elementId, element); @@ -225,38 +229,38 @@ export class SizeWatcher { SizeWatcher.invoke(element); } - public static unobserve(elementId: string) { - if (!elementId || !SizeWatcher.observer) + public static unobserve(elementId: string): void { + if (!elementId || !SizeWatcher.observer) { return; + } - //console.info('Removing size watcher observation...'); - - const element = SizeWatcher.elements.get(elementId)!; - - SizeWatcher.elements.delete(elementId); - SizeWatcher.observer.unobserve(element); + const element = SizeWatcher.elements.get(elementId); + if (element) { + SizeWatcher.elements.delete(elementId); + SizeWatcher.observer.unobserve(element); + } } - static init() { - if (SizeWatcher.observer) + static init(): void { + if (SizeWatcher.observer) { return; - - //console.info('Starting size watcher...'); + } SizeWatcher.elements = new Map(); SizeWatcher.observer = new ResizeObserver((entries) => { - for (let entry of entries) { + for (const entry of entries) { SizeWatcher.invoke(entry.target); } }); } - static invoke(element: Element) { + static invoke(element: Element): void { const watcherElement = element as SizeWatcherElement; const instance = watcherElement.SizeWatcher; - if (!instance || !instance.callback) + if (!instance || !instance.callback) { return; + } return instance.callback(element.clientWidth, element.clientHeight); } @@ -267,13 +271,11 @@ export class DpiWatcher { static timerId: number; static callback: (old: number, newdpi: number) => void; - public static getDpi() { + public static getDpi(): number { return window.devicePixelRatio; } - public static start(callback: (old: number, newdpi: number) => void) : number { - //console.info(`Starting DPI watcher with callback ${callback._id}...`); - + public static start(callback: (old: number, newdpi: number) => void): number { DpiWatcher.lastDpi = window.devicePixelRatio; DpiWatcher.timerId = window.setInterval(DpiWatcher.update, 1000); DpiWatcher.callback = callback; @@ -281,17 +283,14 @@ export class DpiWatcher { return DpiWatcher.lastDpi; } - public static stop() { - //console.info(`Stopping DPI watcher with callback ${DpiWatcher.callback._id}...`); - + public static stop(): void { window.clearInterval(DpiWatcher.timerId); - - //DpiWatcher.callback = undefined; } - static update() { - if (!DpiWatcher.callback) + static update(): void { + if (!DpiWatcher.callback) { return; + } const currentDpi = window.devicePixelRatio; const lastDpi = DpiWatcher.lastDpi; diff --git a/src/Web/Avalonia.Web/webapp/modules/avalonia/CaretHelper.ts b/src/Web/Avalonia.Web/webapp/modules/avalonia/caretHelper.ts similarity index 90% rename from src/Web/Avalonia.Web/webapp/modules/avalonia/CaretHelper.ts rename to src/Web/Avalonia.Web/webapp/modules/avalonia/caretHelper.ts index 5709854087..546b41669f 100644 --- a/src/Web/Avalonia.Web/webapp/modules/avalonia/CaretHelper.ts +++ b/src/Web/Avalonia.Web/webapp/modules/avalonia/caretHelper.ts @@ -1,4 +1,4 @@ -// Based on https://github.com/component/textarea-caret-position/blob/master/index.js +// Based on https://github.com/component/textarea-caret-position/blob/master/index.js export class CaretHelper { public static getCaretCoordinates( element: HTMLInputElement | HTMLTextAreaElement, @@ -11,7 +11,7 @@ export class CaretHelper { ); } - const debug = (options && options.debug) || false; + const debug = options?.debug ?? false; if (debug) { const el = document.querySelector( "#input-textarea-caret-position-mirror-div" @@ -27,7 +27,7 @@ export class CaretHelper { const style = div.style; const computed = window.getComputedStyle ? window.getComputedStyle(element) - : ((element as any)["currentStyle"] as CSSStyleDeclaration); // currentStyle for IE < 9 + : ((element as any).currentStyle as CSSStyleDeclaration); // currentStyle for IE < 9 const isInput = element.nodeName === "INPUT"; // Default textarea styles @@ -51,7 +51,7 @@ export class CaretHelper { parseInt(computed.borderBottomWidth); const targetHeight = outerHeight + parseInt(computed.lineHeight); if (height > targetHeight) { - style.lineHeight = height - outerHeight + "px"; + style.lineHeight = `${height - outerHeight}px`; } else if (height === targetHeight) { style.lineHeight = computed.lineHeight; } else { @@ -67,8 +67,9 @@ export class CaretHelper { if (isFirefox) { // Firefox lies about the overflow property for textareas: https://bugzilla.mozilla.org/show_bug.cgi?id=984275 - if (element.scrollHeight > parseInt(computed.height)) + if (element.scrollHeight > parseInt(computed.height)) { style.overflowY = "scroll"; + } } else { style.overflow = "hidden"; // for Chrome to not render a scrollbar; IE keeps overflowY = 'scroll' } @@ -88,9 +89,9 @@ export class CaretHelper { div.appendChild(span); const coordinates = { - top: span.offsetTop + parseInt(computed["borderTopWidth"]), - left: span.offsetLeft + parseInt(computed["borderLeftWidth"]), - height: parseInt(computed["lineHeight"]), + top: span.offsetTop + parseInt(computed.borderTopWidth), + left: span.offsetLeft + parseInt(computed.borderLeftWidth), + height: parseInt(computed.lineHeight) }; if (debug) { @@ -103,8 +104,7 @@ export class CaretHelper { } } - -var properties = [ +const properties = [ "direction", // RTL support "boxSizing", "width", // on Chrome and IE, exclude the scrollbar, so the mirror div wraps exactly as the textarea does @@ -142,7 +142,7 @@ var properties = [ "wordSpacing", "tabSize", - "MozTabSize", + "MozTabSize" ]; const isBrowser = typeof window !== "undefined"; diff --git a/src/Web/Avalonia.Web/webapp/modules/avalonia/dom.ts b/src/Web/Avalonia.Web/webapp/modules/avalonia/dom.ts index ad6d451343..943b8330d5 100644 --- a/src/Web/Avalonia.Web/webapp/modules/avalonia/dom.ts +++ b/src/Web/Avalonia.Web/webapp/modules/avalonia/dom.ts @@ -1,4 +1,8 @@ export class AvaloniaDOM { + public static addClass(element: HTMLElement, className: string): void { + element.classList.add(className); + } + static createAvaloniaHost(host: HTMLElement) { // Root element host.classList.add("avalonia-container"); @@ -7,26 +11,24 @@ export class AvaloniaDOM { // Rendering target canvas const canvas = document.createElement("canvas"); - canvas.classList.add('avalonia-canvas'); + canvas.classList.add("avalonia-canvas"); canvas.style.backgroundColor = "#ccc"; canvas.style.width = "100%"; canvas.style.height = "100%"; canvas.style.position = "absolute"; - host.appendChild(canvas); - + // Native controls host const nativeHost = document.createElement("div"); - nativeHost.classList.add('avalonia-native-host'); + nativeHost.classList.add("avalonia-native-host"); nativeHost.style.left = "0px"; nativeHost.style.top = "0px"; nativeHost.style.width = "100%"; nativeHost.style.height = "100%"; nativeHost.style.position = "absolute"; - host.appendChild(nativeHost); // IME const inputElement = document.createElement("input"); - inputElement.classList.add('avalonia-input-element'); + inputElement.classList.add("avalonia-input-element"); inputElement.autocapitalize = "none"; inputElement.type = "text"; inputElement.spellcheck = false; @@ -43,7 +45,10 @@ export class AvaloniaDOM { inputElement.onpaste = function () { return false; }; inputElement.oncopy = function () { return false; }; inputElement.oncut = function () { return false; }; - host.appendChild(inputElement); + + host.prepend(inputElement); + host.prepend(nativeHost); + host.prepend(canvas); return { host, diff --git a/src/Web/Avalonia.Web/webapp/modules/avalonia/input.ts b/src/Web/Avalonia.Web/webapp/modules/avalonia/input.ts index b5c8a60a88..768414ccab 100644 --- a/src/Web/Avalonia.Web/webapp/modules/avalonia/input.ts +++ b/src/Web/Avalonia.Web/webapp/modules/avalonia/input.ts @@ -1,7 +1,6 @@ -import { CaretHelper } from "./CaretHelper" +import { CaretHelper } from "./caretHelper"; -enum RawInputModifiers -{ +enum RawInputModifiers { None = 0, Alt = 1, Control = 2, @@ -24,12 +23,7 @@ export class InputHelper { public static subscribeKeyEvents( element: HTMLInputElement, keyDownCallback: (code: string, key: string, modifiers: RawInputModifiers) => boolean, - keyUpCallback: (code: string, key: string, modifiers: RawInputModifiers) => boolean, - inputCallback: (type: string, data: string|null) => boolean, - compositionStartCallback: (args: CompositionEvent) => boolean, - compositionUpdateCallback: (args: CompositionEvent) => boolean, - compositionEndCallback: (args: CompositionEvent) => boolean) - { + keyUpCallback: (code: string, key: string, modifiers: RawInputModifiers) => boolean) { const keyDownHandler = (args: KeyboardEvent) => { if (keyDownCallback(args.code, args.key, this.getModifiers(args))) { args.preventDefault(); @@ -52,14 +46,13 @@ export class InputHelper { } public static subscribeTextEvents( - element: HTMLInputElement, + element: HTMLInputElement, inputCallback: (type: string, data: string | null) => boolean, compositionStartCallback: (args: CompositionEvent) => boolean, compositionUpdateCallback: (args: CompositionEvent) => boolean, compositionEndCallback: (args: CompositionEvent) => boolean) { - const inputHandler = (args: Event) => { - var inputEvent = args as InputEvent; + const inputEvent = args as InputEvent; // todo check cast if (inputCallback(inputEvent.type, inputEvent.data)) { @@ -102,31 +95,27 @@ export class InputHelper { pointerMoveCallback: (args: PointerEvent) => boolean, pointerDownCallback: (args: PointerEvent) => boolean, pointerUpCallback: (args: PointerEvent) => boolean, - wheelCallback: (args: WheelEvent) => boolean, + wheelCallback: (args: WheelEvent) => boolean ) { const pointerMoveHandler = (args: PointerEvent) => { - if (pointerMoveCallback(args)) { args.preventDefault(); } }; const pointerDownHandler = (args: PointerEvent) => { - if (pointerDownCallback(args)) { args.preventDefault(); } }; const pointerUpHandler = (args: PointerEvent) => { - if (pointerUpCallback(args)) { args.preventDefault(); } }; const wheelHandler = (args: WheelEvent) => { - if (wheelCallback(args)) { args.preventDefault(); } @@ -137,8 +126,6 @@ export class InputHelper { element.addEventListener("pointerup", pointerUpHandler); element.addEventListener("wheel", wheelHandler); - - return () => { element.removeEventListener("pointerover", pointerMoveHandler); element.removeEventListener("pointerdown", pointerDownHandler); @@ -146,7 +133,7 @@ export class InputHelper { element.removeEventListener("wheel", wheelHandler); }; } - + public static subscribeInputEvents( element: HTMLInputElement, inputCallback: (value: string) => boolean @@ -179,18 +166,18 @@ export class InputHelper { inputElement.style.left = (x).toFixed(0) + "px"; inputElement.style.top = (y).toFixed(0) + "px"; - let { height, left, top } = CaretHelper.getCaretCoordinates(inputElement, caret); + const { left, top } = CaretHelper.getCaretCoordinates(inputElement, caret); inputElement.style.left = (x - left).toFixed(0) + "px"; inputElement.style.top = (y - top).toFixed(0) + "px"; } public static hide(inputElement: HTMLInputElement) { - inputElement.style.display = 'none'; + inputElement.style.display = "none"; } public static show(inputElement: HTMLInputElement) { - inputElement.style.display = 'block'; + inputElement.style.display = "block"; } public static setSurroundingText(inputElement: HTMLInputElement, text: string, start: number, end: number) { @@ -201,20 +188,16 @@ export class InputHelper { inputElement.value = text; inputElement.setSelectionRange(start, end); inputElement.style.width = "20px"; - inputElement.style.width = inputElement.scrollWidth + "px"; + inputElement.style.width = `${inputElement.scrollWidth}px`; } private static getModifiers(args: KeyboardEvent): RawInputModifiers { - var modifiers = RawInputModifiers.None; - - if (args.ctrlKey) - modifiers |= RawInputModifiers.Control; - if (args.altKey) - modifiers |= RawInputModifiers.Alt; - if (args.shiftKey) - modifiers |= RawInputModifiers.Shift; - if (args.metaKey) - modifiers |= RawInputModifiers.Meta; + let modifiers = RawInputModifiers.None; + + if (args.ctrlKey) { modifiers |= RawInputModifiers.Control; } + if (args.altKey) { modifiers |= RawInputModifiers.Alt; } + if (args.shiftKey) { modifiers |= RawInputModifiers.Shift; } + if (args.metaKey) { modifiers |= RawInputModifiers.Meta; } return modifiers; } diff --git a/src/Web/Avalonia.Web/webapp/modules/avalonia/nativeControlHost.ts b/src/Web/Avalonia.Web/webapp/modules/avalonia/nativeControlHost.ts new file mode 100644 index 0000000000..c65d5836df --- /dev/null +++ b/src/Web/Avalonia.Web/webapp/modules/avalonia/nativeControlHost.ts @@ -0,0 +1,55 @@ +class NativeControlHostTopLevelAttachment { + _child?: HTMLElement; + _host?: HTMLElement; +} + +export class NativeControlHost { + public static createDefaultChild(parent?: HTMLElement): HTMLElement { + return document.createElement("div"); + } + + public static createAttachment(): NativeControlHostTopLevelAttachment { + return new NativeControlHostTopLevelAttachment(); + } + + public static initializeWithChildHandle(element: NativeControlHostTopLevelAttachment, child: HTMLElement): void { + element._child = child; + element._child.style.position = "absolute"; + } + + public static attachTo(element: NativeControlHostTopLevelAttachment, host?: HTMLElement): void { + if (element._host && element._child) { + element._host.removeChild(element._child); + } + + element._host = host; + + if (element._host && element._child) { + element._host.appendChild(element._child); + } + } + + public static showInBounds(element: NativeControlHostTopLevelAttachment, x: number, y: number, width: number, height: number): void { + if (element._child) { + element._child.style.top = `${y}px`; + element._child.style.left = `${x}px`; + element._child.style.width = `${width}px`; + element._child.style.height = `${height}px`; + element._child.style.display = "block"; + } + } + + public static hideWithSize(element: NativeControlHostTopLevelAttachment, width: number, height: number): void { + if (element._child) { + element._child.style.width = `${width}px`; + element._child.style.height = `${height}px`; + element._child.style.display = "none"; + } + } + + public static releaseChild(element: NativeControlHostTopLevelAttachment): void { + if (element._child) { + element._child = undefined; + } + } +} diff --git a/src/Web/Avalonia.Web/webapp/modules/avalonia/stream.ts b/src/Web/Avalonia.Web/webapp/modules/avalonia/stream.ts new file mode 100644 index 0000000000..1f2c181edc --- /dev/null +++ b/src/Web/Avalonia.Web/webapp/modules/avalonia/stream.ts @@ -0,0 +1,40 @@ +import { IMemoryView } from "../../types/dotnet"; + +export class StreamHelper { + public static async seek(stream: FileSystemWritableFileStream, position: number) { + return await stream.seek(position); + } + + public static async truncate(stream: FileSystemWritableFileStream, size: number) { + return await stream.truncate(size); + } + + public static async close(stream: FileSystemWritableFileStream) { + return await stream.close(); + } + + public static async write(stream: FileSystemWritableFileStream, span: IMemoryView) { + const array = new Uint8Array(span.byteLength); + span.copyTo(array); + + const data: WriteParams = { + type: "write", + data: array + }; + + return await stream.write(data); + } + + public static byteLength(stream: Blob) { + return stream.size; + } + + public static async sliceArrayBuffer(stream: Blob, offset: number, count: number) { + const buffer = await stream.slice(offset, offset + count).arrayBuffer(); + return new Uint8Array(buffer); + } + + public static toMemoryView(buffer: Uint8Array): Uint8Array { + return buffer; + } +} diff --git a/src/Web/Avalonia.Web/webapp/modules/storage.ts b/src/Web/Avalonia.Web/webapp/modules/storage.ts index 7b6cd816de..8b9987afa0 100644 --- a/src/Web/Avalonia.Web/webapp/modules/storage.ts +++ b/src/Web/Avalonia.Web/webapp/modules/storage.ts @@ -1,5 +1,2 @@ -export class StorageProvider { - static isFileApiSupported(): boolean { - return (globalThis as any).showOpenFilePicker !== undefined; - } -} \ No newline at end of file +export { StorageItem, StorageItems } from "./storage/storageItem"; +export { StorageProvider } from "./storage/storageProvider"; diff --git a/src/Web/Avalonia.Web/webapp/modules/storage/indexedDb.ts b/src/Web/Avalonia.Web/webapp/modules/storage/indexedDb.ts new file mode 100644 index 0000000000..54b095d39e --- /dev/null +++ b/src/Web/Avalonia.Web/webapp/modules/storage/indexedDb.ts @@ -0,0 +1,84 @@ +class InnerDbConnection { + constructor(private readonly database: IDBDatabase) { } + + private openStore(store: string, mode: IDBTransactionMode): IDBObjectStore { + const tx = this.database.transaction(store, mode); + return tx.objectStore(store); + } + + public async put(store: string, obj: any, key?: IDBValidKey): Promise { + const os = this.openStore(store, "readwrite"); + + return await new Promise((resolve, reject) => { + const response = os.put(obj, key); + response.onsuccess = () => { + resolve(response.result); + }; + response.onerror = () => { + reject(response.error); + }; + }); + } + + public get(store: string, key: IDBValidKey): any { + const os = this.openStore(store, "readonly"); + + return new Promise((resolve, reject) => { + const response = os.get(key); + response.onsuccess = () => { + resolve(response.result); + }; + response.onerror = () => { + reject(response.error); + }; + }); + } + + public async delete(store: string, key: IDBValidKey): Promise { + const os = this.openStore(store, "readwrite"); + + return await new Promise((resolve, reject) => { + const response = os.delete(key); + response.onsuccess = () => { + resolve(); + }; + response.onerror = () => { + reject(response.error); + }; + }); + } + + public close() { + this.database.close(); + } +} + +class IndexedDbWrapper { + constructor(private readonly databaseName: string, private readonly objectStores: [string]) { + } + + public async connect(): Promise { + const conn = window.indexedDB.open(this.databaseName, 1); + + conn.onupgradeneeded = event => { + const db = (event.target as IDBRequest).result; + this.objectStores.forEach(store => { + db.createObjectStore(store); + }); + }; + + return await new Promise((resolve, reject) => { + conn.onsuccess = event => { + resolve(new InnerDbConnection((event.target as IDBRequest).result)); + }; + conn.onerror = event => { + reject((event.target as IDBRequest).error); + }; + }); + } +} + +export const fileBookmarksStore: string = "fileBookmarks"; +export const avaloniaDb = new IndexedDbWrapper("AvaloniaDb", [ + fileBookmarksStore +]); diff --git a/src/Web/Avalonia.Web/webapp/modules/storage/storageItem.ts b/src/Web/Avalonia.Web/webapp/modules/storage/storageItem.ts new file mode 100644 index 0000000000..b4cf3d278b --- /dev/null +++ b/src/Web/Avalonia.Web/webapp/modules/storage/storageItem.ts @@ -0,0 +1,111 @@ +import { avaloniaDb, fileBookmarksStore } from "./indexedDb"; + +export class StorageItem { + constructor(public handle: FileSystemHandle, private readonly bookmarkId?: string) { } + + public get name(): string { + return this.handle.name; + } + + public get kind(): string { + return this.handle.kind; + } + + public static async openRead(item: StorageItem): Promise { + if (!(item.handle instanceof FileSystemFileHandle)) { + throw new Error("StorageItem is not a file"); + } + + await item.verityPermissions("read"); + + const file = await item.handle.getFile(); + return file; + } + + public static async openWrite(item: StorageItem): Promise { + if (!(item.handle instanceof FileSystemFileHandle)) { + throw new Error("StorageItem is not a file"); + } + + await item.verityPermissions("readwrite"); + + return await item.handle.createWritable({ keepExistingData: true }); + } + + public static async getProperties(item: StorageItem): Promise<{ Size: number; LastModified: number; Type: string } | null> { + const file = item.handle instanceof FileSystemFileHandle && + await item.handle.getFile(); + + if (!file) { + return null; + } + + return { + Size: file.size, + LastModified: file.lastModified, + Type: file.type + }; + } + + public static async getItems(item: StorageItem): Promise { + if (item.handle.kind !== "directory") { + return new StorageItems([]); + } + + const items: StorageItem[] = []; + for await (const [, value] of (item.handle as any).entries()) { + items.push(new StorageItem(value)); + } + return new StorageItems(items); + } + + private async verityPermissions(mode: FileSystemPermissionMode): Promise { + if (await this.handle.queryPermission({ mode }) === "granted") { + return; + } + + if (await this.handle.requestPermission({ mode }) === "denied") { + throw new Error("Permissions denied"); + } + } + + public static async saveBookmark(item: StorageItem): Promise { + // If file was previously bookmarked, just return old one. + if (item.bookmarkId) { + return item.bookmarkId; + } + + const connection = await avaloniaDb.connect(); + try { + const key = await connection.put(fileBookmarksStore, item.handle, item.generateBookmarkId()); + return key as string; + } finally { + connection.close(); + } + } + + public static async deleteBookmark(item: StorageItem): Promise { + if (!item.bookmarkId) { + return; + } + + const connection = await avaloniaDb.connect(); + try { + await connection.delete(fileBookmarksStore, item.bookmarkId); + } finally { + connection.close(); + } + } + + private generateBookmarkId(): string { + return Date.now().toString(36) + Math.random().toString(36).substring(2); + } +} + +export class StorageItems { + constructor(private readonly items: StorageItem[]) { } + + public static itemsArray(instance: StorageItems): StorageItem[] { + return instance.items; + } +} diff --git a/src/Web/Avalonia.Web/webapp/modules/storage/storageProvider.ts b/src/Web/Avalonia.Web/webapp/modules/storage/storageProvider.ts new file mode 100644 index 0000000000..0198f92528 --- /dev/null +++ b/src/Web/Avalonia.Web/webapp/modules/storage/storageProvider.ts @@ -0,0 +1,70 @@ +import { avaloniaDb, fileBookmarksStore } from "./indexedDb"; +import { StorageItem, StorageItems } from "./storageItem"; + +declare global { + type WellKnownDirectory = "desktop" | "documents" | "downloads" | "music" | "pictures" | "videos"; + type StartInDirectory = WellKnownDirectory | FileSystemHandle; + interface OpenFilePickerOptions { + startIn?: StartInDirectory; + } + interface SaveFilePickerOptions { + startIn?: StartInDirectory; + } +} + +export class StorageProvider { + public static async selectFolderDialog( + startIn: StorageItem | null): Promise { + // 'Picker' API doesn't accept "null" as a parameter, so it should be set to undefined. + const options: DirectoryPickerOptions = { + startIn: (startIn?.handle ?? undefined) + }; + + const handle = await window.showDirectoryPicker(options); + return new StorageItem(handle); + } + + public static async openFileDialog( + startIn: StorageItem | null, multiple: boolean, + types: FilePickerAcceptType[] | null, excludeAcceptAllOption: boolean): Promise { + const options: OpenFilePickerOptions = { + startIn: (startIn?.handle ?? undefined), + multiple, + excludeAcceptAllOption, + types: (types ?? undefined) + }; + + const handles = await window.showOpenFilePicker(options); + return new StorageItems(handles.map((handle: FileSystemHandle) => new StorageItem(handle))); + } + + public static async saveFileDialog( + startIn: StorageItem | null, suggestedName: string | null, + types: FilePickerAcceptType[] | null, excludeAcceptAllOption: boolean): Promise { + const options: SaveFilePickerOptions = { + startIn: (startIn?.handle ?? undefined), + suggestedName: (suggestedName ?? undefined), + excludeAcceptAllOption, + types: (types ?? undefined) + }; + + const handle = await window.showSaveFilePicker(options); + return new StorageItem(handle); + } + + public static async openBookmark(key: string): Promise { + const connection = await avaloniaDb.connect(); + try { + const handle = await connection.get(fileBookmarksStore, key); + return handle && new StorageItem(handle, key); + } finally { + connection.close(); + } + } + + public static createAcceptType(description: string, mimeTypes: string[]): FilePickerAcceptType { + const accept: Record = {}; + mimeTypes.forEach(a => { accept[a] = []; }); + return { description, accept }; + } +} diff --git a/src/Web/Avalonia.Web/webapp/package-lock.json b/src/Web/Avalonia.Web/webapp/package-lock.json index 5df9471195..947f7e12e7 100644 --- a/src/Web/Avalonia.Web/webapp/package-lock.json +++ b/src/Web/Avalonia.Web/webapp/package-lock.json @@ -1,458 +1,484 @@ { "name": "avalonia.web", - "lockfileVersion": 2, "requires": true, - "packages": { - "": { - "name": "avalonia.web", - "devDependencies": { - "@types/emscripten": "^1.39.6", - "@types/wicg-file-system-access": "^2020.9.5", - "esbuild": "^0.15.7", - "typescript": "^4.7.4" - } - }, - "node_modules/@esbuild/android-arm": { + "lockfileVersion": 1, + "dependencies": { + "@esbuild/android-arm": { "version": "0.15.9", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.9.tgz", "integrity": "sha512-VZPy/ETF3fBG5PiinIkA0W/tlsvlEgJccyN2DzWZEl0DlVKRbu91PvY2D6Lxgluj4w9QtYHjOWjAT44C+oQ+EQ==", - "cpu": [ - "arm" - ], "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" - } + "optional": true }, - "node_modules/@esbuild/linux-loong64": { + "@esbuild/linux-loong64": { "version": "0.15.9", "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.9.tgz", "integrity": "sha512-O+NfmkfRrb3uSsTa4jE3WApidSe3N5++fyOVGP1SmMZi4A3BZELkhUUvj5hwmMuNdlpzAZ8iAPz2vmcR7DCFQA==", - "cpu": [ - "loong64" - ], "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "optional": true + }, + "@eslint/eslintrc": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.2.tgz", + "integrity": "sha512-AXYd23w1S/bv3fTs3Lz0vjiYemS08jWkI3hYyS9I1ry+0f+Yjs1wm+sU0BS8qDOPrBIkp4qHYC16I8uVtpLajQ==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.4.0", + "globals": "^13.15.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + } + }, + "@humanwhocodes/config-array": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.5.tgz", + "integrity": "sha512-XVVDtp+dVvRxMoxSiSfasYaG02VEe1qH5cKgMQJWhol6HwzbcqoCMJi8dAGoYAO57jhUyhI6cWuRiTcRaDaYug==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/gitignore-to-minimatch": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz", + "integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==", + "dev": true + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" } }, - "node_modules/@types/emscripten": { + "@types/emscripten": { "version": "1.39.6", "resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.39.6.tgz", "integrity": "sha512-H90aoynNhhkQP6DRweEjJp5vfUVdIj7tdPLsu7pq89vODD/lcugKfZOsfgwpvM6XUewEp2N5dCg1Uf3Qe55Dcg==", "dev": true }, - "node_modules/@types/wicg-file-system-access": { + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "@types/wicg-file-system-access": { "version": "2020.9.5", "resolved": "https://registry.npmjs.org/@types/wicg-file-system-access/-/wicg-file-system-access-2020.9.5.tgz", "integrity": "sha512-UYK244awtmcUYQfs7FR8710MJcefL2WvkyHMjA8yJzxd1mo0Gfn88sRZ1Bls7hiUhA2w7ne1gpJ9T5g3G0wOyA==", "dev": true }, - "node_modules/esbuild": { - "version": "0.15.9", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.9.tgz", - "integrity": "sha512-OnYr1rkMVxtmMHIAKZLMcEUlJmqcbxBz9QoBU8G9v455na0fuzlT/GLu6l+SRghrk0Mm2fSSciMmzV43Q8e0Gg==", + "@typescript-eslint/eslint-plugin": { + "version": "5.38.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.38.1.tgz", + "integrity": "sha512-ky7EFzPhqz3XlhS7vPOoMDaQnQMn+9o5ICR9CPr/6bw8HrFkzhMSxuA3gRfiJVvs7geYrSeawGJjZoZQKCOglQ==", "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/android-arm": "0.15.9", - "@esbuild/linux-loong64": "0.15.9", - "esbuild-android-64": "0.15.9", - "esbuild-android-arm64": "0.15.9", - "esbuild-darwin-64": "0.15.9", - "esbuild-darwin-arm64": "0.15.9", - "esbuild-freebsd-64": "0.15.9", - "esbuild-freebsd-arm64": "0.15.9", - "esbuild-linux-32": "0.15.9", - "esbuild-linux-64": "0.15.9", - "esbuild-linux-arm": "0.15.9", - "esbuild-linux-arm64": "0.15.9", - "esbuild-linux-mips64le": "0.15.9", - "esbuild-linux-ppc64le": "0.15.9", - "esbuild-linux-riscv64": "0.15.9", - "esbuild-linux-s390x": "0.15.9", - "esbuild-netbsd-64": "0.15.9", - "esbuild-openbsd-64": "0.15.9", - "esbuild-sunos-64": "0.15.9", - "esbuild-windows-32": "0.15.9", - "esbuild-windows-64": "0.15.9", - "esbuild-windows-arm64": "0.15.9" + "requires": { + "@typescript-eslint/scope-manager": "5.38.1", + "@typescript-eslint/type-utils": "5.38.1", + "@typescript-eslint/utils": "5.38.1", + "debug": "^4.3.4", + "ignore": "^5.2.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" } }, - "node_modules/esbuild-android-64": { - "version": "0.15.9", - "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.9.tgz", - "integrity": "sha512-HQCX7FJn9T4kxZQkhPjNZC7tBWZqJvhlLHPU2SFzrQB/7nDXjmTIFpFTjt7Bd1uFpeXmuwf5h5fZm+x/hLnhbw==", - "cpu": [ - "x64" - ], + "@typescript-eslint/parser": { + "version": "5.38.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.38.1.tgz", + "integrity": "sha512-LDqxZBVFFQnQRz9rUZJhLmox+Ep5kdUmLatLQnCRR6523YV+XhRjfYzStQ4MheFA8kMAfUlclHSbu+RKdRwQKw==", "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" + "requires": { + "@typescript-eslint/scope-manager": "5.38.1", + "@typescript-eslint/types": "5.38.1", + "@typescript-eslint/typescript-estree": "5.38.1", + "debug": "^4.3.4" } }, - "node_modules/esbuild-android-arm64": { - "version": "0.15.9", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.9.tgz", - "integrity": "sha512-E6zbLfqbFVCNEKircSHnPiSTsm3fCRxeIMPfrkS33tFjIAoXtwegQfVZqMGR0FlsvVxp2NEDOUz+WW48COCjSg==", - "cpu": [ - "arm64" - ], + "@typescript-eslint/scope-manager": { + "version": "5.38.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.38.1.tgz", + "integrity": "sha512-BfRDq5RidVU3RbqApKmS7RFMtkyWMM50qWnDAkKgQiezRtLKsoyRKIvz1Ok5ilRWeD9IuHvaidaLxvGx/2eqTQ==", "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=12" + "requires": { + "@typescript-eslint/types": "5.38.1", + "@typescript-eslint/visitor-keys": "5.38.1" } }, - "node_modules/esbuild-darwin-64": { - "version": "0.15.9", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.9.tgz", - "integrity": "sha512-gI7dClcDN/HHVacZhTmGjl0/TWZcGuKJ0I7/xDGJwRQQn7aafZGtvagOFNmuOq+OBFPhlPv1T6JElOXb0unkSQ==", - "cpu": [ - "x64" - ], + "@typescript-eslint/type-utils": { + "version": "5.38.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.38.1.tgz", + "integrity": "sha512-UU3j43TM66gYtzo15ivK2ZFoDFKKP0k03MItzLdq0zV92CeGCXRfXlfQX5ILdd4/DSpHkSjIgLLLh1NtkOJOAw==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" + "requires": { + "@typescript-eslint/typescript-estree": "5.38.1", + "@typescript-eslint/utils": "5.38.1", + "debug": "^4.3.4", + "tsutils": "^3.21.0" } }, - "node_modules/esbuild-darwin-arm64": { - "version": "0.15.9", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.9.tgz", - "integrity": "sha512-VZIMlcRN29yg/sv7DsDwN+OeufCcoTNaTl3Vnav7dL/nvsApD7uvhVRbgyMzv0zU/PP0xRhhIpTyc7lxEzHGSw==", - "cpu": [ - "arm64" - ], + "@typescript-eslint/types": { + "version": "5.38.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.38.1.tgz", + "integrity": "sha512-QTW1iHq1Tffp9lNfbfPm4WJabbvpyaehQ0SrvVK2yfV79SytD9XDVxqiPvdrv2LK7DGSFo91TB2FgWanbJAZXg==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.38.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.38.1.tgz", + "integrity": "sha512-99b5e/Enoe8fKMLdSuwrfH/C0EIbpUWmeEKHmQlGZb8msY33qn1KlkFww0z26o5Omx7EVjzVDCWEfrfCDHfE7g==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=12" + "requires": { + "@typescript-eslint/types": "5.38.1", + "@typescript-eslint/visitor-keys": "5.38.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" } }, - "node_modules/esbuild-freebsd-64": { - "version": "0.15.9", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.9.tgz", - "integrity": "sha512-uM4z5bTvuAXqPxrI204txhlsPIolQPWRMLenvGuCPZTnnGlCMF2QLs0Plcm26gcskhxewYo9LkkmYSS5Czrb5A==", - "cpu": [ - "x64" - ], + "@typescript-eslint/utils": { + "version": "5.38.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.38.1.tgz", + "integrity": "sha512-oIuUiVxPBsndrN81oP8tXnFa/+EcZ03qLqPDfSZ5xIJVm7A9V0rlkQwwBOAGtrdN70ZKDlKv+l1BeT4eSFxwXA==", "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" + "requires": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.38.1", + "@typescript-eslint/types": "5.38.1", + "@typescript-eslint/typescript-estree": "5.38.1", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" } }, - "node_modules/esbuild-freebsd-arm64": { - "version": "0.15.9", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.9.tgz", - "integrity": "sha512-HHDjT3O5gWzicGdgJ5yokZVN9K9KG05SnERwl9nBYZaCjcCgj/sX8Ps1jvoFSfNCO04JSsHSOWo4qvxFuj8FoA==", - "cpu": [ - "arm64" - ], + "@typescript-eslint/visitor-keys": { + "version": "5.38.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.38.1.tgz", + "integrity": "sha512-bSHr1rRxXt54+j2n4k54p4fj8AHJ49VDWtjpImOpzQj4qjAiOpPni+V1Tyajh19Api1i844F757cur8wH3YvOA==", "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=12" + "requires": { + "@typescript-eslint/types": "5.38.1", + "eslint-visitor-keys": "^3.3.0" } }, - "node_modules/esbuild-linux-32": { - "version": "0.15.9", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.9.tgz", - "integrity": "sha512-AQIdE8FugGt1DkcekKi5ycI46QZpGJ/wqcMr7w6YUmOmp2ohQ8eO4sKUsOxNOvYL7hGEVwkndSyszR6HpVHLFg==", - "cpu": [ - "ia32" - ], + "acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" } }, - "node_modules/esbuild-linux-64": { - "version": "0.15.9", - "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.9.tgz", - "integrity": "sha512-4RXjae7g6Qs7StZyiYyXTZXBlfODhb1aBVAjd+ANuPmMhWthQilWo7rFHwJwL7DQu1Fjej2sODAVwLbcIVsAYQ==", - "cpu": [ - "x64" - ], + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "requires": { + "color-convert": "^2.0.1" } }, - "node_modules/esbuild-linux-arm": { - "version": "0.15.9", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.9.tgz", - "integrity": "sha512-3Zf2GVGUOI7XwChH3qrnTOSqfV1V4CAc/7zLVm4lO6JT6wbJrTgEYCCiNSzziSju+J9Jhf9YGWk/26quWPC6yQ==", - "cpu": [ - "arm" - ], + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "array-includes": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.5.tgz", + "integrity": "sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5", + "get-intrinsic": "^1.1.1", + "is-string": "^1.0.7" } }, - "node_modules/esbuild-linux-arm64": { - "version": "0.15.9", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.9.tgz", - "integrity": "sha512-a+bTtxJmYmk9d+s2W4/R1SYKDDAldOKmWjWP0BnrWtDbvUBNOm++du0ysPju4mZVoEFgS1yLNW+VXnG/4FNwdQ==", - "cpu": [ - "arm64" - ], + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "array.prototype.flat": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz", + "integrity": "sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" } }, - "node_modules/esbuild-linux-mips64le": { - "version": "0.15.9", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.9.tgz", - "integrity": "sha512-Zn9HSylDp89y+TRREMDoGrc3Z4Hs5u56ozZLQCiZAUx2+HdbbXbWdjmw3FdTJ/i7t5Cew6/Q+6kfO3KCcFGlyw==", - "cpu": [ - "mips64el" - ], + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/esbuild-linux-ppc64le": { - "version": "0.15.9", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.9.tgz", - "integrity": "sha512-OEiOxNAMH9ENFYqRsWUj3CWyN3V8P3ZXyfNAtX5rlCEC/ERXrCEFCJji/1F6POzsXAzxvUJrTSTCy7G6BhA6Fw==", - "cpu": [ - "ppc64" - ], + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "requires": { + "fill-range": "^7.0.1" } }, - "node_modules/esbuild-linux-riscv64": { - "version": "0.15.9", - "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.9.tgz", - "integrity": "sha512-ukm4KsC3QRausEFjzTsOZ/qqazw0YvJsKmfoZZm9QW27OHjk2XKSQGGvx8gIEswft/Sadp03/VZvAaqv5AIwNA==", - "cpu": [ - "riscv64" - ], + "builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "requires": { + "semver": "^7.0.0" } }, - "node_modules/esbuild-linux-s390x": { - "version": "0.15.9", - "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.9.tgz", - "integrity": "sha512-uDOQEH55wQ6ahcIKzQr3VyjGc6Po/xblLGLoUk3fVL1qjlZAibtQr6XRfy5wPJLu/M2o0vQKLq4lyJ2r1tWKcw==", - "cpu": [ - "s390x" - ], + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" } }, - "node_modules/esbuild-netbsd-64": { - "version": "0.15.9", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.9.tgz", - "integrity": "sha512-yWgxaYTQz+TqX80wXRq6xAtb7GSBAp6gqLKfOdANg9qEmAI1Bxn04IrQr0Mzm4AhxvGKoHzjHjMgXbCCSSDxcw==", - "cpu": [ - "x64" - ], + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "node_modules/esbuild-openbsd-64": { - "version": "0.15.9", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.9.tgz", - "integrity": "sha512-JmS18acQl4iSAjrEha1MfEmUMN4FcnnrtTaJ7Qg0tDCOcgpPPQRLGsZqhes0vmx8VA6IqRyScqXvaL7+Q0Uf3A==", - "cpu": [ - "x64" - ], + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" + "requires": { + "color-name": "~1.1.4" } }, - "node_modules/esbuild-sunos-64": { - "version": "0.15.9", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.9.tgz", - "integrity": "sha512-UKynGSWpzkPmXW3D2UMOD9BZPIuRaSqphxSCwScfEE05Be3KAmvjsBhht1fLzKpiFVJb0BYMd4jEbWMyJ/z1hQ==", - "cpu": [ - "x64" - ], + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" } }, - "node_modules/esbuild-windows-32": { - "version": "0.15.9", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.9.tgz", - "integrity": "sha512-aqXvu4/W9XyTVqO/hw3rNxKE1TcZiEYHPsXM9LwYmKSX9/hjvfIJzXwQBlPcJ/QOxedfoMVH0YnhhQ9Ffb0RGA==", - "cpu": [ - "ia32" - ], + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" + "requires": { + "ms": "2.1.2" } }, - "node_modules/esbuild-windows-64": { - "version": "0.15.9", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.9.tgz", - "integrity": "sha512-zm7h91WUmlS4idMtjvCrEeNhlH7+TNOmqw5dJPJZrgFaxoFyqYG6CKDpdFCQXdyKpD5yvzaQBOMVTCBVKGZDEg==", - "cpu": [ - "x64" - ], + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" + "requires": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" } }, - "node_modules/esbuild-windows-arm64": { - "version": "0.15.9", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.9.tgz", - "integrity": "sha512-yQEVIv27oauAtvtuhJVfSNMztJJX47ismRS6Sv2QMVV9RM+6xjbMWuuwM2nxr5A2/gj/mu2z9YlQxiwoFRCfZA==", - "cpu": [ - "arm64" - ], + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" + "requires": { + "path-type": "^4.0.0" } }, - "node_modules/typescript": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz", - "integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==", + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" + "requires": { + "esutils": "^2.0.2" } - } - }, - "dependencies": { - "@esbuild/android-arm": { - "version": "0.15.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.9.tgz", - "integrity": "sha512-VZPy/ETF3fBG5PiinIkA0W/tlsvlEgJccyN2DzWZEl0DlVKRbu91PvY2D6Lxgluj4w9QtYHjOWjAT44C+oQ+EQ==", + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, - "optional": true + "requires": { + "is-arrayish": "^0.2.1" + } }, - "@esbuild/linux-loong64": { - "version": "0.15.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.9.tgz", - "integrity": "sha512-O+NfmkfRrb3uSsTa4jE3WApidSe3N5++fyOVGP1SmMZi4A3BZELkhUUvj5hwmMuNdlpzAZ8iAPz2vmcR7DCFQA==", + "es-abstract": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.3.tgz", + "integrity": "sha512-AyrnaKVpMzljIdwjzrj+LxGmj8ik2LckwXacHqrJJ/jxz6dDDBcZ7I7nlHM0FvEW8MfbWJwOd+yT2XzYW49Frw==", "dev": true, - "optional": true + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.3", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.3", + "is-callable": "^1.2.6", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.2", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" + } }, - "@types/emscripten": { - "version": "1.39.6", - "resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.39.6.tgz", - "integrity": "sha512-H90aoynNhhkQP6DRweEjJp5vfUVdIj7tdPLsu7pq89vODD/lcugKfZOsfgwpvM6XUewEp2N5dCg1Uf3Qe55Dcg==", - "dev": true + "es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "requires": { + "has": "^1.0.3" + } }, - "@types/wicg-file-system-access": { - "version": "2020.9.5", - "resolved": "https://registry.npmjs.org/@types/wicg-file-system-access/-/wicg-file-system-access-2020.9.5.tgz", - "integrity": "sha512-UYK244awtmcUYQfs7FR8710MJcefL2WvkyHMjA8yJzxd1mo0Gfn88sRZ1Bls7hiUhA2w7ne1gpJ9T5g3G0wOyA==", - "dev": true + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } }, "esbuild": { "version": "0.15.9", @@ -624,10 +650,1584 @@ "dev": true, "optional": true }, - "typescript": { - "version": "4.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz", - "integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==", + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "eslint": { + "version": "8.24.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.24.0.tgz", + "integrity": "sha512-dWFaPhGhTAiPcCgm3f6LI2MBWbogMnTJzFBbhXVRQDJPkr9pGZvVjlVfXd+vyDcWPA2Ic9L2AXPIQM0+vk/cSQ==", + "dev": true, + "requires": { + "@eslint/eslintrc": "^1.3.2", + "@humanwhocodes/config-array": "^0.10.5", + "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", + "@humanwhocodes/module-importer": "^1.0.1", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.4.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.1", + "globals": "^13.15.0", + "globby": "^11.1.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-sdsl": "^4.1.4", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "dependencies": { + "eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + } + } + }, + "eslint-config-standard": { + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard/-/eslint-config-standard-17.0.0.tgz", + "integrity": "sha512-/2ks1GKyqSOkH7JFvXJicu0iMpoojkwB+f5Du/1SC0PtBL+s8v30k9njRZ21pm2drKYm2342jFnGWzttxPmZVg==", + "dev": true + }, + "eslint-config-standard-with-typescript": { + "version": "23.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-standard-with-typescript/-/eslint-config-standard-with-typescript-23.0.0.tgz", + "integrity": "sha512-iaaWifImn37Z1OXbNW1es7KI+S7D408F9ys0bpaQf2temeBWlvb0Nc5qHkOgYaRb5QxTZT32GGeN1gtswASOXA==", + "dev": true, + "requires": { + "@typescript-eslint/parser": "^5.0.0", + "eslint-config-standard": "17.0.0" + } + }, + "eslint-import-resolver-node": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz", + "integrity": "sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==", + "dev": true, + "requires": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-module-utils": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", + "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", + "dev": true, + "requires": { + "debug": "^3.2.7" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "eslint-plugin-es": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-4.1.0.tgz", + "integrity": "sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==", + "dev": true, + "requires": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + }, + "dependencies": { + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "eslint-plugin-import": { + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz", + "integrity": "sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==", + "dev": true, + "requires": { + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", + "debug": "^2.6.9", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-module-utils": "^2.7.3", + "has": "^1.0.3", + "is-core-module": "^2.8.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.values": "^1.1.5", + "resolve": "^1.22.0", + "tsconfig-paths": "^3.14.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "eslint-plugin-n": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.3.0.tgz", + "integrity": "sha512-IyzPnEWHypCWasDpxeJnim60jhlumbmq0pubL6IOcnk8u2y53s5QfT8JnXy7skjHJ44yWHRb11PLtDHuu1kg/Q==", + "dev": true, + "requires": { + "builtins": "^5.0.1", + "eslint-plugin-es": "^4.1.0", + "eslint-utils": "^3.0.0", + "ignore": "^5.1.1", + "is-core-module": "^2.10.0", + "minimatch": "^3.1.2", + "resolve": "^1.22.1", + "semver": "^7.3.7" + } + }, + "eslint-plugin-promise": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.0.1.tgz", + "integrity": "sha512-uM4Tgo5u3UWQiroOyDEsYcVMOo7re3zmno0IZmB5auxoaQNIceAbXEkSt8RNrKtaYehARHG06pYK6K1JhtP0Zw==", + "dev": true + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + }, + "espree": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", + "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", + "dev": true, + "requires": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + } + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + } + }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true + }, + "get-intrinsic": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.1" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "internal-slot": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz", + "integrity": "sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "requires": { + "has-bigints": "^1.0.1" + } + }, + "is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true + }, + "is-core-module": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", + "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, + "is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "requires": { + "has-symbols": "^1.0.2" + } + }, + "is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2" + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "js-sdsl": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.4.tgz", + "integrity": "sha512-Y2/yD55y5jteOAmY50JbUZYwk3CP3wnLPEZnlR1w9oKhITrBEtAxwuWKebFf8hMrPMgbYwFoWK/lH2sBkErELw==", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + } + }, + "object.values": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.5.tgz", + "integrity": "sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "dependencies": { + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + } + } + }, + "regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + } + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + } + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "shell-quote": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz", + "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", + "dev": true + }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", + "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", + "dev": true + }, + "string.prototype.padend": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.3.tgz", + "integrity": "sha512-jNIIeokznm8SD/TZISQsZKYu7RJyheFNt84DUPrh482GC8RVp2MKqm2O5oBRdGxbDQoXrhhWtPIWQOiy20svUg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + } + }, + "string.prototype.trimend": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + } + }, + "string.prototype.trimstart": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + }, + "typescript": { + "version": "4.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.3.tgz", + "integrity": "sha512-goMHfm00nWPa8UvR/CPSvykqf6dVV8x/dp0c5mFTMTIu0u0FlGWRioyy7Nn0PGAdHxpJZnuO/ut+PpQ8UiHAig==", + "dev": true + }, + "unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "requires": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true } } diff --git a/src/Web/Avalonia.Web/webapp/package.json b/src/Web/Avalonia.Web/webapp/package.json index ca029ce534..8845dec604 100644 --- a/src/Web/Avalonia.Web/webapp/package.json +++ b/src/Web/Avalonia.Web/webapp/package.json @@ -1,13 +1,22 @@ { "name": "avalonia.web", "scripts": { - "prebuild": "node ./node_modules/typescript/bin/tsc -noEmit", + "typecheck": "npx tsc -noEmit", + "eslint": "npx eslint . --fix", + "prebuild": "npm-run-all typecheck eslint", "build": "node build.js" }, "devDependencies": { "@types/emscripten": "^1.39.6", "@types/wicg-file-system-access": "^2020.9.5", - "typescript": "^4.7.4", - "esbuild": "^0.15.7" + "@typescript-eslint/eslint-plugin": "^5.38.1", + "esbuild": "^0.15.7", + "eslint": "^8.24.0", + "eslint-config-standard-with-typescript": "^23.0.0", + "eslint-plugin-import": "^2.26.0", + "eslint-plugin-n": "^15.3.0", + "eslint-plugin-promise": "^6.0.1", + "npm-run-all": "^4.1.5", + "typescript": "^4.8.3" } } diff --git a/src/Web/Avalonia.Web/webapp/types/dotnet.d.ts b/src/Web/Avalonia.Web/webapp/types/dotnet.d.ts index bc2068cb59..0067ee3e0e 100644 --- a/src/Web/Avalonia.Web/webapp/types/dotnet.d.ts +++ b/src/Web/Avalonia.Web/webapp/types/dotnet.d.ts @@ -246,4 +246,25 @@ declare global { declare const dotnet: ModuleAPI["dotnet"]; declare const exit: ModuleAPI["exit"]; -export { CreateDotnetRuntimeType, DotnetModuleConfig, EmscriptenModule, ModuleAPI, MonoConfig, RuntimeAPI, createDotnetRuntime as default, dotnet, exit }; \ No newline at end of file +export { CreateDotnetRuntimeType, DotnetModuleConfig, EmscriptenModule, ModuleAPI, MonoConfig, RuntimeAPI, createDotnetRuntime as default, dotnet, exit }; + +export interface IMemoryView { + /** + * copies elements from provided source to the wasm memory. + * target has to have the elements of the same type as the underlying C# array. + * same as TypedArray.set() + */ + set(source: TypedArray, targetOffset?: number): void; + /** + * copies elements from wasm memory to provided target. + * target has to have the elements of the same type as the underlying C# array. + */ + copyTo(target: TypedArray, sourceOffset?: number): void; + /** + * same as TypedArray.slice() + */ + slice(start?: number, end?: number): TypedArray; + + get length(): number; + get byteLength(): number; +} diff --git a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj index 55d02b5d31..1d265369fd 100644 --- a/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj +++ b/src/Windows/Avalonia.Win32/Avalonia.Win32.csproj @@ -16,6 +16,9 @@
+ + +