diff --git a/.gitignore b/.gitignore index 61a3b53de1..bbf358b8f4 100644 --- a/.gitignore +++ b/.gitignore @@ -210,9 +210,9 @@ coc-settings.json .ccls-cache .ccls *.map -src/Web/Avalonia.Web.Blazor/wwwroot/*.js -src/Web/Avalonia.Web.Blazor/Interop/Typescript/*.js +src/Browser/Avalonia.Browser.Blazor/wwwroot/*.js +src/Browser/Avalonia.Browser.Blazor/Interop/Typescript/*.js node_modules -src/Web/Avalonia.Web.Blazor/webapp/package-lock.json -src/Web/Avalonia.Web.Blazor/wwwroot -src/Web/Avalonia.Web/wwwroot +src/Browser/Avalonia.Browser.Blazor/webapp/package-lock.json +src/Browser/Avalonia.Browser.Blazor/wwwroot +src/Browser/Avalonia.Browser/wwwroot diff --git a/.ncrunch/Avalonia.Web.Blazor.v3.ncrunchproject b/.ncrunch/Avalonia.Benchmarks.v3.ncrunchproject similarity index 100% rename from .ncrunch/Avalonia.Web.Blazor.v3.ncrunchproject rename to .ncrunch/Avalonia.Benchmarks.v3.ncrunchproject diff --git a/.ncrunch/Avalonia.Web.v3.ncrunchproject b/.ncrunch/Avalonia.Browser.Blazor.v3.ncrunchproject similarity index 100% rename from .ncrunch/Avalonia.Web.v3.ncrunchproject rename to .ncrunch/Avalonia.Browser.Blazor.v3.ncrunchproject diff --git a/.ncrunch/ControlCatalog.Blazor.Web.v3.ncrunchproject b/.ncrunch/Avalonia.Browser.v3.ncrunchproject similarity index 100% rename from .ncrunch/ControlCatalog.Blazor.Web.v3.ncrunchproject rename to .ncrunch/Avalonia.Browser.v3.ncrunchproject diff --git a/.ncrunch/ControlCatalog.Web.v3.ncrunchproject b/.ncrunch/Avalonia.Designer.HostApp.v3.ncrunchproject similarity index 100% rename from .ncrunch/ControlCatalog.Web.v3.ncrunchproject rename to .ncrunch/Avalonia.Designer.HostApp.v3.ncrunchproject diff --git a/.ncrunch/Avalonia.Themes.Fluent.net6.0.v3.ncrunchproject b/.ncrunch/Avalonia.Themes.Fluent.net6.0.v3.ncrunchproject new file mode 100644 index 0000000000..02eb0d211e --- /dev/null +++ b/.ncrunch/Avalonia.Themes.Fluent.net6.0.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + False + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Themes.Fluent.netstandard2.0.v3.ncrunchproject b/.ncrunch/Avalonia.Themes.Fluent.netstandard2.0.v3.ncrunchproject new file mode 100644 index 0000000000..02eb0d211e --- /dev/null +++ b/.ncrunch/Avalonia.Themes.Fluent.netstandard2.0.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + False + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Themes.Simple.net6.0.v3.ncrunchproject b/.ncrunch/Avalonia.Themes.Simple.net6.0.v3.ncrunchproject new file mode 100644 index 0000000000..02eb0d211e --- /dev/null +++ b/.ncrunch/Avalonia.Themes.Simple.net6.0.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + False + + \ No newline at end of file diff --git a/.ncrunch/Avalonia.Themes.Simple.netstandard2.0.v3.ncrunchproject b/.ncrunch/Avalonia.Themes.Simple.netstandard2.0.v3.ncrunchproject new file mode 100644 index 0000000000..02eb0d211e --- /dev/null +++ b/.ncrunch/Avalonia.Themes.Simple.netstandard2.0.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + False + + \ No newline at end of file diff --git a/.ncrunch/ControlCatalog.Browser.Blazor.v3.ncrunchproject b/.ncrunch/ControlCatalog.Browser.Blazor.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/ControlCatalog.Browser.Blazor.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/ControlCatalog.Browser.v3.ncrunchproject b/.ncrunch/ControlCatalog.Browser.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/ControlCatalog.Browser.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/.ncrunch/MobileSandbox.v3.ncrunchproject b/.ncrunch/MobileSandbox.v3.ncrunchproject new file mode 100644 index 0000000000..319cd523ce --- /dev/null +++ b/.ncrunch/MobileSandbox.v3.ncrunchproject @@ -0,0 +1,5 @@ + + + True + + \ No newline at end of file diff --git a/Avalonia.sln b/Avalonia.sln index e2f04ddc35..34b5596119 100644 --- a/Avalonia.sln +++ b/Avalonia.sln @@ -198,9 +198,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationTestApp", "sampl EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.IntegrationTests.Appium", "tests\Avalonia.IntegrationTests.Appium\Avalonia.IntegrationTests.Appium.csproj", "{F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Web", "Web", "{86A3F706-DC3C-43C6-BE1B-B98F5BAAA268}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Web.Blazor", "src\Web\Avalonia.Web.Blazor\Avalonia.Web.Blazor.csproj", "{25831348-EB2A-483E-9576-E8F6528674A5}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Browser", "Browser", "{86A3F706-DC3C-43C6-BE1B-B98F5BAAA268}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WindowsInteropTest", "samples\interop\WindowsInteropTest\WindowsInteropTest.csproj", "{26A98DA1-D89D-4A95-8152-349F404DA2E2}" EndProject @@ -216,8 +214,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DesignerSupport.Te EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DevGenerators", "src\tools\DevGenerators\DevGenerators.csproj", "{1BBFAD42-B99E-47E0-B00A-A4BC6B6BB4BB}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Web", "src\Web\Avalonia.Web\Avalonia.Web.csproj", "{76D39FF6-6B4F-46C4-93CD-E6FC4665739E}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MobileSandbox", "samples\MobileSandbox\MobileSandbox.csproj", "{3B8519C1-2F51-4F12-A348-120AB91D4532}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MobileSandbox.Android", "samples\MobileSandbox.Android\MobileSandbox.Android.csproj", "{C90FE60B-B01E-4F35-91D6-379D6966030F}" @@ -226,9 +222,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MobileSandbox.iOS", "sample EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MobileSandbox.Desktop", "samples\MobileSandbox.Desktop\MobileSandbox.Desktop.csproj", "{62D392C9-81CF-487F-92E8-598B2AF3FDCE}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Blazor.Web", "samples\ControlCatalog.Blazor.Web\ControlCatalog.Blazor.Web.csproj", "{6A710364-AE6D-40BD-968B-024311527AC2}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Browser", "src\Browser\Avalonia.Browser\Avalonia.Browser.csproj", "{4A39637C-9338-4925-A4DB-D072E292EC78}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Browser.Blazor", "src\Browser\Avalonia.Browser.Blazor\Avalonia.Browser.Blazor.csproj", "{47F8530C-F19B-4B1A-B4D6-EB231522AE5D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.Browser", "samples\ControlCatalog.Browser\ControlCatalog.Browser.csproj", "{15B93A4C-1B46-43F6-B534-7B25B6E99932}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Web", "samples\ControlCatalog.Web\ControlCatalog.Web.csproj", "{8B3E8405-DE18-4048-A459-9CA4AC3319A2}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.Browser.Blazor", "samples\ControlCatalog.Browser.Blazor\ControlCatalog.Browser.Blazor.csproj", "{90B08091-9BBD-4362-B712-E9F2CC62B218}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -480,10 +480,6 @@ Global {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Debug|Any CPU.Build.0 = Debug|Any CPU {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|Any CPU.ActiveCfg = Release|Any CPU {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A}.Release|Any CPU.Build.0 = Release|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {25831348-EB2A-483E-9576-E8F6528674A5}.Release|Any CPU.Build.0 = Release|Any CPU {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Debug|Any CPU.Build.0 = Debug|Any CPU {26A98DA1-D89D-4A95-8152-349F404DA2E2}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -512,10 +508,6 @@ Global {1BBFAD42-B99E-47E0-B00A-A4BC6B6BB4BB}.Debug|Any CPU.Build.0 = Debug|Any CPU {1BBFAD42-B99E-47E0-B00A-A4BC6B6BB4BB}.Release|Any CPU.ActiveCfg = Release|Any CPU {1BBFAD42-B99E-47E0-B00A-A4BC6B6BB4BB}.Release|Any CPU.Build.0 = Release|Any CPU - {76D39FF6-6B4F-46C4-93CD-E6FC4665739E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {76D39FF6-6B4F-46C4-93CD-E6FC4665739E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {76D39FF6-6B4F-46C4-93CD-E6FC4665739E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {76D39FF6-6B4F-46C4-93CD-E6FC4665739E}.Release|Any CPU.Build.0 = Release|Any CPU {3B8519C1-2F51-4F12-A348-120AB91D4532}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3B8519C1-2F51-4F12-A348-120AB91D4532}.Debug|Any CPU.Build.0 = Debug|Any CPU {3B8519C1-2F51-4F12-A348-120AB91D4532}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -533,14 +525,22 @@ Global {62D392C9-81CF-487F-92E8-598B2AF3FDCE}.Debug|Any CPU.Build.0 = Debug|Any CPU {62D392C9-81CF-487F-92E8-598B2AF3FDCE}.Release|Any CPU.ActiveCfg = Release|Any CPU {62D392C9-81CF-487F-92E8-598B2AF3FDCE}.Release|Any CPU.Build.0 = Release|Any CPU - {6A710364-AE6D-40BD-968B-024311527AC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6A710364-AE6D-40BD-968B-024311527AC2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6A710364-AE6D-40BD-968B-024311527AC2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6A710364-AE6D-40BD-968B-024311527AC2}.Release|Any CPU.Build.0 = Release|Any CPU - {8B3E8405-DE18-4048-A459-9CA4AC3319A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8B3E8405-DE18-4048-A459-9CA4AC3319A2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8B3E8405-DE18-4048-A459-9CA4AC3319A2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8B3E8405-DE18-4048-A459-9CA4AC3319A2}.Release|Any CPU.Build.0 = Release|Any CPU + {4A39637C-9338-4925-A4DB-D072E292EC78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4A39637C-9338-4925-A4DB-D072E292EC78}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4A39637C-9338-4925-A4DB-D072E292EC78}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4A39637C-9338-4925-A4DB-D072E292EC78}.Release|Any CPU.Build.0 = Release|Any CPU + {47F8530C-F19B-4B1A-B4D6-EB231522AE5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {47F8530C-F19B-4B1A-B4D6-EB231522AE5D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {47F8530C-F19B-4B1A-B4D6-EB231522AE5D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {47F8530C-F19B-4B1A-B4D6-EB231522AE5D}.Release|Any CPU.Build.0 = Release|Any CPU + {15B93A4C-1B46-43F6-B534-7B25B6E99932}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {15B93A4C-1B46-43F6-B534-7B25B6E99932}.Debug|Any CPU.Build.0 = Debug|Any CPU + {15B93A4C-1B46-43F6-B534-7B25B6E99932}.Release|Any CPU.ActiveCfg = Release|Any CPU + {15B93A4C-1B46-43F6-B534-7B25B6E99932}.Release|Any CPU.Build.0 = Release|Any CPU + {90B08091-9BBD-4362-B712-E9F2CC62B218}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {90B08091-9BBD-4362-B712-E9F2CC62B218}.Debug|Any CPU.Build.0 = Debug|Any CPU + {90B08091-9BBD-4362-B712-E9F2CC62B218}.Release|Any CPU.ActiveCfg = Release|Any CPU + {90B08091-9BBD-4362-B712-E9F2CC62B218}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -591,20 +591,20 @@ Global {BC594FD5-4AF2-409E-A1E6-04123F54D7C5} = {9B9E3891-2366-4253-A952-D08BCEB71098} {676D6BFD-029D-4E43-BFC7-3892265CE251} = {9B9E3891-2366-4253-A952-D08BCEB71098} {F2CE566B-E7F6-447A-AB1A-3F574A6FE43A} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} - {25831348-EB2A-483E-9576-E8F6528674A5} = {86A3F706-DC3C-43C6-BE1B-B98F5BAAA268} {26A98DA1-D89D-4A95-8152-349F404DA2E2} = {A0CC0258-D18C-4AB3-854F-7101680FC3F9} {A0D0A6A4-5C72-4ADA-9B27-621C7D94F270} = {9B9E3891-2366-4253-A952-D08BCEB71098} {70B9F5CC-E2F9-4314-9514-EDE762ACCC4B} = {9B9E3891-2366-4253-A952-D08BCEB71098} {2B390431-288C-435C-BB6B-A374033BD8D1} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637} {EABE2161-989B-42BF-BD8D-1E34B20C21F1} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B} {1BBFAD42-B99E-47E0-B00A-A4BC6B6BB4BB} = {4ED8B739-6F4E-4CD4-B993-545E6B5CE637} - {76D39FF6-6B4F-46C4-93CD-E6FC4665739E} = {86A3F706-DC3C-43C6-BE1B-B98F5BAAA268} {3B8519C1-2F51-4F12-A348-120AB91D4532} = {9B9E3891-2366-4253-A952-D08BCEB71098} {C90FE60B-B01E-4F35-91D6-379D6966030F} = {9B9E3891-2366-4253-A952-D08BCEB71098} {FED9A71D-00D7-4F40-A9E4-1229EEA28EEB} = {9B9E3891-2366-4253-A952-D08BCEB71098} {62D392C9-81CF-487F-92E8-598B2AF3FDCE} = {9B9E3891-2366-4253-A952-D08BCEB71098} - {6A710364-AE6D-40BD-968B-024311527AC2} = {9B9E3891-2366-4253-A952-D08BCEB71098} - {8B3E8405-DE18-4048-A459-9CA4AC3319A2} = {9B9E3891-2366-4253-A952-D08BCEB71098} + {4A39637C-9338-4925-A4DB-D072E292EC78} = {86A3F706-DC3C-43C6-BE1B-B98F5BAAA268} + {47F8530C-F19B-4B1A-B4D6-EB231522AE5D} = {86A3F706-DC3C-43C6-BE1B-B98F5BAAA268} + {15B93A4C-1B46-43F6-B534-7B25B6E99932} = {9B9E3891-2366-4253-A952-D08BCEB71098} + {90B08091-9BBD-4362-B712-E9F2CC62B218} = {9B9E3891-2366-4253-A952-D08BCEB71098} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A} diff --git a/packages/Avalonia/AvaloniaBuildTasks.targets b/packages/Avalonia/AvaloniaBuildTasks.targets index 01bf303a20..4f9c4d7720 100644 --- a/packages/Avalonia/AvaloniaBuildTasks.targets +++ b/packages/Avalonia/AvaloniaBuildTasks.targets @@ -99,6 +99,7 @@ AssemblyFile="@(IntermediateAssembly)" ReferencesFilePath="$(AvaloniaXamlReferencesTemporaryFilePath)" OriginalCopyPath="$(AvaloniaXamlOriginalCopyFilePath)" + RefAssemblyFile="@(IntermediateRefAssembly)" ProjectDirectory="$(MSBuildProjectDirectory)" VerifyIl="$(AvaloniaXamlIlVerifyIl)" ReportImportance="$(AvaloniaXamlReportImportance)" diff --git a/samples/ControlCatalog.Blazor.Web/App.razor b/samples/ControlCatalog.Browser.Blazor/App.razor similarity index 100% rename from samples/ControlCatalog.Blazor.Web/App.razor rename to samples/ControlCatalog.Browser.Blazor/App.razor diff --git a/samples/ControlCatalog.Blazor.Web/App.razor.cs b/samples/ControlCatalog.Browser.Blazor/App.razor.cs similarity index 83% rename from samples/ControlCatalog.Blazor.Web/App.razor.cs rename to samples/ControlCatalog.Browser.Blazor/App.razor.cs index 8cc0095f20..f38db2b055 100644 --- a/samples/ControlCatalog.Blazor.Web/App.razor.cs +++ b/samples/ControlCatalog.Browser.Blazor/App.razor.cs @@ -1,7 +1,7 @@ using Avalonia; -using Avalonia.Web.Blazor; +using Avalonia.Browser.Blazor; -namespace ControlCatalog.Blazor.Web; +namespace ControlCatalog.Browser.Blazor; public partial class App { diff --git a/samples/ControlCatalog.Blazor.Web/ControlCatalog.Blazor.Web.csproj b/samples/ControlCatalog.Browser.Blazor/ControlCatalog.Browser.Blazor.csproj similarity index 79% rename from samples/ControlCatalog.Blazor.Web/ControlCatalog.Blazor.Web.csproj rename to samples/ControlCatalog.Browser.Blazor/ControlCatalog.Browser.Blazor.csproj index 03fb31f0d3..d0fb614840 100644 --- a/samples/ControlCatalog.Blazor.Web/ControlCatalog.Blazor.Web.csproj +++ b/samples/ControlCatalog.Browser.Blazor/ControlCatalog.Browser.Blazor.csproj @@ -15,15 +15,15 @@ - + - - + + diff --git a/samples/ControlCatalog.Blazor.Web/Pages/Index.razor b/samples/ControlCatalog.Browser.Blazor/Pages/Index.razor similarity index 50% rename from samples/ControlCatalog.Blazor.Web/Pages/Index.razor rename to samples/ControlCatalog.Browser.Blazor/Pages/Index.razor index 93ca07f9f1..7480e4c5e9 100644 --- a/samples/ControlCatalog.Blazor.Web/Pages/Index.razor +++ b/samples/ControlCatalog.Browser.Blazor/Pages/Index.razor @@ -1,5 +1,5 @@ @page "/" -@using Avalonia.Web.Blazor +@using Avalonia.Browser.Blazor diff --git a/samples/ControlCatalog.Blazor.Web/Program.cs b/samples/ControlCatalog.Browser.Blazor/Program.cs similarity index 94% rename from samples/ControlCatalog.Blazor.Web/Program.cs rename to samples/ControlCatalog.Browser.Blazor/Program.cs index d71b125fa1..eb99ca518e 100644 --- a/samples/ControlCatalog.Blazor.Web/Program.cs +++ b/samples/ControlCatalog.Browser.Blazor/Program.cs @@ -3,7 +3,7 @@ using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.Extensions.DependencyInjection; -using ControlCatalog.Blazor.Web; +using ControlCatalog.Browser.Blazor; public class Program { diff --git a/samples/ControlCatalog.Blazor.Web/Properties/launchSettings.json b/samples/ControlCatalog.Browser.Blazor/Properties/launchSettings.json similarity index 100% rename from samples/ControlCatalog.Blazor.Web/Properties/launchSettings.json rename to samples/ControlCatalog.Browser.Blazor/Properties/launchSettings.json diff --git a/samples/ControlCatalog.Blazor.Web/Shared/MainLayout.razor b/samples/ControlCatalog.Browser.Blazor/Shared/MainLayout.razor similarity index 100% rename from samples/ControlCatalog.Blazor.Web/Shared/MainLayout.razor rename to samples/ControlCatalog.Browser.Blazor/Shared/MainLayout.razor diff --git a/samples/ControlCatalog.Blazor.Web/_Imports.razor b/samples/ControlCatalog.Browser.Blazor/_Imports.razor similarity index 88% rename from samples/ControlCatalog.Blazor.Web/_Imports.razor rename to samples/ControlCatalog.Browser.Blazor/_Imports.razor index 0e6d11b419..dc4f778352 100644 --- a/samples/ControlCatalog.Blazor.Web/_Imports.razor +++ b/samples/ControlCatalog.Browser.Blazor/_Imports.razor @@ -6,5 +6,5 @@ @using Microsoft.AspNetCore.Components.Web.Virtualization @using Microsoft.AspNetCore.Components.WebAssembly.Http @using Microsoft.JSInterop -@using ControlCatalog.Blazor.Web.Shared +@using ControlCatalog.Browser.Blazor.Shared @using SkiaSharp diff --git a/samples/ControlCatalog.Blazor.Web/wwwroot/css/app.css b/samples/ControlCatalog.Browser.Blazor/wwwroot/css/app.css similarity index 100% rename from samples/ControlCatalog.Blazor.Web/wwwroot/css/app.css rename to samples/ControlCatalog.Browser.Blazor/wwwroot/css/app.css diff --git a/samples/ControlCatalog.Blazor.Web/wwwroot/favicon.ico b/samples/ControlCatalog.Browser.Blazor/wwwroot/favicon.ico similarity index 100% rename from samples/ControlCatalog.Blazor.Web/wwwroot/favicon.ico rename to samples/ControlCatalog.Browser.Blazor/wwwroot/favicon.ico diff --git a/samples/ControlCatalog.Blazor.Web/wwwroot/index.html b/samples/ControlCatalog.Browser.Blazor/wwwroot/index.html similarity index 100% rename from samples/ControlCatalog.Blazor.Web/wwwroot/index.html rename to samples/ControlCatalog.Browser.Blazor/wwwroot/index.html diff --git a/samples/ControlCatalog.Web/ControlCatalog.Web.csproj b/samples/ControlCatalog.Browser/ControlCatalog.Browser.csproj similarity index 85% rename from samples/ControlCatalog.Web/ControlCatalog.Web.csproj rename to samples/ControlCatalog.Browser/ControlCatalog.Browser.csproj index 06a5466619..c4278459f3 100644 --- a/samples/ControlCatalog.Web/ControlCatalog.Web.csproj +++ b/samples/ControlCatalog.Browser/ControlCatalog.Browser.csproj @@ -26,7 +26,7 @@ - + @@ -39,6 +39,6 @@ - - + + diff --git a/samples/ControlCatalog.Web/EmbedSample.Browser.cs b/samples/ControlCatalog.Browser/EmbedSample.Browser.cs similarity index 95% rename from samples/ControlCatalog.Web/EmbedSample.Browser.cs rename to samples/ControlCatalog.Browser/EmbedSample.Browser.cs index 5cfbb608cc..c367230ddf 100644 --- a/samples/ControlCatalog.Web/EmbedSample.Browser.cs +++ b/samples/ControlCatalog.Browser/EmbedSample.Browser.cs @@ -1,11 +1,11 @@ using System; using System.Runtime.InteropServices.JavaScript; using Avalonia.Platform; -using Avalonia.Web; +using Avalonia.Browser; using ControlCatalog.Pages; -namespace ControlCatalog.Web; +namespace ControlCatalog.Browser; public class EmbedSampleWeb : INativeDemoControl { diff --git a/samples/ControlCatalog.Web/Logo.svg b/samples/ControlCatalog.Browser/Logo.svg similarity index 100% rename from samples/ControlCatalog.Web/Logo.svg rename to samples/ControlCatalog.Browser/Logo.svg diff --git a/samples/ControlCatalog.Web/Program.cs b/samples/ControlCatalog.Browser/Program.cs similarity index 90% rename from samples/ControlCatalog.Web/Program.cs rename to samples/ControlCatalog.Browser/Program.cs index 7d05c8e462..53b7c60a6f 100644 --- a/samples/ControlCatalog.Web/Program.cs +++ b/samples/ControlCatalog.Browser/Program.cs @@ -1,8 +1,8 @@ using System.Runtime.Versioning; using Avalonia; -using Avalonia.Web; +using Avalonia.Browser; using ControlCatalog; -using ControlCatalog.Web; +using ControlCatalog.Browser; [assembly:SupportedOSPlatform("browser")] diff --git a/samples/ControlCatalog.Web/Roots.xml b/samples/ControlCatalog.Browser/Roots.xml similarity index 100% rename from samples/ControlCatalog.Web/Roots.xml rename to samples/ControlCatalog.Browser/Roots.xml diff --git a/samples/ControlCatalog.Web/app.css b/samples/ControlCatalog.Browser/app.css similarity index 100% rename from samples/ControlCatalog.Web/app.css rename to samples/ControlCatalog.Browser/app.css diff --git a/samples/ControlCatalog.Web/embed.js b/samples/ControlCatalog.Browser/embed.js similarity index 100% rename from samples/ControlCatalog.Web/embed.js rename to samples/ControlCatalog.Browser/embed.js diff --git a/samples/ControlCatalog.Web/favicon.ico b/samples/ControlCatalog.Browser/favicon.ico similarity index 100% rename from samples/ControlCatalog.Web/favicon.ico rename to samples/ControlCatalog.Browser/favicon.ico diff --git a/samples/ControlCatalog.Web/index.html b/samples/ControlCatalog.Browser/index.html similarity index 100% rename from samples/ControlCatalog.Web/index.html rename to samples/ControlCatalog.Browser/index.html diff --git a/samples/ControlCatalog.Web/main.js b/samples/ControlCatalog.Browser/main.js similarity index 100% rename from samples/ControlCatalog.Web/main.js rename to samples/ControlCatalog.Browser/main.js diff --git a/samples/ControlCatalog.Web/runtimeconfig.template.json b/samples/ControlCatalog.Browser/runtimeconfig.template.json similarity index 100% rename from samples/ControlCatalog.Web/runtimeconfig.template.json rename to samples/ControlCatalog.Browser/runtimeconfig.template.json diff --git a/samples/ControlCatalog/App.xaml b/samples/ControlCatalog/App.xaml index 5570ada27b..8f32fa01dd 100644 --- a/samples/ControlCatalog/App.xaml +++ b/samples/ControlCatalog/App.xaml @@ -9,6 +9,15 @@ + + + + + + + + + diff --git a/samples/ControlCatalog/App.xaml.cs b/samples/ControlCatalog/App.xaml.cs index 750c1082a6..6c99eb5289 100644 --- a/samples/ControlCatalog/App.xaml.cs +++ b/samples/ControlCatalog/App.xaml.cs @@ -3,85 +3,46 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; -using Avalonia.Markup.Xaml.Styling; using Avalonia.Styling; using Avalonia.Themes.Simple; using Avalonia.Themes.Fluent; +using ControlCatalog.Models; using ControlCatalog.ViewModels; namespace ControlCatalog { public class App : Application { + private readonly Styles _themeStylesContainer = new(); + private FluentTheme? _fluentTheme; + private SimpleTheme? _simpleTheme; + private IResourceDictionary? _fluentBaseLightColors, _fluentBaseDarkColors; + private IStyle? _colorPickerFluent, _colorPickerSimple; + private IStyle? _dataGridFluent, _dataGridSimple; + public App() { DataContext = new ApplicationViewModel(); } - public static readonly StyleInclude ColorPickerFluent = new StyleInclude(new Uri("avares://ControlCatalog/Styles")) - { - Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml") - }; - - public static readonly StyleInclude ColorPickerSimple = new StyleInclude(new Uri("avares://ControlCatalog/Styles")) - { - Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Simple/Simple.xaml") - }; - - public static readonly StyleInclude DataGridFluent = new StyleInclude(new Uri("avares://ControlCatalog/Styles")) - { - Source = new Uri("avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml") - }; - - public static readonly StyleInclude DataGridSimple = new StyleInclude(new Uri("avares://ControlCatalog/Styles")) - { - Source = new Uri("avares://Avalonia.Controls.DataGrid/Themes/Simple.xaml") - }; - - public static FluentTheme Fluent = new FluentTheme(new Uri("avares://ControlCatalog/Styles")); - - public static SimpleTheme Simple = new SimpleTheme(new Uri("avares://ControlCatalog/Styles")); - - public static Styles SimpleLight = new Styles - { - new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog")) - { - Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/AccentColors.xaml") - }, - new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog")) - { - Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/Base.xaml") - }, - new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog")) - { - Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseLight.xaml") - }, - Simple - }; - - public static Styles SimpleDark = new Styles - { - new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog")) - { - Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/AccentColors.xaml") - }, - new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog")) - { - Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/Base.xaml") - }, - new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog")) - { - Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseDark.xaml") - }, - Simple - }; - public override void Initialize() { - Styles.Insert(0, Fluent); - Styles.Insert(1, ColorPickerFluent); - Styles.Insert(2, DataGridFluent); + Styles.Add(_themeStylesContainer); + AvaloniaXamlLoader.Load(this); + + _fluentTheme = new FluentTheme(); + _simpleTheme = new SimpleTheme(); + _simpleTheme.Resources.MergedDictionaries.Add((IResourceDictionary)Resources["FluentAccentColors"]!); + _simpleTheme.Resources.MergedDictionaries.Add((IResourceDictionary)Resources["FluentBaseColors"]!); + _colorPickerFluent = (IStyle)Resources["ColorPickerFluent"]!; + _colorPickerSimple = (IStyle)Resources["ColorPickerSimple"]!; + _dataGridFluent = (IStyle)Resources["DataGridFluent"]!; + _dataGridSimple = (IStyle)Resources["DataGridSimple"]!; + _fluentBaseLightColors = (IResourceDictionary)Resources["FluentBaseLightColors"]!; + _fluentBaseDarkColors = (IResourceDictionary)Resources["FluentBaseDarkColors"]!; + + SetThemeVariant(CatalogTheme.FluentLight); } public override void OnFrameworkInitializationCompleted() @@ -97,5 +58,78 @@ namespace ControlCatalog base.OnFrameworkInitializationCompleted(); } + + private CatalogTheme _prevTheme; + public static CatalogTheme CurrentTheme => ((App)Current!)._prevTheme; + public static void SetThemeVariant(CatalogTheme theme) + { + var app = (App)Current!; + var prevTheme = app._prevTheme; + app._prevTheme = theme; + var shouldReopenWindow = theme switch + { + CatalogTheme.FluentLight => prevTheme is CatalogTheme.SimpleDark or CatalogTheme.SimpleLight, + CatalogTheme.FluentDark => prevTheme is CatalogTheme.SimpleDark or CatalogTheme.SimpleLight, + CatalogTheme.SimpleLight => prevTheme is CatalogTheme.FluentDark or CatalogTheme.FluentLight, + CatalogTheme.SimpleDark => prevTheme is CatalogTheme.FluentDark or CatalogTheme.FluentLight, + _ => throw new ArgumentOutOfRangeException(nameof(theme), theme, null) + }; + + if (app._themeStylesContainer.Count == 0) + { + app._themeStylesContainer.Add(new Style()); + app._themeStylesContainer.Add(new Style()); + app._themeStylesContainer.Add(new Style()); + } + + if (theme == CatalogTheme.FluentLight) + { + app._fluentTheme!.Mode = FluentThemeMode.Light; + app._themeStylesContainer[0] = app._fluentTheme; + app._themeStylesContainer[1] = app._colorPickerFluent!; + app._themeStylesContainer[2] = app._dataGridFluent!; + } + else if (theme == CatalogTheme.FluentDark) + { + app._fluentTheme!.Mode = FluentThemeMode.Dark; + app._themeStylesContainer[0] = app._fluentTheme; + app._themeStylesContainer[1] = app._colorPickerFluent!; + app._themeStylesContainer[2] = app._dataGridFluent!; + } + else if (theme == CatalogTheme.SimpleLight) + { + app._simpleTheme!.Mode = SimpleThemeMode.Light; + app._simpleTheme.Resources.MergedDictionaries.Remove(app._fluentBaseDarkColors!); + app._simpleTheme.Resources.MergedDictionaries.Add(app._fluentBaseLightColors!); + app._themeStylesContainer[0] = app._simpleTheme; + app._themeStylesContainer[1] = app._colorPickerSimple!; + app._themeStylesContainer[2] = app._dataGridSimple!; + } + else if (theme == CatalogTheme.SimpleDark) + { + app._simpleTheme!.Mode = SimpleThemeMode.Dark; + app._simpleTheme.Resources.MergedDictionaries.Remove(app._fluentBaseLightColors!); + app._simpleTheme.Resources.MergedDictionaries.Add(app._fluentBaseDarkColors!); + app._themeStylesContainer[0] = app._simpleTheme; + app._themeStylesContainer[1] = app._colorPickerSimple!; + app._themeStylesContainer[2] = app._dataGridSimple!; + } + + if (shouldReopenWindow) + { + if (app.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime) + { + var oldWindow = desktopLifetime.MainWindow; + var newWindow = new MainWindow(); + desktopLifetime.MainWindow = newWindow; + newWindow.Show(); + oldWindow?.Close(); + } + else if (app.ApplicationLifetime is ISingleViewApplicationLifetime singleViewLifetime) + { + singleViewLifetime.MainView = new MainView(); + } + } + } } } diff --git a/samples/ControlCatalog/MainView.xaml.cs b/samples/ControlCatalog/MainView.xaml.cs index 7001eb41ea..15e666ae7b 100644 --- a/samples/ControlCatalog/MainView.xaml.cs +++ b/samples/ControlCatalog/MainView.xaml.cs @@ -6,7 +6,6 @@ using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; using Avalonia.Media; using Avalonia.Media.Immutable; -using Avalonia.Themes.Fluent; using ControlCatalog.Models; using ControlCatalog.Pages; @@ -31,46 +30,12 @@ namespace ControlCatalog } var themes = this.Get("Themes"); + themes.SelectedItem = App.CurrentTheme; themes.SelectionChanged += (sender, e) => { if (themes.SelectedItem is CatalogTheme theme) { - var themeStyle = Application.Current!.Styles[0]; - if (theme == CatalogTheme.FluentLight) - { - if (App.Fluent.Mode != FluentThemeMode.Light) - { - App.Fluent.Mode = FluentThemeMode.Light; - } - Application.Current.Styles[0] = App.Fluent; - Application.Current.Styles[1] = App.ColorPickerFluent; - Application.Current.Styles[2] = App.DataGridFluent; - } - else if (theme == CatalogTheme.FluentDark) - { - - if (App.Fluent.Mode != FluentThemeMode.Dark) - { - App.Fluent.Mode = FluentThemeMode.Dark; - } - Application.Current.Styles[0] = App.Fluent; - Application.Current.Styles[1] = App.ColorPickerFluent; - Application.Current.Styles[2] = App.DataGridFluent; - } - else if (theme == CatalogTheme.SimpleLight) - { - App.Simple.Mode = Avalonia.Themes.Simple.SimpleThemeMode.Light; - Application.Current.Styles[0] = App.SimpleLight; - Application.Current.Styles[1] = App.ColorPickerSimple; - Application.Current.Styles[2] = App.DataGridSimple; - } - else if (theme == CatalogTheme.SimpleDark) - { - App.Simple.Mode = Avalonia.Themes.Simple.SimpleThemeMode.Dark; - Application.Current.Styles[0] = App.SimpleDark; - Application.Current.Styles[1] = App.ColorPickerSimple; - Application.Current.Styles[2] = App.DataGridSimple; - } + App.SetThemeVariant(theme); } }; diff --git a/samples/ControlCatalog/Pages/DataGridPage.xaml b/samples/ControlCatalog/Pages/DataGridPage.xaml index f1fcd8035a..4c3c211ca5 100644 --- a/samples/ControlCatalog/Pages/DataGridPage.xaml +++ b/samples/ControlCatalog/Pages/DataGridPage.xaml @@ -14,22 +14,6 @@ - - - - - - A control for displaying and interacting with a data source. @@ -45,8 +29,7 @@ + RowBackground="#1000"> @@ -59,6 +42,24 @@ IsVisible="{Binding #ShowGDP.IsChecked}" x:DataType="local:Country" /> + + + + + + + + + + + + + + @@ -71,6 +72,20 @@ + + + + + + + + diff --git a/samples/ControlCatalog/Pages/ListBoxPage.xaml b/samples/ControlCatalog/Pages/ListBoxPage.xaml index 04129cb82d..dc0eaf0a51 100644 --- a/samples/ControlCatalog/Pages/ListBoxPage.xaml +++ b/samples/ControlCatalog/Pages/ListBoxPage.xaml @@ -32,6 +32,7 @@ diff --git a/samples/ControlCatalog/Pages/TabControlPage.xaml b/samples/ControlCatalog/Pages/TabControlPage.xaml index a830ce69ac..e289de90f9 100644 --- a/samples/ControlCatalog/Pages/TabControlPage.xaml +++ b/samples/ControlCatalog/Pages/TabControlPage.xaml @@ -53,14 +53,8 @@ - - - - - - diff --git a/samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs b/samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs index 59489ebcc0..f89d9d1e20 100644 --- a/samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs +++ b/samples/ControlCatalog/ViewModels/ListBoxPageViewModel.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reactive; using Avalonia.Controls; using Avalonia.Controls.Selection; +using ControlCatalog.Pages; using MiniMvvm; namespace ControlCatalog.ViewModels @@ -20,9 +21,9 @@ namespace ControlCatalog.ViewModels public ListBoxPageViewModel() { - Items = new ObservableCollection(Enumerable.Range(1, 10000).Select(i => GenerateItem())); + Items = new ObservableCollection(Enumerable.Range(1, 10000).Select(i => GenerateItem())); - Selection = new SelectionModel(); + Selection = new SelectionModel(); Selection.Select(1); _selectionMode = this.WhenAnyValue( @@ -58,8 +59,8 @@ namespace ControlCatalog.ViewModels }); } - public ObservableCollection Items { get; } - public SelectionModel Selection { get; } + public ObservableCollection Items { get; } + public SelectionModel Selection { get; } public IObservable SelectionMode => _selectionMode; public bool Multiple @@ -96,6 +97,31 @@ namespace ControlCatalog.ViewModels public MiniCommand RemoveItemCommand { get; } public MiniCommand SelectRandomItemCommand { get; } - private string GenerateItem() => $"Item {_counter++.ToString()}"; + private ItemModel GenerateItem() => new ItemModel(_counter ++); + } + + /// + /// An Item model for the + /// + public class ItemModel + { + /// + /// Creates a new ItemModel with the given ID + /// + /// The ID to display + public ItemModel(int id) + { + ID = id; + } + + /// + /// The ID of this Item + /// + public int ID { get; } + + public override string ToString() + { + return $"Item {ID}"; + } } } diff --git a/src/Android/Avalonia.Android/AndroidInputMethod.cs b/src/Android/Avalonia.Android/AndroidInputMethod.cs index b6adbde738..8d56086470 100644 --- a/src/Android/Avalonia.Android/AndroidInputMethod.cs +++ b/src/Android/Avalonia.Android/AndroidInputMethod.cs @@ -95,6 +95,10 @@ namespace Avalonia.Android _imm.UpdateSelection(_host, surroundingText.AnchorOffset, surroundingText.CursorOffset, surroundingText.AnchorOffset, surroundingText.CursorOffset); } + else + { + _imm.HideSoftInputFromWindow(_host.WindowToken, HideSoftInputFlags.ImplicitOnly); + } } private void SurroundingTextChanged(object sender, EventArgs e) diff --git a/src/Android/Avalonia.Android/AvaloniaMainActivity.cs b/src/Android/Avalonia.Android/AvaloniaMainActivity.cs index de8d02f188..b24581fb8b 100644 --- a/src/Android/Avalonia.Android/AvaloniaMainActivity.cs +++ b/src/Android/Avalonia.Android/AvaloniaMainActivity.cs @@ -4,9 +4,12 @@ using Android.Content; using Android.Content.Res; using Android.OS; using Android.Runtime; +using Android.Views; using AndroidX.AppCompat.App; using AndroidX.Lifecycle; +using AndroidRect = Android.Graphics.Rect; + namespace Avalonia.Android { public abstract class AvaloniaMainActivity : AppCompatActivity, IActivityResultHandler @@ -15,6 +18,7 @@ namespace Avalonia.Android public Action ActivityResult { get; set; } internal AvaloniaView View; + private GlobalLayoutListener _listener; protected override void OnCreate(Bundle savedInstanceState) { @@ -32,6 +36,10 @@ namespace Avalonia.Android base.OnCreate(savedInstanceState); SetContentView(View); + + _listener = new GlobalLayoutListener(View); + + View.ViewTreeObserver?.AddOnGlobalLayoutListener(_listener); } public object Content @@ -57,6 +65,8 @@ namespace Avalonia.Android { View.Content = null; + View.ViewTreeObserver?.RemoveOnGlobalLayoutListener(_listener); + base.OnDestroy(); } @@ -66,5 +76,20 @@ namespace Avalonia.Android ActivityResult?.Invoke(requestCode, resultCode, data); } + + class GlobalLayoutListener : Java.Lang.Object, ViewTreeObserver.IOnGlobalLayoutListener + { + private AvaloniaView _view; + + public GlobalLayoutListener(AvaloniaView view) + { + _view = view; + } + + public void OnGlobalLayout() + { + _view.TopLevelImpl?.Resize(_view.TopLevelImpl.ClientSize); + } + } } } diff --git a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs index 5f53eb36c8..3b29dbc726 100644 --- a/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs +++ b/src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs @@ -26,6 +26,7 @@ using Avalonia.Rendering; using Avalonia.Rendering.Composition; using Java.Lang; using Math = System.Math; +using AndroidRect = Android.Graphics.Rect; namespace Avalonia.Android.Platform.SkiaPlatform { @@ -63,7 +64,21 @@ namespace Avalonia.Android.Platform.SkiaPlatform public IInputRoot InputRoot { get; private set; } - public virtual Size ClientSize => Size.ToSize(RenderScaling); + public virtual Size ClientSize + { + get + { + AndroidRect rect = new AndroidRect(); + AndroidRect intersection = new AndroidRect(); + + _view.GetWindowVisibleDisplayFrame(intersection); + _view.GetGlobalVisibleRect(rect); + + intersection.Intersect(rect); + + return new PixelSize(intersection.Right - intersection.Left, intersection.Bottom - intersection.Top).ToSize(RenderScaling); + } + } public Size? FrameSize => null; @@ -149,6 +164,11 @@ namespace Avalonia.Android.Platform.SkiaPlatform Resized?.Invoke(size, PlatformResizeReason.Unspecified); } + internal void Resize(Size size) + { + Resized?.Invoke(size, PlatformResizeReason.Layout); + } + class ViewImpl : InvalidationAwareSurfaceView, ISurfaceHolderCallback, IInitEditorInfo { private readonly TopLevelImpl _tl; diff --git a/src/Avalonia.Base/Animation/Animatable.cs b/src/Avalonia.Base/Animation/Animatable.cs index b045a32cd1..edaa76233e 100644 --- a/src/Avalonia.Base/Animation/Animatable.cs +++ b/src/Avalonia.Base/Animation/Animatable.cs @@ -235,7 +235,7 @@ namespace Avalonia.Animation private object? GetAnimationBaseValue(AvaloniaProperty property) { - var value = this.GetBaseValue(property, BindingPriority.LocalValue); + var value = this.GetBaseValue(property); if (value == AvaloniaProperty.UnsetValue) { diff --git a/src/Avalonia.Base/Animation/AnimationInstance`1.cs b/src/Avalonia.Base/Animation/AnimationInstance`1.cs index 52cd4b324f..0881fde988 100644 --- a/src/Avalonia.Base/Animation/AnimationInstance`1.cs +++ b/src/Avalonia.Base/Animation/AnimationInstance`1.cs @@ -229,7 +229,7 @@ namespace Avalonia.Animation private void UpdateNeutralValue() { var property = _animator.Property ?? throw new InvalidOperationException("Animator has no property specified."); - var baseValue = _targetControl.GetBaseValue(property, BindingPriority.LocalValue); + var baseValue = _targetControl.GetBaseValue(property); _neutralValue = baseValue != AvaloniaProperty.UnsetValue ? (T)baseValue! : (T)_targetControl.GetValue(property)!; diff --git a/src/Avalonia.Base/Avalonia.Base.csproj b/src/Avalonia.Base/Avalonia.Base.csproj index d8fcce803f..21bdb794b3 100644 --- a/src/Avalonia.Base/Avalonia.Base.csproj +++ b/src/Avalonia.Base/Avalonia.Base.csproj @@ -28,6 +28,7 @@ + @@ -39,8 +40,8 @@ + - diff --git a/src/Avalonia.Base/AvaloniaObject.cs b/src/Avalonia.Base/AvaloniaObject.cs index 6633eabb5d..68c8f19f19 100644 --- a/src/Avalonia.Base/AvaloniaObject.cs +++ b/src/Avalonia.Base/AvaloniaObject.cs @@ -1,11 +1,11 @@ using System; using System.Collections.Generic; using System.ComponentModel; +using System.Runtime.CompilerServices; using Avalonia.Data; using Avalonia.Diagnostics; using Avalonia.Logging; using Avalonia.PropertyStore; -using Avalonia.Reactive; using Avalonia.Threading; namespace Avalonia @@ -18,13 +18,11 @@ namespace Avalonia /// public class AvaloniaObject : IAvaloniaObject, IAvaloniaObjectDebug, INotifyPropertyChanged { + private readonly ValueStore _values; private AvaloniaObject? _inheritanceParent; - private List? _directBindings; private PropertyChangedEventHandler? _inpcChanged; private EventHandler? _propertyChanged; private List? _inheritanceChildren; - private ValueStore? _values; - private bool _batchUpdate; /// /// Initializes a new instance of the class. @@ -32,6 +30,7 @@ namespace Avalonia public AvaloniaObject() { VerifyAccess(); + _values = new ValueStore(this); } /// @@ -59,7 +58,7 @@ namespace Avalonia /// /// The inheritance parent. /// - protected AvaloniaObject? InheritanceParent + protected internal AvaloniaObject? InheritanceParent { get { @@ -72,28 +71,10 @@ namespace Avalonia if (_inheritanceParent != value) { - var oldParent = _inheritanceParent; - var valuestore = _values; - _inheritanceParent?.RemoveInheritanceChild(this); _inheritanceParent = value; - - var properties = AvaloniaPropertyRegistry.Instance.GetRegisteredInherited(GetType()); - var propertiesCount = properties.Count; - - for (var i = 0; i < propertiesCount; i++) - { - var property = properties[i]; - if (valuestore?.IsSet(property) == true) - { - // If local value set there can be no change. - continue; - } - - property.RouteInheritanceParentChanged(this, oldParent); - } - _inheritanceParent?.AddInheritanceChild(this); + _values.SetInheritanceParent(value); } } } @@ -118,24 +99,15 @@ namespace Avalonia set { this.Bind(binding.Property!, value); } } - private ValueStore Values - { - get - { - if (_values is null) - { - _values = new ValueStore(this); - - if (_batchUpdate) - _values.BeginBatchUpdate(); - } - - return _values; - } - } - + /// + /// Returns a value indicating whether the current thread is the UI thread. + /// + /// true if the current thread is the UI thread; otherwise false. public bool CheckAccess() => Dispatcher.UIThread.CheckAccess(); + /// + /// Checks that the current thread is the UI thread and throws if not. + /// public void VerifyAccess() => Dispatcher.UIThread.VerifyAccess(); /// @@ -144,9 +116,9 @@ namespace Avalonia /// The property. public void ClearValue(AvaloniaProperty property) { - property = property ?? throw new ArgumentNullException(nameof(property)); - - property.RouteClearValue(this); + _ = property ?? throw new ArgumentNullException(nameof(property)); + VerifyAccess(); + _values.ClearLocalValue(property); } /// @@ -234,9 +206,12 @@ namespace Avalonia /// The value. public object? GetValue(AvaloniaProperty property) { - property = property ?? throw new ArgumentNullException(nameof(property)); + _ = property ?? throw new ArgumentNullException(nameof(property)); - return property.RouteGetValue(this); + if (property.IsDirect) + return property.RouteGetValue(this); + else + return _values.GetValue(property); } /// @@ -247,10 +222,9 @@ namespace Avalonia /// The value. public T GetValue(StyledPropertyBase property) { - property = property ?? throw new ArgumentNullException(nameof(property)); + _ = property ?? throw new ArgumentNullException(nameof(property)); VerifyAccess(); - - return GetValueOrInheritedOrDefault(property); + return _values.GetValue(property); } /// @@ -269,18 +243,11 @@ namespace Avalonia } /// - public Optional GetBaseValue(StyledPropertyBase property, BindingPriority maxPriority) + public Optional GetBaseValue(StyledPropertyBase property) { - property = property ?? throw new ArgumentNullException(nameof(property)); + _ = property ?? throw new ArgumentNullException(nameof(property)); VerifyAccess(); - - if (_values is object && - _values.TryGetValue(property, maxPriority, out var value)) - { - return value; - } - - return default; + return _values.GetBaseValue(property); } /// @@ -346,26 +313,20 @@ namespace Avalonia T value, BindingPriority priority = BindingPriority.LocalValue) { - property = property ?? throw new ArgumentNullException(nameof(property)); + _ = property ?? throw new ArgumentNullException(nameof(property)); VerifyAccess(); + ValidatePriority(priority); - LogPropertySet(property, value, priority); + LogPropertySet(property, value, BindingPriority.LocalValue); if (value is UnsetValueType) { if (priority == BindingPriority.LocalValue) - { - Values.ClearLocalValue(property); - } - else - { - throw new NotSupportedException( - "Cannot set property to Unset at non-local value priority."); - } + _values.ClearLocalValue(property); } - else if (!(value is DoNothingType)) + else if (value is not DoNothingType) { - return Values.SetValue(property, value, priority); + return _values.SetValue(property, value, priority); } return null; @@ -382,6 +343,7 @@ namespace Avalonia property = property ?? throw new ArgumentNullException(nameof(property)); VerifyAccess(); + property = AvaloniaPropertyRegistry.Instance.GetRegisteredDirect(this, property); LogPropertySet(property, value, BindingPriority.LocalValue); SetDirectValueUnchecked(property, value); } @@ -398,12 +360,52 @@ namespace Avalonia public IDisposable Bind( AvaloniaProperty property, IObservable source, + BindingPriority priority = BindingPriority.LocalValue) => property.RouteBind(this, source, priority); + + /// + /// Binds a to an observable. + /// + /// The type of the property. + /// The property. + /// The observable. + /// The priority of the binding. + /// + /// A disposable which can be used to terminate the binding. + /// + public IDisposable Bind( + StyledPropertyBase property, + IObservable source, + BindingPriority priority = BindingPriority.LocalValue) + { + property = property ?? throw new ArgumentNullException(nameof(property)); + source = source ?? throw new ArgumentNullException(nameof(source)); + VerifyAccess(); + ValidatePriority(priority); + + return _values.AddBinding(property, source, priority); + } + + /// + /// Binds a to an observable. + /// + /// The type of the property. + /// The property. + /// The observable. + /// The priority of the binding. + /// + /// A disposable which can be used to terminate the binding. + /// + public IDisposable Bind( + StyledPropertyBase property, + IObservable source, BindingPriority priority = BindingPriority.LocalValue) { property = property ?? throw new ArgumentNullException(nameof(property)); source = source ?? throw new ArgumentNullException(nameof(source)); + VerifyAccess(); + ValidatePriority(priority); - return property.RouteBind(this, source.ToBindingValue(), priority); + return _values.AddBinding(property, source, priority); } /// @@ -424,8 +426,9 @@ namespace Avalonia property = property ?? throw new ArgumentNullException(nameof(property)); source = source ?? throw new ArgumentNullException(nameof(source)); VerifyAccess(); + ValidatePriority(priority); - return Values.AddBinding(property, source, priority); + return _values.AddBinding(property, source, priority); } /// @@ -439,10 +442,9 @@ namespace Avalonia /// public IDisposable Bind( DirectPropertyBase property, - IObservable> source) + IObservable source) { property = property ?? throw new ArgumentNullException(nameof(property)); - source = source ?? throw new ArgumentNullException(nameof(source)); VerifyAccess(); property = AvaloniaPropertyRegistry.Instance.GetRegisteredDirect(this, property); @@ -452,48 +454,67 @@ namespace Avalonia throw new ArgumentException($"The property {property.Name} is readonly."); } - Logger.TryGet(LogEventLevel.Verbose, LogArea.Property)?.Log( - this, - "Bound {Property} to {Binding} with priority LocalValue", - property, - GetDescription(source)); - - _directBindings ??= new List(); - - return new DirectBindingSubscription(this, property, source); + return _values.AddBinding(property, source); } /// - /// Coerces the specified . + /// Binds a to an observable. /// + /// The type of the property. /// The property. - public void CoerceValue(AvaloniaProperty property) + /// The observable. + /// + /// A disposable which can be used to terminate the binding. + /// + public IDisposable Bind( + DirectPropertyBase property, + IObservable source) { - _values?.CoerceValue(property); - } + property = property ?? throw new ArgumentNullException(nameof(property)); + VerifyAccess(); - public void BeginBatchUpdate() - { - if (_batchUpdate) + property = AvaloniaPropertyRegistry.Instance.GetRegisteredDirect(this, property); + + if (property.IsReadOnly) { - throw new InvalidOperationException("Batch update already in progress."); + throw new ArgumentException($"The property {property.Name} is readonly."); } - _batchUpdate = true; - _values?.BeginBatchUpdate(); + return _values.AddBinding(property, source); } - public void EndBatchUpdate() + /// + /// Binds a to an observable. + /// + /// The type of the property. + /// The property. + /// The observable. + /// + /// A disposable which can be used to terminate the binding. + /// + public IDisposable Bind( + DirectPropertyBase property, + IObservable> source) { - if (!_batchUpdate) + property = property ?? throw new ArgumentNullException(nameof(property)); + VerifyAccess(); + + property = AvaloniaPropertyRegistry.Instance.GetRegisteredDirect(this, property); + + if (property.IsReadOnly) { - throw new InvalidOperationException("No batch update in progress."); + throw new ArgumentException($"The property {property.Name} is readonly."); } - _batchUpdate = false; - _values?.EndBatchUpdate(); + return _values.AddBinding(property, source); } + /// + /// Coerces the specified . + /// + /// The property. + public void CoerceValue(AvaloniaProperty property) => _values.CoerceValue(property); + /// internal void AddInheritanceChild(AvaloniaObject child) { @@ -507,98 +528,12 @@ namespace Avalonia _inheritanceChildren?.Remove(child); } - internal void InheritedPropertyChanged( - AvaloniaProperty property, - Optional oldValue, - Optional newValue) - { - if (property.Inherits && (_values == null || !_values.IsSet(property))) - { - RaisePropertyChanged(property, oldValue, newValue, BindingPriority.LocalValue); - } - } - /// Delegate[]? IAvaloniaObjectDebug.GetPropertyChangedSubscribers() { return _propertyChanged?.GetInvocationList(); } - internal void ValueChanged(AvaloniaPropertyChangedEventArgs change) - { - var property = (StyledPropertyBase)change.Property; - - LogIfError(property, change.NewValue); - - // If the change is to the effective value of the property and no old/new value is set - // then fill in the old/new value from property inheritance/default value. We don't do - // this for non-effective value changes because these are only needed for property - // transitions, where knowing e.g. that an inherited value is active at an arbitrary - // priority isn't of any use and would introduce overhead. - if (change.IsEffectiveValueChange && !change.OldValue.HasValue) - { - change.SetOldValue(GetInheritedOrDefault(property)); - } - - if (change.IsEffectiveValueChange && !change.NewValue.HasValue) - { - change.SetNewValue(GetInheritedOrDefault(property)); - } - - if (!change.IsEffectiveValueChange || - !EqualityComparer.Default.Equals(change.OldValue.Value, change.NewValue.Value)) - { - RaisePropertyChanged(change); - - if (change.IsEffectiveValueChange) - { - Logger.TryGet(LogEventLevel.Verbose, LogArea.Property)?.Log( - this, - "{Property} changed from {$Old} to {$Value} with priority {Priority}", - property, - change.OldValue, - change.NewValue, - change.Priority); - } - } - } - - internal void Completed( - StyledPropertyBase property, - IPriorityValueEntry entry, - Optional oldValue) - { - var change = new AvaloniaPropertyChangedEventArgs( - this, - property, - oldValue, - default, - BindingPriority.Unset); - ValueChanged(change); - } - - /// - /// Called for each inherited property when the changes. - /// - /// The type of the property value. - /// The property. - /// The old inheritance parent. - internal void InheritanceParentChanged( - StyledPropertyBase property, - AvaloniaObject? oldParent) - { - var oldValue = oldParent is not null ? - oldParent.GetValueOrInheritedOrDefault(property) : - property.GetDefaultValue(GetType()); - - var newValue = GetInheritedOrDefault(property); - - if (!EqualityComparer.Default.Equals(oldValue, newValue)) - { - RaisePropertyChanged(property, oldValue, newValue); - } - } - internal AvaloniaPropertyValue GetDiagnosticInternal(AvaloniaProperty property) { if (property.IsDirect) @@ -626,19 +561,23 @@ namespace Avalonia "Unset"); } + internal ValueStore GetValueStore() => _values; + internal IReadOnlyList? GetInheritanceChildren() => _inheritanceChildren; + /// - /// Logs a binding error for a property. + /// Gets a logger to which a binding warning may be written. /// /// The property that the error occurred on. - /// The binding error. - protected internal virtual void LogBindingError(AvaloniaProperty property, Exception e) + /// The binding exception, if any. + /// + /// This is overridden in to prevent logging binding errors when a + /// control is not attached to the visual tree. + /// + internal virtual ParametrizedLogger? GetBindingWarningLogger( + AvaloniaProperty property, + Exception? e) { - Logger.TryGet(LogEventLevel.Warning, LogArea.Binding)?.Log( - this, - "Error in binding to {Target}.{Property}: {Message}", - this, - property, - e.Message); + return Logger.TryGet(LogEventLevel.Warning, LogArea.Binding); } /// @@ -675,6 +614,22 @@ namespace Avalonia { } + /// + /// Raises the event for a direct property. + /// + /// The property that has changed. + /// The old property value. + /// The new property value. + /// The priority of the binding that produced the value. + private protected void RaisePropertyChanged( + DirectPropertyBase property, + Optional oldValue, + BindingValue newValue, + BindingPriority priority = BindingPriority.LocalValue) + { + RaisePropertyChanged(property, oldValue, newValue, priority, true); + } + /// /// Raises the event. /// @@ -682,18 +637,32 @@ namespace Avalonia /// The old property value. /// The new property value. /// The priority of the binding that produced the value. - protected internal void RaisePropertyChanged( + /// + /// Whether the notification represents a change to the effective value of the property. + /// + internal void RaisePropertyChanged( AvaloniaProperty property, Optional oldValue, BindingValue newValue, - BindingPriority priority = BindingPriority.LocalValue) + BindingPriority priority, + bool isEffectiveValue) { - RaisePropertyChanged(new AvaloniaPropertyChangedEventArgs( + var e = new AvaloniaPropertyChangedEventArgs( this, property, oldValue, newValue, - priority)); + priority, + isEffectiveValue); + + OnPropertyChangedCore(e); + + if (isEffectiveValue) + { + property.NotifyChanged(e); + _propertyChanged?.Invoke(this, e); + _inpcChanged?.Invoke(this, new PropertyChangedEventArgs(property.Name)); + } } /// @@ -718,110 +687,24 @@ namespace Avalonia var old = field; field = value; - RaisePropertyChanged(property, old, value); + RaisePropertyChanged(property, old, value, BindingPriority.LocalValue, true); return true; } - private T GetInheritedOrDefault(StyledPropertyBase property) - { - if (property.Inherits && InheritanceParent is AvaloniaObject o) - { - return o.GetValueOrInheritedOrDefault(property); - } - - return property.GetDefaultValue(GetType()); - } - - private T GetValueOrInheritedOrDefault( - StyledPropertyBase property, - BindingPriority maxPriority = BindingPriority.Animation) - { - var o = this; - var inherits = property.Inherits; - var value = default(T); - - while (o != null) - { - var values = o._values; - - if (values != null - && values.TryGetValue(property, maxPriority, out value) == true) - { - return value; - } - - if (!inherits) - { - break; - } - - o = o.InheritanceParent as AvaloniaObject; - } - - return property.GetDefaultValue(GetType()); - } - - protected internal void RaisePropertyChanged(AvaloniaPropertyChangedEventArgs change) - { - VerifyAccess(); - - if (change.IsEffectiveValueChange) - { - change.Property.Notifying?.Invoke(this, true); - } - - try - { - OnPropertyChangedCore(change); - - if (change.IsEffectiveValueChange) - { - change.Property.NotifyChanged(change); - _propertyChanged?.Invoke(this, change); - - if (_inpcChanged != null) - { - var inpce = new PropertyChangedEventArgs(change.Property.Name); - _inpcChanged(this, inpce); - } - - if (change.Property.Inherits && _inheritanceChildren != null) - { - foreach (var child in _inheritanceChildren) - { - child.InheritedPropertyChanged( - change.Property, - change.OldValue, - change.NewValue.ToOptional()); - } - } - } - } - finally - { - if (change.IsEffectiveValueChange) - { - change.Property.Notifying?.Invoke(this, false); - } - } - } - /// /// Sets the value of a direct property. /// /// The property. /// The value. - private void SetDirectValueUnchecked(DirectPropertyBase property, T value) + internal void SetDirectValueUnchecked(DirectPropertyBase property, T value) { - var p = AvaloniaPropertyRegistry.Instance.GetRegisteredDirect(this, property); - if (value is UnsetValueType) { - p.InvokeSetter(this, p.GetUnsetValue(GetType())); + property.InvokeSetter(this, property.GetUnsetValue(GetType())); } else if (!(value is DoNothingType)) { - p.InvokeSetter(this, value); + property.InvokeSetter(this, value); } } @@ -830,16 +713,9 @@ namespace Avalonia /// /// The property. /// The value. - private void SetDirectValueUnchecked(DirectPropertyBase property, BindingValue value) + internal void SetDirectValueUnchecked(DirectPropertyBase property, BindingValue value) { - var p = AvaloniaPropertyRegistry.Instance.FindRegisteredDirect(this, property); - - if (p == null) - { - throw new ArgumentException($"Property '{property.Name} not registered on '{this.GetType()}"); - } - - LogIfError(property, value); + LoggingUtils.LogIfNecessary(this, property, value); switch (value.Type) { @@ -858,7 +734,7 @@ namespace Avalonia break; } - var metadata = p.GetMetadata(GetType()); + var metadata = property.GetMetadata(GetType()); if (metadata.EnableDataValidation == true) { @@ -877,29 +753,6 @@ namespace Avalonia return description?.Description ?? o.ToString() ?? o.GetType().Name; } - /// - /// Logs a message if the notification represents a binding error. - /// - /// The property being bound. - /// The binding notification. - private void LogIfError(AvaloniaProperty property, BindingValue value) - { - if (value.HasError) - { - if (value.Error is AggregateException aggregate) - { - foreach (var inner in aggregate.InnerExceptions) - { - LogBindingError(property, inner); - } - } - else - { - LogBindingError(property, value.Error!); - } - } - } - /// /// Logs a property set message. /// @@ -916,49 +769,16 @@ namespace Avalonia priority); } - private class DirectBindingSubscription : IObserver>, IDisposable + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ValidatePriority(BindingPriority priority) { - private readonly AvaloniaObject _owner; - private readonly DirectPropertyBase _property; - private readonly IDisposable _subscription; - - public DirectBindingSubscription( - AvaloniaObject owner, - DirectPropertyBase property, - IObservable> source) - { - _owner = owner; - _property = property; - _owner._directBindings!.Add(this); - _subscription = source.Subscribe(this); - } - - public void Dispose() - { - // _subscription can be null, if Subscribe failed with an exception. - _subscription?.Dispose(); - _owner._directBindings!.Remove(this); - } - - public void OnCompleted() => Dispose(); - public void OnError(Exception error) => Dispose(); - public void OnNext(BindingValue value) - { - if (Dispatcher.UIThread.CheckAccess()) - { - _owner.SetDirectValueUnchecked(_property, value); - } - else - { - // To avoid allocating closure in the outer scope we need to capture variables - // locally. This allows us to skip most of the allocations when on UI thread. - var instance = _owner; - var property = _property; - var newValue = value; + if (priority < BindingPriority.Animation || priority >= BindingPriority.Inherited) + ThrowInvalidPriority(priority); + } - Dispatcher.UIThread.Post(() => instance.SetDirectValueUnchecked(property, newValue)); - } - } + private static void ThrowInvalidPriority(BindingPriority priority) + { + throw new ArgumentException($"Invalid priority ${priority}", nameof(priority)); } } } diff --git a/src/Avalonia.Base/AvaloniaObjectExtensions.cs b/src/Avalonia.Base/AvaloniaObjectExtensions.cs index 2d7bab6cd6..9644063da7 100644 --- a/src/Avalonia.Base/AvaloniaObjectExtensions.cs +++ b/src/Avalonia.Base/AvaloniaObjectExtensions.cs @@ -261,7 +261,6 @@ namespace Avalonia } throw new NotSupportedException("Custom implementations of IAvaloniaObject not supported."); - } /// @@ -280,14 +279,17 @@ namespace Avalonia IObservable source, BindingPriority priority = BindingPriority.LocalValue) { - target = target ?? throw new ArgumentNullException(nameof(target)); - property = property ?? throw new ArgumentNullException(nameof(property)); - source = source ?? throw new ArgumentNullException(nameof(source)); + if (target is AvaloniaObject ao) + { + return property switch + { + StyledPropertyBase styled => ao.Bind(styled, source, priority), + DirectPropertyBase direct => ao.Bind(direct, source), + _ => throw new NotSupportedException("Unsupported AvaloniaProperty type."), + }; + } - return target.Bind( - property, - source.ToBindingValue(), - priority); + throw new NotSupportedException("Custom implementations of IAvaloniaObject not supported."); } /// @@ -362,10 +364,8 @@ namespace Avalonia /// /// The object. /// The property. - /// The maximum priority for the value. /// - /// For styled properties, gets the value of the property if set on the object with a - /// priority equal or lower to , otherwise + /// For styled properties, gets the value of the property excluding animated values, otherwise /// . Note that this method does not return /// property values that come from inherited or default values. /// @@ -373,14 +373,13 @@ namespace Avalonia /// public static object? GetBaseValue( this IAvaloniaObject target, - AvaloniaProperty property, - BindingPriority maxPriority) + AvaloniaProperty property) { target = target ?? throw new ArgumentNullException(nameof(target)); property = property ?? throw new ArgumentNullException(nameof(property)); if (target is AvaloniaObject ao) - return property.RouteGetBaseValue(ao, maxPriority); + return property.RouteGetBaseValue(ao); throw new NotSupportedException("Custom implementations of IAvaloniaObject not supported."); } @@ -389,10 +388,8 @@ namespace Avalonia /// /// The object. /// The property. - /// The maximum priority for the value. /// - /// For styled properties, gets the value of the property if set on the object with a - /// priority equal or lower to , otherwise + /// For styled properties, gets the value of the property excluding animated values, otherwise /// . Note that this method does not return property values /// that come from inherited or default values. /// @@ -400,8 +397,7 @@ namespace Avalonia /// public static Optional GetBaseValue( this IAvaloniaObject target, - AvaloniaProperty property, - BindingPriority maxPriority) + AvaloniaProperty property) { target = target ?? throw new ArgumentNullException(nameof(target)); property = property ?? throw new ArgumentNullException(nameof(property)); @@ -410,7 +406,7 @@ namespace Avalonia { return property switch { - StyledPropertyBase styled => ao.GetBaseValue(styled, maxPriority), + StyledPropertyBase styled => ao.GetBaseValue(styled), DirectPropertyBase direct => ao.GetValue(direct), _ => throw new NotSupportedException("Unsupported AvaloniaProperty type.") }; diff --git a/src/Avalonia.Base/AvaloniaProperty.cs b/src/Avalonia.Base/AvaloniaProperty.cs index 46ba4082fb..f2e90bb6d7 100644 --- a/src/Avalonia.Base/AvaloniaProperty.cs +++ b/src/Avalonia.Base/AvaloniaProperty.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using Avalonia.Data; using Avalonia.Data.Core; +using Avalonia.PropertyStore; using Avalonia.Styling; using Avalonia.Utilities; @@ -176,7 +177,7 @@ namespace Avalonia { return new IndexerDescriptor { - Priority = BindingPriority.TemplatedParent, + Priority = BindingPriority.Template, Property = property, }; } @@ -455,6 +456,12 @@ namespace Avalonia return Name; } + /// + /// Creates an effective value for the property. + /// + /// The effective value owner. + internal abstract EffectiveValue CreateEffectiveValue(AvaloniaObject o); + /// /// Routes an untyped ClearValue call to a typed call. /// @@ -471,8 +478,7 @@ namespace Avalonia /// Routes an untyped GetBaseValue call to a typed call. /// /// The object instance. - /// The maximum priority for the value. - internal abstract object? RouteGetBaseValue(AvaloniaObject o, BindingPriority maxPriority); + internal abstract object? RouteGetBaseValue(AvaloniaObject o); /// /// Routes an untyped SetValue call to a typed call. @@ -496,12 +502,9 @@ namespace Avalonia /// The priority. internal abstract IDisposable RouteBind( AvaloniaObject o, - IObservable> source, + IObservable source, BindingPriority priority); - internal abstract void RouteInheritanceParentChanged(AvaloniaObject o, AvaloniaObject? oldParent); - internal abstract ISetterInstance CreateSetterInstance(IStyleable target, object? value); - /// /// Overrides the metadata for the property on the specified type. /// diff --git a/src/Avalonia.Base/AvaloniaPropertyChangedEventArgs.cs b/src/Avalonia.Base/AvaloniaPropertyChangedEventArgs.cs index 45c67b9f48..a3ca25bc45 100644 --- a/src/Avalonia.Base/AvaloniaPropertyChangedEventArgs.cs +++ b/src/Avalonia.Base/AvaloniaPropertyChangedEventArgs.cs @@ -17,6 +17,16 @@ namespace Avalonia IsEffectiveValueChange = true; } + internal AvaloniaPropertyChangedEventArgs( + IAvaloniaObject sender, + BindingPriority priority, + bool isEffectiveValueChange) + { + Sender = sender; + Priority = priority; + IsEffectiveValueChange = isEffectiveValueChange; + } + /// /// Gets the that the property changed on. /// @@ -49,20 +59,8 @@ namespace Avalonia /// public BindingPriority Priority { get; private set; } - /// - /// Gets a value indicating whether the change represents a change to the effective value of - /// the property. - /// - /// - /// This will usually be true, except in - /// - /// which receives notifications for all changes to property values, whether a value with a higher - /// priority is present or not. When this property is false, the change that is being signaled - /// has not resulted in a change to the property value on the object. - /// - public bool IsEffectiveValueChange { get; private set; } + internal bool IsEffectiveValueChange { get; private set; } - internal void MarkNonEffectiveValue() => IsEffectiveValueChange = false; protected abstract AvaloniaProperty GetProperty(); protected abstract object? GetOldValue(); protected abstract object? GetNewValue(); diff --git a/src/Avalonia.Base/AvaloniaPropertyChangedEventArgs`1.cs b/src/Avalonia.Base/AvaloniaPropertyChangedEventArgs`1.cs index 734e38596c..2c7a597537 100644 --- a/src/Avalonia.Base/AvaloniaPropertyChangedEventArgs`1.cs +++ b/src/Avalonia.Base/AvaloniaPropertyChangedEventArgs`1.cs @@ -21,7 +21,18 @@ namespace Avalonia Optional oldValue, BindingValue newValue, BindingPriority priority) - : base(sender, priority) + : this(sender, property, oldValue, newValue, priority, true) + { + } + + internal AvaloniaPropertyChangedEventArgs( + IAvaloniaObject sender, + AvaloniaProperty property, + Optional oldValue, + BindingValue newValue, + BindingPriority priority, + bool isEffectiveValueChange) + : base(sender, priority, isEffectiveValueChange) { Property = property; OldValue = oldValue; @@ -39,28 +50,13 @@ namespace Avalonia /// /// Gets the old value of the property. /// - /// - /// When is true, returns the - /// old value of the property on the object. - /// When is false, returns - /// . - /// public new Optional OldValue { get; private set; } /// /// Gets the new value of the property. /// - /// - /// When is true, returns the - /// value of the property on the object. - /// When is false returns the - /// changed value, or if the value was removed. - /// public new BindingValue NewValue { get; private set; } - internal void SetOldValue(Optional value) => OldValue = value; - internal void SetNewValue(BindingValue value) => NewValue = value; - protected override AvaloniaProperty GetProperty() => Property; protected override object? GetOldValue() => OldValue.GetValueOrDefault(AvaloniaProperty.UnsetValue); diff --git a/src/Avalonia.Base/Compatibility/CollectionCompatibilityExtensions.cs b/src/Avalonia.Base/Compatibility/CollectionCompatibilityExtensions.cs new file mode 100644 index 0000000000..e22288a74d --- /dev/null +++ b/src/Avalonia.Base/Compatibility/CollectionCompatibilityExtensions.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace System; + +#if !NET6_0_OR_GREATER +internal static class CollectionCompatibilityExtensions +{ + public static bool Remove( + this Dictionary o, + TKey key, + [MaybeNullWhen(false)] out TValue value) + where TKey : notnull + { + if (o.TryGetValue(key, out value)) + return o.Remove(key); + return false; + } + + public static bool TryAdd(this Dictionary o, TKey key, TValue value) + where TKey : notnull + { + if (!o.ContainsKey(key)) + { + o.Add(key, value); + return true; + } + + return false; + } +} +#endif diff --git a/src/Avalonia.Base/Data/BindingNotification.cs b/src/Avalonia.Base/Data/BindingNotification.cs index 4b97b1a02c..b67056e554 100644 --- a/src/Avalonia.Base/Data/BindingNotification.cs +++ b/src/Avalonia.Base/Data/BindingNotification.cs @@ -241,26 +241,6 @@ namespace Avalonia.Data _value = value; } - public BindingValue ToBindingValue() - { - if (ErrorType == BindingErrorType.None) - { - return HasValue ? new BindingValue(Value) : BindingValue.Unset; - } - else if (ErrorType == BindingErrorType.Error) - { - return BindingValue.BindingError( - Error!, - HasValue ? new Optional(Value) : Optional.Empty); - } - else - { - return BindingValue.DataValidationError( - Error!, - HasValue ? new Optional(Value) : Optional.Empty); - } - } - /// public override string ToString() { diff --git a/src/Avalonia.Base/Data/BindingPriority.cs b/src/Avalonia.Base/Data/BindingPriority.cs index ece64375f2..5fd5aae43b 100644 --- a/src/Avalonia.Base/Data/BindingPriority.cs +++ b/src/Avalonia.Base/Data/BindingPriority.cs @@ -1,7 +1,9 @@ +using System; + namespace Avalonia.Data { /// - /// The priority of a binding. + /// The priority of a value or binding. /// public enum BindingPriority { @@ -16,29 +18,36 @@ namespace Avalonia.Data LocalValue = 0, /// - /// A triggered style binding. + /// A triggered style value. /// /// /// A style trigger is a selector such as .class which overrides a - /// binding. In this way, a basic control can have - /// for example a Background from the templated parent which changes when the - /// control has the :pointerover class. + /// value. In this way, a control can have, e.g. a Background from + /// the template which changes when the control has the :pointerover class. /// StyleTrigger, /// - /// A binding to a property on the templated parent. + /// A value from the control's template. /// - TemplatedParent, + Template, /// - /// A style binding. + /// A style value. /// Style, + + /// + /// The value is inherited from an ancestor element. + /// + Inherited, /// - /// The binding is uninitialized. + /// The value is uninitialized. /// Unset = int.MaxValue, + + [Obsolete("Use Template priority")] + TemplatedParent = Template, } } diff --git a/src/Avalonia.Base/Data/BindingValue.cs b/src/Avalonia.Base/Data/BindingValue.cs index 55be611083..4bb3ad08d5 100644 --- a/src/Avalonia.Base/Data/BindingValue.cs +++ b/src/Avalonia.Base/Data/BindingValue.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using Avalonia.Utilities; @@ -230,19 +231,77 @@ namespace Avalonia.Data /// /// Creates a from an object, handling the special values - /// and . + /// , and + /// . /// /// The untyped value. /// The typed binding value. public static BindingValue FromUntyped(object? value) { - return value switch + return FromUntyped(value, typeof(T)); + } + + /// + /// Creates a from an object, handling the special values + /// , and + /// . + /// + /// The untyped value. + /// The runtime target type. + /// The typed binding value. + public static BindingValue FromUntyped(object? value, Type targetType) + { + if (value == AvaloniaProperty.UnsetValue) + return Unset; + else if (value == BindingOperations.DoNothing) + return DoNothing; + + var type = BindingValueType.Value; + T? v = default; + Exception? error = null; + List? errors = null; + + if (value is BindingNotification n) { - UnsetValueType _ => Unset, - DoNothingType _ => DoNothing, - BindingNotification n => n.ToBindingValue().Cast(), - _ => new BindingValue((T)value!) - }; + error = n.Error; + type = n.ErrorType switch + { + BindingErrorType.Error => BindingValueType.BindingError, + BindingErrorType.DataValidationError => BindingValueType.DataValidationError, + _ => BindingValueType.Value, + }; + + if (n.HasValue) + type |= BindingValueType.HasValue; + value = n.Value; + } + + if ((type & BindingValueType.HasValue) != 0) + { + if (TypeUtilities.TryConvertImplicit(targetType, value, out var typed)) + v = (T)typed!; + else + { + var e = new InvalidCastException( + $"Unable to convert object '{value ?? "(null)"}' " + + $"of type '{value?.GetType()}' to type '{targetType}'."); + + if (error is null) + error = e; + else + { + errors ??= new List() { error }; + errors.Add(e); + } + + type = BindingValueType.BindingError; + } + } + + if (errors is not null) + error = new AggregateException(errors); + + return new BindingValue(type, v, error); } /// @@ -372,61 +431,4 @@ namespace Avalonia.Data } } } - - public static class BindingValueExtensions - { - /// - /// Casts the type of a using only the C# cast operator. - /// - /// The target type. - /// The binding value. - /// The cast value. - public static BindingValue Cast(this BindingValue value) - { - return value.Type switch - { - BindingValueType.DoNothing => BindingValue.DoNothing, - BindingValueType.UnsetValue => BindingValue.Unset, - BindingValueType.Value => new BindingValue((T)value.Value!), - BindingValueType.BindingError => BindingValue.BindingError(value.Error!), - BindingValueType.BindingErrorWithFallback => BindingValue.BindingError( - value.Error!, - (T)value.Value!), - BindingValueType.DataValidationError => BindingValue.DataValidationError(value.Error!), - BindingValueType.DataValidationErrorWithFallback => BindingValue.DataValidationError( - value.Error!, - (T)value.Value!), - _ => throw new NotSupportedException("Invalid BindingValue type."), - }; - } - - /// - /// Casts the type of a using the implicit conversions - /// allowed by the C# language. - /// - /// The target type. - /// The binding value. - /// The cast value. - /// - /// Note that this method uses reflection and as such may be slow. - /// - public static BindingValue Convert(this BindingValue value) - { - return value.Type switch - { - BindingValueType.DoNothing => BindingValue.DoNothing, - BindingValueType.UnsetValue => BindingValue.Unset, - BindingValueType.Value => new BindingValue(TypeUtilities.ConvertImplicit(value.Value!)), - BindingValueType.BindingError => BindingValue.BindingError(value.Error!), - BindingValueType.BindingErrorWithFallback => BindingValue.BindingError( - value.Error!, - TypeUtilities.ConvertImplicit(value.Value!)), - BindingValueType.DataValidationError => BindingValue.DataValidationError(value.Error!), - BindingValueType.DataValidationErrorWithFallback => BindingValue.DataValidationError( - value.Error!, - TypeUtilities.ConvertImplicit(value.Value!)), - _ => throw new NotSupportedException("Invalid BindingValue type."), - }; - } - } } diff --git a/src/Avalonia.Base/Data/Core/ExpressionObserver.cs b/src/Avalonia.Base/Data/Core/ExpressionObserver.cs index e4affd97de..0c7f576da6 100644 --- a/src/Avalonia.Base/Data/Core/ExpressionObserver.cs +++ b/src/Avalonia.Base/Data/Core/ExpressionObserver.cs @@ -52,6 +52,7 @@ namespace Avalonia.Data.Core private static readonly object UninitializedValue = new object(); private readonly ExpressionNode _node; private object? _root; + private Func? _rootGetter; private IDisposable? _rootSubscription; private WeakReference? _value; private IReadOnlyList? _transformNodes; @@ -109,11 +110,9 @@ namespace Avalonia.Data.Core IObservable update, string? description) { - _ = rootGetter ?? throw new ArgumentNullException(nameof(rootGetter)); - Description = description; - _node = node ?? throw new ArgumentNullException(nameof(rootGetter)); - _node.Target = new WeakReference(rootGetter()); + _rootGetter = rootGetter ?? throw new ArgumentNullException(nameof(rootGetter)); + _node = node ?? throw new ArgumentNullException(nameof(node)); _root = update.Select(x => rootGetter()); } @@ -263,6 +262,8 @@ namespace Avalonia.Data.Core protected override void Initialize() { _value = null; + if (_rootGetter is not null) + _node.Target = new WeakReference(_rootGetter()); _node.Subscribe(ValueChanged); StartRoot(); } diff --git a/src/Avalonia.Base/DirectPropertyBase.cs b/src/Avalonia.Base/DirectPropertyBase.cs index 86e4bffaa8..0a8fe19885 100644 --- a/src/Avalonia.Base/DirectPropertyBase.cs +++ b/src/Avalonia.Base/DirectPropertyBase.cs @@ -1,5 +1,6 @@ using System; using Avalonia.Data; +using Avalonia.PropertyStore; using Avalonia.Reactive; using Avalonia.Styling; @@ -105,6 +106,11 @@ namespace Avalonia base.OverrideMetadata(type, metadata); } + internal override EffectiveValue CreateEffectiveValue(AvaloniaObject o) + { + throw new InvalidOperationException("Cannot create EffectiveValue for direct property."); + } + /// internal override void RouteClearValue(AvaloniaObject o) { @@ -117,7 +123,7 @@ namespace Avalonia return o.GetValue(this); } - internal override object? RouteGetBaseValue(AvaloniaObject o, BindingPriority maxPriority) + internal override object? RouteGetBaseValue(AvaloniaObject o) { return o.GetValue(this); } @@ -146,44 +152,18 @@ namespace Avalonia return null; } - /// + /// + /// Routes an untyped Bind call to a typed call. + /// + /// The object instance. + /// The binding source. + /// The priority. internal override IDisposable RouteBind( AvaloniaObject o, - IObservable> source, + IObservable source, BindingPriority priority) { - var adapter = TypedBindingAdapter.Create(o, this, source); - return o.Bind(this, adapter); - } - - internal override void RouteInheritanceParentChanged(AvaloniaObject o, AvaloniaObject? oldParent) - { - throw new NotSupportedException("Direct properties do not support inheritance."); - } - - internal override ISetterInstance CreateSetterInstance(IStyleable target, object? value) - { - if (value is IBinding binding) - { - return new PropertySetterBindingInstance( - target, - this, - binding); - } - else if (value is ITemplate template && !typeof(ITemplate).IsAssignableFrom(PropertyType)) - { - return new PropertySetterTemplateInstance( - target, - this, - template); - } - else - { - return new PropertySetterInstance( - target, - this, - (TValue)value!); - } + return o.Bind(this, source); } } } diff --git a/src/Avalonia.Base/IStyledPropertyAccessor.cs b/src/Avalonia.Base/IStyledPropertyAccessor.cs index c4a0005f55..4cbfd7b759 100644 --- a/src/Avalonia.Base/IStyledPropertyAccessor.cs +++ b/src/Avalonia.Base/IStyledPropertyAccessor.cs @@ -15,5 +15,12 @@ namespace Avalonia /// The default value. /// object? GetDefaultValue(Type type); + + /// + /// Validates the specified property value. + /// + /// The value. + /// True if the value is valid, otherwise false. + bool ValidateValue(object? value); } } diff --git a/src/Avalonia.Base/Layout/Layoutable.cs b/src/Avalonia.Base/Layout/Layoutable.cs index 527b63292d..39edab3fc6 100644 --- a/src/Avalonia.Base/Layout/Layoutable.cs +++ b/src/Avalonia.Base/Layout/Layoutable.cs @@ -1,5 +1,6 @@ using System; using Avalonia.Logging; +using Avalonia.Styling; using Avalonia.VisualTree; #nullable enable @@ -719,9 +720,9 @@ namespace Avalonia.Layout return finalSize; } - protected sealed override void InvalidateStyles() + internal sealed override void InvalidateStyles(bool recurse) { - base.InvalidateStyles(); + base.InvalidateStyles(recurse); InvalidateMeasure(); } @@ -795,6 +796,12 @@ namespace Avalonia.Layout base.OnVisualParentChanged(oldParent, newParent); } + private protected override void OnControlThemeChanged() + { + base.OnControlThemeChanged(); + InvalidateMeasure(); + } + /// /// Called when the layout manager raises a LayoutUpdated event. /// diff --git a/src/Avalonia.Base/Properties/AssemblyInfo.cs b/src/Avalonia.Base/Properties/AssemblyInfo.cs index d5e01087ef..260771ea9d 100644 --- a/src/Avalonia.Base/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Base/Properties/AssemblyInfo.cs @@ -29,6 +29,6 @@ using Avalonia.Metadata; [assembly: InternalsVisibleTo("Avalonia.Skia.RenderTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.Skia.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("Avalonia.UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Web.Blazor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] -[assembly: InternalsVisibleTo("Avalonia.Web, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] +[assembly: InternalsVisibleTo("Avalonia.Browser.Blazor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] +[assembly: InternalsVisibleTo("Avalonia.Browser, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c1bba1142285fe0419326fb25866ba62c47e6c2b5c1ab0c95b46413fad375471232cb81706932e1cef38781b9ebd39d5100401bacb651c6c5bbf59e571e81b3bc08d2a622004e08b1a6ece82a7e0b9857525c86d2b95fab4bc3dce148558d7f3ae61aa3a234086902aeface87d9dfdd32b9d2fe3c6dd4055b5ab4b104998bd87")] [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")] diff --git a/src/Avalonia.Base/PropertyStore/AvaloniaPropertyDictionaryPool.cs b/src/Avalonia.Base/PropertyStore/AvaloniaPropertyDictionaryPool.cs new file mode 100644 index 0000000000..5a57bc3786 --- /dev/null +++ b/src/Avalonia.Base/PropertyStore/AvaloniaPropertyDictionaryPool.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using Avalonia.Utilities; + +namespace Avalonia.PropertyStore +{ + internal static class AvaloniaPropertyDictionaryPool + { + private const int MaxPoolSize = 4; + private static readonly Stack> _pool = new(); + + public static AvaloniaPropertyDictionary Get() + { + return _pool.Count == 0 ? new() : _pool.Pop(); + } + + public static void Release(AvaloniaPropertyDictionary dictionary) + { + if (_pool.Count < MaxPoolSize) + { + dictionary.Clear(); + _pool.Push(dictionary); + } + } + } +} diff --git a/src/Avalonia.Base/PropertyStore/BindingEntry.cs b/src/Avalonia.Base/PropertyStore/BindingEntry.cs deleted file mode 100644 index 9a25e98a23..0000000000 --- a/src/Avalonia.Base/PropertyStore/BindingEntry.cs +++ /dev/null @@ -1,154 +0,0 @@ -using System; -using Avalonia.Data; -using Avalonia.Threading; - -namespace Avalonia.PropertyStore -{ - /// - /// Represents an untyped interface to . - /// - internal interface IBindingEntry : IBatchUpdate, IPriorityValueEntry, IDisposable - { - void Start(bool ignoreBatchUpdate); - } - - /// - /// Stores a binding in a or . - /// - /// The property type. - internal class BindingEntry : IBindingEntry, IPriorityValueEntry, IObserver> - { - private readonly AvaloniaObject _owner; - private ValueOwner _sink; - private IDisposable? _subscription; - private bool _isSubscribed; - private bool _batchUpdate; - private Optional _value; - - public BindingEntry( - AvaloniaObject owner, - StyledPropertyBase property, - IObservable> source, - BindingPriority priority, - ValueOwner sink) - { - _owner = owner; - Property = property; - Source = source; - Priority = priority; - _sink = sink; - } - - public StyledPropertyBase Property { get; } - public BindingPriority Priority { get; private set; } - public IObservable> Source { get; } - Optional IValue.GetValue() => _value.ToObject(); - - public void BeginBatchUpdate() => _batchUpdate = true; - - public void EndBatchUpdate() - { - _batchUpdate = false; - - if (_sink.IsValueStore) - Start(); - } - - public Optional GetValue(BindingPriority maxPriority) - { - return Priority >= maxPriority ? _value : Optional.Empty; - } - - public void Dispose() - { - _subscription?.Dispose(); - _subscription = null; - OnCompleted(); - } - - public void OnCompleted() - { - var oldValue = _value; - _value = default; - Priority = BindingPriority.Unset; - _isSubscribed = false; - _sink.Completed(Property, this, oldValue); - } - - public void OnError(Exception error) - { - throw new NotImplementedException("BindingEntry.OnError is not implemented", error); - } - - public void OnNext(BindingValue value) - { - if (Dispatcher.UIThread.CheckAccess()) - { - UpdateValue(value); - } - else - { - // To avoid allocating closure in the outer scope we need to capture variables - // locally. This allows us to skip most of the allocations when on UI thread. - var instance = this; - var newValue = value; - - Dispatcher.UIThread.Post(() => instance.UpdateValue(newValue)); - } - } - - public void Start() => Start(false); - - public void Start(bool ignoreBatchUpdate) - { - // We can't use _subscription to check whether we're subscribed because it won't be set - // until Subscribe has finished, which will be too late to prevent reentrancy. In addition - // don't re-subscribe to completed/disposed bindings (indicated by Unset priority). - if (!_isSubscribed && - Priority != BindingPriority.Unset && - (!_batchUpdate || ignoreBatchUpdate)) - { - _isSubscribed = true; - _subscription = Source.Subscribe(this); - } - } - - public void Reparent(PriorityValue parent) => _sink = new(parent); - - public void RaiseValueChanged( - AvaloniaObject owner, - AvaloniaProperty property, - Optional oldValue, - Optional newValue) - { - owner.ValueChanged(new AvaloniaPropertyChangedEventArgs( - owner, - (AvaloniaProperty)property, - oldValue.Cast(), - newValue.Cast(), - Priority)); - } - - private void UpdateValue(BindingValue value) - { - if (value.HasValue && Property.ValidateValue?.Invoke(value.Value) == false) - { - value = Property.GetDefaultValue(_owner.GetType()); - } - - if (value.Type == BindingValueType.DoNothing) - { - return; - } - - var old = _value; - - if (value.Type != BindingValueType.DataValidationError) - { - _value = value.ToOptional(); - } - - _sink.ValueChanged(new AvaloniaPropertyChangedEventArgs(_owner, Property, old, value, Priority)); - } - } -} diff --git a/src/Avalonia.Base/PropertyStore/BindingEntryBase.cs b/src/Avalonia.Base/PropertyStore/BindingEntryBase.cs new file mode 100644 index 0000000000..ef14211902 --- /dev/null +++ b/src/Avalonia.Base/PropertyStore/BindingEntryBase.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using System.Reactive.Disposables; +using Avalonia.Data; + +namespace Avalonia.PropertyStore +{ + internal abstract class BindingEntryBase : IValueEntry, + IObserver, + IObserver>, + IDisposable + { + private static readonly IDisposable s_creating = Disposable.Empty; + private static readonly IDisposable s_creatingQuiet = Disposable.Create(() => { }); + private IDisposable? _subscription; + private bool _hasValue; + private TValue? _value; + + protected BindingEntryBase( + ValueFrame frame, + AvaloniaProperty property, + IObservable> source) + { + Frame = frame; + Source = source; + Property = property; + } + + protected BindingEntryBase( + ValueFrame frame, + AvaloniaProperty property, + IObservable source) + { + Frame = frame; + Source = source; + Property = property; + } + + public bool HasValue + { + get + { + Start(produceValue: false); + return _hasValue; + } + } + + public bool IsSubscribed => _subscription is not null; + public AvaloniaProperty Property { get; } + AvaloniaProperty IValueEntry.Property => Property; + protected ValueFrame Frame { get; } + protected object Source { get; } + + public void Dispose() + { + Unsubscribe(); + BindingCompleted(); + } + + public TValue GetValue() + { + Start(produceValue: false); + if (!_hasValue) + throw new AvaloniaInternalException("The binding entry has no value."); + return _value!; + } + + public void Start() => Start(true); + + public void OnCompleted() => BindingCompleted(); + public void OnError(Exception error) => BindingCompleted(); + public void OnNext(TSource value) => SetValue(ConvertAndValidate(value)); + public void OnNext(BindingValue value) => SetValue(ConvertAndValidate(value)); + + public virtual void Unsubscribe() + { + _subscription?.Dispose(); + _subscription = null; + } + + object? IValueEntry.GetValue() + { + Start(produceValue: false); + if (!_hasValue) + throw new AvaloniaInternalException("The BindingEntry has no value."); + return _value!; + } + + protected abstract BindingValue ConvertAndValidate(TSource value); + protected abstract BindingValue ConvertAndValidate(BindingValue value); + + protected virtual void Start(bool produceValue) + { + if (_subscription is not null) + return; + + _subscription = produceValue ? s_creating : s_creatingQuiet; + _subscription = Source switch + { + IObservable> bv => bv.Subscribe(this), + IObservable b => b.Subscribe(this), + _ => throw new AvaloniaInternalException("Unexpected binding source."), + }; + } + + private void ClearValue() + { + if (_hasValue) + { + _hasValue = false; + _value = default; + if (_subscription is not null) + Frame.Owner?.OnBindingValueCleared(Property, Frame.Priority); + } + } + + private void SetValue(BindingValue value) + { + if (Frame.Owner is null) + return; + + LoggingUtils.LogIfNecessary(Frame.Owner.Owner, Property, value); + + if (value.HasValue) + { + if (!_hasValue || !EqualityComparer.Default.Equals(_value, value.Value)) + { + _value = value.Value; + _hasValue = true; + if (_subscription is not null && _subscription != s_creatingQuiet) + Frame.Owner?.OnBindingValueChanged(this, Frame.Priority); + } + } + else if (value.Type != BindingValueType.DoNothing) + { + ClearValue(); + if (_subscription is not null && _subscription != s_creatingQuiet) + Frame.Owner?.OnBindingValueCleared(Property, Frame.Priority); + } + } + + private void BindingCompleted() + { + _subscription = null; + Frame.OnBindingCompleted(this); + } + } +} diff --git a/src/Avalonia.Base/PropertyStore/ConstantValueEntry.cs b/src/Avalonia.Base/PropertyStore/ConstantValueEntry.cs deleted file mode 100644 index 4116f4abd9..0000000000 --- a/src/Avalonia.Base/PropertyStore/ConstantValueEntry.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using System.Diagnostics.CodeAnalysis; -using Avalonia.Data; - -namespace Avalonia.PropertyStore -{ - /// - /// Represents an untyped interface to . - /// - internal interface IConstantValueEntry : IPriorityValueEntry, IDisposable - { - } - - /// - /// Stores a value with a priority in a or - /// . - /// - /// The property type. - internal class ConstantValueEntry : IPriorityValueEntry, IConstantValueEntry - { - private ValueOwner _sink; - private Optional _value; - - public ConstantValueEntry( - StyledPropertyBase property, - T value, - BindingPriority priority, - ValueOwner sink) - { - Property = property; - _value = value; - Priority = priority; - _sink = sink; - } - - public ConstantValueEntry( - StyledPropertyBase property, - Optional value, - BindingPriority priority, - ValueOwner sink) - { - Property = property; - _value = value; - Priority = priority; - _sink = sink; - } - - public StyledPropertyBase Property { get; } - public BindingPriority Priority { get; private set; } - Optional IValue.GetValue() => _value.ToObject(); - - public Optional GetValue(BindingPriority maxPriority = BindingPriority.Animation) - { - return Priority >= maxPriority ? _value : Optional.Empty; - } - - public void Dispose() - { - var oldValue = _value; - _value = default; - Priority = BindingPriority.Unset; - _sink.Completed(Property, this, oldValue); - } - - public void Reparent(PriorityValue sink) => _sink = new(sink); - public void Start() { } - - public void RaiseValueChanged( - AvaloniaObject owner, - AvaloniaProperty property, - Optional oldValue, - Optional newValue) - { - owner.ValueChanged(new AvaloniaPropertyChangedEventArgs( - owner, - (AvaloniaProperty)property, - oldValue.Cast(), - newValue.Cast(), - Priority)); - } - } -} diff --git a/src/Avalonia.Base/PropertyStore/DirectBindingObserver.cs b/src/Avalonia.Base/PropertyStore/DirectBindingObserver.cs new file mode 100644 index 0000000000..cbe2435953 --- /dev/null +++ b/src/Avalonia.Base/PropertyStore/DirectBindingObserver.cs @@ -0,0 +1,76 @@ +using System; +using Avalonia.Data; +using Avalonia.Threading; + +namespace Avalonia.PropertyStore +{ + internal class DirectBindingObserver : IObserver, + IObserver>, + IDisposable + { + private readonly ValueStore _owner; + private IDisposable? _subscription; + + public DirectBindingObserver(ValueStore owner, DirectPropertyBase property) + { + _owner = owner; + Property = property; + } + + public DirectPropertyBase Property { get;} + + public void Start(IObservable source) + { + _subscription = source.Subscribe(this); + } + + public void Start(IObservable> source) + { + _subscription = source.Subscribe(this); + } + + public void Dispose() + { + _subscription?.Dispose(); + _subscription = null; + _owner.OnLocalValueBindingCompleted(Property, this); + } + + public void OnCompleted() => _owner.OnLocalValueBindingCompleted(Property, this); + public void OnError(Exception error) => OnCompleted(); + + public void OnNext(T value) + { + if (Dispatcher.UIThread.CheckAccess()) + { + _owner.Owner.SetDirectValueUnchecked(Property, value); + } + else + { + // To avoid allocating closure in the outer scope we need to capture variables + // locally. This allows us to skip most of the allocations when on UI thread. + var instance = _owner.Owner; + var property = Property; + var newValue = value; + Dispatcher.UIThread.Post(() => instance.SetDirectValueUnchecked(property, newValue)); + } + } + + public void OnNext(BindingValue value) + { + if (Dispatcher.UIThread.CheckAccess()) + { + _owner.Owner.SetDirectValueUnchecked(Property, value); + } + else + { + // To avoid allocating closure in the outer scope we need to capture variables + // locally. This allows us to skip most of the allocations when on UI thread. + var instance = _owner.Owner; + var property = Property; + var newValue = value; + Dispatcher.UIThread.Post(() => instance.SetDirectValueUnchecked(property, newValue)); + } + } + } +} diff --git a/src/Avalonia.Base/PropertyStore/DirectUntypedBindingObserver.cs b/src/Avalonia.Base/PropertyStore/DirectUntypedBindingObserver.cs new file mode 100644 index 0000000000..3d3b1a3e4d --- /dev/null +++ b/src/Avalonia.Base/PropertyStore/DirectUntypedBindingObserver.cs @@ -0,0 +1,55 @@ +using System; +using Avalonia.Data; +using Avalonia.Threading; + +namespace Avalonia.PropertyStore +{ + internal class DirectUntypedBindingObserver : IObserver, + IDisposable + { + private readonly ValueStore _owner; + private IDisposable? _subscription; + + public DirectUntypedBindingObserver(ValueStore owner, DirectPropertyBase property) + { + _owner = owner; + Property = property; + } + + public DirectPropertyBase Property { get;} + + public void Start(IObservable source) + { + _subscription = source.Subscribe(this); + } + + public void Dispose() + { + _subscription?.Dispose(); + _subscription = null; + _owner.OnLocalValueBindingCompleted(Property, this); + } + + public void OnCompleted() => _owner.OnLocalValueBindingCompleted(Property, this); + public void OnError(Exception error) => OnCompleted(); + + public void OnNext(object? value) + { + var typed = BindingValue.FromUntyped(value); + + if (Dispatcher.UIThread.CheckAccess()) + { + _owner.Owner.SetDirectValueUnchecked(Property, typed); + } + else + { + // To avoid allocating closure in the outer scope we need to capture variables + // locally. This allows us to skip most of the allocations when on UI thread. + var instance = _owner.Owner; + var property = Property; + var newValue = value; + Dispatcher.UIThread.Post(() => instance.SetDirectValueUnchecked(property, typed)); + } + } + } +} diff --git a/src/Avalonia.Base/PropertyStore/EffectiveValue.cs b/src/Avalonia.Base/PropertyStore/EffectiveValue.cs new file mode 100644 index 0000000000..04d3c805c2 --- /dev/null +++ b/src/Avalonia.Base/PropertyStore/EffectiveValue.cs @@ -0,0 +1,168 @@ +using System.Diagnostics; +using Avalonia.Data; + +namespace Avalonia.PropertyStore +{ + /// + /// Represents the active value for a property in a . + /// + /// + /// This class is an abstract base for the generic . + /// + internal abstract class EffectiveValue + { + private IValueEntry? _valueEntry; + private IValueEntry? _baseValueEntry; + + /// + /// Gets the current effective value as a boxed value. + /// + public object? Value => GetBoxedValue(); + + /// + /// Gets the priority of the current effective value. + /// + public BindingPriority Priority { get; protected set; } + + /// + /// Gets the priority of the current base value. + /// + public BindingPriority BasePriority { get; protected set; } + + /// + /// Begins a reevaluation pass on the effective value. + /// + /// + /// Determines whether any current local value should be cleared. + /// + /// + /// This method resets the and properties + /// to Unset, pending reevaluation. + /// + public void BeginReevaluation(bool clearLocalValue = false) + { + if (clearLocalValue || Priority != BindingPriority.LocalValue) + Priority = BindingPriority.Unset; + if (clearLocalValue || BasePriority != BindingPriority.LocalValue) + BasePriority = BindingPriority.Unset; + } + + /// + /// Ends a reevaluation pass on the effective value. + /// + /// + /// This method unsubscribes from any unused value entries. + /// + public void EndReevaluation() + { + if (Priority == BindingPriority.Unset) + { + _valueEntry?.Unsubscribe(); + _valueEntry = null; + } + + if (BasePriority == BindingPriority.Unset) + { + _baseValueEntry?.Unsubscribe(); + _baseValueEntry = null; + } + } + + /// + /// Sets the value and base value for a non-LocalValue priority, raising + /// where necessary. + /// + /// The associated value store. + /// The new value of the property. + /// The priority of the new value. + public abstract void SetAndRaise( + ValueStore owner, + IValueEntry value, + BindingPriority priority); + + /// + /// Raises in response to an inherited value + /// change. + /// + /// The owner object. + /// The property being changed. + /// The old value of the property. + /// The new value of the property. + public abstract void RaiseInheritedValueChanged( + AvaloniaObject owner, + AvaloniaProperty property, + EffectiveValue? oldValue, + EffectiveValue? newValue); + + /// + /// Removes the current animation value and reverts to the base value, raising + /// where necessary. + /// + /// The associated value store. + /// The property being changed. + public abstract void RemoveAnimationAndRaise(ValueStore owner, AvaloniaProperty property); + + /// + /// Coerces the property value. + /// + /// The associated value store. + /// The property to coerce. + public abstract void CoerceValue(ValueStore owner, AvaloniaProperty property); + + /// + /// Disposes the effective value, raising + /// where necessary. + /// + /// The associated value store. + /// The property being cleared. + public abstract void DisposeAndRaiseUnset(ValueStore owner, AvaloniaProperty property); + + protected abstract object? GetBoxedValue(); + + protected void UpdateValueEntry(IValueEntry? entry, BindingPriority priority) + { + Debug.Assert(priority != BindingPriority.LocalValue); + + if (priority <= BindingPriority.Animation) + { + // If we've received an animation value and the current value is a non-animation + // value, then the current entry becomes our base entry. + if (Priority > BindingPriority.LocalValue && Priority < BindingPriority.Inherited) + { + Debug.Assert(_valueEntry is not null); + _baseValueEntry = _valueEntry; + _valueEntry = null; + } + + if (_valueEntry != entry) + { + _valueEntry?.Unsubscribe(); + _valueEntry = entry; + } + } + else if (Priority <= BindingPriority.Animation) + { + // We've received a non-animation value and have an active animation value, so the + // new entry becomes our base entry. + if (_baseValueEntry != entry) + { + _baseValueEntry?.Unsubscribe(); + _baseValueEntry = entry; + } + } + else if (_valueEntry != entry) + { + // Both the current value and the new value are non-animation values, so the new + // entry replaces the existing entry. + _valueEntry?.Unsubscribe(); + _valueEntry = entry; + } + } + + protected void UnsubscribeValueEntries() + { + _valueEntry?.Unsubscribe(); + _baseValueEntry?.Unsubscribe(); + } + } +} diff --git a/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs b/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs new file mode 100644 index 0000000000..1d8f6b8408 --- /dev/null +++ b/src/Avalonia.Base/PropertyStore/EffectiveValue`1.cs @@ -0,0 +1,270 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Avalonia.Data; + +namespace Avalonia.PropertyStore +{ + /// + /// Represents the active value for a property in a . + /// + /// + /// Stores the active value in an 's + /// for a single property, when the value is not inherited or unset/default. + /// + internal sealed class EffectiveValue : EffectiveValue + { + private readonly StyledPropertyMetadata _metadata; + private T? _baseValue; + private UncommonFields? _uncommon; + + public EffectiveValue(AvaloniaObject owner, StyledPropertyBase property) + { + Priority = BindingPriority.Unset; + BasePriority = BindingPriority.Unset; + _metadata = property.GetMetadata(owner.GetType()); + + var value = _metadata.DefaultValue; + + if (property.HasCoercion && _metadata.CoerceValue is { } coerce) + { + _uncommon = new() + { + _coerce = coerce, + _uncoercedValue = value, + _uncoercedBaseValue = value, + }; + + Value = coerce(owner, value); + } + else + { + Value = value; + } + } + + /// + /// Gets the current effective value. + /// + public new T Value { get; private set; } + + public override void SetAndRaise( + ValueStore owner, + IValueEntry value, + BindingPriority priority) + { + Debug.Assert(priority != BindingPriority.LocalValue); + UpdateValueEntry(value, priority); + + SetAndRaiseCore(owner, (StyledPropertyBase)value.Property, GetValue(value), priority); + } + + public void SetLocalValueAndRaise( + ValueStore owner, + StyledPropertyBase property, + T value) + { + SetAndRaiseCore(owner, property, value, BindingPriority.LocalValue); + } + + public bool TryGetBaseValue([MaybeNullWhen(false)] out T value) + { + value = _baseValue!; + return BasePriority != BindingPriority.Unset; + } + + public override void RaiseInheritedValueChanged( + AvaloniaObject owner, + AvaloniaProperty property, + EffectiveValue? oldValue, + EffectiveValue? newValue) + { + Debug.Assert(oldValue is not null || newValue is not null); + + var p = (StyledPropertyBase)property; + var o = oldValue is not null ? ((EffectiveValue)oldValue).Value : _metadata.DefaultValue; + var n = newValue is not null ? ((EffectiveValue)newValue).Value : _metadata.DefaultValue; + var priority = newValue is not null ? BindingPriority.Inherited : BindingPriority.Unset; + + if (!EqualityComparer.Default.Equals(o, n)) + { + owner.RaisePropertyChanged(p, o, n, priority, true); + } + } + + public override void RemoveAnimationAndRaise(ValueStore owner, AvaloniaProperty property) + { + Debug.Assert(Priority != BindingPriority.Animation); + Debug.Assert(BasePriority != BindingPriority.Unset); + UpdateValueEntry(null, BindingPriority.Animation); + SetAndRaiseCore(owner, (StyledPropertyBase)property, _baseValue!, BasePriority); + } + + public override void CoerceValue(ValueStore owner, AvaloniaProperty property) + { + if (_uncommon is null) + return; + SetAndRaiseCore( + owner, + (StyledPropertyBase)property, + _uncommon._uncoercedValue!, + Priority, + _uncommon._uncoercedBaseValue!, + BasePriority); + } + + public override void DisposeAndRaiseUnset(ValueStore owner, AvaloniaProperty property) + { + UnsubscribeValueEntries(); + DisposeAndRaiseUnset(owner, (StyledPropertyBase)property); + } + + public void DisposeAndRaiseUnset(ValueStore owner, StyledPropertyBase property) + { + BindingPriority priority; + T oldValue; + + if (property.Inherits && owner.TryGetInheritedValue(property, out var i)) + { + oldValue = ((EffectiveValue)i).Value; + priority = BindingPriority.Inherited; + } + else + { + oldValue = _metadata.DefaultValue; + priority = BindingPriority.Unset; + } + + if (!EqualityComparer.Default.Equals(oldValue, Value)) + { + owner.Owner.RaisePropertyChanged(property, Value, oldValue, priority, true); + if (property.Inherits) + owner.OnInheritedEffectiveValueDisposed(property, Value); + } + } + + protected override object? GetBoxedValue() => Value; + + private static T GetValue(IValueEntry entry) + { + if (entry is IValueEntry typed) + return typed.GetValue(); + else + return (T)entry.GetValue()!; + } + + private void SetAndRaiseCore( + ValueStore owner, + StyledPropertyBase property, + T value, + BindingPriority priority) + { + Debug.Assert(priority < BindingPriority.Inherited); + + var oldValue = Value; + var valueChanged = false; + var baseValueChanged = false; + var v = value; + + if (_uncommon?._coerce is { } coerce) + v = coerce(owner.Owner, value); + + if (priority <= Priority) + { + valueChanged = !EqualityComparer.Default.Equals(Value, v); + Value = v; + Priority = priority; + if (_uncommon is not null) + _uncommon._uncoercedValue = value; + } + + if (priority <= BasePriority && priority >= BindingPriority.LocalValue) + { + baseValueChanged = !EqualityComparer.Default.Equals(_baseValue, v); + _baseValue = v; + BasePriority = priority; + if (_uncommon is not null) + _uncommon._uncoercedBaseValue = value; + } + + if (valueChanged) + { + using var notifying = PropertyNotifying.Start(owner.Owner, property); + owner.Owner.RaisePropertyChanged(property, oldValue, Value, Priority, true); + if (property.Inherits) + owner.OnInheritedEffectiveValueChanged(property, oldValue, this); + } + else if (baseValueChanged) + { + owner.Owner.RaisePropertyChanged(property, default, _baseValue!, BasePriority, false); + } + } + + private void SetAndRaiseCore( + ValueStore owner, + StyledPropertyBase property, + T value, + BindingPriority priority, + T baseValue, + BindingPriority basePriority) + { + Debug.Assert(priority < BindingPriority.Inherited); + Debug.Assert(basePriority > BindingPriority.Animation); + Debug.Assert(priority <= basePriority); + + var oldValue = Value; + var valueChanged = false; + var baseValueChanged = false; + var v = value; + var bv = baseValue; + + if (_uncommon?._coerce is { } coerce) + { + v = coerce(owner.Owner, value); + bv = coerce(owner.Owner, baseValue); + } + + if (priority != BindingPriority.Unset && !EqualityComparer.Default.Equals(Value, v)) + { + Value = v; + valueChanged = true; + if (_uncommon is not null) + _uncommon._uncoercedValue = value; + } + + if (priority != BindingPriority.Unset && + (BasePriority == BindingPriority.Unset || + !EqualityComparer.Default.Equals(_baseValue, bv))) + { + _baseValue = v; + baseValueChanged = true; + if (_uncommon is not null) + _uncommon._uncoercedValue = baseValue; + } + + Priority = priority; + BasePriority = basePriority; + + if (valueChanged) + { + using var notifying = PropertyNotifying.Start(owner.Owner, property); + owner.Owner.RaisePropertyChanged(property, oldValue, Value, Priority, true); + if (property.Inherits) + owner.OnInheritedEffectiveValueChanged(property, oldValue, this); + } + + if (baseValueChanged) + { + owner.Owner.RaisePropertyChanged(property, default, _baseValue!, BasePriority, false); + } + } + + private class UncommonFields + { + public Func? _coerce; + public T? _uncoercedValue; + public T? _uncoercedBaseValue; + } + } +} diff --git a/src/Avalonia.Base/PropertyStore/FramePriority.cs b/src/Avalonia.Base/PropertyStore/FramePriority.cs new file mode 100644 index 0000000000..950a8375f2 --- /dev/null +++ b/src/Avalonia.Base/PropertyStore/FramePriority.cs @@ -0,0 +1,36 @@ +using System.Diagnostics; +using Avalonia.Data; + +namespace Avalonia.PropertyStore +{ + internal enum FramePriority : sbyte + { + Animation, + AnimationTemplatedParentTheme, + AnimationTheme, + StyleTrigger, + StyleTriggerTemplatedParentTheme, + StyleTriggerTheme, + Template, + TemplateTemplatedParentTheme, + TemplateTheme, + Style, + StyleTemplatedParentTheme, + StyleTheme, + } + + internal static class FramePriorityExtensions + { + public static FramePriority ToFramePriority(this BindingPriority priority, FrameType type = FrameType.Style) + { + Debug.Assert(priority != BindingPriority.LocalValue); + var p = (int)(priority > 0 ? priority : priority + 1); + return (FramePriority)(p * 3 + (int)type); + } + + public static bool IsType(this FramePriority priority, FrameType type) + { + return (FrameType)((int)priority % 3) == type; + } + } +} diff --git a/src/Avalonia.Base/PropertyStore/IBatchUpdate.cs b/src/Avalonia.Base/PropertyStore/IBatchUpdate.cs deleted file mode 100644 index af4faf989c..0000000000 --- a/src/Avalonia.Base/PropertyStore/IBatchUpdate.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Avalonia.PropertyStore -{ - internal interface IBatchUpdate - { - void BeginBatchUpdate(); - void EndBatchUpdate(); - } -} diff --git a/src/Avalonia.Base/PropertyStore/IPriorityValueEntry.cs b/src/Avalonia.Base/PropertyStore/IPriorityValueEntry.cs deleted file mode 100644 index 45bbd0cda5..0000000000 --- a/src/Avalonia.Base/PropertyStore/IPriorityValueEntry.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Avalonia.PropertyStore -{ - /// - /// Represents an untyped interface to . - /// - internal interface IPriorityValueEntry : IValue - { - } - - /// - /// Represents an object that can act as an entry in a . - /// - /// The property type. - internal interface IPriorityValueEntry : IPriorityValueEntry, IValue - { - void Reparent(PriorityValue parent); - } -} diff --git a/src/Avalonia.Base/PropertyStore/IValue.cs b/src/Avalonia.Base/PropertyStore/IValue.cs deleted file mode 100644 index b493df92e6..0000000000 --- a/src/Avalonia.Base/PropertyStore/IValue.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Avalonia.Data; - -namespace Avalonia.PropertyStore -{ - /// - /// Represents an untyped interface to . - /// - internal interface IValue - { - BindingPriority Priority { get; } - Optional GetValue(); - void Start(); - void RaiseValueChanged( - AvaloniaObject owner, - AvaloniaProperty property, - Optional oldValue, - Optional newValue); - } - - /// - /// Represents an object that can act as an entry in a . - /// - /// The property type. - internal interface IValue : IValue - { - Optional GetValue(BindingPriority maxPriority = BindingPriority.Animation); - } -} diff --git a/src/Avalonia.Base/PropertyStore/IValueEntry.cs b/src/Avalonia.Base/PropertyStore/IValueEntry.cs new file mode 100644 index 0000000000..271d85f8bc --- /dev/null +++ b/src/Avalonia.Base/PropertyStore/IValueEntry.cs @@ -0,0 +1,30 @@ +using System; + +namespace Avalonia.PropertyStore +{ + /// + /// Represents an untyped value entry in a . + /// + internal interface IValueEntry + { + bool HasValue { get; } + + /// + /// Gets the property that this value applies to. + /// + AvaloniaProperty Property { get; } + + /// + /// Gets the value associated with the entry. + /// + /// + /// The entry has no value. + /// + object? GetValue(); + + /// + /// Called when the value entry is removed from the value store. + /// + void Unsubscribe(); + } +} diff --git a/src/Avalonia.Base/PropertyStore/IValueEntry`1.cs b/src/Avalonia.Base/PropertyStore/IValueEntry`1.cs new file mode 100644 index 0000000000..5b69009e79 --- /dev/null +++ b/src/Avalonia.Base/PropertyStore/IValueEntry`1.cs @@ -0,0 +1,16 @@ +namespace Avalonia.PropertyStore +{ + /// + /// Represents a typed value entry in a . + /// + internal interface IValueEntry : IValueEntry + { + /// + /// Gets the value associated with the entry. + /// + /// + /// The entry has no value. + /// + new T GetValue(); + } +} diff --git a/src/Avalonia.Base/PropertyStore/ImmediateValueEntry.cs b/src/Avalonia.Base/PropertyStore/ImmediateValueEntry.cs new file mode 100644 index 0000000000..364b4e1225 --- /dev/null +++ b/src/Avalonia.Base/PropertyStore/ImmediateValueEntry.cs @@ -0,0 +1,31 @@ +using System; + +namespace Avalonia.PropertyStore +{ + internal class ImmediateValueEntry : IValueEntry, IDisposable + { + private readonly ImmediateValueFrame _owner; + private readonly T _value; + + public ImmediateValueEntry( + ImmediateValueFrame owner, + StyledPropertyBase property, + T value) + { + _owner = owner; + _value = value; + Property = property; + } + + public StyledPropertyBase Property { get; } + public bool HasValue => true; + AvaloniaProperty IValueEntry.Property => Property; + + public void Unsubscribe() { } + + public void Dispose() => _owner.OnEntryDisposed(this); + + object? IValueEntry.GetValue() => _value; + T IValueEntry.GetValue() => _value; + } +} diff --git a/src/Avalonia.Base/PropertyStore/ImmediateValueFrame.cs b/src/Avalonia.Base/PropertyStore/ImmediateValueFrame.cs new file mode 100644 index 0000000000..50d5333b9f --- /dev/null +++ b/src/Avalonia.Base/PropertyStore/ImmediateValueFrame.cs @@ -0,0 +1,63 @@ +using System; +using Avalonia.Data; + +namespace Avalonia.PropertyStore +{ + /// + /// Holds values in a set by one of the SetValue or AddBinding + /// overloads with non-LocalValue priority. + /// + internal sealed class ImmediateValueFrame : ValueFrame + { + public ImmediateValueFrame(BindingPriority priority) + : base(priority, FrameType.Style) + { + } + + public TypedBindingEntry AddBinding( + StyledPropertyBase property, + IObservable> source) + { + var e = new TypedBindingEntry(this, property, source); + Add(e); + return e; + } + + public TypedBindingEntry AddBinding( + StyledPropertyBase property, + IObservable source) + { + var e = new TypedBindingEntry(this, property, source); + Add(e); + return e; + } + + public SourceUntypedBindingEntry AddBinding( + StyledPropertyBase property, + IObservable source) + { + var e = new SourceUntypedBindingEntry(this, property, source); + Add(e); + return e; + } + + public ImmediateValueEntry AddValue(StyledPropertyBase property, T value) + { + var e = new ImmediateValueEntry(this, property, value); + Add(e); + return e; + } + + public void OnEntryDisposed(IValueEntry value) + { + Remove(value.Property); + Owner?.OnValueEntryRemoved(this, value.Property); + } + + protected override bool GetIsActive(out bool hasChanged) + { + hasChanged = false; + return true; + } + } +} diff --git a/src/Avalonia.Base/PropertyStore/LocalValueBindingObserver.cs b/src/Avalonia.Base/PropertyStore/LocalValueBindingObserver.cs new file mode 100644 index 0000000000..4dca6c0100 --- /dev/null +++ b/src/Avalonia.Base/PropertyStore/LocalValueBindingObserver.cs @@ -0,0 +1,59 @@ +using System; +using Avalonia.Data; + +namespace Avalonia.PropertyStore +{ + internal class LocalValueBindingObserver : IObserver, + IObserver>, + IDisposable + { + private readonly ValueStore _owner; + private IDisposable? _subscription; + + public LocalValueBindingObserver(ValueStore owner, StyledPropertyBase property) + { + _owner = owner; + Property = property; + } + + public StyledPropertyBase Property { get;} + + public void Start(IObservable source) + { + _subscription = source.Subscribe(this); + } + + public void Start(IObservable> source) + { + _subscription = source.Subscribe(this); + } + + public void Dispose() + { + _subscription?.Dispose(); + _subscription = null; + _owner.OnLocalValueBindingCompleted(Property, this); + } + + public void OnCompleted() => _owner.OnLocalValueBindingCompleted(Property, this); + public void OnError(Exception error) => OnCompleted(); + + public void OnNext(T value) + { + if (Property.ValidateValue?.Invoke(value) != false) + _owner.SetValue(Property, value, BindingPriority.LocalValue); + else + _owner.ClearLocalValue(Property); + } + + public void OnNext(BindingValue value) + { + LoggingUtils.LogIfNecessary(_owner.Owner, Property, value); + + if (value.HasValue) + _owner.SetValue(Property, value.Value, BindingPriority.LocalValue); + else if (value.Type != BindingValueType.DataValidationError) + _owner.ClearLocalValue(Property); + } + } +} diff --git a/src/Avalonia.Base/PropertyStore/LocalValueEntry.cs b/src/Avalonia.Base/PropertyStore/LocalValueEntry.cs deleted file mode 100644 index 13ca69681f..0000000000 --- a/src/Avalonia.Base/PropertyStore/LocalValueEntry.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using Avalonia.Data; - -namespace Avalonia.PropertyStore -{ - /// - /// Stores a value with local value priority in a or - /// . - /// - /// The property type. - internal class LocalValueEntry : IValue - { - private T _value; - - public LocalValueEntry(T value) => _value = value; - public BindingPriority Priority => BindingPriority.LocalValue; - Optional IValue.GetValue() => new Optional(_value); - - public Optional GetValue(BindingPriority maxPriority) - { - return BindingPriority.LocalValue >= maxPriority ? _value : Optional.Empty; - } - - public void SetValue(T value) => _value = value; - public void Start() { } - - public void RaiseValueChanged( - AvaloniaObject owner, - AvaloniaProperty property, - Optional oldValue, - Optional newValue) - { - owner.ValueChanged(new AvaloniaPropertyChangedEventArgs( - owner, - (AvaloniaProperty)property, - oldValue.Cast(), - newValue.Cast(), - BindingPriority.LocalValue)); - } - } -} diff --git a/src/Avalonia.Base/PropertyStore/LocalValueUntypedBindingObserver.cs b/src/Avalonia.Base/PropertyStore/LocalValueUntypedBindingObserver.cs new file mode 100644 index 0000000000..099e997d38 --- /dev/null +++ b/src/Avalonia.Base/PropertyStore/LocalValueUntypedBindingObserver.cs @@ -0,0 +1,62 @@ +using System; +using Avalonia.Data; + +namespace Avalonia.PropertyStore +{ + internal class LocalValueUntypedBindingObserver : IObserver, + IDisposable + { + private readonly ValueStore _owner; + private IDisposable? _subscription; + + public LocalValueUntypedBindingObserver(ValueStore owner, StyledPropertyBase property) + { + _owner = owner; + Property = property; + } + + public StyledPropertyBase Property { get; } + + public void Start(IObservable source) + { + _subscription = source.Subscribe(this); + } + + public void Dispose() + { + _subscription?.Dispose(); + _subscription = null; + _owner.OnLocalValueBindingCompleted(Property, this); + } + + public void OnCompleted() => _owner.OnLocalValueBindingCompleted(Property, this); + public void OnError(Exception error) => OnCompleted(); + + public void OnNext(object? value) + { + if (value is BindingNotification n) + { + value = n.Value; + LoggingUtils.LogIfNecessary(_owner.Owner, Property, n); + } + + if (value == AvaloniaProperty.UnsetValue) + { + _owner.ClearLocalValue(Property); + } + else if (value == BindingOperations.DoNothing) + { + // Do nothing! + } + else if (UntypedValueUtils.TryConvertAndValidate(Property, value, out var typedValue)) + { + _owner.SetValue(Property, typedValue, BindingPriority.LocalValue); + } + else + { + _owner.ClearLocalValue(Property); + LoggingUtils.LogInvalidValue(_owner.Owner, Property, typeof(T), value); + } + } + } +} diff --git a/src/Avalonia.Base/PropertyStore/LoggingUtils.cs b/src/Avalonia.Base/PropertyStore/LoggingUtils.cs new file mode 100644 index 0000000000..8b3015eca9 --- /dev/null +++ b/src/Avalonia.Base/PropertyStore/LoggingUtils.cs @@ -0,0 +1,82 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using Avalonia.Data; + +namespace Avalonia.PropertyStore +{ + internal static class LoggingUtils + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void LogIfNecessary( + AvaloniaObject owner, + AvaloniaProperty property, + BindingNotification value) + { + if (value.ErrorType != BindingErrorType.None) + Log(owner, property, value.Error!); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void LogIfNecessary( + AvaloniaObject owner, + AvaloniaProperty property, + BindingValue value) + { + if (value.HasError) + Log(owner, property, value.Error!); + } + + public static void LogInvalidValue( + AvaloniaObject owner, + AvaloniaProperty property, + Type expectedType, + object? value) + { + if (value is not null) + { + owner.GetBindingWarningLogger(property, null)?.Log( + owner, + "Error in binding to {Target}.{Property}: expected {ExpectedType}, got {Value} ({ValueType})", + owner, + property, + expectedType, + value, + value.GetType()); + } + else + { + owner.GetBindingWarningLogger(property, null)?.Log( + owner, + "Error in binding to {Target}.{Property}: expected {ExpectedType}, got null", + owner, + property, + expectedType); + } + } + + private static void Log( + AvaloniaObject owner, + AvaloniaProperty property, + Exception e) + { + if (e is TargetInvocationException t) + e = t.InnerException!; + + if (e is AggregateException a) + { + foreach (var i in a.InnerExceptions) + Log(owner, property, i); + } + else + { + owner.GetBindingWarningLogger(property, e)?.Log( + owner, + "Error in binding to {Target}.{Property}: {Message}", + owner, + property, + e.Message); + } + } + } +} diff --git a/src/Avalonia.Base/PropertyStore/PriorityValue.cs b/src/Avalonia.Base/PropertyStore/PriorityValue.cs deleted file mode 100644 index 182b2638c4..0000000000 --- a/src/Avalonia.Base/PropertyStore/PriorityValue.cs +++ /dev/null @@ -1,326 +0,0 @@ -using System; -using System.Collections.Generic; -using Avalonia.Data; - -namespace Avalonia.PropertyStore -{ - /// - /// Represents an untyped interface to . - /// - interface IPriorityValue : IValue - { - void UpdateEffectiveValue(); - } - - /// - /// Stores a set of prioritized values and bindings in a . - /// - /// The property type. - /// - /// When more than a single value or binding is applied to a property in an - /// , the entry in the is converted into - /// a . This class holds any number of - /// entries (sorted first by priority and then in the order - /// they were added) plus a local value. - /// - internal class PriorityValue : IPriorityValue, IValue, IBatchUpdate - { - private readonly AvaloniaObject _owner; - private readonly ValueStore _store; - private readonly List> _entries = new List>(); - private readonly Func? _coerceValue; - private Optional _localValue; - private Optional _value; - private bool _isCalculatingValue; - private bool _batchUpdate; - - public PriorityValue( - AvaloniaObject owner, - StyledPropertyBase property, - ValueStore store) - { - _owner = owner; - Property = property; - _store = store; - - if (property.HasCoercion) - { - var metadata = property.GetMetadata(owner.GetType()); - _coerceValue = metadata.CoerceValue; - } - } - - public PriorityValue( - AvaloniaObject owner, - StyledPropertyBase property, - ValueStore store, - IPriorityValueEntry existing) - : this(owner, property, store) - { - existing.Reparent(this); - _entries.Add(existing); - - if (existing is IBindingEntry binding && - existing.Priority == BindingPriority.LocalValue) - { - // Bit of a special case here: if we have a local value binding that is being - // promoted to a priority value we need to make sure the binding is subscribed - // even if we've got a batch operation in progress because otherwise we don't know - // whether the binding or a subsequent SetValue with local priority will win. A - // notification won't be sent during batch update anyway because it will be - // caught and stored for later by the ValueStore. - binding.Start(ignoreBatchUpdate: true); - } - - var v = existing.GetValue(); - - if (v.HasValue) - { - _value = v; - Priority = existing.Priority; - } - } - - public PriorityValue( - AvaloniaObject owner, - StyledPropertyBase property, - ValueStore sink, - LocalValueEntry existing) - : this(owner, property, sink) - { - _value = _localValue = existing.GetValue(BindingPriority.LocalValue); - Priority = BindingPriority.LocalValue; - } - - public StyledPropertyBase Property { get; } - public BindingPriority Priority { get; private set; } = BindingPriority.Unset; - public IReadOnlyList> Entries => _entries; - Optional IValue.GetValue() => _value.ToObject(); - - public void BeginBatchUpdate() - { - _batchUpdate = true; - - foreach (var entry in _entries) - { - (entry as IBatchUpdate)?.BeginBatchUpdate(); - } - } - - public void EndBatchUpdate() - { - _batchUpdate = false; - - foreach (var entry in _entries) - { - (entry as IBatchUpdate)?.EndBatchUpdate(); - } - - UpdateEffectiveValue(null); - } - - public void ClearLocalValue() - { - _localValue = default; - UpdateEffectiveValue(new AvaloniaPropertyChangedEventArgs( - _owner, - Property, - default, - default, - BindingPriority.LocalValue)); - } - - public Optional GetValue(BindingPriority maxPriority = BindingPriority.Animation) - { - if (Priority == BindingPriority.Unset) - { - return default; - } - - if (Priority >= maxPriority) - { - return _value; - } - - return CalculateValue(maxPriority).Item1; - } - - public IDisposable? SetValue(T value, BindingPriority priority) - { - IDisposable? result = null; - - if (priority == BindingPriority.LocalValue) - { - _localValue = value; - } - else - { - var insert = FindInsertPoint(priority); - var entry = new ConstantValueEntry(Property, value, priority, new ValueOwner(this)); - _entries.Insert(insert, entry); - result = entry; - } - - UpdateEffectiveValue(new AvaloniaPropertyChangedEventArgs( - _owner, - Property, - default, - value, - priority)); - - return result; - } - - public BindingEntry AddBinding(IObservable> source, BindingPriority priority) - { - var binding = new BindingEntry(_owner, Property, source, priority, new(this)); - var insert = FindInsertPoint(binding.Priority); - _entries.Insert(insert, binding); - - if (_batchUpdate) - { - binding.BeginBatchUpdate(); - - if (priority == BindingPriority.LocalValue) - { - binding.Start(ignoreBatchUpdate: true); - } - } - - return binding; - } - - public void UpdateEffectiveValue() => UpdateEffectiveValue(null); - public void Start() => UpdateEffectiveValue(null); - - public void RaiseValueChanged( - AvaloniaObject owner, - AvaloniaProperty property, - Optional oldValue, - Optional newValue) - { - owner.ValueChanged(new AvaloniaPropertyChangedEventArgs( - owner, - (AvaloniaProperty)property, - oldValue.Cast(), - newValue.Cast(), - Priority)); - } - - public void ValueChanged(AvaloniaPropertyChangedEventArgs change) - { - if (change.Priority == BindingPriority.LocalValue) - { - _localValue = default; - } - - if (!_isCalculatingValue && change is AvaloniaPropertyChangedEventArgs c) - { - UpdateEffectiveValue(c); - } - } - - public void Completed(IPriorityValueEntry entry, Optional oldValue) - { - _entries.Remove((IPriorityValueEntry)entry); - UpdateEffectiveValue(new AvaloniaPropertyChangedEventArgs( - _owner, - Property, - oldValue, - default, - entry.Priority)); - } - - private int FindInsertPoint(BindingPriority priority) - { - var result = _entries.Count; - - for (var i = 0; i < _entries.Count; ++i) - { - if (_entries[i].Priority < priority) - { - result = i; - break; - } - } - - return result; - } - - public (Optional, BindingPriority) CalculateValue(BindingPriority maxPriority) - { - _isCalculatingValue = true; - - try - { - for (var i = _entries.Count - 1; i >= 0; --i) - { - var entry = _entries[i]; - - if (entry.Priority < maxPriority) - { - continue; - } - - entry.Start(); - - if (entry.Priority >= BindingPriority.LocalValue && - maxPriority <= BindingPriority.LocalValue && - _localValue.HasValue) - { - return (_localValue, BindingPriority.LocalValue); - } - - var entryValue = entry.GetValue(); - - if (entryValue.HasValue) - { - return (entryValue, entry.Priority); - } - } - - if (maxPriority <= BindingPriority.LocalValue && _localValue.HasValue) - { - return (_localValue, BindingPriority.LocalValue); - } - - return (default, BindingPriority.Unset); - } - finally - { - _isCalculatingValue = false; - } - } - - private void UpdateEffectiveValue(AvaloniaPropertyChangedEventArgs? change) - { - var (value, priority) = CalculateValue(BindingPriority.Animation); - - if (value.HasValue && _coerceValue != null) - { - value = _coerceValue(_owner, value.Value); - } - - Priority = priority; - - if (value != _value) - { - var old = _value; - _value = value; - - _store.ValueChanged(new AvaloniaPropertyChangedEventArgs( - _owner, - Property, - old, - value, - Priority)); - } - else if (change is object) - { - change.MarkNonEffectiveValue(); - change.SetOldValue(default); - _store.ValueChanged(change); - } - } - } -} diff --git a/src/Avalonia.Base/PropertyStore/PropertyNotifying.cs b/src/Avalonia.Base/PropertyStore/PropertyNotifying.cs new file mode 100644 index 0000000000..f508059a74 --- /dev/null +++ b/src/Avalonia.Base/PropertyStore/PropertyNotifying.cs @@ -0,0 +1,35 @@ +using System; +using System.Diagnostics; + +namespace Avalonia.PropertyStore +{ + /// + /// Raises where necessary. + /// + /// + /// Uses the disposable pattern to ensure that the closing Notifying call is made even in the + /// presence of exceptions. + /// + internal readonly struct PropertyNotifying : IDisposable + { + private readonly AvaloniaObject _owner; + private readonly AvaloniaProperty _property; + + private PropertyNotifying(AvaloniaObject owner, AvaloniaProperty property) + { + Debug.Assert(property.Notifying is not null); + _owner = owner; + _property = property; + _property.Notifying!(owner, true); + } + + public void Dispose() => _property.Notifying!(_owner, false); + + public static PropertyNotifying? Start(AvaloniaObject owner, AvaloniaProperty property) + { + if (property.Notifying is null) + return null; + return new PropertyNotifying(owner, property); + } + } +} diff --git a/src/Avalonia.Base/PropertyStore/SourceUntypedBindingEntry.cs b/src/Avalonia.Base/PropertyStore/SourceUntypedBindingEntry.cs new file mode 100644 index 0000000000..b4ac06d2bf --- /dev/null +++ b/src/Avalonia.Base/PropertyStore/SourceUntypedBindingEntry.cs @@ -0,0 +1,35 @@ +using System; +using Avalonia.Data; + +namespace Avalonia.PropertyStore +{ + /// + /// An that holds a binding whose source observable is untyped and + /// target property is typed. + /// + internal sealed class SourceUntypedBindingEntry : BindingEntryBase + { + private readonly Func? _validate; + + public SourceUntypedBindingEntry( + ValueFrame frame, + StyledPropertyBase property, + IObservable source) + : base(frame, property, source) + { + _validate = property.ValidateValue; + } + + public new StyledPropertyBase Property => (StyledPropertyBase)base.Property; + + protected override BindingValue ConvertAndValidate(object? value) + { + return UntypedValueUtils.ConvertAndValidate(value, Property.PropertyType, _validate); + } + + protected override BindingValue ConvertAndValidate(BindingValue value) + { + throw new NotSupportedException(); + } + } +} diff --git a/src/Avalonia.Base/PropertyStore/TypedBindingEntry.cs b/src/Avalonia.Base/PropertyStore/TypedBindingEntry.cs new file mode 100644 index 0000000000..2276991a18 --- /dev/null +++ b/src/Avalonia.Base/PropertyStore/TypedBindingEntry.cs @@ -0,0 +1,52 @@ +using System; +using Avalonia.Data; + +namespace Avalonia.PropertyStore +{ + /// + /// An that holds a binding whose source observable and target + /// property are both typed. + /// + internal sealed class TypedBindingEntry : BindingEntryBase + { + public TypedBindingEntry( + ValueFrame frame, + StyledPropertyBase property, + IObservable source) + : base(frame, property, source) + { + } + + public TypedBindingEntry( + ValueFrame frame, + StyledPropertyBase property, + IObservable> source) + : base(frame, property, source) + { + } + + public new StyledPropertyBase Property => (StyledPropertyBase)base.Property; + + protected override BindingValue ConvertAndValidate(T value) + { + if (Property.ValidateValue?.Invoke(value) == false) + { + return BindingValue.BindingError( + new InvalidCastException($"'{value}' is not a valid value.")); + } + + return value; + } + + protected override BindingValue ConvertAndValidate(BindingValue value) + { + if (value.HasValue && Property.ValidateValue?.Invoke(value.Value) == false) + { + return BindingValue.BindingError( + new InvalidCastException($"'{value.Value}' is not a valid value.")); + } + + return value; + } + } +} diff --git a/src/Avalonia.Base/PropertyStore/UntypedBindingEntry.cs b/src/Avalonia.Base/PropertyStore/UntypedBindingEntry.cs new file mode 100644 index 0000000000..f8becb2e06 --- /dev/null +++ b/src/Avalonia.Base/PropertyStore/UntypedBindingEntry.cs @@ -0,0 +1,33 @@ +using System; +using Avalonia.Data; + +namespace Avalonia.PropertyStore +{ + /// + /// An that holds a binding whose source observable and target + /// property are both untyped. + /// + internal class UntypedBindingEntry : BindingEntryBase + { + private readonly Func? _validate; + + public UntypedBindingEntry( + ValueFrame frame, + AvaloniaProperty property, + IObservable source) + : base(frame, property, source) + { + _validate = ((IStyledPropertyAccessor)property).ValidateValue; + } + + protected override BindingValue ConvertAndValidate(object? value) + { + return UntypedValueUtils.ConvertAndValidate(value, Property.PropertyType, _validate); + } + + protected override BindingValue ConvertAndValidate(BindingValue value) + { + throw new NotSupportedException(); + } + } +} diff --git a/src/Avalonia.Base/PropertyStore/UntypedValueUtils.cs b/src/Avalonia.Base/PropertyStore/UntypedValueUtils.cs new file mode 100644 index 0000000000..acaecc0d52 --- /dev/null +++ b/src/Avalonia.Base/PropertyStore/UntypedValueUtils.cs @@ -0,0 +1,55 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using Avalonia.Data; +using Avalonia.Utilities; + +namespace Avalonia.PropertyStore +{ + internal static class UntypedValueUtils + { + public static BindingValue ConvertAndValidate( + object? value, + Type targetType, + Func? validate) + { + var v = BindingValue.FromUntyped(value, targetType); + + if (v.HasValue && validate?.Invoke(v.Value) == false) + { + return BindingValue.BindingError( + new InvalidCastException($"'{v.Value}' is not a valid value.")); + } + + return v; + } + + public static bool TryConvertAndValidate( + AvaloniaProperty property, + object? value, + out object? result) + { + if (TypeUtilities.TryConvertImplicit(property.PropertyType, value, out result)) + return ((IStyledPropertyAccessor)property).ValidateValue(result); + + result = default; + return false; + } + + public static bool TryConvertAndValidate( + StyledPropertyBase property, + object? value, + [MaybeNullWhen(false)] out T result) + { + if (TypeUtilities.TryConvertImplicit(typeof(T), value, out var v)) + { + result = (T)v!; + + if (property.ValidateValue?.Invoke(result) != false) + return true; + } + + result = default; + return false; + } + } +} diff --git a/src/Avalonia.Base/PropertyStore/ValueFrame.cs b/src/Avalonia.Base/PropertyStore/ValueFrame.cs new file mode 100644 index 0000000000..7a9d1bb13a --- /dev/null +++ b/src/Avalonia.Base/PropertyStore/ValueFrame.cs @@ -0,0 +1,115 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Avalonia.Data; +using Avalonia.Utilities; + +namespace Avalonia.PropertyStore +{ + internal enum FrameType + { + Style, + TemplatedParentTheme, + Theme, + } + + internal abstract class ValueFrame + { + private List? _entries; + private AvaloniaPropertyDictionary _index; + private ValueStore? _owner; + private bool _isShared; + + protected ValueFrame(BindingPriority priority, FrameType type) + { + Priority = priority; + FramePriority = priority.ToFramePriority(type); + } + + public int EntryCount => _index.Count; + public bool IsActive => GetIsActive(out _); + public ValueStore? Owner => !_isShared ? _owner : + throw new AvaloniaInternalException("Cannot get owner for shared ValueFrame"); + public BindingPriority Priority { get; } + public FramePriority FramePriority { get; } + + public bool Contains(AvaloniaProperty property) => _index.ContainsKey(property); + + public IValueEntry GetEntry(int index) => _entries?[index] ?? _index[0]; + + public void SetOwner(ValueStore? owner) + { + if (_owner is not null && owner is not null) + throw new AvaloniaInternalException("ValueFrame already has an owner."); + if (!_isShared) + _owner = owner; + } + + public bool TryGetEntryIfActive( + AvaloniaProperty property, + [NotNullWhen(true)] out IValueEntry? entry, + out bool activeChanged) + { + if (_index.TryGetValue(property, out entry)) + return GetIsActive(out activeChanged); + activeChanged = false; + return false; + } + + public void OnBindingCompleted(IValueEntry binding) + { + var property = binding.Property; + Remove(property); + Owner?.OnValueEntryRemoved(this, property); + } + + public virtual void Dispose() + { + for (var i = 0; i < _index.Count; ++i) + _index[i].Unsubscribe(); + } + + protected abstract bool GetIsActive(out bool hasChanged); + + protected void MakeShared() + { + _isShared = true; + _owner = null; + } + + protected void Add(IValueEntry value) + { + Debug.Assert(!value.Property.IsDirect); + + if (_entries is null && _index.Count == 1) + { + _entries = new(); + _entries.Add(_index[0]); + } + + _index.Add(value.Property, value); + _entries?.Add(value); + } + + protected void Remove(AvaloniaProperty property) + { + Debug.Assert(!property.IsDirect); + + if (_entries is not null) + { + var count = _entries.Count; + + for (var i = 0; i < count; ++i) + { + if (_entries[i].Property == property) + { + _entries.RemoveAt(i); + break; + } + } + } + + _index.Remove(property); + } + } +} diff --git a/src/Avalonia.Base/PropertyStore/ValueOwner.cs b/src/Avalonia.Base/PropertyStore/ValueOwner.cs deleted file mode 100644 index c68435f7a5..0000000000 --- a/src/Avalonia.Base/PropertyStore/ValueOwner.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Avalonia.Data; - -namespace Avalonia.PropertyStore -{ - /// - /// Represents a union type of and , - /// which are the valid owners of a value store . - /// - /// The value type. - internal readonly struct ValueOwner - { - private readonly ValueStore? _store; - private readonly PriorityValue? _priorityValue; - - public ValueOwner(ValueStore o) - { - _store = o; - _priorityValue = null; - } - - public ValueOwner(PriorityValue v) - { - _store = null; - _priorityValue = v; - } - - public bool IsValueStore => _store is not null; - - public void Completed(StyledPropertyBase property, IPriorityValueEntry entry, Optional oldValue) - { - if (_store is not null) - _store?.Completed(property, entry, oldValue); - else - _priorityValue!.Completed(entry, oldValue); - } - - public void ValueChanged(AvaloniaPropertyChangedEventArgs e) - { - if (_store is not null) - _store?.ValueChanged(e); - else - _priorityValue!.ValueChanged(e); - } - } -} diff --git a/src/Avalonia.Base/PropertyStore/ValueStore.cs b/src/Avalonia.Base/PropertyStore/ValueStore.cs new file mode 100644 index 0000000000..92e5288255 --- /dev/null +++ b/src/Avalonia.Base/PropertyStore/ValueStore.cs @@ -0,0 +1,1010 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Avalonia.Data; +using Avalonia.Diagnostics; +using Avalonia.Styling; +using Avalonia.Utilities; +using static Avalonia.Rendering.Composition.Animations.PropertySetSnapshot; + +namespace Avalonia.PropertyStore +{ + internal class ValueStore + { + private readonly List _frames = new(); + private Dictionary? _localValueBindings; + private AvaloniaPropertyDictionary _effectiveValues; + private int _inheritedValueCount; + private int _isEvaluating; + private int _frameGeneration; + private int _styling; + + public ValueStore(AvaloniaObject owner) => Owner = owner; + + public AvaloniaObject Owner { get; } + public ValueStore? InheritanceAncestor { get; private set; } + public bool IsEvaluating => _isEvaluating > 0; + public IReadOnlyList Frames => _frames; + + public void BeginStyling() => ++_styling; + + public void EndStyling() + { + if (--_styling == 0) + ReevaluateEffectiveValues(); + } + + public void AddFrame(ValueFrame style) + { + InsertFrame(style); + ReevaluateEffectiveValues(); + } + + public IDisposable AddBinding( + StyledPropertyBase property, + IObservable> source, + BindingPriority priority) + { + if (priority == BindingPriority.LocalValue) + { + var observer = new LocalValueBindingObserver(this, property); + DisposeExistingLocalValueBinding(property); + _localValueBindings ??= new(); + _localValueBindings[property.Id] = observer; + observer.Start(source); + return observer; + } + else + { + var effective = GetEffectiveValue(property); + + var frame = GetOrCreateImmediateValueFrame(property, priority, out _); + var result = frame.AddBinding(property, source); + + if (effective is null || priority <= effective.Priority) + result.Start(); + + return result; + } + } + + public IDisposable AddBinding( + StyledPropertyBase property, + IObservable source, + BindingPriority priority) + { + if (priority == BindingPriority.LocalValue) + { + var observer = new LocalValueBindingObserver(this, property); + DisposeExistingLocalValueBinding(property); + _localValueBindings ??= new(); + _localValueBindings[property.Id] = observer; + observer.Start(source); + return observer; + } + else + { + var effective = GetEffectiveValue(property); + + var frame = GetOrCreateImmediateValueFrame(property, priority, out _); + var result = frame.AddBinding(property, source); + + if (effective is null || priority <= effective.Priority) + result.Start(); + + return result; + } + } + + public IDisposable AddBinding( + StyledPropertyBase property, + IObservable source, + BindingPriority priority) + { + if (priority == BindingPriority.LocalValue) + { + var observer = new LocalValueUntypedBindingObserver(this, property); + DisposeExistingLocalValueBinding(property); + _localValueBindings ??= new(); + _localValueBindings[property.Id] = observer; + observer.Start(source); + return observer; + } + else + { + var effective = GetEffectiveValue(property); + + var frame = GetOrCreateImmediateValueFrame(property, priority, out _); + var result = frame.AddBinding(property, source); + + if (effective is null || priority <= effective.Priority) + result.Start(); + + return result; + } + } + + public IDisposable AddBinding(DirectPropertyBase property, IObservable> source) + { + var observer = new DirectBindingObserver(this, property); + DisposeExistingLocalValueBinding(property); + _localValueBindings ??= new(); + _localValueBindings[property.Id] = observer; + observer.Start(source); + return observer; + } + + public IDisposable AddBinding(DirectPropertyBase property, IObservable source) + { + var observer = new DirectBindingObserver(this, property); + DisposeExistingLocalValueBinding(property); + _localValueBindings ??= new(); + _localValueBindings[property.Id] = observer; + observer.Start(source); + return observer; + } + + public IDisposable AddBinding(DirectPropertyBase property, IObservable source) + { + var observer = new DirectUntypedBindingObserver(this, property); + DisposeExistingLocalValueBinding(property); + _localValueBindings ??= new(); + _localValueBindings[property.Id] = observer; + observer.Start(source); + return observer; + } + + public void ClearLocalValue(AvaloniaProperty property) + { + if (TryGetEffectiveValue(property, out var effective) && + effective.Priority == BindingPriority.LocalValue) + { + ReevaluateEffectiveValue(property, effective, ignoreLocalValue: true); + } + } + + public IDisposable? SetValue(StyledPropertyBase property, T value, BindingPriority priority) + { + if (property.ValidateValue?.Invoke(value) == false) + { + throw new ArgumentException($"{value} is not a valid value for '{property.Name}."); + } + + if (priority != BindingPriority.LocalValue) + { + var frame = GetOrCreateImmediateValueFrame(property, priority, out _); + var result = frame.AddValue(property, value); + + if (TryGetEffectiveValue(property, out var existing)) + { + var effective = (EffectiveValue)existing; + effective.SetAndRaise(this, result, priority); + } + else + { + var effectiveValue = new EffectiveValue(Owner, property); + AddEffectiveValue(property, effectiveValue); + effectiveValue.SetAndRaise(this, result, priority); + } + + return result; + } + else + { + if (TryGetEffectiveValue(property, out var existing)) + { + var effective = (EffectiveValue)existing; + effective.SetLocalValueAndRaise(this, property, value); + } + else + { + var effectiveValue = new EffectiveValue(Owner, property); + AddEffectiveValue(property, effectiveValue); + effectiveValue.SetLocalValueAndRaise(this, property, value); + } + + return null; + } + } + + public object? GetValue(AvaloniaProperty property) + { + if (_effectiveValues.TryGetValue(property, out var v)) + return v.Value; + if (property.Inherits && TryGetInheritedValue(property, out v)) + return v.Value; + + return GetDefaultValue(property); + } + + public T GetValue(StyledPropertyBase property) + { + if (_effectiveValues.TryGetValue(property, out var v)) + return ((EffectiveValue)v).Value; + if (property.Inherits && TryGetInheritedValue(property, out v)) + return ((EffectiveValue)v).Value; + return property.GetDefaultValue(Owner.GetType()); + } + + public bool IsAnimating(AvaloniaProperty property) + { + if (_effectiveValues.TryGetValue(property, out var v)) + return v.Priority <= BindingPriority.Animation; + return false; + } + + public bool IsSet(AvaloniaProperty property) + { + if (_effectiveValues.TryGetValue(property, out var v)) + return v.Priority < BindingPriority.Inherited; + return false; + } + + public void CoerceValue(AvaloniaProperty property) + { + if (_effectiveValues.TryGetValue(property, out var v)) + v.CoerceValue(this, property); + } + + public Optional GetBaseValue(StyledPropertyBase property) + { + if (TryGetEffectiveValue(property, out var v) && + ((EffectiveValue)v).TryGetBaseValue(out var baseValue)) + { + return baseValue; + } + + return default; + } + + public bool TryGetInheritedValue( + AvaloniaProperty property, + [NotNullWhen(true)] out EffectiveValue? result) + { + Debug.Assert(property.Inherits); + + var i = InheritanceAncestor; + + while (i is not null) + { + if (i.TryGetEffectiveValue(property, out result)) + return true; + i = i.InheritanceAncestor; + } + + result = null; + return false; + } + + public void SetInheritanceParent(AvaloniaObject? newParent) + { + var values = AvaloniaPropertyDictionaryPool.Get(); + var oldAncestor = InheritanceAncestor; + var newAncestor = newParent?.GetValueStore(); + + if (newAncestor?._inheritedValueCount == 0) + newAncestor = newAncestor.InheritanceAncestor; + + // The old and new inheritance ancestors are the same, nothing to do here. + if (oldAncestor == newAncestor) + return; + + // First get the old values from the old inheritance ancestor. + var f = oldAncestor; + + while (f is not null) + { + var count = f._effectiveValues.Count; + + for (var i = 0; i < count; ++i) + { + f._effectiveValues.GetKeyValue(i, out var key, out var value); + if (key.Inherits) + values.TryAdd(key, new(value)); + } + + f = f.InheritanceAncestor; + } + + f = newAncestor; + + // Get the new values from the new inheritance ancestor. + while (f is not null) + { + var count = f._effectiveValues.Count; + + for (var i = 0; i < count; ++i) + { + f._effectiveValues.GetKeyValue(i, out var key, out var value); + + if (!key.Inherits) + continue; + + if (values.TryGetValue(key, out var existing)) + { + if (existing.NewValue is null) + values[key] = existing.WithNewValue(value); + } + else + { + values.Add(key, new(null, value)); + } + } + + f = f.InheritanceAncestor; + } + + OnInheritanceAncestorChanged(newAncestor); + + // Raise PropertyChanged events where necessary on this object and inheritance children. + { + var count = values.Count; + for (var i = 0; i < count; ++i) + { + values.GetKeyValue(i, out var key, out var v); + var oldValue = v.OldValue; + var newValue = v.NewValue; + + if (oldValue != newValue) + InheritedValueChanged(key, oldValue, newValue); + } + } + + AvaloniaPropertyDictionaryPool.Release(values); + } + + /// + /// Called by non-LocalValue binding entries to re-evaluate the effective value when the + /// binding produces a new value. + /// + /// The binding entry. + /// The priority of binding which produced a new value. + public void OnBindingValueChanged( + IValueEntry entry, + BindingPriority priority) + { + Debug.Assert(priority != BindingPriority.LocalValue); + + var property = entry.Property; + + if (TryGetEffectiveValue(property, out var existing)) + { + if (priority <= existing.BasePriority) + ReevaluateEffectiveValue(property, existing); + } + else + { + AddEffectiveValueAndRaise(property, entry, priority); + } + } + + /// + /// Called by non-LocalValue binding entries to re-evaluate the effective value when the + /// binding produces an unset value. + /// + /// The bound property. + /// The priority of binding which produced a new value. + public void OnBindingValueCleared(AvaloniaProperty property, BindingPriority priority) + { + Debug.Assert(priority != BindingPriority.LocalValue); + + if (TryGetEffectiveValue(property, out var existing)) + { + if (priority <= existing.Priority) + ReevaluateEffectiveValue(property, existing); + } + } + + /// + /// Called by a when its + /// state changes. + /// + /// The frame which produced the change. + public void OnFrameActivationChanged(ValueFrame frame) + { + if (frame.EntryCount == 0) + return; + else if (frame.EntryCount == 1) + { + var property = frame.GetEntry(0).Property; + _effectiveValues.TryGetValue(property, out var current); + ReevaluateEffectiveValue(property, current); + } + else + ReevaluateEffectiveValues(); + } + + /// + /// Called by the parent value store when its inheritance ancestor changes. + /// + /// The new inheritance ancestor. + public void OnInheritanceAncestorChanged(ValueStore? ancestor) + { + if (ancestor != this) + { + InheritanceAncestor = ancestor; + if (_inheritedValueCount > 0) + return; + } + + var children = Owner.GetInheritanceChildren(); + + if (children is null) + return; + + var count = children.Count; + + for (var i = 0; i < count; ++i) + { + children[i].GetValueStore().OnInheritanceAncestorChanged(ancestor); + } + } + + /// + /// Called by when an property with inheritance enabled + /// changes its value on this value store. + /// + /// The property whose value changed. + /// The old value of the property. + /// The effective value instance. + public void OnInheritedEffectiveValueChanged( + StyledPropertyBase property, + T oldValue, + EffectiveValue value) + { + Debug.Assert(property.Inherits); + + var children = Owner.GetInheritanceChildren(); + + if (children is null) + return; + + var count = children.Count; + + for (var i = 0; i < count; ++i) + { + children[i].GetValueStore().OnAncestorInheritedValueChanged(property, oldValue, value.Value); + } + } + + /// + /// Called by when an property with inheritance enabled + /// is removed from the effective values. + /// + /// The property whose value changed. + /// The old value of the property. + public void OnInheritedEffectiveValueDisposed(StyledPropertyBase property, T oldValue) + { + Debug.Assert(property.Inherits); + + var children = Owner.GetInheritanceChildren(); + + if (children is not null) + { + var defaultValue = property.GetDefaultValue(Owner.GetType()); + var count = children.Count; + + for (var i = 0; i < count; ++i) + { + children[i].GetValueStore().OnAncestorInheritedValueChanged(property, oldValue, defaultValue); + } + } + } + + /// + /// Called when a or + /// completes. + /// + /// The previously bound property. + /// The observer. + public void OnLocalValueBindingCompleted(AvaloniaProperty property, IDisposable observer) + { + if (_localValueBindings is not null && + _localValueBindings.TryGetValue(property.Id, out var existing)) + { + if (existing == observer) + { + _localValueBindings?.Remove(property.Id); + ClearLocalValue(property); + } + } + } + + /// + /// Called when an inherited property changes on the value store of the inheritance ancestor. + /// + /// The property type. + /// The property. + /// The old value of the property. + /// The new value of the property. + public void OnAncestorInheritedValueChanged( + StyledPropertyBase property, + T oldValue, + T newValue) + { + Debug.Assert(property.Inherits); + + // If the inherited value is set locally, propagation stops here. + if (_effectiveValues.ContainsKey(property)) + return; + + using var notifying = PropertyNotifying.Start(Owner, property); + + Owner.RaisePropertyChanged( + property, + oldValue, + newValue, + BindingPriority.Inherited, + true); + + var children = Owner.GetInheritanceChildren(); + + if (children is null) + return; + + var count = children.Count; + + for (var i = 0; i < count; ++i) + { + children[i].GetValueStore().OnAncestorInheritedValueChanged(property, oldValue, newValue); + } + } + + /// + /// Called by a to re-evaluate the effective value when a value + /// is removed. + /// + /// The frame on which the change occurred. + /// The property whose value was removed. + public void OnValueEntryRemoved(ValueFrame frame, AvaloniaProperty property) + { + if (frame.EntryCount == 0) + _frames.Remove(frame); + + if (TryGetEffectiveValue(property, out var existing)) + { + if (frame.Priority <= existing.Priority) + ReevaluateEffectiveValue(property, existing); + } + } + + public bool RemoveFrame(ValueFrame frame) + { + if (_frames.Remove(frame)) + { + frame.Dispose(); + ++_frameGeneration; + ReevaluateEffectiveValues(); + } + + return false; + } + + public void RemoveFrames(FrameType type) + { + var removed = false; + + for (var i = _frames.Count - 1; i >= 0; --i) + { + var frame = _frames[i]; + + if (frame is not ImmediateValueFrame && frame.FramePriority.IsType(type)) + { + _frames.RemoveAt(i); + frame.Dispose(); + removed = true; + } + } + + if (removed) + { + ++_frameGeneration; + ReevaluateEffectiveValues(); + } + } + + + public void RemoveFrames(IReadOnlyList styles) + { + var removed = false; + + for (var i = _frames.Count - 1; i >= 0; --i) + { + var frame = _frames[i]; + + if (frame is StyleInstance style && styles.Contains(style.Source)) + { + _frames.RemoveAt(i); + frame.Dispose(); + removed = true; + } + } + + if (removed) + { + ++_frameGeneration; + ReevaluateEffectiveValues(); + } + } + + public AvaloniaPropertyValue GetDiagnostic(AvaloniaProperty property) + { + object? value; + BindingPriority priority; + + if (_effectiveValues.TryGetValue(property, out var v)) + { + value = v.Value; + priority = v.Priority; + } + else if (property.Inherits && TryGetInheritedValue(property, out v)) + { + value = v.Value; + priority = BindingPriority.Inherited; + } + else + { + value = GetDefaultValue(property); + priority = BindingPriority.Unset; + } + + return new AvaloniaPropertyValue( + property, + value, + priority, + null); + } + + private int InsertFrame(ValueFrame frame) + { + Debug.Assert(!_frames.Contains(frame)); + + var index = BinarySearchFrame(frame.FramePriority); + _frames.Insert(index, frame); + ++_frameGeneration; + frame.SetOwner(this); + return index; + } + + private ImmediateValueFrame GetOrCreateImmediateValueFrame( + AvaloniaProperty property, + BindingPriority priority, + out int frameIndex) + { + Debug.Assert(priority != BindingPriority.LocalValue); + + var index = BinarySearchFrame(priority.ToFramePriority()); + + if (index > 0 && _frames[index - 1] is ImmediateValueFrame f && + f.Priority == priority && + !f.Contains(property)) + { + frameIndex = index - 1; + return f; + } + + var result = new ImmediateValueFrame(priority); + frameIndex = InsertFrame(result); + return result; + } + + private void AddEffectiveValue(AvaloniaProperty property, EffectiveValue effectiveValue) + { + _effectiveValues.Add(property, effectiveValue); + + if (property.Inherits && _inheritedValueCount++ == 0) + OnInheritanceAncestorChanged(this); + } + + /// + /// Adds a new effective value, raises the initial + /// event and notifies inheritance children if necessary . + /// + /// The property. + /// The value entry. + /// The value priority. + private void AddEffectiveValueAndRaise(AvaloniaProperty property, IValueEntry entry, BindingPriority priority) + { + Debug.Assert(priority < BindingPriority.Inherited); + var effectiveValue = property.CreateEffectiveValue(Owner); + AddEffectiveValue(property, effectiveValue); + effectiveValue.SetAndRaise(this, entry, priority); + } + + private void RemoveEffectiveValue(AvaloniaProperty property, int index) + { + _effectiveValues.RemoveAt(index); + if (property.Inherits && --_inheritedValueCount == 0) + OnInheritanceAncestorChanged(InheritanceAncestor); + } + + private bool RemoveEffectiveValue(AvaloniaProperty property) + { + if (_effectiveValues.Remove(property)) + { + if (property.Inherits && --_inheritedValueCount == 0) + OnInheritanceAncestorChanged(InheritanceAncestor); + return true; + } + + return false; + } + + private void InheritedValueChanged( + AvaloniaProperty property, + EffectiveValue? oldValue, + EffectiveValue? newValue) + { + Debug.Assert(oldValue != newValue); + Debug.Assert(oldValue is not null || newValue is not null); + + // If the value is set locally, propagaton ends here. + if (_effectiveValues.ContainsKey(property) == true) + return; + + using var notifying = PropertyNotifying.Start(Owner, property); + + // Raise PropertyChanged on this object if necessary. + (oldValue ?? newValue!).RaiseInheritedValueChanged(Owner, property, oldValue, newValue); + + var children = Owner.GetInheritanceChildren(); + + if (children is null) + return; + + var count = children.Count; + + for (var i = 0; i < count; ++i) + { + children[i].GetValueStore().InheritedValueChanged(property, oldValue, newValue); + } + } + + private void ReevaluateEffectiveValue( + AvaloniaProperty property, + EffectiveValue? current, + bool ignoreLocalValue = false) + { + ++_isEvaluating; + + try + { + restart: + // Don't reevaluate if a styling pass is in effect, reevaluation will be done when + // it has finished. + if (_styling > 0) + return; + + var generation = _frameGeneration; + + // Notify the existing effective value that reevaluation is starting. + current?.BeginReevaluation(ignoreLocalValue); + + // Iterate the frames to get the effective value. + for (var i = _frames.Count - 1; i >= 0; --i) + { + var frame = _frames[i]; + var priority = frame.Priority; + var foundEntry = frame.TryGetEntryIfActive(property, out var entry, out var activeChanged); + + // If the active state of the frame has changed since the last read, and + // the frame holds multiple values then we need to re-evaluate the + // effective values of all properties. + if (activeChanged && frame.EntryCount > 1) + { + ReevaluateEffectiveValues(); + return; + } + + // We're interested in the value if: + // - There is no current effective value, or + // - The value's priority is higher than the current effective value's priority, or + // - The value is a non-animation value and its priority is higher than the current + // effective value's base priority + var isRelevantPriority = current is null || + priority < current.Priority || + (priority > BindingPriority.Animation && priority < current.BasePriority); + + if (foundEntry && isRelevantPriority && entry!.HasValue) + { + if (current is not null) + { + current.SetAndRaise(this, entry, priority); + } + else + { + current = property.CreateEffectiveValue(Owner); + AddEffectiveValue(property, current); + current.SetAndRaise(this, entry, priority); + } + } + + if (generation != _frameGeneration) + goto restart; + + if (current?.Priority < BindingPriority.Unset && + current?.BasePriority < BindingPriority.Unset) + break; + } + + current?.EndReevaluation(); + + if (current?.Priority == BindingPriority.Unset) + { + if (current.BasePriority == BindingPriority.Unset) + { + RemoveEffectiveValue(property); + current.DisposeAndRaiseUnset(this, property); + } + else + { + current.RemoveAnimationAndRaise(this, property); + } + } + } + finally + { + --_isEvaluating; + } + } + + private void ReevaluateEffectiveValues() + { + ++_isEvaluating; + + try + { + restart: + // Don't reevaluate if a styling pass is in effect, reevaluation will be done when + // it has finished. + if (_styling > 0) + return; + + var generation = _frameGeneration; + var count = _effectiveValues.Count; + + // Notify the existing effective values that reevaluation is starting. + for (var i = 0; i < count; ++i) + _effectiveValues[i].BeginReevaluation(); + + // Iterate the frames, setting and creating effective values. + for (var i = _frames.Count - 1; i >= 0; --i) + { + var frame = _frames[i]; + + if (!frame.IsActive) + continue; + + var priority = frame.Priority; + + count = frame.EntryCount; + + for (var j = 0; j < count; ++j) + { + var entry = frame.GetEntry(j); + var property = entry.Property; + + // Skip if we already have a value/base value for this property. + if (_effectiveValues.TryGetValue(property, out var effectiveValue) && + effectiveValue.BasePriority < BindingPriority.Unset) + continue; + + if (!entry.HasValue) + continue; + + if (effectiveValue is not null) + { + effectiveValue.SetAndRaise(this, entry, priority); + } + else + { + var v = property.CreateEffectiveValue(Owner); + AddEffectiveValue(property, v); + v.SetAndRaise(this, entry, priority); + } + + if (generation != _frameGeneration) + goto restart; + } + } + + // Remove all effective values that are still unset. + for (var i = _effectiveValues.Count - 1; i >= 0; --i) + { + _effectiveValues.GetKeyValue(i, out var key, out var e); + e.EndReevaluation(); + + if (e.Priority == BindingPriority.Unset) + { + RemoveEffectiveValue(key, i); + e.DisposeAndRaiseUnset(this, key); + + if (i > _effectiveValues.Count) + break; + } + } + } + finally + { + --_isEvaluating; + } + } + + private bool TryGetEffectiveValue( + AvaloniaProperty property, + [NotNullWhen(true)] out EffectiveValue? value) + { + if (_effectiveValues.TryGetValue(property, out value)) + return true; + value = null; + return false; + } + + private EffectiveValue? GetEffectiveValue(AvaloniaProperty property) + { + if (_effectiveValues.TryGetValue(property, out var value)) + return value; + return null; + } + + private object? GetDefaultValue(AvaloniaProperty property) + { + return ((IStyledPropertyAccessor)property).GetDefaultValue(Owner.GetType()); + } + + private void DisposeExistingLocalValueBinding(AvaloniaProperty property) + { + if (_localValueBindings is not null && + _localValueBindings.TryGetValue(property.Id, out var existing)) + { + existing.Dispose(); + } + } + + private int BinarySearchFrame(FramePriority priority) + { + var lo = 0; + var hi = _frames.Count - 1; + + // Binary search insertion point. + while (lo <= hi) + { + var i = lo + ((hi - lo) >> 1); + var order = priority - _frames[i].FramePriority; + + if (order <= 0) + { + lo = i + 1; + } + else + { + hi = i - 1; + } + } + + return lo; + } + + private readonly struct OldNewValue + { + public OldNewValue(EffectiveValue? oldValue) + { + OldValue = oldValue; + NewValue = null; + } + + public OldNewValue(EffectiveValue? oldValue, EffectiveValue? newValue) + { + OldValue = oldValue; + NewValue = newValue; + } + + public readonly EffectiveValue? OldValue; + public readonly EffectiveValue? NewValue; + + public OldNewValue WithNewValue(EffectiveValue newValue) => new(OldValue, newValue); + } + } +} diff --git a/src/Avalonia.Base/Reactive/BindingValueAdapter.cs b/src/Avalonia.Base/Reactive/BindingValueAdapter.cs deleted file mode 100644 index 8eaf21dcdc..0000000000 --- a/src/Avalonia.Base/Reactive/BindingValueAdapter.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Reactive.Subjects; -using Avalonia.Data; - -namespace Avalonia.Reactive -{ - internal class BindingValueAdapter : SingleSubscriberObservableBase>, - IObserver - { - private readonly IObservable _source; - private IDisposable? _subscription; - - public BindingValueAdapter(IObservable source) => _source = source; - public void OnCompleted() => PublishCompleted(); - public void OnError(Exception error) => PublishError(error); - public void OnNext(T value) => PublishNext(BindingValue.FromUntyped(value)); - protected override void Subscribed() => _subscription = _source.Subscribe(this); - protected override void Unsubscribed() => _subscription?.Dispose(); - } - - internal class BindingValueSubjectAdapter : SingleSubscriberObservableBase>, - ISubject> - { - private readonly ISubject _source; - private readonly Inner _inner; - private IDisposable? _subscription; - - public BindingValueSubjectAdapter(ISubject source) - { - _source = source; - _inner = new Inner(this); - } - - public void OnCompleted() => _source.OnCompleted(); - public void OnError(Exception error) => _source.OnError(error); - - public void OnNext(BindingValue value) - { - if (value.HasValue) - { - _source.OnNext(value.Value); - } - } - - protected override void Subscribed() => _subscription = _source.Subscribe(_inner); - protected override void Unsubscribed() => _subscription?.Dispose(); - - private class Inner : IObserver - { - private readonly BindingValueSubjectAdapter _owner; - - public Inner(BindingValueSubjectAdapter owner) => _owner = owner; - - public void OnCompleted() => _owner.PublishCompleted(); - public void OnError(Exception error) => _owner.PublishError(error); - public void OnNext(T value) => _owner.PublishNext(BindingValue.FromUntyped(value)); - } - } -} diff --git a/src/Avalonia.Base/Reactive/BindingValueExtensions.cs b/src/Avalonia.Base/Reactive/BindingValueExtensions.cs deleted file mode 100644 index 770da2cab4..0000000000 --- a/src/Avalonia.Base/Reactive/BindingValueExtensions.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Reactive.Subjects; -using Avalonia.Data; - -namespace Avalonia.Reactive -{ - public static class BindingValueExtensions - { - public static IObservable> ToBindingValue(this IObservable source) - { - source = source ?? throw new ArgumentNullException(nameof(source)); - return new BindingValueAdapter(source); - } - - public static ISubject> ToBindingValue(this ISubject source) - { - source = source ?? throw new ArgumentNullException(nameof(source)); - return new BindingValueSubjectAdapter(source); - } - - public static IObservable ToUntyped(this IObservable> source) - { - source = source ?? throw new ArgumentNullException(nameof(source)); - return new UntypedBindingAdapter(source); - } - - public static ISubject ToUntyped(this ISubject> source) - { - source = source ?? throw new ArgumentNullException(nameof(source)); - return new UntypedBindingSubjectAdapter(source); - } - } -} diff --git a/src/Avalonia.Base/Reactive/SingleSubscriberObservableBase.cs b/src/Avalonia.Base/Reactive/SingleSubscriberObservableBase.cs index 5e3a6bf79a..53a0b43c63 100644 --- a/src/Avalonia.Base/Reactive/SingleSubscriberObservableBase.cs +++ b/src/Avalonia.Base/Reactive/SingleSubscriberObservableBase.cs @@ -51,10 +51,11 @@ namespace Avalonia.Reactive protected void PublishCompleted() { + _completed = true; + if (_observer != null) { _observer.OnCompleted(); - _completed = true; Unsubscribed(); _observer = null; } @@ -62,10 +63,11 @@ namespace Avalonia.Reactive protected void PublishError(Exception error) { + _error = error; + if (_observer != null) { _observer.OnError(error); - _error = error; Unsubscribed(); _observer = null; } diff --git a/src/Avalonia.Base/Reactive/TypedBindingAdapter.cs b/src/Avalonia.Base/Reactive/TypedBindingAdapter.cs deleted file mode 100644 index f75917a00e..0000000000 --- a/src/Avalonia.Base/Reactive/TypedBindingAdapter.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using Avalonia.Data; -using Avalonia.Logging; - -namespace Avalonia.Reactive -{ - internal class TypedBindingAdapter : SingleSubscriberObservableBase>, - IObserver> - { - private readonly IAvaloniaObject _target; - private readonly AvaloniaProperty _property; - private readonly IObservable> _source; - private IDisposable? _subscription; - - public TypedBindingAdapter( - IAvaloniaObject target, - AvaloniaProperty property, - IObservable> source) - { - _target = target; - _property = property; - _source = source; - } - - public void OnNext(BindingValue value) - { - try - { - PublishNext(value.Convert()); - } - catch (InvalidCastException e) - { - var unwrappedValue = value.HasValue ? value.Value : null; - - Logger.TryGet(LogEventLevel.Error, LogArea.Binding)?.Log( - _target, - "Binding produced invalid value for {$Property} ({$PropertyType}): {$Value} ({$ValueType})", - _property.Name, - _property.PropertyType, - unwrappedValue, - unwrappedValue?.GetType()); - PublishNext(BindingValue.BindingError(e)); - } - } - - public void OnCompleted() => PublishCompleted(); - public void OnError(Exception error) => PublishError(error); - - public static IObservable> Create( - IAvaloniaObject target, - AvaloniaProperty property, - IObservable> source) - { - return source is IObservable> result ? - result : - new TypedBindingAdapter(target, property, source); - } - - protected override void Subscribed() => _subscription = _source.Subscribe(this); - protected override void Unsubscribed() => _subscription?.Dispose(); - } -} diff --git a/src/Avalonia.Base/Reactive/UntypedBindingAdapter.cs b/src/Avalonia.Base/Reactive/UntypedBindingAdapter.cs deleted file mode 100644 index 58fbee7b8f..0000000000 --- a/src/Avalonia.Base/Reactive/UntypedBindingAdapter.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Reactive.Subjects; -using Avalonia.Data; - -namespace Avalonia.Reactive -{ - internal class UntypedBindingAdapter : SingleSubscriberObservableBase, - IObserver> - { - private readonly IObservable> _source; - private IDisposable? _subscription; - - public UntypedBindingAdapter(IObservable> source) => _source = source; - public void OnCompleted() => PublishCompleted(); - public void OnError(Exception error) => PublishError(error); - public void OnNext(BindingValue value) => value.ToUntyped(); - protected override void Subscribed() => _subscription = _source.Subscribe(this); - protected override void Unsubscribed() => _subscription?.Dispose(); - } - - internal class UntypedBindingSubjectAdapter : SingleSubscriberObservableBase, - ISubject - { - private readonly ISubject> _source; - private readonly Inner _inner; - private IDisposable? _subscription; - - public UntypedBindingSubjectAdapter(ISubject> source) - { - _source = source; - _inner = new Inner(this); - } - - public void OnCompleted() => _source.OnCompleted(); - public void OnError(Exception error) => _source.OnError(error); - public void OnNext(object? value) - { - _source.OnNext(BindingValue.FromUntyped(value)); - } - - protected override void Subscribed() => _subscription = _source.Subscribe(_inner); - protected override void Unsubscribed() => _subscription?.Dispose(); - - private class Inner : IObserver> - { - private readonly UntypedBindingSubjectAdapter _owner; - - public Inner(UntypedBindingSubjectAdapter owner) => _owner = owner; - - public void OnCompleted() => _owner.PublishCompleted(); - public void OnError(Exception error) => _owner.PublishError(error); - public void OnNext(BindingValue value) => _owner.PublishNext(value.ToUntyped()); - } - } -} diff --git a/src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimationInstance.cs b/src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimationInstance.cs index e20a4a9ad8..570c6a6d07 100644 --- a/src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimationInstance.cs +++ b/src/Avalonia.Base/Rendering/Composition/Animations/KeyFrameAnimationInstance.cs @@ -128,8 +128,11 @@ namespace Avalonia.Rendering.Composition.Animations left = kf; right = _keyFrames[c + 1]; - break; } + else if (c == 0) + return ExpressionVariant.Create(GetKeyFrame(ref ctx, kf)); + else + break; } var keyProgress = Math.Max(0, Math.Min(1, (iterationProgress - left.Key) / (right.Key - left.Key))); diff --git a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs index 98a6a3600e..38d9b34937 100644 --- a/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs +++ b/src/Avalonia.Base/Rendering/Composition/CompositingRenderer.cs @@ -146,8 +146,18 @@ public class CompositingRenderer : IRendererWithCompositor return result == 0 ? lhs.index.CompareTo(rhs.index) : result; }); } - - if (compositionChildren.Count == visualChildren.Count) + + var childVisual = v.ChildCompositionVisual; + + // Check if the current visual somehow got migrated to another compositor + if (childVisual != null && childVisual.Compositor != v.CompositionVisual.Compositor) + childVisual = null; + + var expectedCount = visualChildren.Count; + if (childVisual != null) + expectedCount++; + + if (compositionChildren.Count == expectedCount) { bool mismatch = false; if (sortedChildren != null) @@ -167,6 +177,9 @@ public class CompositingRenderer : IRendererWithCompositor break; } + if (childVisual != null && + !ReferenceEquals(compositionChildren[compositionChildren.Count - 1], childVisual)) + mismatch = true; if (!mismatch) { @@ -193,6 +206,9 @@ public class CompositingRenderer : IRendererWithCompositor if (compositionChild != null) compositionChildren.Add(compositionChild); } + + if (childVisual != null) + compositionChildren.Add(childVisual); } private void UpdateCore() diff --git a/src/Avalonia.Base/Rendering/Composition/Compositor.Factories.cs b/src/Avalonia.Base/Rendering/Composition/Compositor.Factories.cs index 1a13d23acd..00fa7b3315 100644 --- a/src/Avalonia.Base/Rendering/Composition/Compositor.Factories.cs +++ b/src/Avalonia.Base/Rendering/Composition/Compositor.Factories.cs @@ -29,4 +29,7 @@ public partial class Compositor public ImplicitAnimationCollection CreateImplicitAnimationCollection() => new ImplicitAnimationCollection(this); public CompositionAnimationGroup CreateAnimationGroup() => new CompositionAnimationGroup(this); + + public CompositionSolidColorVisual CreateSolidColorVisual() => + new(this, new ServerCompositionSolidColorVisual(Server)); } \ No newline at end of file diff --git a/src/Avalonia.Base/Rendering/Composition/ElementCompositionPreview.cs b/src/Avalonia.Base/Rendering/Composition/ElementCompositionPreview.cs index 5bd8e4a4d3..b01321edd8 100644 --- a/src/Avalonia.Base/Rendering/Composition/ElementCompositionPreview.cs +++ b/src/Avalonia.Base/Rendering/Composition/ElementCompositionPreview.cs @@ -1,5 +1,8 @@ // Special license applies License.md +using System; +using Avalonia.VisualTree; + namespace Avalonia.Rendering.Composition; /// @@ -13,4 +16,22 @@ public static class ElementComposition /// /// public static CompositionVisual? GetElementVisual(Visual visual) => visual.CompositionVisual; + + /// + /// Sets a custom as the last child of the element’s visual tree. + /// + public static void SetElementChildVisual(Visual visual, CompositionVisual? compositionVisual) + { + if (compositionVisual != null && visual.CompositionVisual != null && + compositionVisual.Compositor != visual.CompositionVisual.Compositor) + throw new InvalidOperationException("Composition visuals belong to different compositor instances"); + + visual.ChildCompositionVisual = compositionVisual; + visual.GetVisualRoot()?.Renderer.RecalculateChildren(visual); + } + + /// + /// Retrieves a object previously set by a call to . + /// + public static CompositionVisual? GetElementChildVisual(Visual visual) => visual.ChildCompositionVisual; } diff --git a/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionSolidColorVisual.cs b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionSolidColorVisual.cs new file mode 100644 index 0000000000..79abd7ee17 --- /dev/null +++ b/src/Avalonia.Base/Rendering/Composition/Server/ServerCompositionSolidColorVisual.cs @@ -0,0 +1,11 @@ +using Avalonia.Media.Immutable; + +namespace Avalonia.Rendering.Composition.Server; + +internal partial class ServerCompositionSolidColorVisual +{ + protected override void RenderCore(CompositorDrawingContextProxy canvas, Rect currentTransformedClip) + { + canvas.DrawRectangle(new ImmutableSolidColorBrush(Color), null, new Rect(0, 0, Size.X, Size.Y)); + } +} \ No newline at end of file diff --git a/src/Avalonia.Base/StyledElement.cs b/src/Avalonia.Base/StyledElement.cs index 3a42e9bdfe..187edd8335 100644 --- a/src/Avalonia.Base/StyledElement.cs +++ b/src/Avalonia.Base/StyledElement.cs @@ -3,6 +3,8 @@ using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; +using System.Diagnostics; +using System.Linq; using Avalonia.Animation; using Avalonia.Collections; using Avalonia.Controls; @@ -10,6 +12,7 @@ using Avalonia.Data; using Avalonia.Diagnostics; using Avalonia.Logging; using Avalonia.LogicalTree; +using Avalonia.PropertyStore; using Avalonia.Styling; namespace Avalonia @@ -68,11 +71,10 @@ namespace Avalonia private IAvaloniaList? _logicalChildren; private IResourceDictionary? _resources; private Styles? _styles; - private bool _styled; - private List? _appliedStyles; + private bool _stylesApplied; + private bool _themeApplied; private ITemplatedControl? _templatedParent; private bool _dataContextUpdating; - private bool _hasPromotedTheme; private ControlTheme? _implicitTheme; /// @@ -141,7 +143,7 @@ namespace Avalonia set { - if (_styled) + if (_stylesApplied) { throw new InvalidOperationException("Cannot set Name : styled element already styled."); } @@ -333,7 +335,7 @@ namespace Avalonia throw new InvalidOperationException("BeginInit was not called."); } - if (--_initCount == 0 && _logicalRoot != null) + if (--_initCount == 0 && _logicalRoot is not null) { ApplyStyling(); InitializeIfNeeded(); @@ -344,39 +346,42 @@ namespace Avalonia /// Applies styling to the control if the control is initialized and styling is not /// already applied. /// + /// + /// The styling system will automatically apply styling when required, so it should not + /// usually be necessary to call this method manually. + /// /// /// A value indicating whether styling is now applied to the control. /// - protected bool ApplyStyling() + public bool ApplyStyling() { - if (_initCount == 0 && !_styled) + if (_initCount == 0 && (!_stylesApplied || !_themeApplied)) { + GetValueStore().BeginStyling(); + try { - BeginBatchUpdate(); - AvaloniaLocator.Current.GetService()?.ApplyStyles(this); + if (!_themeApplied) + { + ApplyControlTheme(); + _themeApplied = true; + } + + if (!_stylesApplied) + { + ApplyStyles(this); + _stylesApplied = true; + } } finally { - _styled = true; - EndBatchUpdate(); - } - - if (_hasPromotedTheme) - { - _hasPromotedTheme = false; - ClearValue(ThemeProperty); + GetValueStore().EndStyling(); } } - return _styled; + return _stylesApplied; } - /// - /// Detaches all styles from the element and queues a restyle. - /// - protected virtual void InvalidateStyles() => DetachStyles(); - protected void InitializeIfNeeded() { if (_initCount == 0 && !IsInitialized) @@ -389,14 +394,15 @@ namespace Avalonia internal StyleDiagnostics GetStyleDiagnosticsInternal() { - IReadOnlyList? appliedStyles = _appliedStyles; + var styles = new List(); - if (appliedStyles is null) + foreach (var frame in GetValueStore().Frames) { - appliedStyles = Array.Empty(); + if (frame is IStyleInstance style) + styles.Add(style); } - return new StyleDiagnostics(appliedStyles); + return new StyleDiagnostics(styles); } /// @@ -497,54 +503,16 @@ namespace Avalonia }; } - ControlTheme? IStyleable.GetEffectiveTheme() - { - var theme = Theme; - - // Explitly set Theme property takes precedence. - if (theme is not null) - return theme; - - // If the Theme property is not set, try to find a ControlTheme resource with our StyleKey. - if (_implicitTheme is null) - { - var key = ((IStyleable)this).StyleKey; - - if (this.TryFindResource(key, out var value) && value is ControlTheme t) - _implicitTheme = t; - else - _implicitTheme = s_invalidTheme; - } - - if (_implicitTheme != s_invalidTheme) - return _implicitTheme; - - return null; - } - - void IStyleable.StyleApplied(IStyleInstance instance) - { - instance = instance ?? throw new ArgumentNullException(nameof(instance)); - - _appliedStyles ??= new List(); - _appliedStyles.Add(instance); - } - - void IStyleable.DetachStyles() => DetachStyles(); - - void IStyleable.DetachStyles(IReadOnlyList styles) => DetachStyles(styles); - - void IStyleable.InvalidateStyles() => InvalidateStyles(); - void IStyleHost.StylesAdded(IReadOnlyList styles) { - InvalidateStylesOnThisAndDescendents(); + if (HasSettersOrAnimations(styles)) + InvalidateStyles(recurse: true); } void IStyleHost.StylesRemoved(IReadOnlyList styles) { - var allStyles = RecurseStyles(styles); - DetachStylesFromThisAndDescendents(allStyles); + if (FlattenStyles(styles) is { } allStyles) + DetachStyles(allStyles); } protected virtual void LogicalChildrenCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) @@ -643,21 +611,66 @@ namespace Avalonia if (change.Property == ThemeProperty) { - // Changing the theme detaches all styles, meaning that if the theme property was - // set via a style, it will get cleared! To work around this, if the value was - // applied at less than local value priority then promote the value to local value - // priority until styling is re-applied. - if (change.Priority > BindingPriority.LocalValue) - { - Theme = change.GetNewValue(); - _hasPromotedTheme = true; - } - else if (_hasPromotedTheme && change.Priority == BindingPriority.LocalValue) - { - _hasPromotedTheme = false; - } + OnControlThemeChanged(); + _themeApplied = false; + } + } + + private protected virtual void OnControlThemeChanged() + { + var values = GetValueStore(); + values.BeginStyling(); + try { values.RemoveFrames(FrameType.Theme); } + finally { values.EndStyling(); } + } + + internal virtual void OnTemplatedParentControlThemeChanged() + { + var values = GetValueStore(); + values.BeginStyling(); + try { values.RemoveFrames(FrameType.TemplatedParentTheme); } + finally { values.EndStyling(); } + } - InvalidateStyles(); + internal ControlTheme? GetEffectiveTheme() + { + var theme = Theme; + + // Explitly set Theme property takes precedence. + if (theme is not null) + return theme; + + // If the Theme property is not set, try to find a ControlTheme resource with our StyleKey. + if (_implicitTheme is null) + { + var key = ((IStyleable)this).StyleKey; + + if (this.TryFindResource(key, out var value) && value is ControlTheme t) + _implicitTheme = t; + else + _implicitTheme = s_invalidTheme; + } + + if (_implicitTheme != s_invalidTheme) + return _implicitTheme; + + return null; + } + + internal virtual void InvalidateStyles(bool recurse) + { + var values = GetValueStore(); + values.BeginStyling(); + try { values.RemoveFrames(FrameType.Style); } + finally { values.EndStyling(); } + + _stylesApplied = false; + + if (recurse && GetInheritanceChildren() is { } children) + { + var childCount = children.Count; + for (var i = 0; i < childCount; ++i) + (children[i] as StyledElement)?.InvalidateStyles(recurse); } } @@ -725,6 +738,58 @@ namespace Avalonia } } + private void ApplyControlTheme() + { + var theme = GetEffectiveTheme(); + + if (theme is not null) + ApplyControlTheme(theme, FrameType.Theme); + + if (TemplatedParent is StyledElement styleableParent && + styleableParent.GetEffectiveTheme() is { } parentTheme) + { + ApplyControlTheme(parentTheme, FrameType.TemplatedParentTheme); + } + } + + private void ApplyControlTheme(ControlTheme theme, FrameType type) + { + Debug.Assert(type is FrameType.Theme or FrameType.TemplatedParentTheme); + + if (theme.BasedOn is ControlTheme basedOn) + ApplyControlTheme(basedOn, type); + + theme.TryAttach(this, type); + + if (theme.HasChildren) + { + foreach (var child in theme.Children) + ApplyStyle(child, null, type); + } + } + + private void ApplyStyles(IStyleHost host) + { + var parent = host.StylingParent; + if (parent != null) + ApplyStyles(parent); + + if (host.IsStylesInitialized) + { + foreach (var style in host.Styles) + ApplyStyle(style, host, FrameType.Style); + } + } + + private void ApplyStyle(IStyle style, IStyleHost? host, FrameType type) + { + if (style is Style s) + s.TryAttach(this, host, type); + + foreach (var child in style.Children) + ApplyStyle(child, host, type); + } + private void OnAttachedToLogicalTreeCore(LogicalTreeAttachmentEventArgs e) { if (this.GetLogicalParent() == null && !(this is ILogicalRoot)) @@ -768,7 +833,7 @@ namespace Avalonia { _logicalRoot = null; _implicitTheme = null; - DetachStyles(); + InvalidateStyles(recurse: false); OnDetachedFromLogicalTree(e); DetachedFromLogicalTree?.Invoke(this, e); @@ -830,85 +895,20 @@ namespace Avalonia } } - private void DetachStyles() - { - if (_appliedStyles?.Count > 0) - { - BeginBatchUpdate(); - - try - { - foreach (var i in _appliedStyles) - { - i.Dispose(); - } - - _appliedStyles.Clear(); - } - finally - { - EndBatchUpdate(); - } - } - - _styled = false; - } - - private void DetachStyles(IReadOnlyList styles) - { - styles = styles ?? throw new ArgumentNullException(nameof(styles)); - - if (_appliedStyles is null) - { - return; - } - - var count = styles.Count; - - for (var i = 0; i < count; ++i) - { - for (var j = _appliedStyles.Count - 1; j >= 0; --j) - { - var applied = _appliedStyles[j]; - - if (applied.Source == styles[i]) - { - _appliedStyles.RemoveAt(j); - applied.Dispose(); - } - - if (j > _appliedStyles.Count) - j = _appliedStyles.Count; - } - } - } - - private void InvalidateStylesOnThisAndDescendents() + private void DetachStyles(IReadOnlyList - + @@ -337,10 +337,6 @@ - - - + diff --git a/src/Avalonia.Themes.Fluent/Accents/Base.xaml b/src/Avalonia.Themes.Fluent/Accents/Base.xaml index 1e2acf736d..259d107b5c 100644 --- a/src/Avalonia.Themes.Fluent/Accents/Base.xaml +++ b/src/Avalonia.Themes.Fluent/Accents/Base.xaml @@ -1,8 +1,7 @@ - + diff --git a/src/Avalonia.Themes.Fluent/Accents/BaseDark.xaml b/src/Avalonia.Themes.Fluent/Accents/BaseDark.xaml index 915be08e53..89841c92c0 100644 --- a/src/Avalonia.Themes.Fluent/Accents/BaseDark.xaml +++ b/src/Avalonia.Themes.Fluent/Accents/BaseDark.xaml @@ -1,7 +1,5 @@ - + 12,0,12,0 + diff --git a/src/Avalonia.Themes.Fluent/Accents/BaseLight.xaml b/src/Avalonia.Themes.Fluent/Accents/BaseLight.xaml index e5c0babb80..89b646fb52 100644 --- a/src/Avalonia.Themes.Fluent/Accents/BaseLight.xaml +++ b/src/Avalonia.Themes.Fluent/Accents/BaseLight.xaml @@ -1,7 +1,5 @@ - + diff --git a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml index 7e3c8673f5..71a8bc3a3c 100644 --- a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml +++ b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml @@ -1,7 +1,5 @@ - + + diff --git a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml index 7917315e19..d764e1616c 100644 --- a/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml +++ b/src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml @@ -1,7 +1,5 @@ - + diff --git a/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj b/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj index 35603fe216..597fab22f8 100644 --- a/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj +++ b/src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj @@ -10,6 +10,7 @@ + diff --git a/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml b/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml index d95e7dd1ef..3ce4730698 100644 --- a/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/ButtonSpinner.xaml @@ -70,7 +70,6 @@ - diff --git a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml index 5383aa3180..a029be6b8d 100644 --- a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml @@ -1,5 +1,4 @@  diff --git a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml.cs b/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml.cs deleted file mode 100644 index 37822f5c8c..0000000000 --- a/src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Avalonia.Markup.Xaml; -using Avalonia.Styling; - -namespace Avalonia.Themes.Fluent.Controls -{ - /// - /// The default Avalonia theme. - /// - public class FluentControls : Styles - { - } -} diff --git a/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml b/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml index 1eb0493b08..5c48c297b8 100644 --- a/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml +++ b/src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml @@ -1,13 +1,12 @@ - diff --git a/src/Avalonia.Themes.Fluent/DensityStyles/Compact.xaml b/src/Avalonia.Themes.Fluent/DensityStyles/Compact.xaml index 3b9c5038e6..b96671b778 100644 --- a/src/Avalonia.Themes.Fluent/DensityStyles/Compact.xaml +++ b/src/Avalonia.Themes.Fluent/DensityStyles/Compact.xaml @@ -3,21 +3,19 @@ - + + 14 + 14 + 24 + 2,2,6,1 + 32 + 24 + 0,1,0,2 + 0,1,0,2 + 9,0,0,1 + 10,0,30,0 + 24 + 12,1,0,3 + 32 + diff --git a/src/Avalonia.Themes.Fluent/FluentDark.xaml b/src/Avalonia.Themes.Fluent/FluentDark.xaml deleted file mode 100644 index aad71b18fa..0000000000 --- a/src/Avalonia.Themes.Fluent/FluentDark.xaml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - diff --git a/src/Avalonia.Themes.Fluent/FluentLight.xaml b/src/Avalonia.Themes.Fluent/FluentLight.xaml deleted file mode 100644 index 907efe7ee6..0000000000 --- a/src/Avalonia.Themes.Fluent/FluentLight.xaml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - diff --git a/src/Avalonia.Themes.Fluent/FluentTheme.cs b/src/Avalonia.Themes.Fluent/FluentTheme.cs deleted file mode 100644 index 79dd81a068..0000000000 --- a/src/Avalonia.Themes.Fluent/FluentTheme.cs +++ /dev/null @@ -1,244 +0,0 @@ -using System; -using System.Collections.Generic; -using Avalonia.Controls; -using Avalonia.Markup.Xaml; -using Avalonia.Markup.Xaml.Styling; -using Avalonia.Styling; - -#nullable enable - -namespace Avalonia.Themes.Fluent -{ - public enum FluentThemeMode - { - Light, - Dark, - } - - public enum DensityStyle - { - Normal, - Compact - } - - /// - /// Includes the fluent theme in an application. - /// - public class FluentTheme : AvaloniaObject, IStyle, IResourceProvider - { - private readonly Uri _baseUri; - private Styles _fluentDark = new(); - private Styles _fluentLight = new(); - private Styles _sharedStyles = new(); - private Styles _densityStyles = new(); - private bool _isLoading; - private IStyle? _loaded; - - /// - /// Initializes a new instance of the class. - /// - /// The base URL for the XAML context. - public FluentTheme(Uri baseUri) - { - _baseUri = baseUri; - InitStyles(baseUri); - } - - /// - /// Initializes a new instance of the class. - /// - /// The XAML service provider. - public FluentTheme(IServiceProvider serviceProvider) - { - var ctx = serviceProvider.GetService(typeof(IUriContext)) as IUriContext - ?? throw new NullReferenceException("Unable retrive UriContext"); - _baseUri = ctx.BaseUri; - InitStyles(_baseUri); - } - - public static readonly StyledProperty ModeProperty = - AvaloniaProperty.Register(nameof(Mode)); - - public static readonly StyledProperty DensityStyleProperty = - AvaloniaProperty.Register(nameof(DensityStyle)); - - /// - /// Gets or sets the mode of the fluent theme (light, dark). - /// - public FluentThemeMode Mode - { - get => GetValue(ModeProperty); - set => SetValue(ModeProperty, value); - } - - /// - /// Gets or sets the density style of the fluent theme (normal, compact). - /// - public DensityStyle DensityStyle - { - get => GetValue(DensityStyleProperty); - set => SetValue(DensityStyleProperty, value); - } - - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) - { - base.OnPropertyChanged(change); - - if (_loaded is null) - { - // If style wasn't yet loaded, no need to change children styles, - // it will be applied later in Loaded getter. - return; - } - - if (change.Property == ModeProperty) - { - if (Mode == FluentThemeMode.Dark) - { - (Loaded as Styles)![1] = _fluentDark[0]; - (Loaded as Styles)![2] = _fluentDark[1]; - } - else - { - (Loaded as Styles)![1] = _fluentLight[0]; - (Loaded as Styles)![2] = _fluentLight[1]; - } - } - - if (change.Property == DensityStyleProperty) - { - if (DensityStyle == DensityStyle.Compact) - { - (Loaded as Styles)!.Add(_densityStyles[0]); - } - else if (DensityStyle == DensityStyle.Normal) - { - (Loaded as Styles)!.Remove(_densityStyles[0]); - } - } - } - - public IResourceHost? Owner => (Loaded as IResourceProvider)?.Owner; - - /// - /// Gets the loaded style. - /// - public IStyle Loaded - { - get - { - if (_loaded == null) - { - _isLoading = true; - - if (Mode == FluentThemeMode.Light) - { - _loaded = new Styles() { _sharedStyles , _fluentLight[0], _fluentLight[1] }; - } - else if (Mode == FluentThemeMode.Dark) - { - _loaded = new Styles() { _sharedStyles, _fluentDark[0], _fluentDark[1] }; - } - - if (DensityStyle == DensityStyle.Compact) - { - (_loaded as Styles)!.Add(_densityStyles[0]); - } - - _isLoading = false; - } - - return _loaded!; - } - } - - bool IResourceNode.HasResources => (Loaded as IResourceProvider)?.HasResources ?? false; - - IReadOnlyList IStyle.Children => _loaded?.Children ?? Array.Empty(); - - public event EventHandler? OwnerChanged - { - add - { - if (Loaded is IResourceProvider rp) - { - rp.OwnerChanged += value; - } - } - remove - { - if (Loaded is IResourceProvider rp) - { - rp.OwnerChanged -= value; - } - } - } - - public SelectorMatchResult TryAttach(IStyleable target, object? host) => Loaded.TryAttach(target, host); - - public bool TryGetResource(object key, out object? value) - { - if (!_isLoading && Loaded is IResourceProvider p) - { - return p.TryGetResource(key, out value); - } - - value = null; - return false; - } - - void IResourceProvider.AddOwner(IResourceHost owner) => (Loaded as IResourceProvider)?.AddOwner(owner); - void IResourceProvider.RemoveOwner(IResourceHost owner) => (Loaded as IResourceProvider)?.RemoveOwner(owner); - - private void InitStyles(Uri baseUri) - { - _sharedStyles = new Styles - { - new StyleInclude(baseUri) - { - Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/AccentColors.xaml") - }, - new StyleInclude(baseUri) - { - Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/Base.xaml") - }, - new StyleInclude(baseUri) - { - Source = new Uri("avares://Avalonia.Themes.Fluent/Controls/FluentControls.xaml") - } - }; - - _fluentLight = new Styles - { - new StyleInclude(baseUri) - { - Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseLight.xaml") - }, - new StyleInclude(baseUri) - { - Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml") - } - }; - - _fluentDark = new Styles - { - new StyleInclude(baseUri) - { - Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseDark.xaml") - }, - new StyleInclude(baseUri) - { - Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml") - } - }; - - _densityStyles = new Styles - { - new StyleInclude(baseUri) - { - Source = new Uri("avares://Avalonia.Themes.Fluent/DensityStyles/Compact.xaml") - } - }; - } - } -} diff --git a/src/Avalonia.Themes.Fluent/FluentTheme.xaml b/src/Avalonia.Themes.Fluent/FluentTheme.xaml new file mode 100644 index 0000000000..d8f8267fe5 --- /dev/null +++ b/src/Avalonia.Themes.Fluent/FluentTheme.xaml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs b/src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs new file mode 100644 index 0000000000..728e81b198 --- /dev/null +++ b/src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs @@ -0,0 +1,124 @@ +using System.Collections.Generic; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Avalonia.Styling; + +namespace Avalonia.Themes.Fluent +{ + public enum FluentThemeMode + { + Light, + Dark, + } + + public enum DensityStyle + { + Normal, + Compact + } + + /// + /// Includes the fluent theme in an application. + /// + public class FluentTheme : Styles + { + private readonly IResourceDictionary _baseDark; + private readonly IResourceDictionary _fluentDark; + private readonly IResourceDictionary _baseLight; + private readonly IResourceDictionary _fluentLight; + private readonly Styles _compactStyles; + + /// + /// Initializes a new instance of the class. + /// + public FluentTheme() + { + AvaloniaXamlLoader.Load(this); + + _baseDark = (IResourceDictionary)GetAndRemove("BaseDark"); + _fluentDark = (IResourceDictionary)GetAndRemove("FluentDark"); + _baseLight = (IResourceDictionary)GetAndRemove("BaseLight"); + _fluentLight = (IResourceDictionary)GetAndRemove("FluentLight"); + _compactStyles = (Styles)GetAndRemove("CompactStyles"); + + EnsureThemeVariants(); + EnsureCompactStyles(); + + object GetAndRemove(string key) + { + var val = Resources[key] + ?? throw new KeyNotFoundException($"Key {key} was not found in the resources"); + Resources.Remove(key); + return val; + } + } + + public static readonly StyledProperty ModeProperty = + AvaloniaProperty.Register(nameof(Mode)); + + public static readonly StyledProperty DensityStyleProperty = + AvaloniaProperty.Register(nameof(DensityStyle)); + + /// + /// Gets or sets the mode of the fluent theme (light, dark). + /// + public FluentThemeMode Mode + { + get => GetValue(ModeProperty); + set => SetValue(ModeProperty, value); + } + + /// + /// Gets or sets the density style of the fluent theme (normal, compact). + /// + public DensityStyle DensityStyle + { + get => GetValue(DensityStyleProperty); + set => SetValue(DensityStyleProperty, value); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == ModeProperty) + { + EnsureThemeVariants(); + } + + if (change.Property == DensityStyleProperty) + { + EnsureCompactStyles(); + } + } + + private void EnsureThemeVariants() + { + var themeVariantResource1 = Mode == FluentThemeMode.Dark ? _baseDark : _baseLight; + var themeVariantResource2 = Mode == FluentThemeMode.Dark ? _fluentDark : _fluentLight; + var dict = Resources.MergedDictionaries; + if (dict.Count == 2) + { + dict.Insert(1, themeVariantResource1); + dict.Add(themeVariantResource2); + } + else + { + dict[1] = themeVariantResource1; + dict[3] = themeVariantResource2; + } + } + + private void EnsureCompactStyles() + { + if (DensityStyle == DensityStyle.Compact) + { + Add(_compactStyles); + } + else + { + Remove(_compactStyles); + } + } + } +} diff --git a/src/Avalonia.Themes.Fluent/IBitmapToImageConverter.cs b/src/Avalonia.Themes.Fluent/IBitmapToImageConverter.cs index 34670882f8..e6c46122fc 100644 --- a/src/Avalonia.Themes.Fluent/IBitmapToImageConverter.cs +++ b/src/Avalonia.Themes.Fluent/IBitmapToImageConverter.cs @@ -1,9 +1,5 @@ using System; -using System.Collections.Generic; using System.Globalization; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Data.Converters; using Avalonia.Media.Imaging; @@ -12,7 +8,7 @@ namespace Avalonia.Themes.Fluent { internal class IBitmapToImageConverter : IValueConverter { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) { if (value != null && value is IBitmap bm) return new Image { Source=bm }; @@ -20,7 +16,7 @@ namespace Avalonia.Themes.Fluent return null; } - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) { throw new NotImplementedException(); } diff --git a/src/Avalonia.Themes.Fluent/InverseBooleanValueConverter.cs b/src/Avalonia.Themes.Fluent/InverseBooleanValueConverter.cs deleted file mode 100644 index 20bade2a67..0000000000 --- a/src/Avalonia.Themes.Fluent/InverseBooleanValueConverter.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Globalization; -using Avalonia.Data.Converters; - -namespace Avalonia.Themes.Fluent -{ - class InverseBooleanValueConverter : IValueConverter - { - public bool Default { get; set; } - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - return value is bool b ? !b : Default; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - return value is bool b ? !b : !Default; - } - } -} diff --git a/src/Avalonia.Themes.Simple/Accents/Base.xaml b/src/Avalonia.Themes.Simple/Accents/Base.xaml index 1f7d703b54..bffdbd8a27 100644 --- a/src/Avalonia.Themes.Simple/Accents/Base.xaml +++ b/src/Avalonia.Themes.Simple/Accents/Base.xaml @@ -1,8 +1,7 @@ - + diff --git a/src/Avalonia.Themes.Simple/Accents/BaseDark.xaml b/src/Avalonia.Themes.Simple/Accents/BaseDark.xaml index 1843abebfd..9ad9f70c98 100644 --- a/src/Avalonia.Themes.Simple/Accents/BaseDark.xaml +++ b/src/Avalonia.Themes.Simple/Accents/BaseDark.xaml @@ -1,8 +1,6 @@ - + diff --git a/src/Avalonia.Themes.Simple/Accents/BaseLight.xaml b/src/Avalonia.Themes.Simple/Accents/BaseLight.xaml index 6247815303..f96425cf06 100644 --- a/src/Avalonia.Themes.Simple/Accents/BaseLight.xaml +++ b/src/Avalonia.Themes.Simple/Accents/BaseLight.xaml @@ -1,8 +1,6 @@ - + diff --git a/src/Avalonia.Themes.Simple/Avalonia.Themes.Simple.csproj b/src/Avalonia.Themes.Simple/Avalonia.Themes.Simple.csproj index 40ed4a0f87..e614dad4d9 100644 --- a/src/Avalonia.Themes.Simple/Avalonia.Themes.Simple.csproj +++ b/src/Avalonia.Themes.Simple/Avalonia.Themes.Simple.csproj @@ -9,6 +9,7 @@ + diff --git a/src/Avalonia.Themes.Simple/Controls/NativeMenuBar.xaml b/src/Avalonia.Themes.Simple/Controls/NativeMenuBar.xaml index 160acd5872..ba1b35e2ee 100644 --- a/src/Avalonia.Themes.Simple/Controls/NativeMenuBar.xaml +++ b/src/Avalonia.Themes.Simple/Controls/NativeMenuBar.xaml @@ -1,14 +1,12 @@ - - diff --git a/src/Avalonia.Themes.Simple/IBitmapToImageConverter.cs b/src/Avalonia.Themes.Simple/IBitmapToImageConverter.cs index fade026b51..d5bf003288 100644 --- a/src/Avalonia.Themes.Simple/IBitmapToImageConverter.cs +++ b/src/Avalonia.Themes.Simple/IBitmapToImageConverter.cs @@ -12,7 +12,7 @@ namespace Avalonia.Themes.Simple { internal class IBitmapToImageConverter : IValueConverter { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) { if (value != null && value is IBitmap bm) return new Image { Source=bm }; @@ -20,7 +20,7 @@ namespace Avalonia.Themes.Simple return null; } - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) { throw new NotImplementedException(); } diff --git a/src/Avalonia.Themes.Simple/InverseBooleanValueConverter.cs b/src/Avalonia.Themes.Simple/InverseBooleanValueConverter.cs deleted file mode 100644 index 15cc5b4a80..0000000000 --- a/src/Avalonia.Themes.Simple/InverseBooleanValueConverter.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Globalization; -using Avalonia.Data.Converters; - -namespace Avalonia.Themes.Simple -{ - class InverseBooleanValueConverter : IValueConverter - { - public bool Default { get; set; } - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - return value is bool b ? !b : Default; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - return value is bool b ? !b : !Default; - } - } -} diff --git a/src/Avalonia.Themes.Simple/SimpleTheme.cs b/src/Avalonia.Themes.Simple/SimpleTheme.cs deleted file mode 100644 index f225cb4c4d..0000000000 --- a/src/Avalonia.Themes.Simple/SimpleTheme.cs +++ /dev/null @@ -1,166 +0,0 @@ -using System; -using System.Collections.Generic; -using Avalonia.Controls; -using Avalonia.Markup.Xaml; -using Avalonia.Markup.Xaml.Styling; -using Avalonia.Styling; -#nullable enable - -namespace Avalonia.Themes.Simple -{ - public class SimpleTheme : AvaloniaObject, IStyle, IResourceProvider - { - public static readonly StyledProperty ModeProperty = - AvaloniaProperty.Register(nameof(Mode)); - - private readonly Uri _baseUri; - private bool _isLoading; - private IStyle? _loaded; - private Styles _sharedStyles = new(); - private Styles _simpleDark = new(); - private Styles _simpleLight = new(); - /// - /// Initializes a new instance of the class. - /// - /// The base URL for the XAML context. - public SimpleTheme(Uri? baseUri = null) - { - _baseUri = baseUri ?? new Uri("avares://Avalonia.Themes.Simple/"); - InitStyles(_baseUri); - } - - /// - /// Initializes a new instance of the class. - /// - /// The XAML service provider. - public SimpleTheme(IServiceProvider serviceProvider) - { - var service = serviceProvider.GetService(typeof(IUriContext)); - if (service == null) - { - throw new Exception("There is no service object of type IUriContext!"); - } - _baseUri = ((IUriContext)service).BaseUri; - InitStyles(_baseUri); - } - - public event EventHandler? OwnerChanged - { - add - { - if (Loaded is IResourceProvider rp) - { - rp.OwnerChanged += value; - } - } - remove - { - if (Loaded is IResourceProvider rp) - { - rp.OwnerChanged -= value; - } - } - } - - IReadOnlyList IStyle.Children => _loaded?.Children ?? Array.Empty(); - - bool IResourceNode.HasResources => (Loaded as IResourceProvider)?.HasResources ?? false; - - public IStyle Loaded - { - get - { - if (_loaded == null) - { - _isLoading = true; - - if (Mode == SimpleThemeMode.Light) - { - _loaded = new Styles { _sharedStyles, _simpleLight }; - } - else if (Mode == SimpleThemeMode.Dark) - { - _loaded = new Styles { _sharedStyles, _simpleDark }; - } - _isLoading = false; - } - - return _loaded!; - } - } - - /// - /// Gets or sets the mode of the fluent theme (light, dark). - /// - public SimpleThemeMode Mode - { - get => GetValue(ModeProperty); - set => SetValue(ModeProperty, value); - } - public IResourceHost? Owner => (Loaded as IResourceProvider)?.Owner; - - void IResourceProvider.AddOwner(IResourceHost owner) => (Loaded as IResourceProvider)?.AddOwner(owner); - - void IResourceProvider.RemoveOwner(IResourceHost owner) => (Loaded as IResourceProvider)?.RemoveOwner(owner); - - public SelectorMatchResult TryAttach(IStyleable target, object? host) => Loaded.TryAttach(target, host); - - public bool TryGetResource(object key, out object? value) - { - if (!_isLoading && Loaded is IResourceProvider p) - { - return p.TryGetResource(key, out value); - } - - value = null; - return false; - } - - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) - { - base.OnPropertyChanged(change); - if (change.Property == ModeProperty) - { - if (Mode == SimpleThemeMode.Dark) - { - (Loaded as Styles)![1] = _simpleDark[0]; - } - else - { - (Loaded as Styles)![1] = _simpleLight[0]; - } - } - } - - private void InitStyles(Uri baseUri) - { - _sharedStyles = new Styles - { - new StyleInclude(baseUri) - { - Source = new Uri("avares://Avalonia.Themes.Simple/Controls/SimpleControls.xaml") - }, - new StyleInclude(baseUri) - { - Source = new Uri("avares://Avalonia.Themes.Simple/Accents/Base.xaml") - } - }; - _simpleLight = new Styles - { - new StyleInclude(baseUri) - { - Source = new Uri("avares://Avalonia.Themes.Simple/Accents/BaseLight.xaml") - } - }; - - _simpleDark = new Styles - { - new StyleInclude(baseUri) - { - Source = new Uri("avares://Avalonia.Themes.Simple/Accents/BaseDark.xaml") - } - }; - } - - } -} diff --git a/src/Avalonia.Themes.Simple/SimpleTheme.xaml b/src/Avalonia.Themes.Simple/SimpleTheme.xaml new file mode 100644 index 0000000000..fe296bd288 --- /dev/null +++ b/src/Avalonia.Themes.Simple/SimpleTheme.xaml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + diff --git a/src/Avalonia.Themes.Simple/SimpleTheme.xaml.cs b/src/Avalonia.Themes.Simple/SimpleTheme.xaml.cs new file mode 100644 index 0000000000..af9d305043 --- /dev/null +++ b/src/Avalonia.Themes.Simple/SimpleTheme.xaml.cs @@ -0,0 +1,69 @@ +using System.Collections.Generic; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Avalonia.Styling; + +namespace Avalonia.Themes.Simple +{ + public class SimpleTheme : Styles + { + public static readonly StyledProperty ModeProperty = + AvaloniaProperty.Register(nameof(Mode)); + + private readonly IResourceDictionary _simpleDark; + private readonly IResourceDictionary _simpleLight; + + /// + /// Initializes a new instance of the class. + /// + public SimpleTheme() + { + AvaloniaXamlLoader.Load(this); + + _simpleDark = (IResourceDictionary)GetAndRemove("BaseDark"); + _simpleLight = (IResourceDictionary)GetAndRemove("BaseLight"); + EnsureThemeVariant(); + + object GetAndRemove(string key) + { + var val = Resources[key] + ?? throw new KeyNotFoundException($"Key {key} was not found in the resources"); + Resources.Remove(key); + return val; + } + } + + /// + /// Gets or sets the mode of the fluent theme (light, dark). + /// + public SimpleThemeMode Mode + { + get => GetValue(ModeProperty); + set => SetValue(ModeProperty, value); + } + + protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) + { + base.OnPropertyChanged(change); + + if (change.Property == ModeProperty) + { + EnsureThemeVariant(); + } + } + + private void EnsureThemeVariant() + { + var themeVariantResource = Mode == SimpleThemeMode.Dark ? _simpleDark : _simpleLight; + var dict = Resources.MergedDictionaries; + if (dict.Count == 1) + { + dict.Add(themeVariantResource); + } + else + { + dict[1] = themeVariantResource; + } + } + } +} diff --git a/src/Web/Avalonia.Web.Blazor/Avalonia.Web.Blazor.csproj b/src/Browser/Avalonia.Browser.Blazor/Avalonia.Browser.Blazor.csproj similarity index 87% rename from src/Web/Avalonia.Web.Blazor/Avalonia.Web.Blazor.csproj rename to src/Browser/Avalonia.Browser.Blazor/Avalonia.Browser.Blazor.csproj index 1c31e0eb5d..4537f8d73e 100644 --- a/src/Web/Avalonia.Web.Blazor/Avalonia.Web.Blazor.csproj +++ b/src/Browser/Avalonia.Browser.Blazor/Avalonia.Browser.Blazor.csproj @@ -2,7 +2,7 @@ net7.0 - Avalonia.Web.Blazor + Avalonia.Browser.Blazor _IncludeGeneratedAvaloniaStaticFiles;$(ResolveStaticWebAssetsInputsDependsOn) @@ -17,12 +17,12 @@ - + - <_AvaloniaWebAssets Include="$(MSBuildThisFileDirectory)../Avalonia.Web/wwwroot/**/*.*" /> + <_AvaloniaWebAssets Include="$(MSBuildThisFileDirectory)../Avalonia.Browser/wwwroot/**/*.*" /> diff --git a/src/Web/Avalonia.Web.Blazor/AvaloniaView.cs b/src/Browser/Avalonia.Browser.Blazor/AvaloniaView.cs similarity index 79% rename from src/Web/Avalonia.Web.Blazor/AvaloniaView.cs rename to src/Browser/Avalonia.Browser.Blazor/AvaloniaView.cs index 909e2dd441..2cc74273c0 100644 --- a/src/Web/Avalonia.Web.Blazor/AvaloniaView.cs +++ b/src/Browser/Avalonia.Browser.Blazor/AvaloniaView.cs @@ -2,17 +2,18 @@ using System.Runtime.InteropServices.JavaScript; using System.Runtime.Versioning; using System.Threading.Tasks; using System; +using Avalonia.Browser.Interop; using Avalonia.Controls.ApplicationLifetimes; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Rendering; -using BrowserView = Avalonia.Web.AvaloniaView; +using BrowserView = Avalonia.Browser.AvaloniaView; -namespace Avalonia.Web.Blazor; +namespace Avalonia.Browser.Blazor; [SupportedOSPlatform("browser")] public class AvaloniaView : ComponentBase { - private BrowserView? _browserView; + private Browser.AvaloniaView? _browserView; private readonly string _containerId; public AvaloniaView() @@ -32,9 +33,9 @@ public class AvaloniaView : ComponentBase { if (OperatingSystem.IsBrowser()) { - await Avalonia.Web.Interop.AvaloniaModule.ImportMain(); + await AvaloniaModule.ImportMain(); - _browserView = new BrowserView(_containerId); + _browserView = new Browser.AvaloniaView(_containerId); if (Application.Current?.ApplicationLifetime is ISingleViewApplicationLifetime lifetime) { _browserView.Content = lifetime.MainView; diff --git a/src/Web/Avalonia.Web.Blazor/BlazorSingleViewLifetime.cs b/src/Browser/Avalonia.Browser.Blazor/BlazorSingleViewLifetime.cs similarity index 92% rename from src/Web/Avalonia.Web.Blazor/BlazorSingleViewLifetime.cs rename to src/Browser/Avalonia.Browser.Blazor/BlazorSingleViewLifetime.cs index f38779f834..2432dc29a3 100644 --- a/src/Web/Avalonia.Web.Blazor/BlazorSingleViewLifetime.cs +++ b/src/Browser/Avalonia.Browser.Blazor/BlazorSingleViewLifetime.cs @@ -3,7 +3,7 @@ using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; -namespace Avalonia.Web.Blazor; +namespace Avalonia.Browser.Blazor; [SupportedOSPlatform("browser")] public static class WebAppBuilder @@ -21,7 +21,7 @@ public static class WebAppBuilder .UseBrowser() .With(new BrowserPlatformOptions { - FrameworkAssetPathResolver = new(filePath => $"/_content/Avalonia.Web.Blazor/{filePath}") + FrameworkAssetPathResolver = new(filePath => $"/_content/Avalonia.Browser.Blazor/{filePath}") }); } diff --git a/src/Web/Avalonia.Web/Avalonia.Web.csproj b/src/Browser/Avalonia.Browser/Avalonia.Browser.csproj similarity index 95% rename from src/Web/Avalonia.Web/Avalonia.Web.csproj rename to src/Browser/Avalonia.Browser/Avalonia.Browser.csproj index 88b23cdad2..2564140a03 100644 --- a/src/Web/Avalonia.Web/Avalonia.Web.csproj +++ b/src/Browser/Avalonia.Browser/Avalonia.Browser.csproj @@ -50,7 +50,7 @@ - + diff --git a/src/Web/Avalonia.Web/Avalonia.Web.props b/src/Browser/Avalonia.Browser/Avalonia.Browser.props similarity index 100% rename from src/Web/Avalonia.Web/Avalonia.Web.props rename to src/Browser/Avalonia.Browser/Avalonia.Browser.props diff --git a/src/Web/Avalonia.Web/Avalonia.Web.targets b/src/Browser/Avalonia.Browser/Avalonia.Browser.targets similarity index 100% rename from src/Web/Avalonia.Web/Avalonia.Web.targets rename to src/Browser/Avalonia.Browser/Avalonia.Browser.targets diff --git a/src/Web/Avalonia.Web/AvaloniaView.cs b/src/Browser/Avalonia.Browser/AvaloniaView.cs similarity index 99% rename from src/Web/Avalonia.Web/AvaloniaView.cs rename to src/Browser/Avalonia.Browser/AvaloniaView.cs index 37614399ee..a407e1e4d8 100644 --- a/src/Web/Avalonia.Web/AvaloniaView.cs +++ b/src/Browser/Avalonia.Browser/AvaloniaView.cs @@ -2,7 +2,8 @@ using System.Collections.Generic; using System.Reflection; using System.Runtime.InteropServices.JavaScript; - +using Avalonia.Browser.Interop; +using Avalonia.Browser.Skia; using Avalonia.Collections.Pooled; using Avalonia.Controls; using Avalonia.Controls.Embedding; @@ -12,12 +13,9 @@ using Avalonia.Input.Raw; using Avalonia.Input.TextInput; using Avalonia.Rendering.Composition; using Avalonia.Threading; -using Avalonia.Web.Interop; -using Avalonia.Web.Skia; - using SkiaSharp; -namespace Avalonia.Web +namespace Avalonia.Browser { [System.Runtime.Versioning.SupportedOSPlatform("browser")] // gets rid of callsite warnings public partial class AvaloniaView : ITextInputMethodImpl diff --git a/src/Web/Avalonia.Web/BrowserNativeControlHost.cs b/src/Browser/Avalonia.Browser/BrowserNativeControlHost.cs similarity index 98% rename from src/Web/Avalonia.Web/BrowserNativeControlHost.cs rename to src/Browser/Avalonia.Browser/BrowserNativeControlHost.cs index 4cdcf627e6..7e91d29019 100644 --- a/src/Web/Avalonia.Web/BrowserNativeControlHost.cs +++ b/src/Browser/Avalonia.Browser/BrowserNativeControlHost.cs @@ -1,12 +1,11 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices.JavaScript; - +using Avalonia.Browser.Interop; using Avalonia.Controls.Platform; using Avalonia.Platform; -using Avalonia.Web.Interop; -namespace Avalonia.Web +namespace Avalonia.Browser { internal class BrowserNativeControlHost : INativeControlHostImpl { diff --git a/src/Web/Avalonia.Web/BrowserRuntimePlatform.cs b/src/Browser/Avalonia.Browser/BrowserRuntimePlatform.cs similarity index 92% rename from src/Web/Avalonia.Web/BrowserRuntimePlatform.cs rename to src/Browser/Avalonia.Browser/BrowserRuntimePlatform.cs index ebcd3a9921..0abc7703da 100644 --- a/src/Web/Avalonia.Web/BrowserRuntimePlatform.cs +++ b/src/Browser/Avalonia.Browser/BrowserRuntimePlatform.cs @@ -2,10 +2,10 @@ using System; using System.Runtime.InteropServices; using System.Runtime.InteropServices.JavaScript; using System.Text.RegularExpressions; +using Avalonia.Browser.Interop; using Avalonia.Platform; -using Avalonia.Web.Interop; -namespace Avalonia.Web; +namespace Avalonia.Browser; internal class BrowserRuntimePlatform : StandardRuntimePlatform { diff --git a/src/Web/Avalonia.Web/BrowserSingleViewLifetime.cs b/src/Browser/Avalonia.Browser/BrowserSingleViewLifetime.cs similarity index 95% rename from src/Web/Avalonia.Web/BrowserSingleViewLifetime.cs rename to src/Browser/Avalonia.Browser/BrowserSingleViewLifetime.cs index 0dcc474f76..ee4f6eca9b 100644 --- a/src/Web/Avalonia.Web/BrowserSingleViewLifetime.cs +++ b/src/Browser/Avalonia.Browser/BrowserSingleViewLifetime.cs @@ -1,11 +1,11 @@ using System; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; -using Avalonia.Web.Skia; using System.Runtime.Versioning; +using Avalonia.Browser.Skia; using Avalonia.Platform; -namespace Avalonia.Web; +namespace Avalonia.Browser; [SupportedOSPlatform("browser")] public class BrowserSingleViewLifetime : ISingleViewApplicationLifetime diff --git a/src/Web/Avalonia.Web/BrowserTopLevelImpl.cs b/src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs similarity index 98% rename from src/Web/Avalonia.Web/BrowserTopLevelImpl.cs rename to src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs index ed8f417870..69e2d27181 100644 --- a/src/Web/Avalonia.Web/BrowserTopLevelImpl.cs +++ b/src/Browser/Avalonia.Browser/BrowserTopLevelImpl.cs @@ -2,6 +2,8 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using Avalonia.Browser.Skia; +using Avalonia.Browser.Storage; using Avalonia.Controls; using Avalonia.Controls.Platform; using Avalonia.Input; @@ -11,10 +13,8 @@ using Avalonia.Platform; using Avalonia.Platform.Storage; using Avalonia.Rendering; using Avalonia.Rendering.Composition; -using Avalonia.Web.Skia; -using Avalonia.Web.Storage; -namespace Avalonia.Web +namespace Avalonia.Browser { [System.Runtime.Versioning.SupportedOSPlatform("browser")] // gets rid of callsite warnings internal class BrowserTopLevelImpl : ITopLevelImplWithTextInputMethod, ITopLevelImplWithNativeControlHost, ITopLevelImplWithStorageProvider diff --git a/src/Web/Avalonia.Web/ClipboardImpl.cs b/src/Browser/Avalonia.Browser/ClipboardImpl.cs similarity index 92% rename from src/Web/Avalonia.Web/ClipboardImpl.cs rename to src/Browser/Avalonia.Browser/ClipboardImpl.cs index 793099f55a..f24d607dae 100644 --- a/src/Web/Avalonia.Web/ClipboardImpl.cs +++ b/src/Browser/Avalonia.Browser/ClipboardImpl.cs @@ -1,10 +1,10 @@ using System; using System.Threading.Tasks; +using Avalonia.Browser.Interop; using Avalonia.Input; using Avalonia.Input.Platform; -using Avalonia.Web.Interop; -namespace Avalonia.Web +namespace Avalonia.Browser { internal class ClipboardImpl : IClipboard { diff --git a/src/Web/Avalonia.Web/Cursor.cs b/src/Browser/Avalonia.Browser/Cursor.cs similarity index 99% rename from src/Web/Avalonia.Web/Cursor.cs rename to src/Browser/Avalonia.Browser/Cursor.cs index af7098f800..ec0dcc51b4 100644 --- a/src/Web/Avalonia.Web/Cursor.cs +++ b/src/Browser/Avalonia.Browser/Cursor.cs @@ -3,7 +3,7 @@ using System.IO; using Avalonia.Input; using Avalonia.Platform; -namespace Avalonia.Web +namespace Avalonia.Browser { internal class CssCursor : ICursorImpl { diff --git a/src/Web/Avalonia.Web/Interop/AvaloniaModule.cs b/src/Browser/Avalonia.Browser/Interop/AvaloniaModule.cs similarity index 96% rename from src/Web/Avalonia.Web/Interop/AvaloniaModule.cs rename to src/Browser/Avalonia.Browser/Interop/AvaloniaModule.cs index 0e54deb515..b283fbaa56 100644 --- a/src/Web/Avalonia.Web/Interop/AvaloniaModule.cs +++ b/src/Browser/Avalonia.Browser/Interop/AvaloniaModule.cs @@ -1,7 +1,7 @@ using System.Runtime.InteropServices.JavaScript; using System.Threading.Tasks; -namespace Avalonia.Web.Interop; +namespace Avalonia.Browser.Interop; internal static partial class AvaloniaModule { diff --git a/src/Web/Avalonia.Web/Interop/CanvasHelper.cs b/src/Browser/Avalonia.Browser/Interop/CanvasHelper.cs similarity index 97% rename from src/Web/Avalonia.Web/Interop/CanvasHelper.cs rename to src/Browser/Avalonia.Browser/Interop/CanvasHelper.cs index 5bbe503bc1..8321b00658 100644 --- a/src/Web/Avalonia.Web/Interop/CanvasHelper.cs +++ b/src/Browser/Avalonia.Browser/Interop/CanvasHelper.cs @@ -2,7 +2,7 @@ using System.Runtime.InteropServices; using System.Runtime.InteropServices.JavaScript; -namespace Avalonia.Web.Interop; +namespace Avalonia.Browser.Interop; internal record GLInfo(int ContextId, uint FboId, int Stencils, int Samples, int Depth); diff --git a/src/Web/Avalonia.Web/Interop/DomHelper.cs b/src/Browser/Avalonia.Browser/Interop/DomHelper.cs similarity index 96% rename from src/Web/Avalonia.Web/Interop/DomHelper.cs rename to src/Browser/Avalonia.Browser/Interop/DomHelper.cs index 80f146a57a..b97a03209b 100644 --- a/src/Web/Avalonia.Web/Interop/DomHelper.cs +++ b/src/Browser/Avalonia.Browser/Interop/DomHelper.cs @@ -1,7 +1,7 @@ using System; using System.Runtime.InteropServices.JavaScript; -namespace Avalonia.Web.Interop; +namespace Avalonia.Browser.Interop; internal static partial class DomHelper { diff --git a/src/Web/Avalonia.Web/Interop/InputHelper.cs b/src/Browser/Avalonia.Browser/Interop/InputHelper.cs similarity index 99% rename from src/Web/Avalonia.Web/Interop/InputHelper.cs rename to src/Browser/Avalonia.Browser/Interop/InputHelper.cs index 904fa915a8..7a010dc782 100644 --- a/src/Web/Avalonia.Web/Interop/InputHelper.cs +++ b/src/Browser/Avalonia.Browser/Interop/InputHelper.cs @@ -3,7 +3,7 @@ using System.Runtime.InteropServices; using System.Runtime.InteropServices.JavaScript; using System.Threading.Tasks; -namespace Avalonia.Web.Interop; +namespace Avalonia.Browser.Interop; internal static partial class InputHelper { diff --git a/src/Web/Avalonia.Web/Interop/NativeControlHostHelper.cs b/src/Browser/Avalonia.Browser/Interop/NativeControlHostHelper.cs similarity index 97% rename from src/Web/Avalonia.Web/Interop/NativeControlHostHelper.cs rename to src/Browser/Avalonia.Browser/Interop/NativeControlHostHelper.cs index d3baaa2533..a362be82d8 100644 --- a/src/Web/Avalonia.Web/Interop/NativeControlHostHelper.cs +++ b/src/Browser/Avalonia.Browser/Interop/NativeControlHostHelper.cs @@ -1,7 +1,7 @@ using System; using System.Runtime.InteropServices.JavaScript; -namespace Avalonia.Web.Interop; +namespace Avalonia.Browser.Interop; internal static partial class NativeControlHostHelper { diff --git a/src/Web/Avalonia.Web/Interop/StorageHelper.cs b/src/Browser/Avalonia.Browser/Interop/StorageHelper.cs similarity index 98% rename from src/Web/Avalonia.Web/Interop/StorageHelper.cs rename to src/Browser/Avalonia.Browser/Interop/StorageHelper.cs index 9a6cfb9fc2..c44af810d1 100644 --- a/src/Web/Avalonia.Web/Interop/StorageHelper.cs +++ b/src/Browser/Avalonia.Browser/Interop/StorageHelper.cs @@ -1,7 +1,7 @@ using System.Runtime.InteropServices.JavaScript; using System.Threading.Tasks; -namespace Avalonia.Web.Interop; +namespace Avalonia.Browser.Interop; internal static partial class StorageHelper { diff --git a/src/Web/Avalonia.Web/Interop/StreamHelper.cs b/src/Browser/Avalonia.Browser/Interop/StreamHelper.cs similarity index 97% rename from src/Web/Avalonia.Web/Interop/StreamHelper.cs rename to src/Browser/Avalonia.Browser/Interop/StreamHelper.cs index d9de7bcbd8..46fa671779 100644 --- a/src/Web/Avalonia.Web/Interop/StreamHelper.cs +++ b/src/Browser/Avalonia.Browser/Interop/StreamHelper.cs @@ -2,7 +2,7 @@ using System.Runtime.InteropServices.JavaScript; using System.Threading.Tasks; -namespace Avalonia.Web.Interop; +namespace Avalonia.Browser.Interop; /// /// Set of FileSystemWritableFileStream and Blob methods. diff --git a/src/Web/Avalonia.Web/JSObjectControlHandle.cs b/src/Browser/Avalonia.Browser/JSObjectControlHandle.cs similarity index 96% rename from src/Web/Avalonia.Web/JSObjectControlHandle.cs rename to src/Browser/Avalonia.Browser/JSObjectControlHandle.cs index e56ca123eb..b0c8cecca6 100644 --- a/src/Web/Avalonia.Web/JSObjectControlHandle.cs +++ b/src/Browser/Avalonia.Browser/JSObjectControlHandle.cs @@ -3,7 +3,7 @@ using System.Runtime.InteropServices.JavaScript; using Avalonia.Controls.Platform; -namespace Avalonia.Web; +namespace Avalonia.Browser; public class JSObjectControlHandle : INativeControlHostDestroyableControlHandle { diff --git a/src/Web/Avalonia.Web/Keycodes.cs b/src/Browser/Avalonia.Browser/Keycodes.cs similarity index 99% rename from src/Web/Avalonia.Web/Keycodes.cs rename to src/Browser/Avalonia.Browser/Keycodes.cs index d1185f6e45..5b6e3a329a 100644 --- a/src/Web/Avalonia.Web/Keycodes.cs +++ b/src/Browser/Avalonia.Browser/Keycodes.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using Avalonia.Input; -namespace Avalonia.Web +namespace Avalonia.Browser { internal static class Keycodes { diff --git a/src/Web/Avalonia.Web/ManualTriggerRenderTimer.cs b/src/Browser/Avalonia.Browser/ManualTriggerRenderTimer.cs similarity index 94% rename from src/Web/Avalonia.Web/ManualTriggerRenderTimer.cs rename to src/Browser/Avalonia.Browser/ManualTriggerRenderTimer.cs index 3309a6dd9f..e9a314e823 100644 --- a/src/Web/Avalonia.Web/ManualTriggerRenderTimer.cs +++ b/src/Browser/Avalonia.Browser/ManualTriggerRenderTimer.cs @@ -2,7 +2,7 @@ using System; using System.Diagnostics; using Avalonia.Rendering; -namespace Avalonia.Web +namespace Avalonia.Browser { internal class ManualTriggerRenderTimer : IRenderTimer { diff --git a/src/Web/Avalonia.Web/Skia/BrowserSkiaGpu.cs b/src/Browser/Avalonia.Browser/Skia/BrowserSkiaGpu.cs similarity index 95% rename from src/Web/Avalonia.Web/Skia/BrowserSkiaGpu.cs rename to src/Browser/Avalonia.Browser/Skia/BrowserSkiaGpu.cs index f80838232b..a96ead93cb 100644 --- a/src/Web/Avalonia.Web/Skia/BrowserSkiaGpu.cs +++ b/src/Browser/Avalonia.Browser/Skia/BrowserSkiaGpu.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using Avalonia.Skia; -namespace Avalonia.Web.Skia +namespace Avalonia.Browser.Skia { public class BrowserSkiaGpu : ISkiaGpu { diff --git a/src/Web/Avalonia.Web/Skia/BrowserSkiaGpuRenderSession.cs b/src/Browser/Avalonia.Browser/Skia/BrowserSkiaGpuRenderSession.cs similarity index 96% rename from src/Web/Avalonia.Web/Skia/BrowserSkiaGpuRenderSession.cs rename to src/Browser/Avalonia.Browser/Skia/BrowserSkiaGpuRenderSession.cs index a7f7d9db3d..81b37da7bd 100644 --- a/src/Web/Avalonia.Web/Skia/BrowserSkiaGpuRenderSession.cs +++ b/src/Browser/Avalonia.Browser/Skia/BrowserSkiaGpuRenderSession.cs @@ -1,7 +1,7 @@ using Avalonia.Skia; using SkiaSharp; -namespace Avalonia.Web.Skia +namespace Avalonia.Browser.Skia { internal class BrowserSkiaGpuRenderSession : ISkiaGpuRenderSession { diff --git a/src/Web/Avalonia.Web/Skia/BrowserSkiaGpuRenderTarget.cs b/src/Browser/Avalonia.Browser/Skia/BrowserSkiaGpuRenderTarget.cs similarity index 97% rename from src/Web/Avalonia.Web/Skia/BrowserSkiaGpuRenderTarget.cs rename to src/Browser/Avalonia.Browser/Skia/BrowserSkiaGpuRenderTarget.cs index dba9b34166..f69dd3c344 100644 --- a/src/Web/Avalonia.Web/Skia/BrowserSkiaGpuRenderTarget.cs +++ b/src/Browser/Avalonia.Browser/Skia/BrowserSkiaGpuRenderTarget.cs @@ -1,7 +1,7 @@ using Avalonia.Skia; using SkiaSharp; -namespace Avalonia.Web.Skia +namespace Avalonia.Browser.Skia { internal class BrowserSkiaGpuRenderTarget : ISkiaGpuRenderTarget { diff --git a/src/Web/Avalonia.Web/Skia/BrowserSkiaRasterSurface.cs b/src/Browser/Avalonia.Browser/Skia/BrowserSkiaRasterSurface.cs similarity index 98% rename from src/Web/Avalonia.Web/Skia/BrowserSkiaRasterSurface.cs rename to src/Browser/Avalonia.Browser/Skia/BrowserSkiaRasterSurface.cs index c7005583ac..76b07a6827 100644 --- a/src/Web/Avalonia.Web/Skia/BrowserSkiaRasterSurface.cs +++ b/src/Browser/Avalonia.Browser/Skia/BrowserSkiaRasterSurface.cs @@ -5,7 +5,7 @@ using Avalonia.Platform; using Avalonia.Skia; using SkiaSharp; -namespace Avalonia.Web.Skia +namespace Avalonia.Browser.Skia { internal class BrowserSkiaRasterSurface : IBrowserSkiaSurface, IFramebufferPlatformSurface, IDisposable { diff --git a/src/Web/Avalonia.Web/Skia/BrowserSkiaSurface.cs b/src/Browser/Avalonia.Browser/Skia/BrowserSkiaSurface.cs similarity index 91% rename from src/Web/Avalonia.Web/Skia/BrowserSkiaSurface.cs rename to src/Browser/Avalonia.Browser/Skia/BrowserSkiaSurface.cs index 27a206c0ec..418b34a975 100644 --- a/src/Web/Avalonia.Web/Skia/BrowserSkiaSurface.cs +++ b/src/Browser/Avalonia.Browser/Skia/BrowserSkiaSurface.cs @@ -1,7 +1,7 @@ -using Avalonia.Web.Interop; +using Avalonia.Browser.Interop; using SkiaSharp; -namespace Avalonia.Web.Skia +namespace Avalonia.Browser.Skia { internal class BrowserSkiaSurface : IBrowserSkiaSurface { diff --git a/src/Web/Avalonia.Web/Skia/IBrowserSkiaSurface.cs b/src/Browser/Avalonia.Browser/Skia/IBrowserSkiaSurface.cs similarity index 82% rename from src/Web/Avalonia.Web/Skia/IBrowserSkiaSurface.cs rename to src/Browser/Avalonia.Browser/Skia/IBrowserSkiaSurface.cs index 7301ae45cd..1585d7f283 100644 --- a/src/Web/Avalonia.Web/Skia/IBrowserSkiaSurface.cs +++ b/src/Browser/Avalonia.Browser/Skia/IBrowserSkiaSurface.cs @@ -1,4 +1,4 @@ -namespace Avalonia.Web.Skia +namespace Avalonia.Browser.Skia { internal interface IBrowserSkiaSurface { diff --git a/src/Web/Avalonia.Web/Storage/BlobReadableStream.cs b/src/Browser/Avalonia.Browser/Storage/BlobReadableStream.cs similarity index 97% rename from src/Web/Avalonia.Web/Storage/BlobReadableStream.cs rename to src/Browser/Avalonia.Browser/Storage/BlobReadableStream.cs index 77734ea62f..4fce190346 100644 --- a/src/Web/Avalonia.Web/Storage/BlobReadableStream.cs +++ b/src/Browser/Avalonia.Browser/Storage/BlobReadableStream.cs @@ -3,10 +3,9 @@ using System.IO; using System.Runtime.InteropServices.JavaScript; using System.Threading; using System.Threading.Tasks; +using Avalonia.Browser.Interop; -using Avalonia.Web.Interop; - -namespace Avalonia.Web.Storage; +namespace Avalonia.Browser.Storage; [System.Runtime.Versioning.SupportedOSPlatform("browser")] internal class BlobReadableStream : Stream diff --git a/src/Web/Avalonia.Web/Storage/BrowserStorageProvider.cs b/src/Browser/Avalonia.Browser/Storage/BrowserStorageProvider.cs similarity index 99% rename from src/Web/Avalonia.Web/Storage/BrowserStorageProvider.cs rename to src/Browser/Avalonia.Browser/Storage/BrowserStorageProvider.cs index 3932b79ad0..28de55092b 100644 --- a/src/Web/Avalonia.Web/Storage/BrowserStorageProvider.cs +++ b/src/Browser/Avalonia.Browser/Storage/BrowserStorageProvider.cs @@ -6,11 +6,10 @@ using System.Linq; using System.Runtime.InteropServices.JavaScript; using System.Runtime.Versioning; using System.Threading.Tasks; - +using Avalonia.Browser.Interop; using Avalonia.Platform.Storage; -using Avalonia.Web.Interop; -namespace Avalonia.Web.Storage; +namespace Avalonia.Browser.Storage; internal record FilePickerAcceptType(string Description, IReadOnlyDictionary> Accept); diff --git a/src/Web/Avalonia.Web/Storage/WriteableStream.cs b/src/Browser/Avalonia.Browser/Storage/WriteableStream.cs similarity index 97% rename from src/Web/Avalonia.Web/Storage/WriteableStream.cs rename to src/Browser/Avalonia.Browser/Storage/WriteableStream.cs index 09e438c34e..f29f7420ac 100644 --- a/src/Web/Avalonia.Web/Storage/WriteableStream.cs +++ b/src/Browser/Avalonia.Browser/Storage/WriteableStream.cs @@ -3,10 +3,9 @@ using System.IO; using System.Runtime.InteropServices.JavaScript; using System.Threading; using System.Threading.Tasks; +using Avalonia.Browser.Interop; -using Avalonia.Web.Interop; - -namespace Avalonia.Web.Storage; +namespace Avalonia.Browser.Storage; [System.Runtime.Versioning.SupportedOSPlatform("browser")] // Loose wrapper implementaion of a stream on top of FileAPI FileSystemWritableFileStream diff --git a/src/Web/Avalonia.Web/WebEmbeddableControlRoot.cs b/src/Browser/Avalonia.Browser/WebEmbeddableControlRoot.cs similarity index 98% rename from src/Web/Avalonia.Web/WebEmbeddableControlRoot.cs rename to src/Browser/Avalonia.Browser/WebEmbeddableControlRoot.cs index 19f36403ad..e389ee98ea 100644 --- a/src/Web/Avalonia.Web/WebEmbeddableControlRoot.cs +++ b/src/Browser/Avalonia.Browser/WebEmbeddableControlRoot.cs @@ -4,7 +4,7 @@ using Avalonia.Media; using Avalonia.Platform; using Avalonia.Rendering.SceneGraph; -namespace Avalonia.Web +namespace Avalonia.Browser { internal class WebEmbeddableControlRoot : EmbeddableControlRoot { diff --git a/src/Web/Avalonia.Web/WinStubs.cs b/src/Browser/Avalonia.Browser/WinStubs.cs similarity index 97% rename from src/Web/Avalonia.Web/WinStubs.cs rename to src/Browser/Avalonia.Browser/WinStubs.cs index b0961115fe..dee97a3612 100644 --- a/src/Web/Avalonia.Web/WinStubs.cs +++ b/src/Browser/Avalonia.Browser/WinStubs.cs @@ -4,7 +4,7 @@ using Avalonia.Platform; #nullable enable -namespace Avalonia.Web +namespace Avalonia.Browser { internal class IconLoaderStub : IPlatformIconLoader { diff --git a/src/Web/Avalonia.Web/WindowingPlatform.cs b/src/Browser/Avalonia.Browser/WindowingPlatform.cs similarity index 99% rename from src/Web/Avalonia.Web/WindowingPlatform.cs rename to src/Browser/Avalonia.Browser/WindowingPlatform.cs index 828964afa7..6535e9534c 100644 --- a/src/Web/Avalonia.Web/WindowingPlatform.cs +++ b/src/Browser/Avalonia.Browser/WindowingPlatform.cs @@ -6,7 +6,7 @@ using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.Threading; -namespace Avalonia.Web +namespace Avalonia.Browser { internal class BrowserWindowingPlatform : IWindowingPlatform, IPlatformThreadingInterface { diff --git a/src/Web/Avalonia.Web/interop.js b/src/Browser/Avalonia.Browser/interop.js similarity index 100% rename from src/Web/Avalonia.Web/interop.js rename to src/Browser/Avalonia.Browser/interop.js diff --git a/src/Web/Avalonia.Web/webapp/.eslintrc.json b/src/Browser/Avalonia.Browser/webapp/.eslintrc.json similarity index 100% rename from src/Web/Avalonia.Web/webapp/.eslintrc.json rename to src/Browser/Avalonia.Browser/webapp/.eslintrc.json diff --git a/src/Web/Avalonia.Web/webapp/build.js b/src/Browser/Avalonia.Browser/webapp/build.js similarity index 100% rename from src/Web/Avalonia.Web/webapp/build.js rename to src/Browser/Avalonia.Browser/webapp/build.js diff --git a/src/Web/Avalonia.Web/webapp/modules/avalonia.ts b/src/Browser/Avalonia.Browser/webapp/modules/avalonia.ts similarity index 100% rename from src/Web/Avalonia.Web/webapp/modules/avalonia.ts rename to src/Browser/Avalonia.Browser/webapp/modules/avalonia.ts diff --git a/src/Web/Avalonia.Web/webapp/modules/avalonia/caniuse.ts b/src/Browser/Avalonia.Browser/webapp/modules/avalonia/caniuse.ts similarity index 100% rename from src/Web/Avalonia.Web/webapp/modules/avalonia/caniuse.ts rename to src/Browser/Avalonia.Browser/webapp/modules/avalonia/caniuse.ts diff --git a/src/Web/Avalonia.Web/webapp/modules/avalonia/canvas.ts b/src/Browser/Avalonia.Browser/webapp/modules/avalonia/canvas.ts similarity index 100% rename from src/Web/Avalonia.Web/webapp/modules/avalonia/canvas.ts rename to src/Browser/Avalonia.Browser/webapp/modules/avalonia/canvas.ts diff --git a/src/Web/Avalonia.Web/webapp/modules/avalonia/caretHelper.ts b/src/Browser/Avalonia.Browser/webapp/modules/avalonia/caretHelper.ts similarity index 100% rename from src/Web/Avalonia.Web/webapp/modules/avalonia/caretHelper.ts rename to src/Browser/Avalonia.Browser/webapp/modules/avalonia/caretHelper.ts diff --git a/src/Web/Avalonia.Web/webapp/modules/avalonia/dom.ts b/src/Browser/Avalonia.Browser/webapp/modules/avalonia/dom.ts similarity index 100% rename from src/Web/Avalonia.Web/webapp/modules/avalonia/dom.ts rename to src/Browser/Avalonia.Browser/webapp/modules/avalonia/dom.ts diff --git a/src/Web/Avalonia.Web/webapp/modules/avalonia/input.ts b/src/Browser/Avalonia.Browser/webapp/modules/avalonia/input.ts similarity index 100% rename from src/Web/Avalonia.Web/webapp/modules/avalonia/input.ts rename to src/Browser/Avalonia.Browser/webapp/modules/avalonia/input.ts diff --git a/src/Web/Avalonia.Web/webapp/modules/avalonia/nativeControlHost.ts b/src/Browser/Avalonia.Browser/webapp/modules/avalonia/nativeControlHost.ts similarity index 100% rename from src/Web/Avalonia.Web/webapp/modules/avalonia/nativeControlHost.ts rename to src/Browser/Avalonia.Browser/webapp/modules/avalonia/nativeControlHost.ts diff --git a/src/Web/Avalonia.Web/webapp/modules/avalonia/stream.ts b/src/Browser/Avalonia.Browser/webapp/modules/avalonia/stream.ts similarity index 100% rename from src/Web/Avalonia.Web/webapp/modules/avalonia/stream.ts rename to src/Browser/Avalonia.Browser/webapp/modules/avalonia/stream.ts diff --git a/src/Web/Avalonia.Web/webapp/modules/storage.ts b/src/Browser/Avalonia.Browser/webapp/modules/storage.ts similarity index 100% rename from src/Web/Avalonia.Web/webapp/modules/storage.ts rename to src/Browser/Avalonia.Browser/webapp/modules/storage.ts diff --git a/src/Web/Avalonia.Web/webapp/modules/storage/indexedDb.ts b/src/Browser/Avalonia.Browser/webapp/modules/storage/indexedDb.ts similarity index 100% rename from src/Web/Avalonia.Web/webapp/modules/storage/indexedDb.ts rename to src/Browser/Avalonia.Browser/webapp/modules/storage/indexedDb.ts diff --git a/src/Web/Avalonia.Web/webapp/modules/storage/storageItem.ts b/src/Browser/Avalonia.Browser/webapp/modules/storage/storageItem.ts similarity index 100% rename from src/Web/Avalonia.Web/webapp/modules/storage/storageItem.ts rename to src/Browser/Avalonia.Browser/webapp/modules/storage/storageItem.ts diff --git a/src/Web/Avalonia.Web/webapp/modules/storage/storageProvider.ts b/src/Browser/Avalonia.Browser/webapp/modules/storage/storageProvider.ts similarity index 100% rename from src/Web/Avalonia.Web/webapp/modules/storage/storageProvider.ts rename to src/Browser/Avalonia.Browser/webapp/modules/storage/storageProvider.ts diff --git a/src/Browser/Avalonia.Browser/webapp/package-lock.json b/src/Browser/Avalonia.Browser/webapp/package-lock.json new file mode 100644 index 0000000000..06e94629d7 --- /dev/null +++ b/src/Browser/Avalonia.Browser/webapp/package-lock.json @@ -0,0 +1,5496 @@ +{ + "name": "avalonia.browser", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "avalonia.browser", + "devDependencies": { + "@types/emscripten": "^1.39.6", + "@types/wicg-file-system-access": "^2020.9.5", + "@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" + } + }, + "node_modules/@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" + } + }, + "node_modules/@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" + } + }, + "node_modules/@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, + "dependencies": { + "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" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@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, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@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, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@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, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@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 + }, + "node_modules/@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, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@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, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@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, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@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/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 + }, + "node_modules/@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 + }, + "node_modules/@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/@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, + "dependencies": { + "@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" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@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, + "dependencies": { + "@typescript-eslint/scope-manager": "5.38.1", + "@typescript-eslint/types": "5.38.1", + "@typescript-eslint/typescript-estree": "5.38.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@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, + "dependencies": { + "@typescript-eslint/types": "5.38.1", + "@typescript-eslint/visitor-keys": "5.38.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@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, + "dependencies": { + "@typescript-eslint/typescript-estree": "5.38.1", + "@typescript-eslint/utils": "5.38.1", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@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, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@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, + "dependencies": { + "@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" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@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, + "dependencies": { + "@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" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@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, + "dependencies": { + "@typescript-eslint/types": "5.38.1", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/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, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/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, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, + "dependencies": { + "semver": "^7.0.0" + } + }, + "node_modules/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, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/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 + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/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, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/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, + "dependencies": { + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "dependencies": { + "has": "^1.0.3" + } + }, + "node_modules/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, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.15.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.9.tgz", + "integrity": "sha512-OnYr1rkMVxtmMHIAKZLMcEUlJmqcbxBz9QoBU8G9v455na0fuzlT/GLu6l+SRghrk0Mm2fSSciMmzV43Q8e0Gg==", + "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" + } + }, + "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" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "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" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/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, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.24.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.24.0.tgz", + "integrity": "sha512-dWFaPhGhTAiPcCgm3f6LI2MBWbogMnTJzFBbhXVRQDJPkr9pGZvVjlVfXd+vyDcWPA2Ic9L2AXPIQM0+vk/cSQ==", + "dev": true, + "dependencies": { + "@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" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/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, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "peerDependencies": { + "eslint": "^8.0.1", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-n": "^15.0.0", + "eslint-plugin-promise": "^6.0.0" + } + }, + "node_modules/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, + "dependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint-config-standard": "17.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^5.0.0", + "eslint": "^8.0.1", + "eslint-plugin-import": "^2.25.2", + "eslint-plugin-n": "^15.0.0", + "eslint-plugin-promise": "^6.0.0", + "typescript": "*" + } + }, + "node_modules/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, + "dependencies": { + "debug": "^3.2.7", + "resolve": "^1.20.0" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/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, + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/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, + "dependencies": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=4.19.1" + } + }, + "node_modules/eslint-plugin-es/node_modules/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, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/eslint-plugin-es/node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "dependencies": { + "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" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "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" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/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, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/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, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/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, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/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, + "engines": { + "node": ">=10" + } + }, + "node_modules/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, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/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, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint/node_modules/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, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/espree": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", + "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", + "dev": true, + "dependencies": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/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, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/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 + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/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, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/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, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/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 + }, + "node_modules/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 + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz", + "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/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, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/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, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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 + }, + "node_modules/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 + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/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, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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 + }, + "node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/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, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/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, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "get-intrinsic": "^1.1.0", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/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 + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/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, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/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 + }, + "node_modules/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 + }, + "node_modules/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 + }, + "node_modules/json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/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, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/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, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true + }, + "node_modules/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 + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/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, + "dependencies": { + "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" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm-run-all/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/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, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/npm-run-all/node_modules/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 + }, + "node_modules/npm-run-all/node_modules/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, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/npm-run-all/node_modules/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, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/npm-run-all/node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/npm-run-all/node_modules/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, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-all/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-all/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/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, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/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, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/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, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/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, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/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 + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=6" + } + }, + "node_modules/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, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/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, + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/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, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/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, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/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, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/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, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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 + }, + "node_modules/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, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/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, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/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, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/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, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/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, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/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, + "dependencies": { + "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" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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 + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "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==", + "dev": true, + "optional": true + }, + "@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==", + "dev": true, + "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" + } + }, + "@types/emscripten": { + "version": "1.39.6", + "resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.39.6.tgz", + "integrity": "sha512-H90aoynNhhkQP6DRweEjJp5vfUVdIj7tdPLsu7pq89vODD/lcugKfZOsfgwpvM6XUewEp2N5dCg1Uf3Qe55Dcg==", + "dev": true + }, + "@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 + }, + "@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, + "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" + } + }, + "@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, + "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" + } + }, + "@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, + "requires": { + "@typescript-eslint/types": "5.38.1", + "@typescript-eslint/visitor-keys": "5.38.1" + } + }, + "@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, + "requires": { + "@typescript-eslint/typescript-estree": "5.38.1", + "@typescript-eslint/utils": "5.38.1", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + } + }, + "@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, + "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" + } + }, + "@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, + "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" + } + }, + "@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, + "requires": { + "@typescript-eslint/types": "5.38.1", + "eslint-visitor-keys": "^3.3.0" + } + }, + "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, + "requires": {} + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "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" + } + }, + "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, + "requires": { + "color-convert": "^2.0.1" + } + }, + "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, + "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" + } + }, + "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, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.2", + "es-shim-unscopables": "^1.0.0" + } + }, + "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, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, + "requires": { + "semver": "^7.0.0" + } + }, + "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, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, + "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, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "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, + "requires": { + "color-name": "~1.1.4" + } + }, + "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, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "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, + "requires": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, + "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, + "requires": { + "path-type": "^4.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "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, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "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, + "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" + } + }, + "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" + } + }, + "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", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.9.tgz", + "integrity": "sha512-OnYr1rkMVxtmMHIAKZLMcEUlJmqcbxBz9QoBU8G9v455na0fuzlT/GLu6l+SRghrk0Mm2fSSciMmzV43Q8e0Gg==", + "dev": true, + "requires": { + "@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" + } + }, + "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==", + "dev": true, + "optional": true + }, + "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==", + "dev": true, + "optional": true + }, + "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==", + "dev": true, + "optional": true + }, + "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==", + "dev": true, + "optional": true + }, + "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==", + "dev": true, + "optional": true + }, + "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==", + "dev": true, + "optional": true + }, + "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==", + "dev": true, + "optional": true + }, + "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==", + "dev": true, + "optional": true + }, + "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==", + "dev": true, + "optional": true + }, + "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==", + "dev": true, + "optional": true + }, + "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==", + "dev": true, + "optional": true + }, + "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==", + "dev": true, + "optional": true + }, + "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==", + "dev": true, + "optional": true + }, + "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==", + "dev": true, + "optional": true + }, + "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==", + "dev": true, + "optional": true + }, + "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==", + "dev": true, + "optional": true + }, + "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==", + "dev": true, + "optional": true + }, + "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==", + "dev": true, + "optional": true + }, + "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==", + "dev": true, + "optional": true + }, + "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==", + "dev": true, + "optional": true + }, + "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, + "requires": {} + }, + "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, + "requires": {} + }, + "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/Browser/Avalonia.Browser/webapp/package.json similarity index 95% rename from src/Web/Avalonia.Web/webapp/package.json rename to src/Browser/Avalonia.Browser/webapp/package.json index 8845dec604..05a3976ccc 100644 --- a/src/Web/Avalonia.Web/webapp/package.json +++ b/src/Browser/Avalonia.Browser/webapp/package.json @@ -1,5 +1,5 @@ { - "name": "avalonia.web", + "name": "avalonia.browser", "scripts": { "typecheck": "npx tsc -noEmit", "eslint": "npx eslint . --fix", diff --git a/src/Web/Avalonia.Web/webapp/tsconfig.json b/src/Browser/Avalonia.Browser/webapp/tsconfig.json similarity index 100% rename from src/Web/Avalonia.Web/webapp/tsconfig.json rename to src/Browser/Avalonia.Browser/webapp/tsconfig.json diff --git a/src/Web/Avalonia.Web/webapp/types/dotnet.d.ts b/src/Browser/Avalonia.Browser/webapp/types/dotnet.d.ts similarity index 100% rename from src/Web/Avalonia.Web/webapp/types/dotnet.d.ts rename to src/Browser/Avalonia.Browser/webapp/types/dotnet.d.ts diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs index 1692238d06..e601701d5c 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs @@ -56,7 +56,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions new AvaloniaXamlIlSetterTransformer(), new AvaloniaXamlIlConstructorServiceProviderTransformer(), new AvaloniaXamlIlTransitionsTypeMetadataTransformer(), - new AvaloniaXamlIlResolveByNameMarkupExtensionReplacer() + new AvaloniaXamlIlResolveByNameMarkupExtensionReplacer(), + new AvaloniaXamlIlAssetIncludeTransformer() ); InsertBefore( new AvaloniaXamlIlOptionMarkupExtensionTransformer()); diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlAssetIncludeTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlAssetIncludeTransformer.cs new file mode 100644 index 0000000000..377a9e72d9 --- /dev/null +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlAssetIncludeTransformer.cs @@ -0,0 +1,93 @@ +using System.Linq; +using XamlX; +using XamlX.Ast; +using XamlX.Emit; +using XamlX.IL; +using XamlX.Transform; +using XamlX.TypeSystem; + +namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers; + +internal class AvaloniaXamlIlAssetIncludeTransformer : IXamlAstTransformer +{ + public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) + { + if (node is not XamlAstObjectNode objectNode + || (objectNode.Type.GetClrType() != context.GetAvaloniaTypes().StyleInclude + && objectNode.Type.GetClrType() != context.GetAvaloniaTypes().ResourceInclude)) + { + return node; + } + + var nodeTypeName = objectNode.Type.GetClrType().Name; + + var sourceProperty = objectNode.Children.OfType().FirstOrDefault(n => n.Property.GetClrProperty().Name == "Source"); + var directives = objectNode.Children.OfType().ToList(); + if (sourceProperty is null + || objectNode.Children.Count != (directives.Count + 1)) + { + throw new XamlParseException($"Unexpected property on the {nodeTypeName} node", node); + } + + if (sourceProperty.Values.OfType().FirstOrDefault() is not { } sourceTextNode) + { + // TODO: make it a compiler warning + // Source value can be set with markup extension instead of a text node, we don't support it here yet. + return node; + } + + var originalAssetPath = sourceTextNode.Text; + if (!(originalAssetPath.StartsWith("avares://") || originalAssetPath.StartsWith("/"))) + { + return node; + } + + var runtimeHelpers = context.GetAvaloniaTypes().RuntimeHelpers; + var markerMethodName = "Resolve" + nodeTypeName; + var markerMethod = runtimeHelpers.FindMethod(m => m.Name == markerMethodName && m.Parameters.Count == 3); + if (markerMethod is null) + { + throw new XamlParseException($"Marker method \"{markerMethodName}\" was not found for the \"{nodeTypeName}\" node", node); + } + + return new XamlValueWithManipulationNode( + node, + new AssetIncludeMethodNode(node, markerMethod, originalAssetPath), + new XamlManipulationGroupNode(node, directives)); + } + + private class AssetIncludeMethodNode : XamlAstNode, IXamlAstValueNode, IXamlAstILEmitableNode + { + private readonly IXamlMethod _method; + private readonly string _originalAssetPath; + + public AssetIncludeMethodNode( + IXamlAstNode original, IXamlMethod method, string originalAssetPath) + : base(original) + { + _method = method; + _originalAssetPath = originalAssetPath; + } + + public IXamlAstTypeReference Type => new XamlAstClrTypeReference(this, _method.ReturnType, false); + + public XamlILNodeEmitResult Emit(XamlEmitContext context, IXamlILEmitter codeGen) + { + var absoluteSource = _originalAssetPath; + if (absoluteSource.StartsWith("/")) + { + // Avoid Uri class here to avoid potential problems with escaping. + // Keeping string as close to the original as possible. + var absoluteBaseUrl = context.RuntimeContext.BaseUrl; + absoluteSource = absoluteBaseUrl.Substring(0, absoluteBaseUrl.LastIndexOf('/')) + absoluteSource; + } + + codeGen.Ldstr(absoluteSource); + codeGen.Ldc_I4(Line); + codeGen.Ldc_I4(Position); + codeGen.EmitCall(_method); + + return XamlILNodeEmitResult.Type(0, _method.ReturnType); + } + } +} diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplatePriorityTransformer.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplatePriorityTransformer.cs index 6cab68e756..76fd07f15c 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplatePriorityTransformer.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlControlTemplatePriorityTransformer.cs @@ -42,7 +42,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers if (priorityValueSetters.Count > 0) { prop.PossibleSetters = priorityValueSetters; - prop.Values.Insert(0, new XamlConstantNode(node, bindingPriorityType, (int)BindingPriority.TemplatedParent)); + prop.Values.Insert(0, new XamlConstantNode(node, bindingPriorityType, (int)BindingPriority.Template)); } } diff --git a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs index bf8427a129..dd37ae6c93 100644 --- a/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs +++ b/src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs @@ -102,6 +102,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers public IXamlType TextDecorations { get; } public IXamlType TextTrimming { get; } public IXamlType ISetter { get; } + public IXamlType IStyle { get; } + public IXamlType StyleInclude { get; } + public IXamlType ResourceInclude { get; } public IXamlType IResourceDictionary { get; } public IXamlType ResourceDictionary { get; } public IXamlMethod ResourceDictionaryDeferredAdd { get; } @@ -232,6 +235,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers TextDecorations = cfg.TypeSystem.GetType("Avalonia.Media.TextDecorations"); TextTrimming = cfg.TypeSystem.GetType("Avalonia.Media.TextTrimming"); ISetter = cfg.TypeSystem.GetType("Avalonia.Styling.ISetter"); + IStyle = cfg.TypeSystem.GetType("Avalonia.Styling.IStyle"); + StyleInclude = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.Styling.StyleInclude"); + ResourceInclude = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.Styling.ResourceInclude"); IResourceDictionary = cfg.TypeSystem.GetType("Avalonia.Controls.IResourceDictionary"); ResourceDictionary = cfg.TypeSystem.GetType("Avalonia.Controls.ResourceDictionary"); ResourceDictionaryDeferredAdd = ResourceDictionary.FindMethod("AddDeferred", XamlIlTypes.Void, true, XamlIlTypes.Object, diff --git a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj index 070f0c1cc3..b5ba49ce2c 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj +++ b/src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj @@ -33,9 +33,7 @@ - - @@ -45,6 +43,7 @@ + diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs index f13ecab4e1..56b653ca47 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs +++ b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/DynamicResourceExtension.cs @@ -27,7 +27,7 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions public IBinding ProvideValue(IServiceProvider serviceProvider) { if (serviceProvider.IsInControlTemplate()) - _priority = BindingPriority.TemplatedParent; + _priority = BindingPriority.Template; var provideTarget = serviceProvider.GetService(); diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StyleIncludeExtension.cs b/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StyleIncludeExtension.cs deleted file mode 100644 index 129fa66912..0000000000 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StyleIncludeExtension.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Avalonia.Markup.Xaml.Styling; -using Avalonia.Styling; -using System.ComponentModel; -using System; - -namespace Avalonia.Markup.Xaml.MarkupExtensions -{ - public class StyleIncludeExtension - { - public StyleIncludeExtension() - { - } - - public IStyle ProvideValue(IServiceProvider serviceProvider) - { - return new StyleInclude(serviceProvider.GetContextBaseUri()) { Source = Source }; - } - - public Uri Source { get; set; } - - } -} diff --git a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs b/src/Markup/Avalonia.Markup.Xaml/Styling/ResourceInclude.cs similarity index 70% rename from src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs rename to src/Markup/Avalonia.Markup.Xaml/Styling/ResourceInclude.cs index 1091b3ec7e..01db2c081f 100644 --- a/src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Styling/ResourceInclude.cs @@ -1,20 +1,37 @@ using System; -using System.ComponentModel; using Avalonia.Controls; #nullable enable -namespace Avalonia.Markup.Xaml.MarkupExtensions +namespace Avalonia.Markup.Xaml.Styling { /// /// Loads a resource dictionary from a specified URL. /// public class ResourceInclude : IResourceProvider { - private Uri? _baseUri; + private readonly Uri? _baseUri; private IResourceDictionary? _loaded; private bool _isLoading; + /// + /// Initializes a new instance of the class. + /// + /// The base URL for the XAML context. + public ResourceInclude(Uri? baseUri) + { + _baseUri = baseUri; + } + + /// + /// Initializes a new instance of the class. + /// + /// The XAML service provider. + public ResourceInclude(IServiceProvider serviceProvider) + { + _baseUri = serviceProvider.GetContextBaseUri(); + } + /// /// Gets the loaded resource dictionary. /// @@ -61,12 +78,5 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions void IResourceProvider.AddOwner(IResourceHost owner) => Loaded.AddOwner(owner); void IResourceProvider.RemoveOwner(IResourceHost owner) => Loaded.RemoveOwner(owner); - - public ResourceInclude ProvideValue(IServiceProvider serviceProvider) - { - var tdc = (ITypeDescriptorContext)serviceProvider; - _baseUri = tdc?.GetContextBaseUri(); - return this; - } } } diff --git a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs index d92003ad9f..8af49b5480 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs @@ -12,7 +12,7 @@ namespace Avalonia.Markup.Xaml.Styling /// public class StyleInclude : IStyle, IResourceProvider { - private readonly Uri _baseUri; + private readonly Uri? _baseUri; private IStyle[]? _loaded; private bool _isLoading; @@ -20,7 +20,7 @@ namespace Avalonia.Markup.Xaml.Styling /// Initializes a new instance of the class. /// /// The base URL for the XAML context. - public StyleInclude(Uri baseUri) + public StyleInclude(Uri? baseUri) { _baseUri = baseUri; } @@ -82,8 +82,6 @@ namespace Avalonia.Markup.Xaml.Styling } } - public SelectorMatchResult TryAttach(IStyleable target, object? host) => Loaded.TryAttach(target, host); - public bool TryGetResource(object key, out object? value) { if (!_isLoading) diff --git a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs index 6682532455..f547888d6e 100644 --- a/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs +++ b/src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs @@ -5,7 +5,10 @@ using System.Reflection; using Avalonia.Controls; using Avalonia.Controls.Templates; using Avalonia.Data; +using Avalonia.Markup.Xaml.MarkupExtensions; +using Avalonia.Markup.Xaml.Styling; using Avalonia.Platform; +using Avalonia.Styling; // ReSharper disable UnusedMember.Global // ReSharper disable UnusedParameter.Global @@ -14,6 +17,16 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime { public static class XamlIlRuntimeHelpers { + public static IStyle ResolveStyleInclude(string absoluteSource, int line, int position) + { + return new StyleInclude((Uri)null) { Source = new Uri(absoluteSource) }.Loaded; + } + + public static IResourceDictionary ResolveResourceInclude(string absoluteSource, int line, int position) + { + return new ResourceInclude((Uri)null) { Source = new Uri(absoluteSource) }.Loaded; + } + public static Func DeferredTransformationFactoryV1(Func builder, IServiceProvider provider) { diff --git a/src/Markup/Avalonia.Markup/Data/TemplateBinding.cs b/src/Markup/Avalonia.Markup/Data/TemplateBinding.cs index 4a3538fff2..0d8925c6fa 100644 --- a/src/Markup/Avalonia.Markup/Data/TemplateBinding.cs +++ b/src/Markup/Avalonia.Markup/Data/TemplateBinding.cs @@ -51,7 +51,7 @@ namespace Avalonia.Data return new InstancedBinding( this, Mode == BindingMode.Default ? BindingMode.OneWay : Mode, - BindingPriority.TemplatedParent); + BindingPriority.Template); } else { diff --git a/src/Web/Avalonia.Web/webapp/package-lock.json b/src/Web/Avalonia.Web/webapp/package-lock.json deleted file mode 100644 index 947f7e12e7..0000000000 --- a/src/Web/Avalonia.Web/webapp/package-lock.json +++ /dev/null @@ -1,2234 +0,0 @@ -{ - "name": "avalonia.web", - "requires": true, - "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==", - "dev": true, - "optional": true - }, - "@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==", - "dev": true, - "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" - } - }, - "@types/emscripten": { - "version": "1.39.6", - "resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.39.6.tgz", - "integrity": "sha512-H90aoynNhhkQP6DRweEjJp5vfUVdIj7tdPLsu7pq89vODD/lcugKfZOsfgwpvM6XUewEp2N5dCg1Uf3Qe55Dcg==", - "dev": true - }, - "@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 - }, - "@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, - "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" - } - }, - "@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, - "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" - } - }, - "@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, - "requires": { - "@typescript-eslint/types": "5.38.1", - "@typescript-eslint/visitor-keys": "5.38.1" - } - }, - "@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, - "requires": { - "@typescript-eslint/typescript-estree": "5.38.1", - "@typescript-eslint/utils": "5.38.1", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - } - }, - "@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, - "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" - } - }, - "@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, - "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" - } - }, - "@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, - "requires": { - "@typescript-eslint/types": "5.38.1", - "eslint-visitor-keys": "^3.3.0" - } - }, - "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, - "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" - } - }, - "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, - "requires": { - "color-convert": "^2.0.1" - } - }, - "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, - "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" - } - }, - "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, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.2", - "es-shim-unscopables": "^1.0.0" - } - }, - "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, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "builtins": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", - "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", - "dev": true, - "requires": { - "semver": "^7.0.0" - } - }, - "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, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, - "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, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "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, - "requires": { - "color-name": "~1.1.4" - } - }, - "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, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "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, - "requires": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - } - }, - "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, - "requires": { - "path-type": "^4.0.0" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "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, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "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, - "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" - } - }, - "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" - } - }, - "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", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.9.tgz", - "integrity": "sha512-OnYr1rkMVxtmMHIAKZLMcEUlJmqcbxBz9QoBU8G9v455na0fuzlT/GLu6l+SRghrk0Mm2fSSciMmzV43Q8e0Gg==", - "dev": true, - "requires": { - "@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" - } - }, - "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==", - "dev": true, - "optional": true - }, - "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==", - "dev": true, - "optional": true - }, - "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==", - "dev": true, - "optional": true - }, - "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==", - "dev": true, - "optional": true - }, - "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==", - "dev": true, - "optional": true - }, - "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==", - "dev": true, - "optional": true - }, - "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==", - "dev": true, - "optional": true - }, - "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==", - "dev": true, - "optional": true - }, - "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==", - "dev": true, - "optional": true - }, - "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==", - "dev": true, - "optional": true - }, - "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==", - "dev": true, - "optional": true - }, - "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==", - "dev": true, - "optional": true - }, - "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==", - "dev": true, - "optional": true - }, - "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==", - "dev": true, - "optional": true - }, - "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==", - "dev": true, - "optional": true - }, - "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==", - "dev": true, - "optional": true - }, - "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==", - "dev": true, - "optional": true - }, - "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==", - "dev": true, - "optional": true - }, - "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==", - "dev": true, - "optional": true - }, - "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==", - "dev": true, - "optional": true - }, - "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/tests/Avalonia.Base.UnitTests/Animation/AnimatableTests.cs b/tests/Avalonia.Base.UnitTests/Animation/AnimatableTests.cs index f9ff8caab1..d81c6e75f3 100644 --- a/tests/Avalonia.Base.UnitTests/Animation/AnimatableTests.cs +++ b/tests/Avalonia.Base.UnitTests/Animation/AnimatableTests.cs @@ -5,6 +5,7 @@ using Avalonia.Controls.Shapes; using Avalonia.Data; using Avalonia.Layout; using Avalonia.Media; +using Avalonia.PropertyStore; using Avalonia.Styling; using Avalonia.UnitTests; using Moq; @@ -413,31 +414,92 @@ namespace Avalonia.Base.UnitTests.Animation } [Fact] - public void Transitions_Can_Re_Set_During_Batch_Update() + public void Transitions_Can_Re_Set_During_Styling() { var target = CreateTarget(); var control = CreateControl(target.Object); // Assigning and then clearing Transitions ensures we have a transition state // collection created. - control.Transitions = null; + control.ClearValue(Control.TransitionsProperty); - control.BeginBatchUpdate(); + control.GetValueStore().BeginStyling(); // Setting opacity then Transitions means that we receive the Transitions change - // after the Opacity change when EndBatchUpdate is called. - control.Opacity = 0.5; - control.Transitions = new Transitions { target.Object }; + // after the Opacity change when EndStyling is called. + var style = new Style + { + Setters = + { + new Setter(Control.OpacityProperty, 0.5), + new Setter(Control.TransitionsProperty, new Transitions { target.Object }), + } + }; + + StyleHelpers.TryAttach(style, control); // Which means that the transition state hasn't been initialized with the new // Transitions when the Opacity change notification gets raised here. - control.EndBatchUpdate(); + control.GetValueStore().EndStyling(); + } + + [Fact] + public void Transitions_Can_Be_Removed_While_Transition_In_Progress() + { + using var app = Start(); + + var opacityTransition = new DoubleTransition + { + Property = Control.OpacityProperty, + Duration = TimeSpan.FromSeconds(1), + }; + + var transitions = new Transitions { opacityTransition }; + var borderTheme = new ControlTheme(typeof(Border)) + { + Setters = + { + new Setter(Control.TransitionsProperty, transitions), + } + }; + + var clock = new TestClock(); + var root = new TestRoot + { + Clock = clock, + Resources = + { + { typeof(Border), borderTheme }, + } + }; + + var border = new Border(); + root.Child = border; + + root.LayoutManager.ExecuteInitialLayoutPass(); + + Assert.Same(transitions, border.Transitions); + + // First set property with a transition to a new value, and step the clock until + // transition is complete. + border.Opacity = 0; + clock.Step(TimeSpan.FromSeconds(0)); + clock.Step(TimeSpan.FromSeconds(1)); + Assert.Equal(0, border.Opacity); + + // Now clear the property; a transition is now in progress but no local value is + // set. + border.ClearValue(Border.OpacityProperty); + + // Remove the transition by removing the control from the logical tree. This was + // causing an exception. + root.Child = null; } private static IDisposable Start() { var clock = new MockGlobalClock(); - var services = TestServices.RealStyler.With(globalClock: clock); + var services = new TestServices(globalClock: clock); return UnitTestApplication.Start(services); } diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_BatchUpdate.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_BatchUpdate.cs deleted file mode 100644 index 45de860894..0000000000 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_BatchUpdate.cs +++ /dev/null @@ -1,695 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reactive; -using System.Reactive.Disposables; -using System.Reactive.Linq; -using System.Text; -using Avalonia.Data; -using Avalonia.Layout; -using Xunit; - -namespace Avalonia.Base.UnitTests -{ - public class AvaloniaObjectTests_BatchUpdate - { - [Fact] - public void SetValue_Should_Not_Raise_Property_Changes_During_Batch_Update() - { - var target = new TestClass(); - var raised = new List(); - - target.GetObservable(TestClass.FooProperty).Skip(1).Subscribe(x => raised.Add(x)); - target.BeginBatchUpdate(); - target.SetValue(TestClass.FooProperty, "foo", BindingPriority.LocalValue); - - Assert.Empty(raised); - } - - [Fact] - public void Binding_Should_Not_Raise_Property_Changes_During_Batch_Update() - { - var target = new TestClass(); - var observable = new TestObservable("foo"); - var raised = new List(); - - target.GetObservable(TestClass.FooProperty).Skip(1).Subscribe(x => raised.Add(x)); - target.BeginBatchUpdate(); - target.Bind(TestClass.FooProperty, observable, BindingPriority.LocalValue); - - Assert.Empty(raised); - } - - [Fact] - public void Binding_Completion_Should_Not_Raise_Property_Changes_During_Batch_Update() - { - var target = new TestClass(); - var observable = new TestObservable("foo"); - var raised = new List(); - - target.Bind(TestClass.FooProperty, observable, BindingPriority.LocalValue); - target.GetObservable(TestClass.FooProperty).Skip(1).Subscribe(x => raised.Add(x)); - target.BeginBatchUpdate(); - observable.OnCompleted(); - - Assert.Empty(raised); - } - - [Fact] - public void Binding_Disposal_Should_Not_Raise_Property_Changes_During_Batch_Update() - { - var target = new TestClass(); - var observable = new TestObservable("foo"); - var raised = new List(); - - var sub = target.Bind(TestClass.FooProperty, observable, BindingPriority.LocalValue); - target.GetObservable(TestClass.FooProperty).Skip(1).Subscribe(x => raised.Add(x)); - target.BeginBatchUpdate(); - sub.Dispose(); - - Assert.Empty(raised); - } - - [Fact] - public void SetValue_Change_Should_Be_Raised_After_Batch_Update_1() - { - var target = new TestClass(); - var raised = new List(); - - target.PropertyChanged += (s, e) => raised.Add(e); - - target.BeginBatchUpdate(); - target.SetValue(TestClass.FooProperty, "foo", BindingPriority.LocalValue); - target.EndBatchUpdate(); - - Assert.Equal(1, raised.Count); - Assert.Equal("foo", target.Foo); - Assert.Null(raised[0].OldValue); - Assert.Equal("foo", raised[0].NewValue); - } - - [Fact] - public void SetValue_Change_Should_Be_Raised_After_Batch_Update_2() - { - var target = new TestClass(); - var raised = new List(); - - target.SetValue(TestClass.FooProperty, "foo", BindingPriority.LocalValue); - target.PropertyChanged += (s, e) => raised.Add(e); - - target.BeginBatchUpdate(); - target.SetValue(TestClass.FooProperty, "bar", BindingPriority.LocalValue); - target.SetValue(TestClass.FooProperty, "baz", BindingPriority.LocalValue); - target.EndBatchUpdate(); - - Assert.Equal(1, raised.Count); - Assert.Equal("baz", target.Foo); - } - - [Fact] - public void SetValue_Change_Should_Be_Raised_After_Batch_Update_3() - { - var target = new TestClass(); - var raised = new List(); - - target.PropertyChanged += (s, e) => raised.Add(e); - - target.BeginBatchUpdate(); - target.SetValue(TestClass.BazProperty, Orientation.Horizontal, BindingPriority.LocalValue); - target.EndBatchUpdate(); - - Assert.Equal(1, raised.Count); - Assert.Equal(TestClass.BazProperty, raised[0].Property); - Assert.Equal(Orientation.Vertical, raised[0].OldValue); - Assert.Equal(Orientation.Horizontal, raised[0].NewValue); - Assert.Equal(Orientation.Horizontal, target.Baz); - } - - [Fact] - public void SetValue_Changes_Should_Be_Raised_In_Correct_Order_After_Batch_Update() - { - var target = new TestClass(); - var raised = new List(); - - target.PropertyChanged += (s, e) => raised.Add(e); - - target.BeginBatchUpdate(); - target.SetValue(TestClass.FooProperty, "foo", BindingPriority.LocalValue); - target.SetValue(TestClass.BarProperty, "bar", BindingPriority.LocalValue); - target.SetValue(TestClass.FooProperty, "baz", BindingPriority.LocalValue); - target.EndBatchUpdate(); - - Assert.Equal(2, raised.Count); - Assert.Equal(TestClass.BarProperty, raised[0].Property); - Assert.Equal(TestClass.FooProperty, raised[1].Property); - Assert.Equal("baz", target.Foo); - Assert.Equal("bar", target.Bar); - } - - [Fact] - public void SetValue_And_Binding_Changes_Should_Be_Raised_In_Correct_Order_After_Batch_Update_1() - { - var target = new TestClass(); - var observable = new TestObservable("baz"); - var raised = new List(); - - target.PropertyChanged += (s, e) => raised.Add(e); - - target.BeginBatchUpdate(); - target.SetValue(TestClass.FooProperty, "foo", BindingPriority.LocalValue); - target.SetValue(TestClass.BarProperty, "bar", BindingPriority.LocalValue); - target.Bind(TestClass.FooProperty, observable, BindingPriority.LocalValue); - target.EndBatchUpdate(); - - Assert.Equal(2, raised.Count); - Assert.Equal(TestClass.BarProperty, raised[0].Property); - Assert.Equal(TestClass.FooProperty, raised[1].Property); - Assert.Equal("baz", target.Foo); - Assert.Equal("bar", target.Bar); - } - - [Fact] - public void SetValue_And_Binding_Changes_Should_Be_Raised_In_Correct_Order_After_Batch_Update_2() - { - var target = new TestClass(); - var observable = new TestObservable("foo"); - var raised = new List(); - - target.PropertyChanged += (s, e) => raised.Add(e); - - target.BeginBatchUpdate(); - target.Bind(TestClass.FooProperty, observable, BindingPriority.LocalValue); - target.SetValue(TestClass.BarProperty, "bar", BindingPriority.LocalValue); - target.SetValue(TestClass.FooProperty, "baz", BindingPriority.LocalValue); - target.EndBatchUpdate(); - - Assert.Equal(2, raised.Count); - Assert.Equal(TestClass.BarProperty, raised[0].Property); - Assert.Equal(TestClass.FooProperty, raised[1].Property); - Assert.Equal("baz", target.Foo); - Assert.Equal("bar", target.Bar); - } - - [Fact] - public void SetValue_And_Binding_Changes_Should_Be_Raised_In_Correct_Order_After_Batch_Update_3() - { - var target = new TestClass(); - var observable1 = new TestObservable("foo"); - var observable2 = new TestObservable("qux"); - var raised = new List(); - - target.PropertyChanged += (s, e) => raised.Add(e); - - target.BeginBatchUpdate(); - target.Bind(TestClass.FooProperty, observable2, BindingPriority.LocalValue); - target.Bind(TestClass.FooProperty, observable1, BindingPriority.LocalValue); - target.SetValue(TestClass.BarProperty, "bar", BindingPriority.LocalValue); - target.SetValue(TestClass.FooProperty, "baz", BindingPriority.LocalValue); - target.EndBatchUpdate(); - - Assert.Equal(2, raised.Count); - Assert.Equal(TestClass.BarProperty, raised[0].Property); - Assert.Equal(TestClass.FooProperty, raised[1].Property); - Assert.Equal("baz", target.Foo); - Assert.Equal("bar", target.Bar); - } - - [Fact] - public void Binding_Change_Should_Be_Raised_After_Batch_Update_1() - { - var target = new TestClass(); - var observable = new TestObservable("foo"); - var raised = new List(); - - target.PropertyChanged += (s, e) => raised.Add(e); - - target.BeginBatchUpdate(); - target.Bind(TestClass.FooProperty, observable, BindingPriority.LocalValue); - target.EndBatchUpdate(); - - Assert.Equal(1, raised.Count); - Assert.Equal("foo", target.Foo); - Assert.Null(raised[0].OldValue); - Assert.Equal("foo", raised[0].NewValue); - } - - [Fact] - public void Binding_Change_Should_Be_Raised_After_Batch_Update_2() - { - var target = new TestClass(); - var observable1 = new TestObservable("bar"); - var observable2 = new TestObservable("baz"); - var raised = new List(); - - target.SetValue(TestClass.FooProperty, "foo", BindingPriority.LocalValue); - target.PropertyChanged += (s, e) => raised.Add(e); - - target.BeginBatchUpdate(); - target.Bind(TestClass.FooProperty, observable1, BindingPriority.LocalValue); - target.Bind(TestClass.FooProperty, observable2, BindingPriority.LocalValue); - target.EndBatchUpdate(); - - Assert.Equal(1, raised.Count); - Assert.Equal("baz", target.Foo); - Assert.Equal("foo", raised[0].OldValue); - Assert.Equal("baz", raised[0].NewValue); - } - - [Fact] - public void Binding_Change_Should_Be_Raised_After_Batch_Update_3() - { - var target = new TestClass(); - var observable = new TestObservable(Orientation.Horizontal); - var raised = new List(); - - target.PropertyChanged += (s, e) => raised.Add(e); - - target.BeginBatchUpdate(); - target.Bind(TestClass.BazProperty, observable, BindingPriority.LocalValue); - target.EndBatchUpdate(); - - Assert.Equal(1, raised.Count); - Assert.Equal(TestClass.BazProperty, raised[0].Property); - Assert.Equal(Orientation.Vertical, raised[0].OldValue); - Assert.Equal(Orientation.Horizontal, raised[0].NewValue); - Assert.Equal(Orientation.Horizontal, target.Baz); - } - - [Fact] - public void Binding_Completion_Should_Be_Raised_After_Batch_Update() - { - var target = new TestClass(); - var observable = new TestObservable("foo"); - var raised = new List(); - - target.Bind(TestClass.FooProperty, observable, BindingPriority.LocalValue); - target.PropertyChanged += (s, e) => raised.Add(e); - - target.BeginBatchUpdate(); - observable.OnCompleted(); - target.EndBatchUpdate(); - - Assert.Equal(1, raised.Count); - Assert.Null(target.Foo); - Assert.Equal("foo", raised[0].OldValue); - Assert.Null(raised[0].NewValue); - Assert.Equal(BindingPriority.Unset, raised[0].Priority); - } - - [Fact] - public void Binding_Disposal_Should_Be_Raised_After_Batch_Update() - { - var target = new TestClass(); - var observable = new TestObservable("foo"); - var raised = new List(); - - var sub = target.Bind(TestClass.FooProperty, observable, BindingPriority.LocalValue); - target.PropertyChanged += (s, e) => raised.Add(e); - - target.BeginBatchUpdate(); - sub.Dispose(); - target.EndBatchUpdate(); - - Assert.Equal(1, raised.Count); - Assert.Null(target.Foo); - Assert.Equal("foo", raised[0].OldValue); - Assert.Null(raised[0].NewValue); - Assert.Equal(BindingPriority.Unset, raised[0].Priority); - } - - [Fact] - public void ClearValue_Change_Should_Be_Raised_After_Batch_Update_1() - { - var target = new TestClass(); - var raised = new List(); - - target.Foo = "foo"; - target.PropertyChanged += (s, e) => raised.Add(e); - - target.BeginBatchUpdate(); - target.ClearValue(TestClass.FooProperty); - target.EndBatchUpdate(); - - Assert.Equal(1, raised.Count); - Assert.Null(target.Foo); - Assert.Equal("foo", raised[0].OldValue); - Assert.Null(raised[0].NewValue); - Assert.Equal(BindingPriority.Unset, raised[0].Priority); - } - - [Fact] - public void Bindings_Should_Be_Subscribed_Before_Batch_Update() - { - var target = new TestClass(); - var observable1 = new TestObservable("foo"); - var observable2 = new TestObservable("bar"); - - target.Bind(TestClass.FooProperty, observable1, BindingPriority.LocalValue); - target.Bind(TestClass.FooProperty, observable2, BindingPriority.LocalValue); - - Assert.Equal(1, observable1.SubscribeCount); - Assert.Equal(1, observable2.SubscribeCount); - } - - [Fact] - public void Non_Active_Binding_Should_Not_Be_Subscribed_Before_Batch_Update() - { - var target = new TestClass(); - var observable1 = new TestObservable("foo"); - var observable2 = new TestObservable("bar"); - - target.Bind(TestClass.FooProperty, observable1, BindingPriority.LocalValue); - target.Bind(TestClass.FooProperty, observable2, BindingPriority.Style); - - Assert.Equal(1, observable1.SubscribeCount); - Assert.Equal(0, observable2.SubscribeCount); - } - - [Fact] - public void LocalValue_Bindings_Should_Be_Subscribed_During_Batch_Update() - { - var target = new TestClass(); - var observable1 = new TestObservable("foo"); - var observable2 = new TestObservable("bar"); - var raised = new List(); - - target.PropertyChanged += (s, e) => raised.Add(e); - - // We need to subscribe to LocalValue bindings even if we've got a batch operation - // in progress because otherwise we don't know whether the binding or a subsequent - // SetValue with local priority will win. Notifications however shouldn't be sent. - target.BeginBatchUpdate(); - target.Bind(TestClass.FooProperty, observable1, BindingPriority.LocalValue); - target.Bind(TestClass.FooProperty, observable2, BindingPriority.LocalValue); - - Assert.Equal(1, observable1.SubscribeCount); - Assert.Equal(1, observable2.SubscribeCount); - Assert.Empty(raised); - } - - [Fact] - public void Style_Bindings_Should_Not_Be_Subscribed_During_Batch_Update() - { - var target = new TestClass(); - var observable1 = new TestObservable("foo"); - var observable2 = new TestObservable("bar"); - - target.BeginBatchUpdate(); - target.Bind(TestClass.FooProperty, observable1, BindingPriority.Style); - target.Bind(TestClass.FooProperty, observable2, BindingPriority.StyleTrigger); - - Assert.Equal(0, observable1.SubscribeCount); - Assert.Equal(0, observable2.SubscribeCount); - } - - [Fact] - public void Active_Style_Binding_Should_Be_Subscribed_After_Batch_Uppdate_1() - { - var target = new TestClass(); - var observable1 = new TestObservable("foo"); - var observable2 = new TestObservable("bar"); - - target.BeginBatchUpdate(); - target.Bind(TestClass.FooProperty, observable1, BindingPriority.Style); - target.Bind(TestClass.FooProperty, observable2, BindingPriority.Style); - target.EndBatchUpdate(); - - Assert.Equal(0, observable1.SubscribeCount); - Assert.Equal(1, observable2.SubscribeCount); - } - - [Fact] - public void Active_Style_Binding_Should_Be_Subscribed_After_Batch_Uppdate_2() - { - var target = new TestClass(); - var observable1 = new TestObservable("foo"); - var observable2 = new TestObservable("bar"); - - target.BeginBatchUpdate(); - target.Bind(TestClass.FooProperty, observable1, BindingPriority.StyleTrigger); - target.Bind(TestClass.FooProperty, observable2, BindingPriority.Style); - target.EndBatchUpdate(); - - Assert.Equal(1, observable1.SubscribeCount); - Assert.Equal(0, observable2.SubscribeCount); - } - - [Fact] - public void Change_Can_Be_Triggered_By_Ending_Batch_Update_1() - { - var target = new TestClass(); - var raised = new List(); - - target.PropertyChanged += (s, e) => raised.Add(e); - - target.BeginBatchUpdate(); - target.Foo = "foo"; - - target.PropertyChanged += (s, e) => - { - if (e.Property == TestClass.FooProperty && (string)e.NewValue == "foo") - target.Bar = "bar"; - }; - - target.EndBatchUpdate(); - - Assert.Equal("foo", target.Foo); - Assert.Equal("bar", target.Bar); - Assert.Equal(2, raised.Count); - Assert.Equal(TestClass.FooProperty, raised[0].Property); - Assert.Equal(TestClass.BarProperty, raised[1].Property); - } - - [Fact] - public void Change_Can_Be_Triggered_By_Ending_Batch_Update_2() - { - var target = new TestClass(); - var raised = new List(); - - target.PropertyChanged += (s, e) => raised.Add(e); - - target.BeginBatchUpdate(); - target.Foo = "foo"; - target.Bar = "baz"; - - target.PropertyChanged += (s, e) => - { - if (e.Property == TestClass.FooProperty && (string)e.NewValue == "foo") - target.Bar = "bar"; - }; - - target.EndBatchUpdate(); - - Assert.Equal("foo", target.Foo); - Assert.Equal("bar", target.Bar); - Assert.Equal(2, raised.Count); - } - - [Fact] - public void Batch_Update_Can_Be_Triggered_By_Ending_Batch_Update() - { - var target = new TestClass(); - var raised = new List(); - - target.PropertyChanged += (s, e) => raised.Add(e); - - target.BeginBatchUpdate(); - target.Foo = "foo"; - target.Bar = "baz"; - - // Simulates the following scenario: - // - A control is added to the logical tree - // - A batch update is started to apply styles - // - Ending the batch update triggers something which removes the control from the logical tree - // - A new batch update is started to detach styles - target.PropertyChanged += (s, e) => - { - if (e.Property == TestClass.FooProperty && (string)e.NewValue == "foo") - { - target.BeginBatchUpdate(); - target.ClearValue(TestClass.FooProperty); - target.ClearValue(TestClass.BarProperty); - target.EndBatchUpdate(); - } - }; - - target.EndBatchUpdate(); - - Assert.Null(target.Foo); - Assert.Null(target.Bar); - Assert.Equal(2, raised.Count); - Assert.Equal(TestClass.FooProperty, raised[0].Property); - Assert.Null(raised[0].OldValue); - Assert.Equal("foo", raised[0].NewValue); - Assert.Equal(TestClass.FooProperty, raised[1].Property); - Assert.Equal("foo", raised[1].OldValue); - Assert.Null(raised[1].NewValue); - } - - [Fact] - public void Can_Set_Cleared_Value_When_Ending_Batch_Update() - { - var target = new TestClass(); - var raised = 0; - - target.Foo = "foo"; - - target.BeginBatchUpdate(); - target.ClearValue(TestClass.FooProperty); - target.PropertyChanged += (sender, e) => - { - if (e.Property == TestClass.FooProperty && e.NewValue is null) - { - target.Foo = "bar"; - ++raised; - } - }; - target.EndBatchUpdate(); - - Assert.Equal("bar", target.Foo); - Assert.Equal(1, raised); - } - - [Fact] - public void Can_Bind_Cleared_Value_When_Ending_Batch_Update() - { - var target = new TestClass(); - var raised = 0; - var notifications = new List(); - - target.Foo = "foo"; - - target.BeginBatchUpdate(); - target.ClearValue(TestClass.FooProperty); - target.PropertyChanged += (sender, e) => - { - if (e.Property == TestClass.FooProperty && e.NewValue is null) - { - target.Bind(TestClass.FooProperty, new TestObservable("bar")); - ++raised; - } - - notifications.Add(e); - }; - target.EndBatchUpdate(); - - Assert.Equal("bar", target.Foo); - Assert.Equal(1, raised); - Assert.Equal(2, notifications.Count); - Assert.Equal(null, notifications[0].NewValue); - Assert.Equal("bar", notifications[1].NewValue); - } - - [Fact] - public void Can_Bind_Completed_Binding_Back_To_Original_Value_When_Ending_Batch_Update() - { - var target = new TestClass(); - var raised = 0; - var notifications = new List(); - var observable1 = new TestObservable("foo"); - var observable2 = new TestObservable("foo"); - - target.Bind(TestClass.FooProperty, observable1); - - target.BeginBatchUpdate(); - observable1.OnCompleted(); - target.PropertyChanged += (sender, e) => - { - if (e.Property == TestClass.FooProperty && e.NewValue is null) - { - target.Bind(TestClass.FooProperty, observable2); - ++raised; - } - - notifications.Add(e); - }; - target.EndBatchUpdate(); - - Assert.Equal("foo", target.Foo); - Assert.Equal(1, raised); - Assert.Equal(2, notifications.Count); - Assert.Equal(null, notifications[0].NewValue); - Assert.Equal("foo", notifications[1].NewValue); - } - - [Fact] - public void Can_Run_Empty_Batch_Update_When_Ending_Batch_Update() - { - var target = new TestClass(); - var raised = 0; - var notifications = new List(); - - target.Foo = "foo"; - target.Bar = "bar"; - - target.BeginBatchUpdate(); - target.ClearValue(TestClass.FooProperty); - target.ClearValue(TestClass.BarProperty); - target.PropertyChanged += (sender, e) => - { - if (e.Property == TestClass.BarProperty) - { - target.BeginBatchUpdate(); - target.EndBatchUpdate(); - } - - ++raised; - }; - target.EndBatchUpdate(); - - Assert.Null(target.Foo); - Assert.Null(target.Bar); - Assert.Equal(2, raised); - } - - public class TestClass : AvaloniaObject - { - public static readonly StyledProperty FooProperty = - AvaloniaProperty.Register(nameof(Foo)); - - public static readonly StyledProperty BarProperty = - AvaloniaProperty.Register(nameof(Bar)); - - public static readonly StyledProperty BazProperty = - AvaloniaProperty.Register(nameof(Bar), Orientation.Vertical); - - public string Foo - { - get => GetValue(FooProperty); - set => SetValue(FooProperty, value); - } - - public string Bar - { - get => GetValue(BarProperty); - set => SetValue(BarProperty, value); - } - - public Orientation Baz - { - get => GetValue(BazProperty); - set => SetValue(BazProperty, value); - } - } - - public class TestObservable : ObservableBase> - { - private readonly T _value; - private IObserver> _observer; - - public TestObservable(T value) => _value = value; - - public int SubscribeCount { get; private set; } - - public void OnCompleted() => _observer.OnCompleted(); - public void OnError(Exception e) => _observer.OnError(e); - - protected override IDisposable SubscribeCore(IObserver> observer) - { - ++SubscribeCount; - _observer = observer; - observer.OnNext(_value); - return Disposable.Empty; - } - } - } -} diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs index 6b4a6f89df..6db339c4cd 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Binding.cs @@ -4,18 +4,18 @@ using System.Reactive.Linq; using System.Reactive.Subjects; using System.Threading; using System.Threading.Tasks; - +using Avalonia.Base.UnitTests.Styling; using Avalonia.Controls; using Avalonia.Data; using Avalonia.Logging; using Avalonia.Platform; using Avalonia.Threading; using Avalonia.UnitTests; -using Avalonia.Utilities; -using Microsoft.Reactive.Testing; using Moq; using Xunit; +#nullable enable + namespace Avalonia.Base.UnitTests { public class AvaloniaObjectTests_Binding @@ -24,11 +24,10 @@ namespace Avalonia.Base.UnitTests public void Bind_Sets_Current_Value() { var target = new Class1(); - var source = new Class1(); + var source = new BehaviorSubject>("initial"); var property = Class1.FooProperty; - source.SetValue(property, "initial"); - target.Bind(property, source.GetObservable(property)); + target.Bind(property, source); Assert.Equal("initial", target.GetValue(property)); } @@ -38,18 +37,21 @@ namespace Avalonia.Base.UnitTests { var target = new Class1(); var source = new Subject>(); - bool raised = false; + var raised = 0; target.PropertyChanged += (s, e) => - raised = e.Property == Class1.FooProperty && - (string)e.OldValue == "foodefault" && - (string)e.NewValue == "newvalue" && - e.Priority == BindingPriority.LocalValue; + { + Assert.Equal(Class1.FooProperty, e.Property); + Assert.Equal("foodefault", (string?)e.OldValue); + Assert.Equal("newvalue", (string?)e.NewValue); + Assert.Equal(BindingPriority.LocalValue, e.Priority); + ++raised; + }; target.Bind(Class1.FooProperty, source); source.OnNext("newvalue"); - Assert.True(raised); + Assert.Equal(1, raised); } [Fact] @@ -71,7 +73,7 @@ namespace Avalonia.Base.UnitTests public void Setting_LocalValue_Overrides_Binding_Until_Binding_Produces_Next_Value() { var target = new Class1(); - var source = new Subject(); + var source = new Subject>(); var property = Class1.FooProperty; target.Bind(property, source); @@ -81,7 +83,7 @@ namespace Avalonia.Base.UnitTests target.SetValue(property, "bar"); Assert.Equal("bar", target.GetValue(property)); - source.OnNext("baz"); + source.OnNext("baz"); Assert.Equal("baz", target.GetValue(property)); } @@ -89,7 +91,7 @@ namespace Avalonia.Base.UnitTests public void Completing_LocalValue_Binding_Reverts_To_Default_Value_Even_When_Local_Value_Set_Earlier() { var target = new Class1(); - var source = new Subject(); + var source = new Subject>(); var property = Class1.FooProperty; target.Bind(property, source); @@ -102,10 +104,10 @@ namespace Avalonia.Base.UnitTests } [Fact] - public void Completing_LocalValue_Binding_Should_Not_Revert_To_Set_LocalValue() + public void Disposing_LocalValue_Binding_Should_Not_Revert_To_Set_LocalValue() { var target = new Class1(); - var source = new BehaviorSubject("bar"); + var source = new BehaviorSubject>("bar"); target.SetValue(Class1.FooProperty, "foo"); var sub = target.Bind(Class1.FooProperty, source); @@ -117,11 +119,43 @@ namespace Avalonia.Base.UnitTests Assert.Equal("foodefault", target.GetValue(Class1.FooProperty)); } + [Fact] + public void LocalValue_Binding_Should_Override_Style_Binding() + { + var target = new Class1(); + var source1 = new BehaviorSubject>("foo"); + var source2 = new BehaviorSubject>("bar"); + + target.Bind(Class1.FooProperty, source1, BindingPriority.Style); + + Assert.Equal("foo", target.GetValue(Class1.FooProperty)); + + target.Bind(Class1.FooProperty, source2, BindingPriority.LocalValue); + + Assert.Equal("bar", target.GetValue(Class1.FooProperty)); + } + + [Fact] + public void Style_Binding_Should_NotOverride_LocalValue_Binding() + { + var target = new Class1(); + var source1 = new BehaviorSubject>("foo"); + var source2 = new BehaviorSubject>("bar"); + + target.Bind(Class1.FooProperty, source1, BindingPriority.LocalValue); + + Assert.Equal("foo", target.GetValue(Class1.FooProperty)); + + target.Bind(Class1.FooProperty, source2, BindingPriority.Style); + + Assert.Equal("foo", target.GetValue(Class1.FooProperty)); + } + [Fact] public void Completing_Animation_Binding_Reverts_To_Set_LocalValue() { var target = new Class1(); - var source = new Subject(); + var source = new Subject>(); var property = Class1.FooProperty; target.SetValue(property, "foo"); @@ -192,7 +226,7 @@ namespace Avalonia.Base.UnitTests var property = Class1.FooProperty; var raised = 0; - target.Bind(property, new BehaviorSubject("bar"), BindingPriority.Style); + target.Bind(property, new BehaviorSubject>("bar"), BindingPriority.Style); target.Bind(property, source); Assert.Equal("foo", target.GetValue(property)); @@ -255,18 +289,18 @@ namespace Avalonia.Base.UnitTests } [Fact] - public void Second_LocalValue_Binding_Overrides_First() + public void Second_LocalValue_Binding_Unsubscribes_First() { var property = Class1.FooProperty; var target = new Class1(); - var source1 = new Subject(); - var source2 = new Subject(); + var source1 = new Subject>(); + var source2 = new Subject>(); target.Bind(property, source1, BindingPriority.LocalValue); target.Bind(property, source2, BindingPriority.LocalValue); source1.OnNext("foo"); - Assert.Equal("foo", target.GetValue(property)); + Assert.Equal("foodefault", target.GetValue(property)); source2.OnNext("bar"); Assert.Equal("bar", target.GetValue(property)); @@ -276,12 +310,12 @@ namespace Avalonia.Base.UnitTests } [Fact] - public void Completing_Second_LocalValue_Binding_Reverts_To_First() + public void Completing_Second_LocalValue_Binding_Doesnt_Revert_To_First() { var property = Class1.FooProperty; var target = new Class1(); - var source1 = new Subject(); - var source2 = new Subject(); + var source1 = new Subject>(); + var source2 = new Subject>(); target.Bind(property, source1, BindingPriority.LocalValue); target.Bind(property, source2, BindingPriority.LocalValue); @@ -291,7 +325,7 @@ namespace Avalonia.Base.UnitTests source1.OnNext("baz"); source2.OnCompleted(); - Assert.Equal("baz", target.GetValue(property)); + Assert.Equal("foodefault", target.GetValue(property)); } [Fact] @@ -299,8 +333,8 @@ namespace Avalonia.Base.UnitTests { var property = Class1.FooProperty; var target = new Class1(); - var source1 = new Subject(); - var source2 = new Subject(); + var source1 = new Subject>(); + var source2 = new Subject>(); target.Bind(property, source1, BindingPriority.Style); target.Bind(property, source2, BindingPriority.StyleTrigger); @@ -326,7 +360,19 @@ namespace Avalonia.Base.UnitTests } [Fact] - public void Bind_To_ValueType_Accepts_UnsetValue() + public void Bind_NonGeneric_Can_Set_Null_On_Reference_Type() + { + var target = new Class1(); + var source = new BehaviorSubject(null); + var property = Class1.FooProperty; + + target.Bind(property, source); + + Assert.Null(target.GetValue(property)); + } + + [Fact] + public void LocalValue_Bind_NonGeneric_To_ValueType_Accepts_UnsetValue() { var target = new Class1(); var source = new Subject(); @@ -339,6 +385,46 @@ namespace Avalonia.Base.UnitTests Assert.False(target.IsSet(Class1.QuxProperty)); } + [Fact] + public void Style_Bind_NonGeneric_To_ValueType_Accepts_UnsetValue() + { + var target = new Class1(); + var source = new Subject(); + + target.Bind(Class1.QuxProperty, source, BindingPriority.Style); + source.OnNext(6.7); + source.OnNext(AvaloniaProperty.UnsetValue); + + Assert.Equal(5.6, target.GetValue(Class1.QuxProperty)); + Assert.False(target.IsSet(Class1.QuxProperty)); + } + + [Fact] + public void LocalValue_Bind_NonGeneric_To_ValueType_Accepts_DoNothing() + { + var target = new Class1(); + var source = new Subject(); + + target.Bind(Class1.QuxProperty, source); + source.OnNext(6.7); + source.OnNext(BindingOperations.DoNothing); + + Assert.Equal(6.7, target.GetValue(Class1.QuxProperty)); + } + + [Fact] + public void Style_Bind_NonGeneric_To_ValueType_Accepts_DoNothing() + { + var target = new Class1(); + var source = new Subject(); + + target.Bind(Class1.QuxProperty, source, BindingPriority.Style); + source.OnNext(6.7); + source.OnNext(BindingOperations.DoNothing); + + Assert.Equal(6.7, target.GetValue(Class1.QuxProperty)); + } + [Fact] public void OneTime_Binding_Ignores_UnsetValue() { @@ -374,7 +460,7 @@ namespace Avalonia.Base.UnitTests { Class1 target = new Class1(); - target.Bind(Class2.BarProperty, Observable.Never().StartWith("foo")); + target.Bind(Class2.BarProperty, Observable.Never>().StartWith("foo")); Assert.Equal("foo", target.GetValue(Class2.BarProperty)); } @@ -403,17 +489,122 @@ namespace Avalonia.Base.UnitTests [Fact] public void Observable_Is_Unsubscribed_When_Subscription_Disposed() { - var scheduler = new TestScheduler(); - var source = scheduler.CreateColdObservable(); + var source = new TestSubject>("foo"); var target = new Class1(); var subscription = target.Bind(Class1.FooProperty, source); - Assert.Equal(1, source.Subscriptions.Count); - Assert.Equal(Subscription.Infinite, source.Subscriptions[0].Unsubscribe); + Assert.Equal(1, source.SubscriberCount); subscription.Dispose(); - Assert.Equal(1, source.Subscriptions.Count); - Assert.Equal(0, source.Subscriptions[0].Unsubscribe); + Assert.Equal(0, source.SubscriberCount); + } + + [Theory] + [InlineData(BindingPriority.LocalValue)] + [InlineData(BindingPriority.Style)] + [InlineData(BindingPriority.Animation)] + public void Observable_Is_Unsubscribed_When_New_Binding_Of_Same_Priority_Is_Added(BindingPriority priority) + { + var source1 = new TestSubject>("foo"); + var source2 = new TestSubject>("bar"); + var target = new Class1(); + + target.Bind(Class1.FooProperty, source1, priority); + Assert.Equal(1, source1.SubscriberCount); + + target.Bind(Class1.FooProperty, source2, priority); + Assert.Equal(1, source2.SubscriberCount); + Assert.Equal(0, source1.SubscriberCount); + } + + [Theory] + [InlineData(BindingPriority.Style)] + public void Observable_Is_Unsubscribed_When_New_Binding_Of_Higher_Priority_Is_Added(BindingPriority priority) + { + var source1 = new TestSubject>("foo"); + var source2 = new TestSubject>("bar"); + var target = new Class1(); + + target.Bind(Class1.FooProperty, source1, priority); + Assert.Equal(1, source1.SubscriberCount); + + target.Bind(Class1.FooProperty, source2, priority - 1); + Assert.Equal(1, source2.SubscriberCount); + Assert.Equal(0, source1.SubscriberCount); + } + + [Theory] + [InlineData(BindingPriority.Style)] + [InlineData(BindingPriority.Animation)] + public void Observable_Is_Unsubscribed_When_New_Value_Of_Same_Priority_Is_Added(BindingPriority priority) + { + var source = new TestSubject>("foo"); + var target = new Class1(); + + target.Bind(Class1.FooProperty, source, priority); + Assert.Equal(1, source.SubscriberCount); + + target.SetValue(Class1.FooProperty, "foo", priority); + Assert.Equal(0, source.SubscriberCount); + } + + [Theory] + [InlineData(BindingPriority.Style)] + public void Observable_Is_Unsubscribed_When_New_Value_Of_Higher_Priority_Is_Added(BindingPriority priority) + { + var source = new TestSubject>("foo"); + var target = new Class1(); + + target.Bind(Class1.FooProperty, source, priority); + Assert.Equal(1, source.SubscriberCount); + + target.SetValue(Class1.FooProperty, "foo", priority - 1); + Assert.Equal(0, source.SubscriberCount); + } + + [Theory] + [InlineData(BindingPriority.LocalValue)] + [InlineData(BindingPriority.Style)] + public void Observable_Is_Not_Unsubscribed_When_Animation_Value_Is_Set(BindingPriority priority) + { + var source = new TestSubject>("foo"); + var target = new Class1(); + + target.Bind(Class1.FooProperty, source, priority); + Assert.Equal(1, source.SubscriberCount); + + target.SetValue(Class1.FooProperty, "bar", BindingPriority.Animation); + Assert.Equal(1, source.SubscriberCount); + } + + [Theory] + [InlineData(BindingPriority.LocalValue)] + [InlineData(BindingPriority.Style)] + public void Observable_Is_Not_Unsubscribed_When_Animation_Binding_Is_Added(BindingPriority priority) + { + var source1 = new TestSubject>("foo"); + var source2 = new TestSubject>("bar"); + var target = new Class1(); + + target.Bind(Class1.FooProperty, source1, priority); + Assert.Equal(1, source1.SubscriberCount); + + target.Bind(Class1.FooProperty, source2, BindingPriority.Animation); + Assert.Equal(1, source1.SubscriberCount); + Assert.Equal(1, source2.SubscriberCount); + } + + [Fact] + public void LocalValue_Binding_Is_Not_Unsubscribed_When_LocalValue_Is_Set() + { + var source = new TestSubject>("foo"); + var target = new Class1(); + + target.Bind(Class1.FooProperty, source); + Assert.Equal(1, source.SubscriberCount); + + target.SetValue(Class1.FooProperty, "foo"); + Assert.Equal(1, source.SubscriberCount); } [Fact] @@ -482,7 +673,7 @@ namespace Avalonia.Base.UnitTests public void Local_Binding_Overwrites_Local_Value() { var target = new Class1(); - var binding = new Subject(); + var binding = new Subject>(); target.Bind(Class1.FooProperty, binding); @@ -660,6 +851,75 @@ namespace Avalonia.Base.UnitTests } } + [Fact] + public void Untyped_LocalValue_Binding_Logs_Invalid_Value_Type() + { + var target = new Class1(); + var source = new Subject(); + var called = false; + var expectedMessageTemplate = "Error in binding to {Target}.{Property}: expected {ExpectedType}, got {Value} ({ValueType})"; + + LogCallback checkLogMessage = (level, area, src, mt, pv) => + { + if (level == LogEventLevel.Warning && + area == LogArea.Binding && + mt == expectedMessageTemplate && + src == target && + pv[0].GetType() == typeof(Class1) && + (AvaloniaProperty)pv[1] == Class1.QuxProperty && + (Type)pv[2] == typeof(double) && + (string)pv[3] == "foo" && + (Type)pv[4] == typeof(string)) + { + called = true; + } + }; + + using (TestLogSink.Start(checkLogMessage)) + { + target.Bind(Class1.QuxProperty, source); + source.OnNext(1.2); + source.OnNext("foo"); + + Assert.Equal(5.6, target.GetValue(Class1.QuxProperty)); + Assert.True(called); + } + } + + [Fact] + public void Untyped_Style_Binding_Logs_Invalid_Value_Type() + { + var target = new Class1(); + var source = new Subject(); + var called = false; + var expectedMessageTemplate = "Error in binding to {Target}.{Property}: {Message}"; + var expectedMessage = "Unable to convert object 'foo' of type 'System.String' to type 'System.Double'."; + + LogCallback checkLogMessage = (level, area, src, mt, pv) => + { + if (level == LogEventLevel.Warning && + area == LogArea.Binding && + mt == expectedMessageTemplate && + src == target && + pv[0].GetType() == typeof(Class1) && + (AvaloniaProperty)pv[1] == Class1.QuxProperty && + (string)pv[2] == expectedMessage) + { + called = true; + } + }; + + using (TestLogSink.Start(checkLogMessage)) + { + target.Bind(Class1.QuxProperty, source, BindingPriority.Style); + source.OnNext(1.2); + source.OnNext("foo"); + + Assert.Equal(5.6, target.GetValue(Class1.QuxProperty)); + Assert.True(called); + } + } + [Fact] public async Task Bind_With_Scheduler_Executes_On_Scheduler() { @@ -726,8 +986,9 @@ namespace Avalonia.Base.UnitTests public void IsAnimating_On_Property_With_Animation_Value_Returns_True() { var target = new Class1(); + var source = new BehaviorSubject>("foo"); - target.SetValue(Class1.FooProperty, "foo", BindingPriority.Animation); + target.Bind(Class1.FooProperty, source, BindingPriority.Animation); Assert.True(target.IsAnimating(Class1.FooProperty)); } @@ -778,6 +1039,20 @@ namespace Avalonia.Base.UnitTests Assert.True(target.IsAnimating(Class1.FooProperty)); } + [Fact] + public void TwoWay_Binding_Should_Update_Source() + { + var target = new Class1(); + var source = new TestTwoWayBindingViewModel(); + + target.Bind(Class1.DoubleValueProperty, new Binding(nameof(source.Value), BindingMode.TwoWay) { Source = source }); + + target.DoubleValue = 123.4; + + Assert.True(source.SetterCalled); + Assert.Equal(source.Value, 123.4); + } + [Fact] public void TwoWay_Binding_Should_Not_Call_Setter_On_Creation() { @@ -786,7 +1061,7 @@ namespace Avalonia.Base.UnitTests target.Bind(Class1.DoubleValueProperty, new Binding(nameof(source.Value), BindingMode.TwoWay) { Source = source }); - Assert.False(source.ValueSetterCalled); + Assert.False(source.SetterCalled); } [Fact] @@ -797,7 +1072,7 @@ namespace Avalonia.Base.UnitTests target.Bind(Class1.DoubleValueProperty, new Binding("[0]", BindingMode.TwoWay) { Source = source }); - Assert.False(source.ValueSetterCalled); + Assert.False(source.SetterCalled); } [Fact] @@ -818,69 +1093,111 @@ namespace Avalonia.Base.UnitTests target.Bind(TextBlock.TextProperty, new Binding("[0]", BindingMode.TwoWay)); } - [Fact] - public void Disposing_Completed_Binding_Does_Not_Throw() + [Theory(Skip = "Will need changes to binding internals in order to pass")] + [InlineData(BindingPriority.LocalValue)] + [InlineData(BindingPriority.StyleTrigger)] + [InlineData(BindingPriority.Style)] + public void TwoWay_Binding_Should_Not_Update_Source_When_Higher_Priority_Value_Set(BindingPriority priority) { var target = new Class1(); - var source = new Subject(); - var subscription = target.Bind(Class1.FooProperty, source); + var source = new TestTwoWayBindingViewModel(); + var binding = new Binding(nameof(source.Value), BindingMode.TwoWay) { Source = source }; - source.OnCompleted(); + target.Bind(Class1.DoubleValueProperty, binding, priority); + target.SetValue(Class1.DoubleValueProperty, 123.4, priority - 1); - subscription.Dispose(); + // Setter should not be called because the TwoWay binding with LocalValue priority + // should be overridden by the animated value and the binding made inactive. + Assert.False(source.SetterCalled); } - [Fact] - public void TwoWay_Binding_Should_Not_Call_Setter_On_Creation_With_Value() + [Theory(Skip = "Will need changes to binding internals in order to pass")] + [InlineData(BindingPriority.LocalValue)] + [InlineData(BindingPriority.StyleTrigger)] + [InlineData(BindingPriority.Style)] + public void TwoWay_Binding_Should_Not_Update_Source_When_Higher_Priority_Binding_Added(BindingPriority priority) + { + var target = new Class1(); + var source = new TestTwoWayBindingViewModel(); + var binding1 = new Binding(nameof(source.Value), BindingMode.TwoWay) { Source = source }; + var binding2 = new BehaviorSubject(123.4); + + target.Bind(Class1.DoubleValueProperty, binding1, priority); + target.Bind(Class1.DoubleValueProperty, binding2, priority - 1); + + // Setter should not be called because the TwoWay binding with LocalValue priority + // should be overridden by the animated binding and the binding made inactive. + Assert.False(source.SetterCalled); + } + + [Fact(Skip = "Will need changes to binding internals in order to pass")] + public void TwoWay_Style_Binding_Should_Not_Update_Source_When_StyleTrigger_Value_Set() { var target = new Class1(); - var source = new TestTwoWayBindingViewModel() { Value = 1 }; - source.ResetSetterCalled(); + var source = new TestTwoWayBindingViewModel(); target.Bind(Class1.DoubleValueProperty, new Binding(nameof(source.Value), BindingMode.TwoWay) { Source = source }); + target.SetValue(Class1.DoubleValueProperty, 123.4, BindingPriority.Animation); - Assert.False(source.ValueSetterCalled); + // Setter should not be called because the TwoWay binding with Style priority + // should be overridden by the animated value and the binding made inactive. + Assert.False(source.SetterCalled); } - [Fact] - public void TwoWay_Binding_Should_Not_Call_Setter_On_Creation_Indexer_With_Value() + [Fact(Skip = "Will need changes to binding internals in order to pass")] + public void TwoWay_Style_Binding_Should_Not_Update_Source_When_Animated_Binding_Added() { var target = new Class1(); - var source = new TestTwoWayBindingViewModel() { [0] = 1 }; - source.ResetSetterCalled(); + var source1 = new TestTwoWayBindingViewModel(); + var source2 = new BehaviorSubject(123.4); - target.Bind(Class1.DoubleValueProperty, new Binding("[0]", BindingMode.TwoWay) { Source = source }); + target.Bind(Class1.DoubleValueProperty, new Binding(nameof(source1.Value), BindingMode.TwoWay) { Source = source1 }); + target.Bind(Class1.DoubleValueProperty, source2, BindingPriority.Animation); - Assert.False(source.ValueSetterCalled); + // Setter should not be called because the TwoWay binding with Style priority + // should be overridden by the animated binding and the binding made inactive. + Assert.False(source1.SetterCalled); } + [Fact] + public void Disposing_Completed_Binding_Does_Not_Throw() + { + var target = new Class1(); + var source = new Subject>(); + var subscription = target.Bind(Class1.FooProperty, source); + + source.OnCompleted(); + + subscription.Dispose(); + } [Fact] - public void Disposing_a_TwoWay_Binding_Should_Set_Default_Value_On_Binding_Target_But_Not_On_Source() + public void Produces_Correct_Values_And_Base_Values_With_Multiple_Animation_Bindings() { - var target = new Class3(); + var target = new Class1(); + var source1 = new BehaviorSubject>(12.2); + var source2 = new BehaviorSubject>(13.3); - // Create a source class which has a Value set to -1 and a Minimum set to -2 - var source = new TestTwoWayBindingViewModel() { Value = -1, Minimum = -2 }; + target.SetValue(Class1.QuxProperty, 11.1); + target.Bind(Class1.QuxProperty, source1, BindingPriority.Animation); - // Reset the setter counter - source.ResetSetterCalled(); + Assert.Equal(12.2, target.GetValue(Class1.QuxProperty)); + Assert.Equal(11.1, target.GetBaseValue(Class1.QuxProperty)); - // 1. bind the minimum - var disposable_1 = target.Bind(Class3.MinimumProperty, new Binding("Minimum", BindingMode.TwoWay) { Source = source }); - // 2. Bind the value - var disposable_2 = target.Bind(Class3.ValueProperty, new Binding("Value", BindingMode.TwoWay) { Source = source }); + target.Bind(Class1.QuxProperty, source2, BindingPriority.Animation); - // Dispose the minimum binding - disposable_1.Dispose(); - // Dispose the value binding - disposable_2.Dispose(); + Assert.Equal(13.3, target.GetValue(Class1.QuxProperty)); + Assert.Equal(11.1, target.GetBaseValue(Class1.QuxProperty)); + source2.OnCompleted(); - // The value setter should be called here as we have disposed minimum fist and the default value of minimum is 0, so this should be changed. - Assert.True(source.ValueSetterCalled); - // The minimum value should not be changed in the source. - Assert.False(source.MinimumSetterCalled); + Assert.Equal(12.2, target.GetValue(Class1.QuxProperty)); + Assert.Equal(11.1, target.GetBaseValue(Class1.QuxProperty)); + + source1.OnCompleted(); + + Assert.Equal(11.1, target.GetValue(Class1.QuxProperty)); + Assert.Equal(11.1, target.GetBaseValue(Class1.QuxProperty)); } /// @@ -889,9 +1206,9 @@ namespace Avalonia.Base.UnitTests /// The type of the observable. /// The value. /// The observable. - private IObservable Single(T value) + private IObservable> Single(T value) { - return Observable.Never().StartWith(value); + return Observable.Never>().StartWith(value); } private class Class1 : AvaloniaObject @@ -918,56 +1235,6 @@ namespace Avalonia.Base.UnitTests AvaloniaProperty.Register("Bar", "bardefault"); } - private class Class3 : AvaloniaObject - { - static Class3() - { - MinimumProperty.Changed.Subscribe(x => OnMinimumChanged(x)); - } - - private static void OnMinimumChanged(AvaloniaPropertyChangedEventArgs e) - { - if (e.Sender is Class3 s) - { - s.SetValue(ValueProperty, MathUtilities.Clamp(s.Value, e.NewValue.Value, double.PositiveInfinity)); - } - } - - /// - /// Defines the property. - /// - public static readonly StyledProperty ValueProperty = - AvaloniaProperty.Register(nameof(Value), 0); - - /// - /// Gets or sets the Value property - /// - public double Value - { - get { return GetValue(ValueProperty); } - set { SetValue(ValueProperty, value); } - } - - - /// - /// Defines the property. - /// - public static readonly StyledProperty MinimumProperty = - AvaloniaProperty.Register(nameof(Minimum), 0); - - /// - /// Gets or sets the minimum property - /// - public double Minimum - { - get { return GetValue(MinimumProperty); } - set { SetValue(MinimumProperty, value); } - } - - - } - - private class TestOneTimeBinding : IBinding { private IObservable _source; @@ -979,8 +1246,8 @@ namespace Avalonia.Base.UnitTests public InstancedBinding Initiate( IAvaloniaObject target, - AvaloniaProperty targetProperty, - object anchor = null, + AvaloniaProperty? targetProperty, + object? anchor = null, bool enableDataValidation = false) { return InstancedBinding.OneTime(_source); @@ -995,7 +1262,7 @@ namespace Avalonia.Base.UnitTests private double _value; - public event PropertyChangedEventHandler PropertyChanged; + public event PropertyChangedEventHandler? PropertyChanged; public double Value { @@ -1008,8 +1275,10 @@ namespace Avalonia.Base.UnitTests if (SetterInvokedCount < MaxInvokedCount) { _value = (int)value; - if (_value > 75) _value = 75; - if (_value < 25) _value = 25; + if (_value > 75) + _value = 75; + if (_value < 25) + _value = 25; } else { @@ -1032,18 +1301,7 @@ namespace Avalonia.Base.UnitTests set { _value = value; - ValueSetterCalled = true; - } - } - - private double _minimum; - public double Minimum - { - get => _minimum; - set - { - _minimum = value; - MinimumSetterCalled = true; + SetterCalled = true; } } @@ -1053,18 +1311,11 @@ namespace Avalonia.Base.UnitTests set { _value = value; - ValueSetterCalled = true; + SetterCalled = true; } } - public bool ValueSetterCalled { get; private set; } - public bool MinimumSetterCalled { get; private set; } - - public void ResetSetterCalled() - { - ValueSetterCalled = false; - MinimumSetterCalled = false; - } + public bool SetterCalled { get; private set; } } } } diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Coercion.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Coercion.cs index e2e87d4dfa..2e1944fc76 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Coercion.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Coercion.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Reactive.Subjects; using Avalonia.Data; using Xunit; @@ -52,6 +53,64 @@ namespace Avalonia.Base.UnitTests Assert.Equal(50, target.Foo); } + [Fact] + public void CoerceValue_Updates_Base_Value() + { + var target = new Class1 { Foo = 99 }; + + target.SetValue(Class1.FooProperty, 88, BindingPriority.Animation); + + Assert.Equal(88, target.Foo); + Assert.Equal(99, target.GetBaseValue(Class1.FooProperty)); + + target.MaxFoo = 50; + target.CoerceValue(Class1.FooProperty); + + Assert.Equal(50, target.Foo); + Assert.Equal(50, target.GetBaseValue(Class1.FooProperty)); + } + + [Fact] + public void CoerceValue_Raises_PropertyChanged() + { + var target = new Class1 { Foo = 99 }; + var raised = 0; + + target.PropertyChanged += (s, e) => + { + Assert.Equal(Class1.FooProperty, e.Property); + Assert.Equal(99, e.OldValue); + Assert.Equal(50, e.NewValue); + Assert.Equal(BindingPriority.LocalValue, e.Priority); + ++raised; + }; + + Assert.Equal(99, target.Foo); + + target.MaxFoo = 50; + target.CoerceValue(Class1.FooProperty); + + Assert.Equal(50, target.Foo); + Assert.Equal(1, raised); + } + + [Fact] + public void CoerceValue_Raises_PropertyChangedCore_For_Base_Value() + { + var target = new Class1 { Foo = 99 }; + + target.SetValue(Class1.FooProperty, 88, BindingPriority.Animation); + + Assert.Equal(88, target.Foo); + Assert.Equal(99, target.GetBaseValue(Class1.FooProperty)); + + target.MaxFoo = 50; + target.CoreChanges.Clear(); + target.CoerceValue(Class1.FooProperty); + + Assert.Equal(2, target.CoreChanges.Count); + } + [Fact] public void Coerced_Value_Can_Be_Restored_If_Limit_Changed() { @@ -73,7 +132,7 @@ namespace Avalonia.Base.UnitTests var source1 = new Subject>(); var source2 = new Subject>(); - target.Bind(Class1.FooProperty, source1); + target.Bind(Class1.FooProperty, source1, BindingPriority.Style); source1.OnNext(150); target.Bind(Class1.FooProperty, source2); @@ -87,6 +146,32 @@ namespace Avalonia.Base.UnitTests Assert.Equal(150, target.Foo); } + [Fact] + public void CoerceValue_Updates_Inherited_Value() + { + var parent = new Class1 { Inherited = 99 }; + var child = new AvaloniaObject { InheritanceParent = parent }; + var raised = 0; + + child.InheritanceParent = parent; + child.PropertyChanged += (s, e) => + { + Assert.Equal(Class1.InheritedProperty, e.Property); + Assert.Equal(99, e.OldValue); + Assert.Equal(50, e.NewValue); + Assert.Equal(BindingPriority.Inherited, e.Priority); + ++raised; + }; + + Assert.Equal(99, child.GetValue(Class1.InheritedProperty)); + + parent.MaxFoo = 50; + parent.CoerceValue(Class1.InheritedProperty); + + Assert.Equal(50, child.GetValue(Class1.InheritedProperty)); + Assert.Equal(1, raised); + } + [Fact] public void Coercion_Can_Be_Overridden() { @@ -111,18 +196,51 @@ namespace Avalonia.Base.UnitTests defaultValue: 11, coerce: CoerceFoo); + public static readonly StyledProperty InheritedProperty = + AvaloniaProperty.RegisterAttached( + "Attached", + defaultValue: 11, + inherits: true, + coerce: CoerceFoo); + public int Foo { get => GetValue(FooProperty); set => SetValue(FooProperty, value); } + public int Inherited + { + get => GetValue(InheritedProperty); + set => SetValue(InheritedProperty, value); + } + public int MaxFoo { get; set; } = 100; + public List CoreChanges { get; } = new(); + public static int CoerceFoo(IAvaloniaObject instance, int value) { return Math.Min(((Class1)instance).MaxFoo, value); } + + protected override void OnPropertyChangedCore(AvaloniaPropertyChangedEventArgs change) + { + CoreChanges.Add(Clone(change)); + base.OnPropertyChangedCore(change); + } + + private static AvaloniaPropertyChangedEventArgs Clone(AvaloniaPropertyChangedEventArgs change) + { + var e = (AvaloniaPropertyChangedEventArgs)change; + return new AvaloniaPropertyChangedEventArgs( + change.Sender, + e.Property, + e.OldValue, + e.NewValue, + change.Priority, + change.IsEffectiveValueChange); + } } private class Class2 : AvaloniaObject diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_GetValue.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_GetValue.cs index 6bd29a1577..c20b75443c 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_GetValue.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_GetValue.cs @@ -65,53 +65,42 @@ namespace Avalonia.Base.UnitTests } [Fact] - public void GetBaseValue_LocalValue_Ignores_Default_Value() + public void GetBaseValue_Ignores_Default_Value() { var target = new Class3(); target.SetValue(Class1.FooProperty, "animated", BindingPriority.Animation); - Assert.False(target.GetBaseValue(Class1.FooProperty, BindingPriority.LocalValue).HasValue); + Assert.False(target.GetBaseValue(Class1.FooProperty).HasValue); } [Fact] - public void GetBaseValue_LocalValue_Returns_Local_Value() + public void GetBaseValue_Returns_Local_Value() { var target = new Class3(); target.SetValue(Class1.FooProperty, "local"); target.SetValue(Class1.FooProperty, "animated", BindingPriority.Animation); - Assert.Equal("local", target.GetBaseValue(Class1.FooProperty, BindingPriority.LocalValue).Value); + Assert.Equal("local", target.GetBaseValue(Class1.FooProperty).Value); } [Fact] - public void GetBaseValue_LocalValue_Returns_Style_Value() + public void GetBaseValue_Returns_Style_Value() { var target = new Class3(); target.SetValue(Class1.FooProperty, "style", BindingPriority.Style); target.SetValue(Class1.FooProperty, "animated", BindingPriority.Animation); - Assert.Equal("style", target.GetBaseValue(Class1.FooProperty, BindingPriority.LocalValue).Value); + Assert.Equal("style", target.GetBaseValue(Class1.FooProperty).Value); } [Fact] - public void GetBaseValue_Style_Ignores_LocalValue_Animated_Value() + public void GetBaseValue_Returns_Style_Value_Set_Via_Untyped_Setters() { var target = new Class3(); - target.Bind(Class1.FooProperty, new BehaviorSubject("animated"), BindingPriority.Animation); - target.SetValue(Class1.FooProperty, "local"); - Assert.False(target.GetBaseValue(Class1.FooProperty, BindingPriority.Style).HasValue); - } - - [Fact] - public void GetBaseValue_Style_Returns_Style_Value() - { - var target = new Class3(); - - target.SetValue(Class1.FooProperty, "local"); - target.SetValue(Class1.FooProperty, "style", BindingPriority.Style); - target.Bind(Class1.FooProperty, new BehaviorSubject("animated"), BindingPriority.Animation); - Assert.Equal("style", target.GetBaseValue(Class1.FooProperty, BindingPriority.Style)); + target.SetValue(Class1.FooProperty, (object)"style", BindingPriority.Style); + target.SetValue(Class1.FooProperty, (object)"animated", BindingPriority.Animation); + Assert.Equal("style", target.GetBaseValue(Class1.FooProperty).Value); } private class Class1 : AvaloniaObject diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Inheritance.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Inheritance.cs index 9e9ae4ec74..c5cbda5325 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Inheritance.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Inheritance.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Avalonia.Data; using Xunit; namespace Avalonia.Base.UnitTests @@ -6,7 +7,28 @@ namespace Avalonia.Base.UnitTests public class AvaloniaObjectTests_Inheritance { [Fact] - public void GetValue_Returns_Inherited_Value() + public void GetValue_Returns_Inherited_Value_1() + { + Class1 parent = new Class1(); + parent.SetValue(Class1.BazProperty, "changed"); + + Class2 child = new Class2 { Parent = parent }; + Assert.Equal("changed", child.GetValue(Class1.BazProperty)); + } + + [Fact] + public void GetValue_Returns_Inherited_Value_2() + { + Class1 parent = new Class1(); + Class2 child = new Class2 { Parent = parent }; + + parent.SetValue(Class1.BazProperty, "changed"); + + Assert.Equal("changed", child.GetValue(Class1.BazProperty)); + } + + [Fact] + public void ClearValue_Clears_Inherited_Value() { Class1 parent = new Class1(); Class2 child = new Class2 { Parent = parent }; @@ -14,10 +36,62 @@ namespace Avalonia.Base.UnitTests parent.SetValue(Class1.BazProperty, "changed"); Assert.Equal("changed", child.GetValue(Class1.BazProperty)); + + parent.ClearValue(Class1.BazProperty); + + Assert.Equal("bazdefault", parent.GetValue(Class1.BazProperty)); + Assert.Equal("bazdefault", child.GetValue(Class1.BazProperty)); + } + + [Fact] + public void ClearValue_On_Parent_Raises_PropertyChanged_On_Child() + { + Class1 parent = new Class1(); + Class2 child = new Class2 { Parent = parent }; + var raised = 0; + + parent.SetValue(Class1.BazProperty, "changed"); + + child.PropertyChanged += (s, e) => + { + Assert.Same(child, e.Sender); + Assert.Equal("changed", e.OldValue); + Assert.Equal("bazdefault", e.NewValue); + Assert.Equal(BindingPriority.Inherited, e.Priority); + ++raised; + }; + + parent.ClearValue(Class1.BazProperty); + + Assert.Equal(1, raised); + } + + [Fact] + public void ClearValue_On_Child_Raises_PropertyChanged_With_Inherited_Parent_Value() + { + Class1 parent = new Class1(); + Class2 child = new Class2 { Parent = parent }; + var raised = 0; + + parent.SetValue(Class1.BazProperty, "parent"); + child.SetValue(Class1.BazProperty, "child"); + + child.PropertyChanged += (s, e) => + { + Assert.Same(child, e.Sender); + Assert.Equal("child", e.OldValue); + Assert.Equal("parent", e.NewValue); + Assert.Equal(BindingPriority.Inherited, e.Priority); + ++raised; + }; + + child.ClearValue(Class1.BazProperty); + + Assert.Equal(1, raised); } [Fact] - public void Setting_InheritanceParent_Raises_PropertyChanged_When_Value_Changed_In_Parent() + public void Setting_InheritanceParent_Raises_PropertyChanged_When_Parent_Has_Value_Set() { bool raised = false; @@ -29,15 +103,41 @@ namespace Avalonia.Base.UnitTests raised = s == child && e.Property == Class1.BazProperty && (string)e.OldValue == "bazdefault" && - (string)e.NewValue == "changed"; + (string)e.NewValue == "changed" && + e.Priority == BindingPriority.Inherited; + + child.Parent = parent; + + Assert.True(raised); + Assert.Equal("changed", child.GetValue(Class1.BazProperty)); + } + + [Fact] + public void Setting_InheritanceParent_Raises_PropertyChanged_When_Parent_And_Grandparent_Has_Value_Set() + { + Class1 grandparent = new Class1(); + Class2 parent = new Class2 { Parent = grandparent }; + bool raised = false; + + grandparent.SetValue(Class1.BazProperty, "changed1"); + parent.SetValue(Class1.BazProperty, "changed2"); + + Class2 child = new Class2(); + child.PropertyChanged += (s, e) => + raised = s == child && + e.Property == Class1.BazProperty && + (string)e.OldValue == "bazdefault" && + (string)e.NewValue == "changed2" && + e.Priority == BindingPriority.Inherited; child.Parent = parent; Assert.True(raised); + Assert.Equal("changed2", child.GetValue(Class1.BazProperty)); } [Fact] - public void Setting_InheritanceParent_Raises_PropertyChanged_For_Attached_Property_When_Value_Changed_In_Parent() + public void Setting_InheritanceParent_Raises_PropertyChanged_For_Attached_Property_When_Parent_Has_Value_Set() { bool raised = false; @@ -54,6 +154,7 @@ namespace Avalonia.Base.UnitTests child.Parent = parent; Assert.True(raised); + Assert.Equal("changed", child.GetValue(AttachedOwner.AttachedProperty)); } [Fact] @@ -71,6 +172,7 @@ namespace Avalonia.Base.UnitTests child.Parent = parent; Assert.False(raised); + Assert.Equal("localvalue", child.GetValue(Class1.BazProperty)); } [Fact] @@ -91,6 +193,7 @@ namespace Avalonia.Base.UnitTests parent.SetValue(Class1.BazProperty, "changed"); Assert.True(raised); + Assert.Equal("changed", child.GetValue(Class1.BazProperty)); } [Fact] @@ -111,6 +214,29 @@ namespace Avalonia.Base.UnitTests parent.SetValue(AttachedOwner.AttachedProperty, "changed"); Assert.True(raised); + Assert.Equal("changed", child.GetValue(AttachedOwner.AttachedProperty)); + } + + [Fact] + public void Clearing_Value_In_InheritanceParent_Raises_PropertyChanged() + { + bool raised = false; + + Class1 parent = new Class1(); + parent.SetValue(Class1.BazProperty, "changed"); + + Class2 child = new Class2 { Parent = parent }; + + child.PropertyChanged += (s, e) => + raised = s == child && + e.Property == Class1.BazProperty && + (string)e.OldValue == "changed" && + (string)e.NewValue == "bazdefault"; + + parent.ClearValue(Class1.BazProperty); + + Assert.True(raised); + Assert.Equal("bazdefault", child.GetValue(Class1.BazProperty)); } [Fact] @@ -128,6 +254,85 @@ namespace Avalonia.Base.UnitTests Assert.Equal(new[] { parent, child }, result); } + [Fact] + public void Reparenting_Raises_PropertyChanged_For_Old_And_New_Inherited_Values() + { + var oldParent = new Class1(); + oldParent.SetValue(Class1.BazProperty, "oldvalue"); + + var newParent = new Class1(); + newParent.SetValue(Class1.BazProperty, "newvalue"); + + var child = new Class2 { Parent = oldParent }; + var raised = 0; + + child.PropertyChanged += (s, e) => + { + Assert.Equal(child, e.Sender); + Assert.Equal("oldvalue", e.GetOldValue()); + Assert.Equal("newvalue", e.GetNewValue()); + Assert.Equal(BindingPriority.Inherited, e.Priority); + ++raised; + }; + + child.Parent = newParent; + + Assert.Equal(1, raised); + Assert.Equal("newvalue", child.GetValue(Class1.BazProperty)); + } + + [Fact] + public void Reparenting_Raises_PropertyChanged_On_GrandChild_For_Old_And_New_Inherited_Values() + { + var oldParent = new Class1(); + oldParent.SetValue(Class1.BazProperty, "oldvalue"); + + var newParent = new Class1(); + newParent.SetValue(Class1.BazProperty, "newvalue"); + + var child = new Class2 { Parent = oldParent }; + var grandchild = new Class2 { Parent = child }; + var raised = 0; + + grandchild.PropertyChanged += (s, e) => + { + Assert.Equal(grandchild, e.Sender); + Assert.Equal("oldvalue", e.GetOldValue()); + Assert.Equal("newvalue", e.GetNewValue()); + Assert.Equal(BindingPriority.Inherited, e.Priority); + ++raised; + }; + + child.Parent = newParent; + + Assert.Equal(1, raised); + Assert.Equal("newvalue", grandchild.GetValue(Class1.BazProperty)); + } + + [Fact] + public void Reparenting_Retains_Inherited_Property_Set_On_Child() + { + var oldParent = new Class1(); + oldParent.SetValue(Class1.BazProperty, "oldvalue"); + + var newParent = new Class1(); + newParent.SetValue(Class1.BazProperty, "newvalue"); + + var child = new Class2 { Parent = oldParent }; + child.SetValue(Class1.BazProperty, "childvalue"); + + var grandchild = new Class2 { Parent = child }; + var raised = 0; + + grandchild.PropertyChanged += (s, e) => ++raised; + + child.Parent = newParent; + + Assert.Equal(0, raised); + Assert.Equal("childvalue", child.GetValue(Class1.BazProperty)); + Assert.Equal("childvalue", grandchild.GetValue(Class1.BazProperty)); + } + private class Class1 : AvaloniaObject { public static readonly StyledProperty FooProperty = diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_OnPropertyChanged.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_OnPropertyChanged.cs index 7f4dcace71..326199b3c2 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_OnPropertyChanged.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_OnPropertyChanged.cs @@ -35,7 +35,7 @@ namespace Avalonia.Base.UnitTests { var target = new Class1(); - target.SetValue(Class1.FooProperty, "newvalue"); + target.SetValue(Class1.FooProperty, "newvalue", BindingPriority.Animation); target.SetValue(Class1.FooProperty, "styled", BindingPriority.Style); Assert.Equal(2, target.CoreChanges.Count); @@ -49,38 +49,23 @@ namespace Avalonia.Base.UnitTests } [Fact] - public void OnPropertyChangedCore_Is_Called_On_All_Binding_Property_Changes() + public void OnPropertyChangedCore_Is_Called_On_Non_Effective_Property_Binding_Value_Change() { var target = new Class1(); - var style = new Subject>(); - var animation = new Subject>(); - var templatedParent = new Subject>(); - - target.Bind(Class1.FooProperty, style, BindingPriority.Style); - target.Bind(Class1.FooProperty, animation, BindingPriority.Animation); - target.Bind(Class1.FooProperty, templatedParent, BindingPriority.TemplatedParent); - - style.OnNext("style1"); - templatedParent.OnNext("tp1"); - animation.OnNext("a1"); - templatedParent.OnNext("tp2"); - templatedParent.OnCompleted(); - animation.OnNext("a2"); - style.OnNext("style2"); - style.OnCompleted(); - animation.OnCompleted(); - - var changes = target.CoreChanges.Cast>(); - - Assert.Equal( - new[] { true, true, true, false, false, true, false, false, true }, - changes.Select(x => x.IsEffectiveValueChange).ToList()); - Assert.Equal( - new[] { "style1", "tp1", "a1", "tp2", "$unset", "a2", "style2", "$unset", "foodefault" }, - changes.Select(x => x.NewValue.GetValueOrDefault("$unset")).ToList()); - Assert.Equal( - new[] { "foodefault", "style1", "tp1", "$unset", "$unset", "a1", "$unset", "$unset", "a2" }, - changes.Select(x => x.OldValue.GetValueOrDefault("$unset")).ToList()); + var source = new BehaviorSubject>("styled1"); + + target.Bind(Class1.FooProperty, source, BindingPriority.Style); + target.SetValue(Class1.FooProperty, "newvalue", BindingPriority.Animation); + source.OnNext("styled2"); + + Assert.Equal(3, target.CoreChanges.Count); + + var change = (AvaloniaPropertyChangedEventArgs)target.CoreChanges[2]; + + Assert.Equal("styled2", change.NewValue.Value); + Assert.False(change.OldValue.HasValue); + Assert.Equal(BindingPriority.Style, change.Priority); + Assert.False(change.IsEffectiveValueChange); } [Fact] @@ -88,7 +73,7 @@ namespace Avalonia.Base.UnitTests { var target = new Class1(); - target.SetValue(Class1.FooProperty, "newvalue"); + target.SetValue(Class1.FooProperty, "newvalue", BindingPriority.Animation); target.SetValue(Class1.FooProperty, "styled", BindingPriority.Style); Assert.Equal(1, target.Changes.Count); @@ -124,19 +109,13 @@ namespace Avalonia.Base.UnitTests private static AvaloniaPropertyChangedEventArgs Clone(AvaloniaPropertyChangedEventArgs change) { var e = (AvaloniaPropertyChangedEventArgs)change; - var result = new AvaloniaPropertyChangedEventArgs( + return new AvaloniaPropertyChangedEventArgs( change.Sender, e.Property, e.OldValue, e.NewValue, - change.Priority); - - if (!change.IsEffectiveValueChange) - { - result.MarkNonEffectiveValue(); - } - - return result; + change.Priority, + change.IsEffectiveValueChange); } } } diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_SetValue.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_SetValue.cs index 72162a4d8e..f878977cf2 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_SetValue.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_SetValue.cs @@ -251,7 +251,7 @@ namespace Avalonia.Base.UnitTests { Class1 target = new Class1(); - target.SetValue(Class1.FooProperty, "one", BindingPriority.TemplatedParent); + target.SetValue(Class1.FooProperty, "one", BindingPriority.Template); Assert.Equal("one", target.GetValue(Class1.FooProperty)); target.SetValue(Class1.FooProperty, "two", BindingPriority.Style); Assert.Equal("one", target.GetValue(Class1.FooProperty)); diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Validation.cs b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Validation.cs index c14332e1fe..e8175cf477 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Validation.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaObjectTests_Validation.cs @@ -1,6 +1,7 @@ using System; using System.Reactive.Subjects; using Avalonia.Controls; +using Avalonia.Data; using Xunit; namespace Avalonia.Base.UnitTests @@ -41,7 +42,7 @@ namespace Avalonia.Base.UnitTests } [Fact] - public void Reverts_To_DefaultValue_If_Binding_Fails_Validation() + public void Reverts_To_DefaultValue_If_LocalValue_Binding_Fails_Validation() { var target = new Class1(); var source = new Subject(); @@ -52,6 +53,31 @@ namespace Avalonia.Base.UnitTests Assert.Equal(11, target.GetValue(Class1.FooProperty)); } + [Fact] + public void Reverts_To_DefaultValue_If_Style_Binding_Fails_Validation() + { + var target = new Class1(); + var source = new Subject(); + + target.Bind(Class1.FooProperty, source, BindingPriority.Style); + source.OnNext(150); + + Assert.Equal(11, target.GetValue(Class1.FooProperty)); + } + + [Fact] + public void Reverts_To_Lower_Priority_If_Style_Binding_Fails_Validation() + { + var target = new Class1(); + var source = new Subject(); + + target.SetValue(Class1.FooProperty, 10, BindingPriority.Style); + target.Bind(Class1.FooProperty, source, BindingPriority.StyleTrigger); + source.OnNext(150); + + Assert.Equal(10, target.GetValue(Class1.FooProperty)); + } + [Fact] public void Reverts_To_DefaultValue_Even_In_Presence_Of_Other_Bindings() { diff --git a/tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs b/tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs index f3f39b465b..232b6ccf73 100644 --- a/tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs +++ b/tests/Avalonia.Base.UnitTests/AvaloniaPropertyTests.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Avalonia.Data; +using Avalonia.PropertyStore; using Avalonia.Styling; using Avalonia.Utilities; using Xunit; @@ -149,7 +150,7 @@ namespace Avalonia.Base.UnitTests internal override IDisposable RouteBind( AvaloniaObject o, - IObservable> source, + IObservable source, BindingPriority priority) { throw new NotImplementedException(); @@ -165,12 +166,7 @@ namespace Avalonia.Base.UnitTests throw new NotImplementedException(); } - internal override object RouteGetBaseValue(AvaloniaObject o, BindingPriority maxPriority) - { - throw new NotImplementedException(); - } - - internal override void RouteInheritanceParentChanged(AvaloniaObject o, AvaloniaObject oldParent) + internal override object RouteGetBaseValue(AvaloniaObject o) { throw new NotImplementedException(); } @@ -183,7 +179,7 @@ namespace Avalonia.Base.UnitTests throw new NotImplementedException(); } - internal override ISetterInstance CreateSetterInstance(IStyleable target, object value) + internal override EffectiveValue CreateEffectiveValue(AvaloniaObject o) { throw new NotImplementedException(); } diff --git a/tests/Avalonia.Base.UnitTests/Data/Core/ExpressionObserverTests_Property.cs b/tests/Avalonia.Base.UnitTests/Data/Core/ExpressionObserverTests_Property.cs index a9c62a3c4a..f911048960 100644 --- a/tests/Avalonia.Base.UnitTests/Data/Core/ExpressionObserverTests_Property.cs +++ b/tests/Avalonia.Base.UnitTests/Data/Core/ExpressionObserverTests_Property.cs @@ -3,14 +3,13 @@ using System.Collections.Generic; using System.Reactive; using System.Reactive.Linq; using System.Reactive.Subjects; -using Microsoft.Reactive.Testing; +using System.Threading.Tasks; using Avalonia.Data; using Avalonia.Data.Core; +using Avalonia.Threading; using Avalonia.UnitTests; +using Microsoft.Reactive.Testing; using Xunit; -using System.Threading.Tasks; -using Avalonia.Markup.Parsers; -using Avalonia.Threading; namespace Avalonia.Base.UnitTests.Data.Core { @@ -636,7 +635,25 @@ namespace Avalonia.Base.UnitTests.Data.Core target.Subscribe(x => result.Add(x)); } - + + [Fact] + public void RootGetter_Is_Reevaluated_On_Subscribe() + { + var data = "foo"; + var target = new ExpressionObserver(() => data, new EmptyExpressionNode(), new Subject(), null); + var result = new List(); + var sub = target.Subscribe(x => result.Add(x)); + + Assert.Equal(new object[] { "foo" }, result); + + sub.Dispose(); + data = "bar"; + + target.Subscribe(x => result.Add(x)); + + Assert.Equal(new object[] { "foo", "bar" }, result); + } + public class MyViewModelBase { public object Name => "Name"; } public class MyViewModel : MyViewModelBase { public new string Name => "NewName"; } diff --git a/tests/Avalonia.Base.UnitTests/PriorityValueTests.cs b/tests/Avalonia.Base.UnitTests/PriorityValueTests.cs deleted file mode 100644 index aa5993f3b2..0000000000 --- a/tests/Avalonia.Base.UnitTests/PriorityValueTests.cs +++ /dev/null @@ -1,314 +0,0 @@ -using System; -using System.Linq; -using System.Reactive.Disposables; -using Avalonia.Data; -using Avalonia.PropertyStore; -using Moq; -using Xunit; - -namespace Avalonia.Base.UnitTests -{ - public class PriorityValueTests - { - private static readonly AvaloniaObject Owner = new AvaloniaObject(); - private static readonly ValueStore ValueStore = new ValueStore(Owner); - private static readonly StyledProperty TestProperty = new StyledProperty( - "Test", - typeof(PriorityValueTests), - new StyledPropertyMetadata()); - - [Fact] - public void Constructor_Should_Set_Value_Based_On_Initial_Entry() - { - var target = new PriorityValue( - Owner, - TestProperty, - ValueStore, - new ConstantValueEntry( - TestProperty, - "1", - BindingPriority.StyleTrigger, - new(ValueStore))); - - Assert.Equal("1", target.GetValue().Value); - Assert.Equal(BindingPriority.StyleTrigger, target.Priority); - } - - [Fact] - public void GetValue_Should_Respect_MaxPriority() - { - var target = new PriorityValue( - Owner, - TestProperty, - ValueStore); - - target.SetValue("animation", BindingPriority.Animation); - target.SetValue("local", BindingPriority.LocalValue); - target.SetValue("styletrigger", BindingPriority.StyleTrigger); - target.SetValue("style", BindingPriority.Style); - - Assert.Equal("animation", target.GetValue(BindingPriority.Animation)); - Assert.Equal("local", target.GetValue(BindingPriority.LocalValue)); - Assert.Equal("styletrigger", target.GetValue(BindingPriority.StyleTrigger)); - Assert.Equal("style", target.GetValue(BindingPriority.TemplatedParent)); - Assert.Equal("style", target.GetValue(BindingPriority.Style)); - } - - [Fact] - public void SetValue_LocalValue_Should_Not_Add_Entries() - { - var target = new PriorityValue( - Owner, - TestProperty, - ValueStore); - - target.SetValue("1", BindingPriority.LocalValue); - target.SetValue("2", BindingPriority.LocalValue); - - Assert.Empty(target.Entries); - } - - [Fact] - public void SetValue_Non_LocalValue_Should_Add_Entries() - { - var target = new PriorityValue( - Owner, - TestProperty, - ValueStore); - - target.SetValue("1", BindingPriority.Style); - target.SetValue("2", BindingPriority.Animation); - - var result = target.Entries - .OfType>() - .Select(x => x.GetValue().Value) - .ToList(); - - Assert.Equal(new[] { "1", "2" }, result); - } - - [Fact] - public void Priority_Should_Be_Set() - { - var target = new PriorityValue( - Owner, - TestProperty, - ValueStore); - - Assert.Equal(BindingPriority.Unset, target.Priority); - target.SetValue("style", BindingPriority.Style); - Assert.Equal(BindingPriority.Style, target.Priority); - target.SetValue("local", BindingPriority.LocalValue); - Assert.Equal(BindingPriority.LocalValue, target.Priority); - target.SetValue("animation", BindingPriority.Animation); - Assert.Equal(BindingPriority.Animation, target.Priority); - target.SetValue("local2", BindingPriority.LocalValue); - Assert.Equal(BindingPriority.Animation, target.Priority); - } - - [Fact] - public void Binding_With_Same_Priority_Should_Be_Appended() - { - var target = new PriorityValue(Owner, TestProperty, ValueStore); - var source1 = new Source("1"); - var source2 = new Source("2"); - - target.AddBinding(source1, BindingPriority.LocalValue); - target.AddBinding(source2, BindingPriority.LocalValue); - - var result = target.Entries - .OfType>() - .Select(x => x.Source) - .OfType() - .Select(x => x.Id) - .ToList(); - - Assert.Equal(new[] { "1", "2" }, result); - } - - [Fact] - public void Binding_With_Higher_Priority_Should_Be_Appended() - { - var target = new PriorityValue(Owner, TestProperty, ValueStore); - var source1 = new Source("1"); - var source2 = new Source("2"); - - target.AddBinding(source1, BindingPriority.LocalValue); - target.AddBinding(source2, BindingPriority.Animation); - - var result = target.Entries - .OfType>() - .Select(x => x.Source) - .OfType() - .Select(x => x.Id) - .ToList(); - - Assert.Equal(new[] { "1", "2" }, result); - } - - [Fact] - public void Binding_With_Lower_Priority_Should_Be_Prepended() - { - var target = new PriorityValue(Owner, TestProperty, ValueStore); - var source1 = new Source("1"); - var source2 = new Source("2"); - - target.AddBinding(source1, BindingPriority.LocalValue); - target.AddBinding(source2, BindingPriority.Style); - - var result = target.Entries - .OfType>() - .Select(x => x.Source) - .OfType() - .Select(x => x.Id) - .ToList(); - - Assert.Equal(new[] { "2", "1" }, result); - } - - [Fact] - public void Second_Binding_With_Lower_Priority_Should_Be_Inserted_In_Middle() - { - var target = new PriorityValue(Owner, TestProperty, ValueStore); - var source1 = new Source("1"); - var source2 = new Source("2"); - var source3 = new Source("3"); - - target.AddBinding(source1, BindingPriority.LocalValue); - target.AddBinding(source2, BindingPriority.Style); - target.AddBinding(source3, BindingPriority.Style); - - var result = target.Entries - .OfType>() - .Select(x => x.Source) - .OfType() - .Select(x => x.Id) - .ToList(); - - Assert.Equal(new[] { "2", "3", "1" }, result); - } - - [Fact] - public void Competed_Binding_Should_Be_Removed() - { - var target = new PriorityValue(Owner, TestProperty, ValueStore); - var source1 = new Source("1"); - var source2 = new Source("2"); - var source3 = new Source("3"); - - target.AddBinding(source1, BindingPriority.LocalValue).Start(); - target.AddBinding(source2, BindingPriority.Style).Start(); - target.AddBinding(source3, BindingPriority.Style).Start(); - source3.OnCompleted(); - - var result = target.Entries - .OfType>() - .Select(x => x.Source) - .OfType() - .Select(x => x.Id) - .ToList(); - - Assert.Equal(new[] { "2", "1" }, result); - } - - [Fact] - public void Value_Should_Come_From_Last_Entry() - { - var target = new PriorityValue(Owner, TestProperty, ValueStore); - var source1 = new Source("1"); - var source2 = new Source("2"); - var source3 = new Source("3"); - - target.AddBinding(source1, BindingPriority.LocalValue).Start(); - target.AddBinding(source2, BindingPriority.Style).Start(); - target.AddBinding(source3, BindingPriority.Style).Start(); - - Assert.Equal("1", target.GetValue().Value); - } - - [Fact] - public void LocalValue_Should_Override_LocalValue_Binding() - { - var target = new PriorityValue(Owner, TestProperty, ValueStore); - var source1 = new Source("1"); - - target.AddBinding(source1, BindingPriority.LocalValue).Start(); - target.SetValue("2", BindingPriority.LocalValue); - - Assert.Equal("2", target.GetValue().Value); - } - - [Fact] - public void LocalValue_Should_Override_Style_Binding() - { - var target = new PriorityValue(Owner, TestProperty, ValueStore); - var source1 = new Source("1"); - - target.AddBinding(source1, BindingPriority.Style).Start(); - target.SetValue("2", BindingPriority.LocalValue); - - Assert.Equal("2", target.GetValue().Value); - } - - [Fact] - public void LocalValue_Should_Not_Override_Animation_Binding() - { - var target = new PriorityValue(Owner, TestProperty, ValueStore); - var source1 = new Source("1"); - - target.AddBinding(source1, BindingPriority.Animation).Start(); - target.SetValue("2", BindingPriority.LocalValue); - - Assert.Equal("1", target.GetValue().Value); - } - - [Fact] - public void NonAnimated_Value_Should_Be_Correct_1() - { - var target = new PriorityValue(Owner, TestProperty, ValueStore); - var source1 = new Source("1"); - var source2 = new Source("2"); - var source3 = new Source("3"); - - target.AddBinding(source1, BindingPriority.LocalValue).Start(); - target.AddBinding(source2, BindingPriority.Style).Start(); - target.AddBinding(source3, BindingPriority.Animation).Start(); - - Assert.Equal("3", target.GetValue().Value); - Assert.Equal("1", target.GetValue(BindingPriority.LocalValue).Value); - } - - [Fact] - public void NonAnimated_Value_Should_Be_Correct_2() - { - var target = new PriorityValue(Owner, TestProperty, ValueStore); - var source1 = new Source("1"); - var source2 = new Source("2"); - var source3 = new Source("3"); - - target.AddBinding(source1, BindingPriority.Animation).Start(); - target.AddBinding(source2, BindingPriority.Style).Start(); - target.AddBinding(source3, BindingPriority.Style).Start(); - - Assert.Equal("1", target.GetValue().Value); - Assert.Equal("3", target.GetValue(BindingPriority.LocalValue).Value); - } - - private class Source : IObservable> - { - private IObserver> _observer; - - public Source(string id) => Id = id; - public string Id { get; } - - public IDisposable Subscribe(IObserver> observer) - { - _observer = observer; - observer.OnNext(Id); - return Disposable.Empty; - } - - public void OnCompleted() => _observer.OnCompleted(); - } - } -} diff --git a/tests/Avalonia.Base.UnitTests/PropertyStore/ValueStoreTests_Frames.cs b/tests/Avalonia.Base.UnitTests/PropertyStore/ValueStoreTests_Frames.cs new file mode 100644 index 0000000000..3a307447ac --- /dev/null +++ b/tests/Avalonia.Base.UnitTests/PropertyStore/ValueStoreTests_Frames.cs @@ -0,0 +1,142 @@ +using System.Collections.Generic; +using System.Reactive.Subjects; +using Avalonia.Data; +using Avalonia.PropertyStore; +using Avalonia.Styling; +using Microsoft.Reactive.Testing; +using Xunit; +using static Microsoft.Reactive.Testing.ReactiveTest; + +#nullable enable + +namespace Avalonia.Base.UnitTests.PropertyStore +{ + public class ValueStoreTests_Frames + { + [Fact] + public void Adding_Frame_Raises_PropertyChanged() + { + var target = new Class1(); + var subject = new BehaviorSubject("bar"); + var result = new List(); + var style = new Style + { + Setters = + { + new Setter(Class1.FooProperty, "foo"), + new Setter(Class1.BarProperty, subject.ToBinding()), + } + }; + + target.PropertyChanged += (s, e) => + { + result.Add(new(e.Property, e.OldValue, e.NewValue)); + }; + + var frame = InstanceStyle(style, target); + target.GetValueStore().AddFrame(frame); + + Assert.Equal(new PropertyChange[] + { + new(Class1.FooProperty, "foodefault", "foo"), + new(Class1.BarProperty, "bardefault", "bar"), + }, result); + } + + [Fact] + public void Removing_Frame_Raises_PropertyChanged() + { + var target = new Class1(); + var subject = new BehaviorSubject("bar"); + var result = new List(); + var style = new Style + { + Setters = + { + new Setter(Class1.FooProperty, "foo"), + new Setter(Class1.BarProperty, subject.ToBinding()), + } + }; + var frame = InstanceStyle(style, target); + target.GetValueStore().AddFrame(frame); + + target.PropertyChanged += (s, e) => + { + result.Add(new(e.Property, e.OldValue, e.NewValue)); + }; + + target.GetValueStore().RemoveFrame(frame); + + Assert.Equal(new PropertyChange[] + { + new(Class1.BarProperty, "bar", "bardefault"), + new(Class1.FooProperty, "foo", "foodefault"), + }, result); + } + + [Fact] + public void Removing_Frame_Unsubscribes_Binding() + { + var target = new Class1(); + var scheduler = new TestScheduler(); + var obs = scheduler.CreateColdObservable(OnNext(0, "bar")); + var style = new Style + { + Setters = + { + new Setter(Class1.FooProperty, "foo"), + new Setter(Class1.BarProperty, obs.ToBinding()), + } + }; + var frame = InstanceStyle(style, target); + + target.GetValueStore().AddFrame(frame); + target.GetValueStore().RemoveFrame(frame); + + Assert.Single(obs.Subscriptions); + Assert.Equal(0, obs.Subscriptions[0].Subscribe); + Assert.NotEqual(Subscription.Infinite, obs.Subscriptions[0].Unsubscribe); + } + + [Fact] + public void Completing_Binding_Removes_ImmediateValueFrame() + { + var target = new Class1(); + var source = new BehaviorSubject>("foo"); + + target.Bind(Class1.FooProperty, source, BindingPriority.Animation); + + var valueStore = target.GetValueStore(); + Assert.Equal(1, valueStore.Frames.Count); + Assert.IsType(valueStore.Frames[0]); + + source.OnCompleted(); + + Assert.Equal(0, valueStore.Frames.Count); + } + + private static StyleInstance InstanceStyle(Style style, StyledElement target) + { + var result = new StyleInstance(style, null, FrameType.Style); + + foreach (var setter in style.Setters) + result.Add(setter.Instance(result, target)); + + return result; + } + + private class Class1 : StyledElement + { + public static readonly StyledProperty FooProperty = + AvaloniaProperty.Register("Foo", "foodefault"); + + public static readonly StyledProperty BarProperty = + AvaloniaProperty.Register("Bar", "bardefault", true); + } + + private record PropertyChange( + AvaloniaProperty Property, + object? OldValue, + object? NewValue); + } +} diff --git a/tests/Avalonia.Base.UnitTests/PropertyStore/ValueStoreTests_Inheritance.cs b/tests/Avalonia.Base.UnitTests/PropertyStore/ValueStoreTests_Inheritance.cs new file mode 100644 index 0000000000..ed122e4ddc --- /dev/null +++ b/tests/Avalonia.Base.UnitTests/PropertyStore/ValueStoreTests_Inheritance.cs @@ -0,0 +1,134 @@ +using Xunit; + +#nullable enable + +namespace Avalonia.Base.UnitTests.PropertyStore +{ + public class ValueStoreTests_Inheritance + { + [Fact] + public void InheritanceAncestor_Is_Initially_Null() + { + var parent = new Class1(); + var child = new Class1 { Parent = parent }; + var grandchild = new Class1 { Parent = child }; + + Assert.Null(parent.GetValueStore().InheritanceAncestor); + Assert.Null(child.GetValueStore().InheritanceAncestor); + Assert.Null(grandchild.GetValueStore().InheritanceAncestor); + } + + [Fact] + public void Setting_Value_In_Parent_Updates_InheritanceAncestor() + { + var parent = new Class1(); + var child = new Class1 { Parent = parent }; + var grandchild = new Class1 { Parent = child }; + + parent.Foo = "changed"; + + var parentStore = parent.GetValueStore(); + Assert.Null(parentStore.InheritanceAncestor); + Assert.Same(parentStore, child.GetValueStore().InheritanceAncestor); + Assert.Same(parentStore, grandchild.GetValueStore().InheritanceAncestor); + } + + [Fact] + public void Setting_Value_In_Parent_Doesnt_Update_Grandchild_InheritanceAncestor_If_Child_Has_Value_Set() + { + var parent = new Class1(); + var child = new Class1 { Parent = parent }; + var grandchild = new Class1 { Parent = child }; + + child.Foo = "foochanged"; + parent.Foo = "changed"; + + var parentStore = parent.GetValueStore(); + Assert.Null(parentStore.InheritanceAncestor); + Assert.Same(parentStore, child.GetValueStore().InheritanceAncestor); + Assert.Same(child.GetValueStore(), grandchild.GetValueStore().InheritanceAncestor); + } + + [Fact] + public void Clearing_Value_In_Parent_Updates_InheritanceAncestor() + { + var parent = new Class1(); + var child = new Class1 { Parent = parent }; + var grandchild = new Class1 { Parent = child }; + + parent.Foo = "changed"; + parent.ClearValue(Class1.FooProperty); + + Assert.Null(parent.GetValueStore().InheritanceAncestor); + Assert.Null(child.GetValueStore().InheritanceAncestor); + Assert.Null(grandchild.GetValueStore().InheritanceAncestor); + } + + [Fact] + public void Clear_Value_In_Parent_Doesnt_Update_Grandchild_InheritanceAncestor_If_Child_Has_Value_Set() + { + var parent = new Class1(); + var child = new Class1 { Parent = parent }; + var grandchild = new Class1 { Parent = child }; + + child.Foo = "foochanged"; + parent.Foo = "changed"; + parent.ClearValue(Class1.FooProperty); + + Assert.Null(parent.GetValueStore().InheritanceAncestor); + Assert.Null(child.GetValueStore().InheritanceAncestor); + Assert.Same(child.GetValueStore(), grandchild.GetValueStore().InheritanceAncestor); + } + + [Fact] + public void Clearing_Value_In_Child_Updates_InheritanceAncestor() + { + var parent = new Class1(); + var child = new Class1 { Parent = parent }; + var grandchild = new Class1 { Parent = child }; + + parent.Foo = "changed"; + child.Foo = "foochanged"; + child.ClearValue(Class1.FooProperty); + + var parentStore = parent.GetValueStore(); + Assert.Null(parentStore.InheritanceAncestor); + Assert.Same(parentStore, child.GetValueStore().InheritanceAncestor); + Assert.Same(parentStore, grandchild.GetValueStore().InheritanceAncestor); + } + + [Fact] + public void Adding_Child_Sets_InheritanceAncestor() + { + var parent = new Class1(); + var child = new Class1(); + var grandchild = new Class1 { Parent = child }; + + parent.Foo = "changed"; + child.Parent = parent; + + var parentStore = parent.GetValueStore(); + Assert.Null(parentStore.InheritanceAncestor); + Assert.Same(parentStore, child.GetValueStore().InheritanceAncestor); + Assert.Same(parentStore, grandchild.GetValueStore().InheritanceAncestor); + } + + private class Class1 : AvaloniaObject + { + public static readonly StyledProperty FooProperty = + AvaloniaProperty.Register("Foo", "foodefault", inherits: true); + + public string Foo + { + get => GetValue(FooProperty); + set => SetValue(FooProperty, value); + } + + public Class1? Parent + { + get { return (Class1?)InheritanceParent; } + set { InheritanceParent = value; } + } + } + } +} diff --git a/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs index 9048b488b6..72df072ea6 100644 --- a/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Nesting.cs @@ -292,9 +292,15 @@ namespace Avalonia.Base.UnitTests.Styling private class ActivatorSink : IStyleActivatorSink { - public ActivatorSink(IStyleActivator source) => source.Subscribe(this); + public ActivatorSink(IStyleActivator source) + { + source.Subscribe(this); + Active = source.GetIsActive(); + } + + public bool Active { get; private set; } - public void OnNext(bool value, int tag) => Active = value; + public void OnNext(bool value) => Active = value; } } } diff --git a/tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs b/tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs index b57a024f41..33023c4851 100644 --- a/tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/SetterTests.cs @@ -5,11 +5,15 @@ using Avalonia.Controls; using Avalonia.Controls.Templates; using Avalonia.Data; using Avalonia.Data.Converters; -using Avalonia.Diagnostics; +using Avalonia.Media; +using Avalonia.PropertyStore; using Avalonia.Styling; +using Avalonia.UnitTests; using Moq; using Xunit; +#nullable enable + namespace Avalonia.Base.UnitTests.Styling { public class SetterTests @@ -28,13 +32,13 @@ namespace Avalonia.Base.UnitTests.Styling var control = new TextBlock(); var subject = new BehaviorSubject("foo"); var descriptor = InstancedBinding.OneWay(subject); - var binding = Mock.Of(x => x.Initiate(control, TextBlock.TextProperty, null, false) == descriptor); + var binding = Mock.Of(x => x.Initiate(control, TextBlock.TagProperty, null, false) == descriptor); var style = Mock.Of(); - var setter = new Setter(TextBlock.TextProperty, binding); + var setter = new Setter(TextBlock.TagProperty, binding); - setter.Instance(control).Start(false); + Apply(setter, control); - Assert.Equal("foo", control.Text); + Assert.Equal("foo", control.Tag); } [Fact] @@ -47,7 +51,7 @@ namespace Avalonia.Base.UnitTests.Styling var style = Mock.Of(); var setter = new Setter(TextBlock.TagProperty, binding); - setter.Instance(control).Start(false); + Apply(setter, control); Assert.Equal(null, control.Text); } @@ -60,133 +64,463 @@ namespace Avalonia.Base.UnitTests.Styling var style = Mock.Of(); var setter = new Setter(Decorator.ChildProperty, template); - setter.Instance(control).Start(false); + Apply(setter, control); Assert.IsType(control.Child); } + [Fact] + public void Can_Set_Direct_Property_In_Style_Without_Activator() + { + var control = new TextBlock(); + var target = new Setter(); + var style = new Style(x => x.Is()) + { + Setters = + { + new Setter(TextBlock.TextProperty, "foo"), + } + }; + + Apply(style, control); + + Assert.Equal("foo", control.Text); + } + + [Fact] + public void Can_Set_Direct_Property_Binding_In_Style_Without_Activator() + { + var control = new TextBlock(); + var target = new Setter(); + var source = new BehaviorSubject("foo"); + var style = new Style(x => x.Is()) + { + Setters = + { + new Setter(TextBlock.TextProperty, source.ToBinding()), + } + }; + + Apply(style, control); + + Assert.Equal("foo", control.Text); + } + + [Fact] + public void Cannot_Set_Direct_Property_Binding_In_Style_With_Activator() + { + var control = new TextBlock(); + var target = new Setter(); + var source = new BehaviorSubject("foo"); + var style = new Style(x => x.Is().Class("foo")) + { + Setters = + { + new Setter(TextBlock.TextProperty, source.ToBinding()), + } + }; + + Assert.Throws(() => Apply(style, control)); + } + + [Fact] + public void Cannot_Set_Direct_Property_In_Style_With_Activator() + { + var control = new TextBlock(); + var target = new Setter(); + var style = new Style(x => x.Is().Class("foo")) + { + Setters = + { + new Setter(TextBlock.TextProperty, "foo"), + } + }; + + Assert.Throws(() => Apply(style, control)); + } + [Fact] public void Does_Not_Call_Converter_ConvertBack_On_OneWay_Binding() { - var control = new Decorator { Name = "foo" }; - var style = Mock.Of(); + var control = new Decorator + { + Name = "foo", + Classes = { "foo" }, + }; + var binding = new Binding("Name", BindingMode.OneWay) { Converter = new TestConverter(), RelativeSource = new RelativeSource(RelativeSourceMode.Self), }; - var setter = new Setter(Decorator.TagProperty, binding); - var instance = setter.Instance(control); - instance.Start(true); - instance.Activate(); + var style = new Style(x => x.OfType().Class("foo")) + { + Setters = + { + new Setter(Decorator.TagProperty, binding) + }, + }; + + Apply(style, control); Assert.Equal("foobar", control.Tag); // Issue #1218 caused TestConverter.ConvertBack to throw here. - instance.Deactivate(); + control.Classes.Remove("foo"); Assert.Null(control.Tag); } [Fact] public void Setter_Should_Apply_Value_Without_Activator_With_Style_Priority() { - var control = new Control(); - var setter = new Setter(TextBlock.TagProperty, "foo"); + var control = new Border(); + var style = new Style(x => x.OfType()) + { + Setters = + { + new Setter(Control.TagProperty, "foo"), + }, + }; + var raised = 0; - setter.Instance(control).Start(false); + control.PropertyChanged += (s, e) => + { + Assert.Equal(Control.TagProperty, e.Property); + Assert.Equal(BindingPriority.Style, e.Priority); + ++raised; + }; - Assert.Equal("foo", control.Tag); - Assert.Equal(BindingPriority.Style, control.GetDiagnostic(TextBlock.TagProperty).Priority); + Apply(style, control); + + Assert.Equal(1, raised); } [Fact] - public void Setter_Should_Apply_Value_With_Activator_As_Binding_With_StyleTrigger_Priority() + public void Setter_Should_Apply_Value_With_Activator_With_StyleTrigger_Priority() { - var control = new Canvas(); - var setter = new Setter(TextBlock.TagProperty, "foo"); + var control = new Border { Classes = { "foo" } }; + var style = new Style(x => x.OfType().Class("foo")) + { + Setters = + { + new Setter(Control.TagProperty, "foo"), + }, + }; + var activator = new Subject(); + var raised = 0; - var instance = setter.Instance(control); - instance.Start(true); - instance.Activate(); + control.PropertyChanged += (s, e) => + { + Assert.Equal(Border.TagProperty, e.Property); + Assert.Equal(BindingPriority.StyleTrigger, e.Priority); + ++raised; + }; - Assert.Equal("foo", control.Tag); - Assert.Equal(BindingPriority.StyleTrigger, control.GetDiagnostic(TextBlock.TagProperty).Priority); + Apply(style, control); + + Assert.Equal(1, raised); } [Fact] public void Setter_Should_Apply_Binding_Without_Activator_With_Style_Priority() { - var control = new Canvas(); - var source = new { Foo = "foo" }; - var setter = new Setter(TextBlock.TagProperty, new Binding + var control = new Border + { + DataContext = "foo", + }; + + var style = new Style(x => x.OfType()) { - Source = source, - Path = nameof(source.Foo), - }); + Setters = + { + new Setter(Control.TagProperty, new Binding()), + }, + }; - setter.Instance(control).Start(false); + var raised = 0; - Assert.Equal("foo", control.Tag); - Assert.Equal(BindingPriority.Style, control.GetDiagnostic(TextBlock.TagProperty).Priority); + control.PropertyChanged += (s, e) => + { + Assert.Equal(Control.TagProperty, e.Property); + Assert.Equal(BindingPriority.Style, e.Priority); + ++raised; + }; + + Apply(style, control); + + Assert.Equal(1, raised); } [Fact] public void Setter_Should_Apply_Binding_With_Activator_With_StyleTrigger_Priority() { - var control = new Canvas(); - var source = new { Foo = "foo" }; - var setter = new Setter(TextBlock.TagProperty, new Binding + var control = new Border + { + Classes = { "foo" }, + DataContext = "foo", + }; + + var style = new Style(x => x.OfType().Class("foo")) + { + Setters = + { + new Setter(Control.TagProperty, new Binding()), + }, + }; + + var raised = 0; + + control.PropertyChanged += (s, e) => + { + Assert.Equal(Control.TagProperty, e.Property); + Assert.Equal(BindingPriority.StyleTrigger, e.Priority); + ++raised; + }; + + Apply(style, control); + + Assert.Equal(1, raised); + } + + [Fact] + public void Direct_Property_Setter_With_TwoWay_Binding_Should_Update_Source() + { + using var app = UnitTestApplication.Start(TestServices.MockThreadingInterface); + var data = new Data { Foo = "foo" }; + var control = new TextBox { - Source = source, - Path = nameof(source.Foo), - }); + DataContext = data, + }; - var instance = setter.Instance(control); - instance.Start(true); - instance.Activate(); + var style = new Style(x => x.OfType()) + { + Setters = + { + new Setter + { + Property = TextBox.TextProperty, + Value = new Binding + { + Path = "Foo", + Mode = BindingMode.TwoWay + } + } + }, + }; - Assert.Equal("foo", control.Tag); - Assert.Equal(BindingPriority.StyleTrigger, control.GetDiagnostic(TextBlock.TagProperty).Priority); + Apply(style, control); + Assert.Equal("foo", control.Text); + + control.Text = "bar"; + Assert.Equal("bar", data.Foo); + } + + [Fact] + public void Styled_Property_Setter_With_TwoWay_Binding_Should_Update_Source() + { + var data = new Data { Bar = Brushes.Red }; + var control = new Border + { + DataContext = data, + }; + + var style = new Style(x => x.OfType()) + { + Setters = + { + new Setter + { + Property = Border.BackgroundProperty, + Value = new Binding + { + Path = "Bar", + Mode = BindingMode.TwoWay + } + } + }, + }; + + Apply(style, control); + Assert.Equal(Brushes.Red, control.Background); + + control.Background = Brushes.Green; + Assert.Equal(Brushes.Green, data.Bar); + } + + [Fact] + public void Non_Active_Styled_Property_Binding_Should_Be_Unsubscribed() + { + var data = new Data { Bar = Brushes.Red }; + var control = new Border + { + DataContext = data, + }; + + var style1 = new Style(x => x.OfType()) + { + Setters = + { + new Setter + { + Property = Border.BackgroundProperty, + Value = new Binding("Bar"), + } + }, + }; + + var style2 = new Style(x => x.OfType().Class("foo")) + { + Setters = + { + new Setter + { + Property = Border.BackgroundProperty, + Value = Brushes.Green, + } + }, + }; + + Apply(style1, control); + Apply(style2, control); + + // `style1` is initially active. + Assert.Equal(Brushes.Red, control.Background); + Assert.Equal(1, data.PropertyChangedSubscriptionCount); + + // Activate `style2`. + control.Classes.Add("foo"); + Assert.Equal(Brushes.Green, control.Background); + + // The binding from `style1` is now inactive and so should be unsubscribed. + Assert.Equal(0, data.PropertyChangedSubscriptionCount); } [Fact] - public void Disposing_Setter_Should_Preserve_LocalValue() + public void Non_Active_Styled_Property_Setter_With_TwoWay_Binding_Should_Not_Update_Source() { - var control = new Canvas(); - var setter = new Setter(TextBlock.TagProperty, "foo"); + var data = new Data { Bar = Brushes.Red }; + var control = new Border + { + DataContext = data, + }; - var instance = setter.Instance(control); - instance.Start(true); - instance.Activate(); + var style1 = new Style(x => x.OfType()) + { + Setters = + { + new Setter + { + Property = Border.BackgroundProperty, + Value = new Binding + { + Path = "Bar", + Mode = BindingMode.TwoWay + } + } + }, + }; - control.Tag = "bar"; + var style2 = new Style(x => x.OfType().Class("foo")) + { + Setters = + { + new Setter + { + Property = Border.BackgroundProperty, + Value = Brushes.Green, + } + }, + }; + + Apply(style1, control); + Apply(style2, control); - instance.Dispose(); + // `style1` is initially active. + Assert.Equal(Brushes.Red, control.Background); - Assert.Equal("bar", control.Tag); + // Activate `style2`. + control.Classes.Add("foo"); + Assert.Equal(Brushes.Green, control.Background); + + // The two-way binding from `style1` is now inactive and so should not write back to + // the DataContext. + Assert.Equal(Brushes.Red, data.Bar); } [Fact] - public void Disposing_Binding_Setter_Should_Preserve_LocalValue() + public void Styled_Property_Setter_With_TwoWay_Binding_Updates_Source_When_Made_Active() { - var control = new Canvas(); - var source = new { Foo = "foo" }; - var setter = new Setter(TextBlock.TagProperty, new Binding + var data = new Data { Bar = Brushes.Red }; + var control = new Border { - Source = source, - Path = nameof(source.Foo), - }); + Classes = { "foo" }, + DataContext = data, + }; - var instance = setter.Instance(control); - instance.Start(true); - instance.Activate(); + var style1 = new Style(x => x.OfType()) + { + Setters = + { + new Setter + { + Property = Border.BackgroundProperty, + Value = new Binding + { + Path = "Bar", + Mode = BindingMode.TwoWay + } + } + }, + }; + + var style2 = new Style(x => x.OfType().Class("foo")) + { + Setters = + { + new Setter + { + Property = Border.BackgroundProperty, + Value = Brushes.Green, + } + }, + }; + + Apply(style1, control); + Apply(style2, control); + + // `style2` is initially active. + Assert.Equal(Brushes.Green, control.Background); + + // Deactivate `style2`. + control.Classes.Remove("foo"); + Assert.Equal(Brushes.Red, control.Background); + + // The two-way binding from `style1` is now active and so should write back to the + // DataContext. + control.Background = Brushes.Blue; + Assert.Equal(Brushes.Blue, data.Bar); + } - control.Tag = "bar"; + private void Apply(Style style, Control control) + { + StyleHelpers.TryAttach(style, control); + } - instance.Dispose(); + private void Apply(Setter setter, Control control) + { + var style = new Style(x => x.Is()) + { + Setters = { setter }, + }; + + Apply(style, control); + } - Assert.Equal("bar", control.Tag); + private class Data : NotifyingBase + { + public string? Foo { get; set; } + public IBrush? Bar { get; set; } } private class TestConverter : IValueConverter diff --git a/tests/Avalonia.Base.UnitTests/Styling/StyleActivatorExtensions.cs b/tests/Avalonia.Base.UnitTests/Styling/StyleActivatorExtensions.cs index f5b3bb40a3..e69eae43f0 100644 --- a/tests/Avalonia.Base.UnitTests/Styling/StyleActivatorExtensions.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/StyleActivatorExtensions.cs @@ -33,10 +33,16 @@ namespace Avalonia.Base.UnitTests.Styling private readonly IStyleActivator _source; public ObservableAdapter(IStyleActivator source) => _source = source; + protected override void Initialize() => _source.Subscribe(this); protected override void Deinitialize() => _source.Unsubscribe(this); + + protected override void Subscribed(IObserver observer, bool first) + { + observer.OnNext(_source.GetIsActive()); + } - void IStyleActivatorSink.OnNext(bool value, int tag) + void IStyleActivatorSink.OnNext(bool value) { PublishNext(value); } diff --git a/tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs b/tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs index f2dfe66054..805b3e7aa6 100644 --- a/tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/StyleTests.cs @@ -1,8 +1,11 @@ using System; using System.Collections.Generic; +using Avalonia.Animation; +using Avalonia.Base.UnitTests.Animation; using Avalonia.Controls; using Avalonia.Controls.Templates; using Avalonia.Data; +using Avalonia.PropertyStore; using Avalonia.Styling; using Avalonia.UnitTests; using Moq; @@ -25,7 +28,7 @@ namespace Avalonia.Base.UnitTests.Styling var target = new Class1(); - style.TryAttach(target, null); + StyleHelpers.TryAttach(style, target); Assert.Equal("Foo", target.Foo); } @@ -43,7 +46,7 @@ namespace Avalonia.Base.UnitTests.Styling var target = new Class1(); - style.TryAttach(target, null); + StyleHelpers.TryAttach(style, target); Assert.Equal("foodefault", target.Foo); target.Classes.Add("foo"); Assert.Equal("Foo", target.Foo); @@ -64,7 +67,7 @@ namespace Avalonia.Base.UnitTests.Styling var target = new Class1(); - style.TryAttach(target, target); + StyleHelpers.TryAttach(style, target); Assert.Equal("Foo", target.Foo); } @@ -90,7 +93,7 @@ namespace Avalonia.Base.UnitTests.Styling var target = new Class1(); var other = new Class1(); - style.TryAttach(target, other); + StyleHelpers.TryAttach(style, target, host: other); Assert.Equal("foodefault", target.Foo); } @@ -111,7 +114,7 @@ namespace Avalonia.Base.UnitTests.Styling Foo = "Original", }; - style.TryAttach(target, null); + StyleHelpers.TryAttach(style, target); Assert.Equal("Original", target.Foo); } @@ -146,7 +149,7 @@ namespace Avalonia.Base.UnitTests.Styling target.Classes.Add("foo"); target.Classes.Remove("foo"); - Assert.Equal(new[] { "foodefault", "Foo", "Bar", "foodefault" }, values); + Assert.Equal(new[] { "foodefault", "Bar", "foodefault" }, values); } [Fact] @@ -227,10 +230,77 @@ namespace Avalonia.Base.UnitTests.Styling } [Fact] - public void Inactive_Values_Should_Not_Be_Made_Active_During_Style_Attach() + public void Later_Styles_Should_Override_Earlier_4() + { + Styles styles = new Styles + { + new Style(x => x.OfType().Class("foo")) + { + Setters = + { + new Setter(Class1.FooProperty, "foo1"), + }, + }, + + new Style(x => x.OfType().Class("foo")) + { + Setters = + { + new Setter(Class1.FooProperty, "foo2"), + new Setter(Class1.DoubleProperty, 123.4), + }, + } + }; + + var target = new Class1(); + styles.TryAttach(target, null); + target.Classes.Add("foo"); + + Assert.Equal("foo2", target.Foo); + Assert.Equal(123.4, target.Double); + } + + [Fact] + public void Later_Styles_Should_Override_Earlier_With_Begin_End_Styling() { - using var app = UnitTestApplication.Start(TestServices.RealStyler); + Styles styles = new Styles + { + new Style(x => x.OfType().Class("foo")) + { + Setters = + { + new Setter(Class1.FooProperty, "foo1"), + new Setter(Class1.DoubleProperty, 123.4), + }, + }, + + new Style(x => x.OfType().Class("foo").Class("bar")) + { + Setters = + { + new Setter(Class1.FooProperty, "foo2"), + }, + }, + }; + + var target = new Class1(); + target.GetValueStore().BeginStyling(); + styles.TryAttach(target, null); + target.GetValueStore().EndStyling(); + target.Classes.Add("bar"); + target.Classes.Add("foo"); + + Assert.Equal("foo2", target.Foo); + Assert.Equal(123.4, target.Double); + + target.Classes.Remove("foo"); + Assert.Equal(0, target.Double); + } + + [Fact] + public void Inactive_Values_Should_Not_Be_Made_Active_During_Style_Attach() + { var root = new TestRoot { Styles = @@ -265,8 +335,6 @@ namespace Avalonia.Base.UnitTests.Styling [Fact] public void Inactive_Bindings_Should_Not_Be_Made_Active_During_Style_Attach() { - using var app = UnitTestApplication.Start(TestServices.RealStyler); - var root = new TestRoot { Styles = @@ -308,8 +376,6 @@ namespace Avalonia.Base.UnitTests.Styling [Fact] public void Inactive_Values_Should_Not_Be_Made_Active_During_Style_Detach() { - using var app = UnitTestApplication.Start(TestServices.RealStyler); - var root = new TestRoot { Styles = @@ -345,8 +411,6 @@ namespace Avalonia.Base.UnitTests.Styling [Fact] public void Inactive_Values_Should_Not_Be_Made_Active_During_Style_Detach_2() { - using var app = UnitTestApplication.Start(TestServices.RealStyler); - var root = new TestRoot { Styles = @@ -382,8 +446,6 @@ namespace Avalonia.Base.UnitTests.Styling [Fact] public void Inactive_Bindings_Should_Not_Be_Made_Active_During_Style_Detach() { - using var app = UnitTestApplication.Start(TestServices.RealStyler); - var root = new TestRoot { Styles = @@ -490,9 +552,9 @@ namespace Avalonia.Base.UnitTests.Styling }; var target = new Class1(); - target.BeginBatchUpdate(); + target.GetValueStore().BeginStyling(); styles.TryAttach(target, null); - target.EndBatchUpdate(); + target.GetValueStore().EndStyling(); Assert.NotNull(target.Child); Assert.Equal(1, instantiationCount); @@ -516,7 +578,7 @@ namespace Avalonia.Base.UnitTests.Styling Child = border = new Border(), }; - style.TryAttach(border, null); + StyleHelpers.TryAttach(style, border); Assert.Equal(new Thickness(4), border.BorderThickness); root.Child = null; @@ -526,180 +588,214 @@ namespace Avalonia.Base.UnitTests.Styling [Fact] public void Removing_Style_Should_Detach_From_Control() { - using (UnitTestApplication.Start(TestServices.RealStyler)) + var border = new Border(); + var root = new TestRoot { - var border = new Border(); - var root = new TestRoot - { - Styles = - { - new Style(x => x.OfType()) + Styles = + { + new Style(x => x.OfType()) + { + Setters = { - Setters = - { - new Setter(Border.BorderThicknessProperty, new Thickness(4)), - } + new Setter(Border.BorderThicknessProperty, new Thickness(4)), } - }, - Child = border, - }; + } + }, + Child = border, + }; - root.Measure(Size.Infinity); - Assert.Equal(new Thickness(4), border.BorderThickness); + root.Measure(Size.Infinity); + Assert.Equal(new Thickness(4), border.BorderThickness); - root.Styles.RemoveAt(0); - Assert.Equal(new Thickness(0), border.BorderThickness); - } + root.Styles.RemoveAt(0); + Assert.Equal(new Thickness(0), border.BorderThickness); } [Fact] public void Adding_Style_Should_Attach_To_Control() { - using (UnitTestApplication.Start(TestServices.RealStyler)) + var border = new Border(); + var root = new TestRoot { - var border = new Border(); - var root = new TestRoot + Styles = { - Styles = + new Style(x => x.OfType()) { - new Style(x => x.OfType()) + Setters = { - Setters = - { - new Setter(Border.BorderThicknessProperty, new Thickness(4)), - } + new Setter(Border.BorderThicknessProperty, new Thickness(4)), } - }, - Child = border, - }; + } + }, + Child = border, + }; - root.Measure(Size.Infinity); - Assert.Equal(new Thickness(4), border.BorderThickness); + root.Measure(Size.Infinity); + Assert.Equal(new Thickness(4), border.BorderThickness); - root.Styles.Add(new Style(x => x.OfType()) + root.Styles.Add(new Style(x => x.OfType()) + { + Setters = { - Setters = - { - new Setter(Border.BorderThicknessProperty, new Thickness(6)), - } - }); + new Setter(Border.BorderThicknessProperty, new Thickness(6)), + } + }); - root.Measure(Size.Infinity); - Assert.Equal(new Thickness(6), border.BorderThickness); - } + root.Measure(Size.Infinity); + Assert.Equal(new Thickness(6), border.BorderThickness); } [Fact] public void Removing_Style_With_Nested_Style_Should_Detach_From_Control() { - using (UnitTestApplication.Start(TestServices.RealStyler)) + var border = new Border(); + var root = new TestRoot { - var border = new Border(); - var root = new TestRoot + Styles = { - Styles = + new Styles { - new Styles + new Style(x => x.OfType()) { - new Style(x => x.OfType()) + Setters = { - Setters = - { - new Setter(Border.BorderThicknessProperty, new Thickness(4)), - } + new Setter(Border.BorderThicknessProperty, new Thickness(4)), } } - }, - Child = border, - }; + } + }, + Child = border, + }; - root.Measure(Size.Infinity); - Assert.Equal(new Thickness(4), border.BorderThickness); + root.Measure(Size.Infinity); + Assert.Equal(new Thickness(4), border.BorderThickness); - root.Styles.RemoveAt(0); - Assert.Equal(new Thickness(0), border.BorderThickness); - } + root.Styles.RemoveAt(0); + Assert.Equal(new Thickness(0), border.BorderThickness); } - + [Fact] public void Adding_Nested_Style_Should_Attach_To_Control() { - using (UnitTestApplication.Start(TestServices.RealStyler)) + var border = new Border(); + var root = new TestRoot { - var border = new Border(); - var root = new TestRoot + Styles = { - Styles = + new Styles { - new Styles + new Style(x => x.OfType()) { - new Style(x => x.OfType()) + Setters = { - Setters = - { - new Setter(Border.BorderThicknessProperty, new Thickness(4)), - } + new Setter(Border.BorderThicknessProperty, new Thickness(4)), } } - }, - Child = border, - }; + } + }, + Child = border, + }; - root.Measure(Size.Infinity); - Assert.Equal(new Thickness(4), border.BorderThickness); + root.Measure(Size.Infinity); + Assert.Equal(new Thickness(4), border.BorderThickness); - ((Styles)root.Styles[0]).Add(new Style(x => x.OfType()) + ((Styles)root.Styles[0]).Add(new Style(x => x.OfType()) + { + Setters = { - Setters = - { - new Setter(Border.BorderThicknessProperty, new Thickness(6)), - } - }); + new Setter(Border.BorderThicknessProperty, new Thickness(6)), + } + }); - root.Measure(Size.Infinity); - Assert.Equal(new Thickness(6), border.BorderThickness); - } + root.Measure(Size.Infinity); + Assert.Equal(new Thickness(6), border.BorderThickness); } [Fact] public void Removing_Nested_Style_Should_Detach_From_Control() { - using (UnitTestApplication.Start(TestServices.RealStyler)) + var border = new Border(); + var root = new TestRoot { - var border = new Border(); - var root = new TestRoot + Styles = { - Styles = + new Styles { - new Styles + new Style(x => x.OfType()) { - new Style(x => x.OfType()) + Setters = { - Setters = - { - new Setter(Border.BorderThicknessProperty, new Thickness(4)), - } - }, - new Style(x => x.OfType()) + new Setter(Border.BorderThicknessProperty, new Thickness(4)), + } + }, + new Style(x => x.OfType()) + { + Setters = { - Setters = - { - new Setter(Border.BorderThicknessProperty, new Thickness(6)), - } - }, + new Setter(Border.BorderThicknessProperty, new Thickness(6)), + } + }, + } + }, + Child = border, + }; + + root.Measure(Size.Infinity); + Assert.Equal(new Thickness(6), border.BorderThickness); + + ((Styles)root.Styles[0]).RemoveAt(1); + + root.Measure(Size.Infinity); + Assert.Equal(new Thickness(4), border.BorderThickness); + } + + [Fact] + public void Adding_Style_With_No_Setters_Or_Animations_Should_Not_Invalidate_Styles() + { + var border = new Border(); + var root = new TestRoot + { + Styles = + { + new Style(x => x.OfType()) + { + Setters = + { + new Setter(Border.BorderThicknessProperty, new Thickness(4)), + } } }, - Child = border, - }; + Child = border, + }; - root.Measure(Size.Infinity); - Assert.Equal(new Thickness(6), border.BorderThickness); + root.Measure(Size.Infinity); + Assert.Equal(new Thickness(4), border.BorderThickness); - ((Styles)root.Styles[0]).RemoveAt(1); + root.Styles.Add(new Style(x => x.OfType())); - root.Measure(Size.Infinity); - Assert.Equal(new Thickness(4), border.BorderThickness); - } + Assert.Equal(new Thickness(4), border.BorderThickness); + } + + [Fact] + public void Invalidating_Styles_Should_Detach_Activator() + { + Style style = new Style(x => x.OfType().Class("foo")) + { + Setters = + { + new Setter(Class1.FooProperty, "Foo"), + }, + }; + + var target = new Class1(); + + StyleHelpers.TryAttach(style, target); + + Assert.Equal(1, target.Classes.ListenerCount); + + target.InvalidateStyles(recurse: false); + + Assert.Equal(0, target.Classes.ListenerCount); } [Fact] @@ -771,6 +867,56 @@ namespace Avalonia.Base.UnitTests.Styling Assert.Throws(() => parent.Children.Add(nested)); } + [Fact] + public void Animations_Should_Be_Activated_And_Deactivated() + { + Style style = new Style(x => x.OfType().Class("foo")) + { + Animations = + { + new Avalonia.Animation.Animation + { + Duration = TimeSpan.FromSeconds(1), + Children = + { + new KeyFrame + { + Setters = + { + new Setter { Property = Class1.DoubleProperty, Value = 5.0 } + }, + }, + new KeyFrame + { + Setters = + { + new Setter { Property = Class1.DoubleProperty, Value = 10.0 } + }, + Cue = new Cue(1d) + } + }, + } + } + }; + + var clock = new TestClock(); + var target = new Class1 { Clock = clock }; + + StyleHelpers.TryAttach(style, target); + + Assert.Equal(0.0, target.Double); + + target.Classes.Add("foo"); + clock.Step(TimeSpan.Zero); + Assert.Equal(5.0, target.Double); + + clock.Step(TimeSpan.FromSeconds(0.5)); + Assert.Equal(7.5, target.Double); + + target.Classes.Remove("foo"); + Assert.Equal(0.0, target.Double); + } + private class Class1 : Control { public static readonly StyledProperty FooProperty = @@ -779,6 +925,9 @@ namespace Avalonia.Base.UnitTests.Styling public static readonly StyledProperty ChildProperty = AvaloniaProperty.Register(nameof(Child)); + public static readonly StyledProperty DoubleProperty = + AvaloniaProperty.Register(nameof(Double)); + public string Foo { get { return GetValue(FooProperty); } @@ -791,6 +940,12 @@ namespace Avalonia.Base.UnitTests.Styling set => SetValue(ChildProperty, value); } + public double Double + { + get => GetValue(DoubleProperty); + set => SetValue(DoubleProperty, value); + } + protected override Size MeasureOverride(Size availableSize) { throw new NotImplementedException(); diff --git a/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests.cs b/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests.cs index bb4d590060..65fe50b545 100644 --- a/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests.cs @@ -6,6 +6,7 @@ using Avalonia.Controls; using Avalonia.Controls.Templates; using Avalonia.Data; using Avalonia.LogicalTree; +using Avalonia.Media; using Avalonia.Styling; using Avalonia.UnitTests; using Moq; @@ -35,20 +36,6 @@ namespace Avalonia.Base.UnitTests.Styling Assert.Equal(parent, target.InheritanceParent); } - [Fact] - public void Setting_Parent_Should_Not_Set_InheritanceParent_If_Already_Set() - { - var parent = new Decorator(); - var inheritanceParent = new Decorator(); - var target = new TestControl(); - - ((ISetInheritanceParent)target).SetParent(inheritanceParent); - parent.Child = target; - - Assert.Equal(parent, target.Parent); - Assert.Equal(inheritanceParent, target.InheritanceParent); - } - [Fact] public void InheritanceParent_Should_Be_Cleared_When_Removed_From_Parent() { @@ -61,20 +48,6 @@ namespace Avalonia.Base.UnitTests.Styling Assert.Null(target.InheritanceParent); } - [Fact] - public void InheritanceParent_Should_Be_Cleared_When_Removed_From_Parent_When_Has_Different_InheritanceParent() - { - var parent = new Decorator(); - var inheritanceParent = new Decorator(); - var target = new TestControl(); - - ((ISetInheritanceParent)target).SetParent(inheritanceParent); - parent.Child = target; - parent.Child = null; - - Assert.Null(target.InheritanceParent); - } - [Fact] public void Adding_Element_With_Null_Parent_To_Logical_Tree_Should_Throw() { @@ -126,7 +99,7 @@ namespace Avalonia.Base.UnitTests.Styling Assert.True(childRaised); Assert.True(grandchildRaised); } - + [Fact] public void AttachedToLogicalTree_Should_Be_Called_Before_Parent_Change_Signalled() { @@ -276,63 +249,67 @@ namespace Avalonia.Base.UnitTests.Styling } [Fact] - public void Adding_Tree_To_IStyleRoot_Should_Style_Controls() + public void Adding_Tree_To_Root_Should_Style_Controls() { - using (AvaloniaLocator.EnterScope()) + var root = new TestRoot { - var root = new TestRoot(); - var parent = new Border(); - var child = new Border(); - var grandchild = new Control(); - var styler = new Mock(); - - AvaloniaLocator.CurrentMutable.Bind().ToConstant(styler.Object); + Styles = + { + new Style(x => x.Is()) + { + Setters = { new Setter(Control.TagProperty, "foo") } + } + } + }; - parent.Child = child; - child.Child = grandchild; + var grandchild = new Control(); + var child = new Border { Child = grandchild }; + var parent = new Border { Child = child }; - styler.Verify(x => x.ApplyStyles(It.IsAny()), Times.Never()); + Assert.Null(parent.Tag); + Assert.Null(child.Tag); + Assert.Null(grandchild.Tag); - root.Child = parent; + root.Child = parent; - styler.Verify(x => x.ApplyStyles(parent), Times.Once()); - styler.Verify(x => x.ApplyStyles(child), Times.Once()); - styler.Verify(x => x.ApplyStyles(grandchild), Times.Once()); - } + Assert.Equal("foo", parent.Tag); + Assert.Equal("foo", child.Tag); + Assert.Equal("foo", grandchild.Tag); } [Fact] public void Styles_Not_Applied_Until_Initialization_Finished() { - using (AvaloniaLocator.EnterScope()) + var root = new TestRoot { - var root = new TestRoot(); - var child = new Border(); - var styler = new Mock(); + Styles = + { + new Style(x => x.Is()) + { + Setters = { new Setter(Control.TagProperty, "foo") } + } + } + }; - AvaloniaLocator.CurrentMutable.Bind().ToConstant(styler.Object); + var child = new Border(); - ((ISupportInitialize)child).BeginInit(); - root.Child = child; - styler.Verify(x => x.ApplyStyles(It.IsAny()), Times.Never()); + ((ISupportInitialize)child).BeginInit(); + root.Child = child; + Assert.Null(child.Tag); - ((ISupportInitialize)child).EndInit(); - styler.Verify(x => x.ApplyStyles(child), Times.Once()); - } + ((ISupportInitialize)child).EndInit(); + Assert.Equal("foo", child.Tag); } [Fact] public void Name_Cannot_Be_Set_After_Added_To_Logical_Tree() { - using (AvaloniaLocator.EnterScope()) - { - var root = new TestRoot(); - var child = new Border(); + var root = new TestRoot(); + var child = new Border(); - root.Child = child; + root.Child = child; - Assert.Throws(() => child.Name = "foo"); - } + Assert.Throws(() => child.Name = "foo"); } [Fact] @@ -351,22 +328,28 @@ namespace Avalonia.Base.UnitTests.Styling } [Fact] - public void StyleInstance_Is_Disposed_When_Control_Removed_From_Logical_Tree() + public void Style_Is_Removed_When_Control_Removed_From_Logical_Tree() { - using (AvaloniaLocator.EnterScope()) + var app = UnitTestApplication.Start(); + var target = new Border(); + var root = new TestRoot { - var root = new TestRoot(); - var child = new Border(); - - root.Child = child; - - var styleInstance = new Mock(); - ((IStyleable)child).StyleApplied(styleInstance.Object); - - root.Child = null; + Styles = + { + new Style(x => x.OfType()) + { + Setters = + { + new Setter(Border.BackgroundProperty, Brushes.Red), + } + } + }, + Child = target, + }; - styleInstance.Verify(x => x.Dispose(), Times.Once); - } + Assert.Equal(Brushes.Red, target.Background); + root.Child = null; + Assert.Null(target.Background); } [Fact] @@ -474,7 +457,7 @@ namespace Avalonia.Base.UnitTests.Styling root.DataContext = "foo"; Assert.Equal( - new[] + new[] { "begin root", "begin a1", @@ -489,6 +472,57 @@ namespace Avalonia.Base.UnitTests.Styling called); } + + [Fact] + public void DataContext_Notifications_Should_Be_Called_In_Correct_Order_When_Setting_Parent() + { + var root = new TestStackPanel + { + Name = "root", + DataContext = "foo", + }; + + var children = new[] + { + new TestControl + { + Name = "a1", + Child = new TestControl + { + Name = "b1", + } + }, + new TestControl + { + Name = "a2", + DataContext = "foo", + }, + }; + + var called = new List(); + + foreach (IDataContextEvents c in new[] { children[0], children[0].Child, children[1] }) + { + c.DataContextBeginUpdate += (s, e) => called.Add("begin " + ((StyledElement)s).Name); + c.DataContextChanged += (s, e) => called.Add("changed " + ((StyledElement)s).Name); + c.DataContextEndUpdate += (s, e) => called.Add("end " + ((StyledElement)s).Name); + } + + root.Children.AddRange(children); + + Assert.Equal( + new[] + { + "begin a1", + "begin b1", + "changed a1", + "changed b1", + "end b1", + "end a1", + }, + called); + } + [Fact] public void Resources_Owner_Is_Set() { diff --git a/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Theming.cs b/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Theming.cs index 45188de6cb..b5524affb7 100644 --- a/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Theming.cs +++ b/tests/Avalonia.Base.UnitTests/Styling/StyledElementTests_Theming.cs @@ -19,12 +19,11 @@ public class StyledElementTests_Theming [Fact] public void Theme_Is_Applied_When_Attached_To_Logical_Tree() { - using var app = UnitTestApplication.Start(TestServices.RealStyler); var target = CreateTarget(); Assert.Null(target.Template); - var root = CreateRoot(target); + CreateRoot(target); Assert.NotNull(target.Template); var border = Assert.IsType(target.VisualChild); @@ -37,7 +36,6 @@ public class StyledElementTests_Theming [Fact] public void Theme_Is_Applied_To_Derived_Class_When_Attached_To_Logical_Tree() { - using var app = UnitTestApplication.Start(TestServices.RealStyler); var target = new DerivedThemedControl { Theme = CreateTheme(), @@ -45,7 +43,7 @@ public class StyledElementTests_Theming Assert.Null(target.Template); - var root = CreateRoot(target); + CreateRoot(target); Assert.NotNull(target.Template); var border = Assert.IsType(target.VisualChild); @@ -58,9 +56,8 @@ public class StyledElementTests_Theming [Fact] public void Theme_Is_Detached_When_Theme_Property_Cleared() { - using var app = UnitTestApplication.Start(TestServices.RealStyler); var target = CreateTarget(); - var root = CreateRoot(target); + CreateRoot(target); Assert.NotNull(target.Template); @@ -69,10 +66,48 @@ public class StyledElementTests_Theming } [Fact] - public void Theme_Is_Detached_From_Template_Controls_When_Theme_Property_Cleared() + public void Setting_Explicit_Theme_Detaches_Default_Theme() + { + var target = new ThemedControl(); + var root = new TestRoot + { + Resources = { { typeof(ThemedControl), CreateTheme() } }, + Child = target, + }; + + root.LayoutManager.ExecuteInitialLayoutPass(); + + Assert.Equal("theme", target.Tag); + + target.Theme = new ControlTheme(typeof(ThemedControl)) + { + Setters = + { + new Setter(ThemedControl.BackgroundProperty, Brushes.Yellow), + } + }; + + root.LayoutManager.ExecuteLayoutPass(); + + Assert.Null(target.Tag); + Assert.Equal(Brushes.Yellow, target.Background); + } + + [Fact] + public void Unrelated_Styles_Are_Not_Detached_When_Theme_Property_Cleared() { - using var app = UnitTestApplication.Start(TestServices.RealStyler); + var target = CreateTarget(); + CreateRoot(target, createAdditionalStyles: true); + Assert.Equal("style", target.Tag); + + target.Theme = null; + Assert.Equal("style", target.Tag); + } + + [Fact] + public void TemplatedParent_Theme_Is_Detached_From_Template_Controls_When_Theme_Property_Cleared() + { var theme = new ControlTheme { TargetType = typeof(ThemedControl), @@ -98,14 +133,118 @@ public class StyledElementTests_Theming target.Theme = null; - Assert.IsType(target.VisualChild); + Assert.Same(canvas, target.VisualChild); Assert.Null(canvas.Background); } + [Fact] + public void Primary_Theme_Is_Not_Detached_From_Template_Controls_When_Theme_Property_Cleared() + { + var templatedParentTheme = new ControlTheme + { + TargetType = typeof(ThemedControl), + Children = + { + new Style(x => x.Nesting().Template().OfType